diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-11-17 01:07:57 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-12-08 22:41:36 -0500 |
commit | 21fc61c73c3903c4c312d0802da01ec2b323d174 (patch) | |
tree | a26c29e3fbb766f00b023724e9e5b8b9bba9b879 | |
parent | aa80deab33a8fb180e718f5e45514db19aade165 (diff) | |
download | linux-21fc61c73c3903c4c312d0802da01ec2b323d174.tar.gz linux-21fc61c73c3903c4c312d0802da01ec2b323d174.tar.bz2 linux-21fc61c73c3903c4c312d0802da01ec2b323d174.zip |
don't put symlink bodies in pagecache into highmem
kmap() in page_follow_link_light() needed to go - allowing to hold
an arbitrary number of kmaps for long is a great way to deadlocking
the system.
new helper (inode_nohighmem(inode)) needs to be used for pagecache
symlinks inodes; done for all in-tree cases. page_follow_link_light()
instrumented to yell about anything missed.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
57 files changed, 81 insertions, 46 deletions
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index f24d1b833957..3eb7c35c9698 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -504,3 +504,8 @@ in your dentry operations instead. [mandatory] __fd_install() & fd_install() can now sleep. Callers should not hold a spinlock or other resources that do not allow a schedule. +-- +[mandatory] + any symlink that might use page_follow_link_light/page_put_link() must + have inode_nohighmem(inode) called before anything might start playing with + its pagecache. diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 17349500592d..0fdb0f5b2239 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -140,6 +140,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino) break; case ST_SOFTLINK: inode->i_mode |= S_IFLNK; + inode_nohighmem(inode); inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops; break; diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 181e05b46e72..00d3002a6780 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -344,6 +344,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) return -ENOSPC; inode->i_op = &affs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &affs_symlink_aops; inode->i_mode = S_IFLNK | 0777; mode_to_prot(inode); diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index ea5b69a18ba9..e3f9dc3ae8cc 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -14,7 +14,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page) { struct buffer_head *bh; struct inode *inode = page->mapping->host; - char *link = kmap(page); + char *link = page_address(page); struct slink_front *lf; int i, j; char c; @@ -57,12 +57,10 @@ static int affs_symlink_readpage(struct file *file, struct page *page) link[i] = '\0'; affs_brelse(bh); SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: SetPageError(page); - kunmap(page); unlock_page(page); return -EIO; } diff --git a/fs/afs/inode.c b/fs/afs/inode.c index e06f5a23352a..86cc7264c21c 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -56,6 +56,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) case AFS_FTYPE_SYMLINK: inode->i_mode = S_IFLNK | vnode->status.mode; inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); break; default: printk("kAFS: AFS vnode with undefined type\n"); diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 1c8b0dca8b50..25250fa87086 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -397,6 +397,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) } else if (S_ISLNK(inode->i_mode)) { if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &befs_symlink_aops; } else { inode->i_link = befs_ino->i_data.symlink; @@ -469,7 +470,7 @@ static int befs_symlink_readpage(struct file *unused, struct page *page) struct befs_inode_info *befs_ino = BEFS_I(inode); befs_data_stream *data = &befs_ino->i_data.ds; befs_off_t len = data->size; - char *link = kmap(page); + char *link = page_address(page); if (len == 0 || len > PAGE_SIZE) { befs_error(sb, "Long symlink with illegal length"); @@ -483,12 +484,10 @@ static int befs_symlink_readpage(struct file *unused, struct page *page) } link[len - 1] = '\0'; SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: SetPageError(page); - kunmap(page); unlock_page(page); return -EIO; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a70c5790f8f5..70f98bfde277 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3774,6 +3774,7 @@ cache_acl: break; case S_IFLNK: inode->i_op = &btrfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &btrfs_symlink_aops; break; default: @@ -9705,6 +9706,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, btrfs_free_path(path); inode->i_op = &btrfs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &btrfs_symlink_aops; inode_set_bytes(inode, name_len); btrfs_i_size_write(inode, name_len); diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 7740b1c871c1..dd6a79ef4750 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -8,6 +8,7 @@ #include <linux/coda.h> #include <linux/coda_psdev.h> +#include <linux/pagemap.h> #include "coda_linux.h" static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) @@ -35,6 +36,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) inode->i_fop = &coda_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &coda_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &coda_symlink_aops; inode->i_mapping = &inode->i_data; } else diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index ab94ef63caef..03736e20d720 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -26,7 +26,7 @@ static int coda_symlink_filler(struct file *file, struct page *page) int error; struct coda_inode_info *cii; unsigned int len = PAGE_SIZE; - char *p = kmap(page); + char *p = page_address(page); cii = ITOC(inode); @@ -34,13 +34,11 @@ static int coda_symlink_filler(struct file *file, struct page *page) if (error) goto fail; SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: SetPageError(page); - kunmap(page); unlock_page(page); return error; } diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 355c522f3585..b862bc219cd7 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -100,6 +100,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb, break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &cramfs_aops; break; default: diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 079d20306ee1..cdf0872382af 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -151,6 +151,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino) break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &efs_symlink_aops; break; case S_IFCHR: diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 75117d0dac2b..4870cc82deb0 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -13,7 +13,7 @@ static int efs_symlink_readpage(struct file *file, struct page *page) { - char *link = kmap(page); + char *link = page_address(page); struct buffer_head * bh; struct inode * inode = page->mapping->host; efs_block_t size = inode->i_size; @@ -39,12 +39,10 @@ static int efs_symlink_readpage(struct file *file, struct page *page) } link[size] = '\0'; SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: SetPageError(page); - kunmap(page); unlock_page(page); return err; } diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 73c64daa0f55..d8e9c18171a9 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1227,6 +1227,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino) inode->i_link = (char *)oi->i_data; } else { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &exofs_aops; } } else { diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c index 994e078da4bb..c20d77df2679 100644 --- a/fs/exofs/namei.c +++ b/fs/exofs/namei.c @@ -111,6 +111,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry, if (l > sizeof(oi->i_data)) { /* slow symlink */ inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &exofs_aops; memset(oi->i_data, 0, sizeof(oi->i_data)); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 0aa9bf6e6e53..338eefda70c6 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1420,6 +1420,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) sizeof(ei->i_data) - 1); } else { inode->i_op = &ext2_symlink_inode_operations; + inode_nohighmem(inode); if (test_opt(inode->i_sb, NOBH)) inode->i_mapping->a_ops = &ext2_nobh_aops; else diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3267a80dbbe2..7a2be8f7f3c3 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -183,6 +183,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry, if (l > sizeof (EXT2_I(inode)->i_data)) { /* slow symlink */ inode->i_op = &ext2_symlink_inode_operations; + inode_nohighmem(inode); if (test_opt(inode->i_sb, NOBH)) inode->i_mapping->a_ops = &ext2_nobh_aops; else diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ea433a7f4bca..b3bd912df6bf 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4283,6 +4283,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); } + inode_nohighmem(inode); } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { inode->i_op = &ext4_special_inode_operations; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a969ab39f302..f27e0c2598c5 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir, if ((disk_link.len > EXT4_N_BLOCKS * 4)) { if (!encryption_required) inode->i_op = &ext4_symlink_inode_operations; + inode_nohighmem(inode); ext4_set_aops(inode); /* * We cannot call page_symlink() with transaction started diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index abe2401ce405..0e6dc44c5ebf 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -45,7 +45,7 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook cpage = read_mapping_page(inode->i_mapping, 0, NULL); if (IS_ERR(cpage)) return ERR_CAST(cpage); - caddr = kmap(cpage); + caddr = page_address(cpage); caddr[size] = 0; } @@ -75,16 +75,12 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook /* Null-terminate the name */ if (res <= plen) paddr[res] = '\0'; - if (cpage) { - kunmap(cpage); + if (cpage) page_cache_release(cpage); - } return *cookie = paddr; errout: - if (cpage) { - kunmap(cpage); + if (cpage) page_cache_release(cpage); - } kfree(paddr); return ERR_PTR(res); } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 97e20decacb4..5528801a5baf 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -202,6 +202,7 @@ make_now: inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 2c32110f9fc0..484df6850747 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -351,6 +351,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; f2fs_lock_op(sbi); @@ -942,7 +943,7 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook cpage = read_mapping_page(inode->i_mapping, 0, NULL); if (IS_ERR(cpage)) return ERR_CAST(cpage); - caddr = kmap(cpage); + caddr = page_address(cpage); caddr[size] = 0; /* Symlink is encrypted */ @@ -982,13 +983,11 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook /* Null-terminate the name */ paddr[res] = '\0'; - kunmap(cpage); page_cache_release(cpage); return *cookie = paddr; errout: kfree(cstr.name); f2fs_fname_crypto_free_buffer(&pstr); - kunmap(cpage); page_cache_release(cpage); return ERR_PTR(res); } diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index ef73ed674a27..3e2ccade61ed 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -326,6 +326,7 @@ vxfs_iget(struct super_block *sbp, ino_t ino) } else if (S_ISLNK(ip->i_mode)) { if (!VXFS_ISIMMED(vip)) { ip->i_op = &page_symlink_inode_operations; + inode_nohighmem(ip); ip->i_mapping->a_ops = &vxfs_aops; } else { ip->i_op = &simple_symlink_inode_operations; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 6dd107d7421e..19b33f8151f1 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -403,6 +403,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) } else if (S_ISLNK(inode->i_mode)) { sbi->file_count++; inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; hip->clump_blocks = 1; } else @@ -526,6 +527,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_mapping->a_ops = &hfsplus_aops; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &hfsplus_aops; } else { init_special_inode(inode, inode->i_mode, diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 933c73780813..1f3c6d76200b 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -77,6 +77,7 @@ void hpfs_read_inode(struct inode *i) kfree(ea); i->i_mode = S_IFLNK | 0777; i->i_op = &page_symlink_inode_operations; + inode_nohighmem(i); i->i_data.a_ops = &hpfs_symlink_aops; set_nlink(i, 1); i->i_size = ea_size; diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index ae4d5a1fa4c9..506765afa1a3 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -332,6 +332,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy result->i_blocks = 1; set_nlink(result, 1); result->i_size = strlen(symlink); + inode_nohighmem(result); result->i_op = &page_symlink_inode_operations; result->i_data.a_ops = &hpfs_symlink_aops; @@ -500,7 +501,7 @@ out: static int hpfs_symlink_readpage(struct file *file, struct page *page) { - char *link = kmap(page); + char *link = page_address(page); struct inode *i = page->mapping->host; struct fnode *fnode; struct buffer_head *bh; @@ -516,14 +517,12 @@ static int hpfs_symlink_readpage(struct file *file, struct page *page) goto fail; hpfs_unlock(i->i_sb); SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; fail: hpfs_unlock(i->i_sb); SetPageError(page); - kunmap(page); unlock_page(page); return err; } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index de4bdfac0cec..d8f51ee8126b 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -760,6 +760,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); break; } lockdep_annotate_inode_mutex_key(inode); diff --git a/fs/inode.c b/fs/inode.c index 1be5f9003eb3..5bb85a064ce7 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2028,3 +2028,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags, new_flags) != old_flags)); } EXPORT_SYMBOL(inode_set_flags); + +void inode_nohighmem(struct inode *inode) +{ + mapping_set_gfp_mask(inode->i_mapping, GFP_USER); +} +EXPORT_SYMBOL(inode_nohighmem); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d67a16f2a45d..61abdc4920da 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1417,6 +1417,7 @@ static int isofs_read_inode(struct inode *inode, int relocated) inode->i_fop = &isofs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_data.a_ops = &isofs_symlink_aops; } else /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 735d7522a3a9..5384ceb35b1c 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -687,7 +687,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) struct inode *inode = page->mapping->host; struct iso_inode_info *ei = ISOFS_I(inode); struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); - char *link = kmap(page); + char *link = page_address(page); unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); struct buffer_head *bh; char *rpnt = link; @@ -774,7 +774,6 @@ repeat: brelse(bh); *rpnt = '\0'; SetPageUptodate(page); - kunmap(page); unlock_page(page); return 0; @@ -791,7 +790,6 @@ fail: brelse(bh); error: SetPageError(page); - kunmap(page); unlock_page(page); return -EIO; } diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 41aa3ca6a6a4..9d9bae63ae2a 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -60,6 +60,7 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino) } else if (S_ISLNK(inode->i_mode)) { if (inode->i_size >= IDATASIZE) { inode->i_op = &page_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &jfs_aops; } else { inode->i_op = &jfs_fast_symlink_inode_operations; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 9d7551f5c32a..701f89370de7 100644 --- a/fs/jfs/namei.c +++ b/ |