From: Josef Bacik <jbacik@redhat.com> Date: Tue, 10 Feb 2009 13:49:34 -0500 Subject: [fs] fix symlink allocation context Message-id: 1234291777-15344-22-git-send-email-jbacik@redhat.com O-Subject: [PATCH 21/24] [RHEL 5.4] fs: fix symlink allocation context Bugzilla: 445433 RH-Acked-by: Steven Whitehouse <swhiteho@redhat.com> RH-Acked-by: Jeff Layton <jlayton@redhat.com> RH-Acked-by: Jeff Layton <jlayton@redhat.com> This is a backport of upstream commit 54566b2c1594c2326a645a3551f9d989f7ba3c5e and is in reference to bz 445433. This fixes a problem with the new aops where page_symlink couldn't pass a GFP_NOFS type mask where the allocations occured, which could cause a deadlock if we need to reclaim memory. Signed-off-by: Josef Bacik <jbacik@redhat.com> diff --git a/fs/buffer.c b/fs/buffer.c index b3f5428..0ba5cce 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2128,7 +2128,7 @@ int block_write_begin(struct file *file, struct address_space *mapping, page = *pagep; if (page == NULL) { ownpage = 1; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { status = -ENOMEM; goto out; diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 4fbfe1f..cb9156b 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -732,7 +732,7 @@ int gfs2_write_begin(struct file *file, struct address_space *mapping, goto out_trans_fail; error = -ENOMEM; - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); *pagep = page; if (unlikely(!page)) goto out_endtrans; diff --git a/fs/libfs.c b/fs/libfs.c index 87ef1dc..7c12fa4 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -359,7 +359,7 @@ int simple_write_begin(struct file *file, struct address_space *mapping, index = pos >> PAGE_CACHE_SHIFT; from = pos & (PAGE_CACHE_SIZE - 1); - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); if (!page) return -ENOMEM; diff --git a/fs/namei.c b/fs/namei.c index 33ffdd6..f9d5904 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2686,18 +2686,23 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) } } -int __page_symlink(struct inode *inode, const char *symname, int len, - gfp_t gfp_mask) +/* + * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS + */ +int __page_symlink(struct inode *inode, const char *symname, int len, int nofs) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; int err; char *kaddr; + unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE; + if (nofs) + flags |= AOP_FLAG_NOFS; retry: err = pagecache_write_begin(NULL, mapping, 0, len-1, - AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); + flags, &page, &fsdata); if (err) goto fail; @@ -2721,7 +2726,7 @@ fail: int page_symlink(struct inode *inode, const char *symname, int len) { return __page_symlink(inode, symname, len, - mapping_gfp_mask(inode->i_mapping)); + !(mapping_gfp_mask(inode->i_mapping) & __GFP_FS)); } struct inode_operations page_symlink_inode_operations = { diff --git a/include/linux/fs.h b/include/linux/fs.h index e244844..5f5f524 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -399,6 +399,9 @@ enum positive_aop_returns { #define AOP_FLAG_UNINTERRUPTIBLE 0x0001 /* will not do a short write */ #define AOP_FLAG_CONT_EXPAND 0x0002 /* called from cont_expand */ +#define AOP_FLAG_NOFS 0x0004 /* used by filesystem to direct + * helper code (eg buffer layer) + * to clear GFP_FS from alloc */ /* * oh the beauties of C type declarations. @@ -1956,7 +1959,7 @@ extern int page_readlink(struct dentry *, char __user *, int); extern void *page_follow_link_light(struct dentry *, struct nameidata *); extern void page_put_link(struct dentry *, struct nameidata *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, - gfp_t gfp_mask); + int nofs); extern int page_symlink(struct inode *inode, const char *symname, int len); extern struct inode_operations page_symlink_inode_operations; extern int generic_readlink(struct dentry *, char __user *, int); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index f4a0aa5..78f2264 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -89,7 +89,8 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start, unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, int tag, unsigned int nr_pages, struct page **pages); -struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index); +struct page *grab_cache_page_write_begin(struct address_space *mapping, + pgoff_t index, unsigned flags); /* * Returns locked page at given index in given cache, creating it if needed. diff --git a/mm/filemap.c b/mm/filemap.c index c42c4da..15282de 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2141,7 +2141,7 @@ int pagecache_write_begin(struct file *file, struct address_space *mapping, struct inode *inode = mapping->host; struct page *page; again: - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, flags); *pagep = page; if (!page) return -ENOMEM; @@ -2257,19 +2257,24 @@ EXPORT_SYMBOL(generic_file_direct_write); * Find or create a page at the given pagecache position. Return the locked * page. This function is specifically for buffered writes. */ -struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index) +struct page *grab_cache_page_write_begin(struct address_space *mapping, + pgoff_t index, unsigned flags) { int status; struct page *page; + gfp_t gfp_notmask = 0; + if (flags & AOP_FLAG_NOFS) + gfp_notmask = __GFP_FS; repeat: page = find_lock_page(mapping, index); if (likely(page)) return page; - page = page_cache_alloc(mapping); + page = __page_cache_alloc(mapping_gfp_mask(mapping) & ~gfp_notmask); if (!page) return NULL; - status = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL); + status = add_to_page_cache_lru(page, mapping, index, + GFP_KERNEL & ~gfp_notmask); if (unlikely(status)) { page_cache_release(page); if (status == -EEXIST) @@ -2278,7 +2283,7 @@ repeat: } return page; } -EXPORT_SYMBOL(__grab_cache_page); +EXPORT_SYMBOL(grab_cache_page_write_begin); static ssize_t generic_perform_write_2copy(struct file *file, struct iov_iter *i, loff_t pos) @@ -2323,7 +2328,7 @@ static ssize_t generic_perform_write_2copy(struct file *file, break; } - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, 0); if (!page) { status = -ENOMEM; break; @@ -2605,7 +2610,7 @@ generic_file_buffered_write_one_kernel_page(struct address_space *mapping, mapping->tree_lock.magic, RWLOCK_MAGIC); #endif - page = __grab_cache_page(mapping, index); + page = grab_cache_page_write_begin(mapping, index, 0); if (!page) return -ENOMEM;