diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 6b402e685f1e..3ed98d66b152 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -28,9 +28,9 @@ const struct file_operations sysv_dir_operations = { .fsync = generic_file_fsync, }; -inline void dir_put_page(struct page *page) +inline void dir_put_page(struct page *page, void *page_addr) { - kunmap(page); + kunmap_local((void *)((unsigned long)page_addr & PAGE_MASK)); put_page(page); } @@ -57,15 +57,21 @@ static int sysv_handle_dirsync(struct inode *dir) return err; } +/* + * Calls to dir_get_page()/dir_put_page() must be nested according to the + * rules documented in mm/highmem.rst. + * + * NOTE: sysv_find_entry() and sysv_dotdot() act as calls to dir_get_page() + * and must be treated accordingly for nesting purposes. + */ static void *dir_get_page(struct inode *dir, unsigned long n, struct page **p) { struct address_space *mapping = dir->i_mapping; struct page *page = read_mapping_page(mapping, n, NULL); if (IS_ERR(page)) return ERR_CAST(page); - kmap(page); *p = page; - return page_address(page); + return kmap_local_page(page); } static int sysv_readdir(struct file *file, struct dir_context *ctx) @@ -103,11 +109,11 @@ static int sysv_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit(ctx, name, strnlen(name,SYSV_NAMELEN), fs16_to_cpu(SYSV_SB(sb), de->inode), DT_UNKNOWN)) { - dir_put_page(page); + dir_put_page(page, kaddr); return 0; } } - dir_put_page(page); + dir_put_page(page, kaddr); } return 0; } @@ -130,6 +136,11 @@ static inline int namecompare(int len, int maxlen, * returns the cache buffer in which the entry was found, and the entry * itself (as a parameter - res_dir). It does NOT read the inode of the * entry - you'll have to do that yourself if you want to. + * + * On Success dir_put_page() should be called on *res_page. + * + * sysv_find_entry() acts as a call to dir_get_page() and must be treated + * accordingly for nesting purposes. */ struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page) { @@ -161,7 +172,7 @@ struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_ name, de->name)) goto found; } - dir_put_page(page); + dir_put_page(page, kaddr); } if (++n >= npages) @@ -204,7 +215,7 @@ int sysv_add_link(struct dentry *dentry, struct inode *inode) goto out_page; de++; } - dir_put_page(page); + dir_put_page(page, kaddr); } BUG(); return -EINVAL; @@ -223,7 +234,7 @@ got_it: mark_inode_dirty(dir); err = sysv_handle_dirsync(dir); out_page: - dir_put_page(page); + dir_put_page(page, kaddr); return err; out_unlock: unlock_page(page); @@ -241,7 +252,7 @@ int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) BUG_ON(err); de->inode = 0; dir_commit_chunk(page, pos, SYSV_DIRSIZE); - dir_put_page(page); + dir_put_page(page, de); inode->i_ctime = inode->i_mtime = current_time(inode); mark_inode_dirty(inode); return sysv_handle_dirsync(inode); @@ -261,9 +272,7 @@ int sysv_make_empty(struct inode *inode, struct inode *dir) unlock_page(page); goto fail; } - kmap(page); - - base = (char*)page_address(page); + base = kmap_local_page(page); memset(base, 0, PAGE_SIZE); de = (struct sysv_dir_entry *) base; @@ -273,7 +282,7 @@ int sysv_make_empty(struct inode *inode, struct inode *dir) de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), dir->i_ino); strcpy(de->name,".."); - kunmap(page); + kunmap_local(base); dir_commit_chunk(page, 0, 2 * SYSV_DIRSIZE); err = sysv_handle_dirsync(inode); fail: @@ -289,10 +298,10 @@ int sysv_empty_dir(struct inode * inode) struct super_block *sb = inode->i_sb; struct page *page = NULL; unsigned long i, npages = dir_pages(inode); + char *kaddr; for (i = 0; i < npages; i++) { - char *kaddr; - struct sysv_dir_entry * de; + struct sysv_dir_entry *de; kaddr = dir_get_page(inode, i, &page); if (IS_ERR(kaddr)) @@ -316,12 +325,12 @@ int sysv_empty_dir(struct inode * inode) if (de->name[1] != '.' || de->name[2]) goto not_empty; } - dir_put_page(page); + dir_put_page(page, kaddr); } return 1; not_empty: - dir_put_page(page); + dir_put_page(page, kaddr); return 0; } @@ -338,12 +347,19 @@ void sysv_set_link(struct sysv_dir_entry *de, struct page *page, BUG_ON(err); de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); dir_commit_chunk(page, pos, SYSV_DIRSIZE); - dir_put_page(page); + dir_put_page(page, de); dir->i_mtime = dir->i_ctime = current_time(dir); mark_inode_dirty(dir); sysv_handle_dirsync(inode); } +/* + * Calls to dir_get_page()/dir_put_page() must be nested according to the + * rules documented in mm/highmem.rst. + * + * sysv_dotdot() acts as a call to dir_get_page() and must be treated + * accordingly for nesting purposes. + */ struct sysv_dir_entry *sysv_dotdot(struct inode *dir, struct page **p) { struct sysv_dir_entry *de = dir_get_page(dir, 0, p); @@ -362,7 +378,7 @@ ino_t sysv_inode_by_name(struct dentry *dentry) if (de) { res = fs16_to_cpu(SYSV_SB(dentry->d_sb), de->inode); - dir_put_page(page); + dir_put_page(page, de); } return res; } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 981c1d76f342..371cf9012052 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -251,9 +251,9 @@ static int sysv_rename(struct user_namespace *mnt_userns, struct inode *old_dir, out_dir: if (dir_de) - dir_put_page(dir_page); + dir_put_page(dir_page, dir_de); out_old: - dir_put_page(old_page); + dir_put_page(old_page, old_de); out: return err; } diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index b250ac1dd348..50f19bfd8d10 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -148,7 +148,7 @@ extern void sysv_destroy_icache(void); /* dir.c */ -extern void dir_put_page(struct page *page); +extern void dir_put_page(struct page *page, void *vaddr); extern struct sysv_dir_entry *sysv_find_entry(struct dentry *, struct page **); extern int sysv_add_link(struct dentry *, struct inode *); extern int sysv_delete_entry(struct sysv_dir_entry *, struct page *);