From 21fc61c73c3903c4c312d0802da01ec2b323d174 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 01:07:57 -0500 Subject: 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 --- mm/shmem.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'mm') diff --git a/mm/shmem.c b/mm/shmem.c index 9187eee..64bf5ac 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2444,7 +2444,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s int len; struct inode *inode; struct page *page; - char *kaddr; struct shmem_inode_info *info; len = strlen(symname) + 1; @@ -2483,9 +2482,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s } inode->i_mapping->a_ops = &shmem_aops; inode->i_op = &shmem_symlink_inode_operations; - kaddr = kmap_atomic(page); - memcpy(kaddr, symname, len); - kunmap_atomic(kaddr); + inode_nohighmem(inode); + memcpy(page_address(page), symname, len); SetPageUptodate(page); set_page_dirty(page); unlock_page(page); @@ -2506,13 +2504,12 @@ static const char *shmem_follow_link(struct dentry *dentry, void **cookie) return ERR_PTR(error); unlock_page(page); *cookie = page; - return kmap(page); + return page_address(page); } static void shmem_put_link(struct inode *unused, void *cookie) { struct page *page = cookie; - kunmap(page); mark_page_accessed(page); page_cache_release(page); } -- cgit v1.1 From 6b2553918d8b4e6de9853fd6315bec7271a2e592 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 10:20:54 -0500 Subject: replace ->follow_link() with new method that could stay in RCU mode new method: ->get_link(); replacement of ->follow_link(). The differences are: * inode and dentry are passed separately * might be called both in RCU and non-RCU mode; the former is indicated by passing it a NULL dentry. * when called that way it isn't allowed to block and should return ERR_PTR(-ECHILD) if it needs to be called in non-RCU mode. It's a flagday change - the old method is gone, all in-tree instances converted. Conversion isn't hard; said that, so far very few instances do not immediately bail out when called in RCU mode. That'll change in the next commits. Signed-off-by: Al Viro --- mm/shmem.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'mm') diff --git a/mm/shmem.c b/mm/shmem.c index 64bf5ac..684dbc3 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2496,10 +2496,14 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static const char *shmem_follow_link(struct dentry *dentry, void **cookie) +static const char *shmem_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { struct page *page = NULL; - int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL); + int error; + if (!dentry) + return ERR_PTR(-ECHILD); + error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); if (error) return ERR_PTR(error); unlock_page(page); @@ -2656,7 +2660,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, @@ -2667,7 +2671,7 @@ static const struct inode_operations shmem_short_symlink_operations = { static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = shmem_follow_link, + .get_link = shmem_get_link, .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, -- cgit v1.1 From 6a6c99049635473b64c384135a6906a10df2c916 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 10:54:32 -0500 Subject: teach shmem_get_link() to work in RCU mode Signed-off-by: Al Viro --- mm/shmem.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'mm') diff --git a/mm/shmem.c b/mm/shmem.c index 684dbc3..0605716 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2501,12 +2501,20 @@ static const char *shmem_get_link(struct dentry *dentry, { struct page *page = NULL; int error; - if (!dentry) - return ERR_PTR(-ECHILD); - error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); - if (error) - return ERR_PTR(error); - unlock_page(page); + if (!dentry) { + page = find_get_page(inode->i_mapping, 0); + if (!page) + return ERR_PTR(-ECHILD); + if (!PageUptodate(page)) { + put_page(page); + return ERR_PTR(-ECHILD); + } + } else { + error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); + if (error) + return ERR_PTR(error); + unlock_page(page); + } *cookie = page; return page_address(page); } -- cgit v1.1 From fceef393a538134f03b778c5d2519e670269342f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 29 Dec 2015 15:58:39 -0500 Subject: switch ->get_link() to delayed_call, kill ->put_link() Signed-off-by: Al Viro --- mm/shmem.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'mm') diff --git a/mm/shmem.c b/mm/shmem.c index 0605716..bab9041 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2496,8 +2496,15 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } +static void shmem_put_link(void *arg) +{ + mark_page_accessed(arg); + put_page(arg); +} + static const char *shmem_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct page *page = NULL; int error; @@ -2515,17 +2522,10 @@ static const char *shmem_get_link(struct dentry *dentry, return ERR_PTR(error); unlock_page(page); } - *cookie = page; + set_delayed_call(done, shmem_put_link, page); return page_address(page); } -static void shmem_put_link(struct inode *unused, void *cookie) -{ - struct page *page = cookie; - mark_page_accessed(page); - page_cache_release(page); -} - #ifdef CONFIG_TMPFS_XATTR /* * Superblocks without xattr inode operations may get some security.* xattr @@ -2680,7 +2680,6 @@ static const struct inode_operations shmem_short_symlink_operations = { static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, .get_link = shmem_get_link, - .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, -- cgit v1.1