fs: symlink write_begin allocation context fix
authorNick Piggin <npiggin@suse.de>
Sun, 4 Jan 2009 20:00:53 +0000 (12:00 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Sun, 18 Jan 2009 18:35:43 +0000 (10:35 -0800)
commit 54566b2c1594c2326a645a3551f9d989f7ba3c5e upstream.

With the write_begin/write_end aops, page_symlink was broken because it
could no longer pass a GFP_NOFS type mask into the point where the
allocations happened.  They are done in write_begin, which would always
assume that the filesystem can be entered from reclaim.  This bug could
cause filesystem deadlocks.

The funny thing with having a gfp_t mask there is that it doesn't really
allow the caller to arbitrarily tinker with the context in which it can be
called.  It couldn't ever be GFP_ATOMIC, for example, because it needs to
take the page lock.  The only thing any callers care about is __GFP_FS
anyway, so turn that into a single flag.

Add a new flag for write_begin, AOP_FLAG_NOFS.  Filesystems can now act on
this flag in their write_begin function.  Change __grab_cache_page to
accept a nofs argument as well, to honour that flag (while we're there,
change the name to grab_cache_page_write_begin which is more instructive
and does away with random leading underscores).

This is really a more flexible way to go in the end anyway -- if a
filesystem happens to want any extra allocations aside from the pagecache
ones in ints write_begin function, it may now use GFP_KERNEL (rather than
GFP_NOFS) for common case allocations (eg.  ocfs2_alloc_write_ctxt, for a
random example).

[kosaki.motohiro@jp.fujitsu.com: fix ubifs]
[kosaki.motohiro@jp.fujitsu.com: fix fuse]
Signed-off-by: Nick Piggin <npiggin@suse.de>
Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: <stable@kernel.org>
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[ Cleaned up the calling convention: just pass in the AOP flags
  untouched to the grab_cache_page_write_begin() function.  That
  just simplifies everybody, and may even allow future expansion of the
  logic.   - Linus ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
19 files changed:
fs/affs/file.c
fs/buffer.c
fs/ext3/inode.c
fs/ext3/namei.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/fuse/file.c
fs/gfs2/ops_address.c
fs/hostfs/hostfs_kern.c
fs/jffs2/file.c
fs/libfs.c
fs/namei.c
fs/nfs/file.c
fs/reiserfs/inode.c
fs/smbfs/file.c
fs/ubifs/file.c
include/linux/fs.h
include/linux/pagemap.h
mm/filemap.c

index 1377b12..9246cb4 100644 (file)
@@ -628,7 +628,7 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping
        }
 
        index = pos >> PAGE_CACHE_SHIFT;
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
        *pagep = page;
index 95a165d..a542f97 100644 (file)
@@ -1988,7 +1988,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;
@@ -2494,7 +2494,7 @@ int nobh_write_begin(struct file *file, struct address_space *mapping,
        from = pos & (PAGE_CACHE_SIZE - 1);
        to = from + len;
 
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
        *pagep = page;
index 507d868..c5f40a0 100644 (file)
@@ -1152,7 +1152,7 @@ static int ext3_write_begin(struct file *file, struct address_space *mapping,
        to = from + len;
 
 retry:
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
        *pagep = page;
index de13e91..1f55382 100644 (file)
@@ -2187,8 +2187,7 @@ retry:
                 * We have a transaction open.  All is sweetness.  It also sets
                 * i_size in generic_commit_write().
                 */
-               err = __page_symlink(inode, symname, l,
-                               mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
+               err = __page_symlink(inode, symname, l, 1);
                if (err) {
                        drop_nlink(inode);
                        ext3_mark_inode_dirty(handle, inode);
index 846a790..d77f674 100644 (file)
@@ -1370,7 +1370,7 @@ retry:
                goto out;
        }
 
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page) {
                ext4_journal_stop(handle);
                ret = -ENOMEM;
@@ -2421,7 +2421,7 @@ retry:
                goto out;
        }
 
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page) {
                ext4_journal_stop(handle);
                ret = -ENOMEM;
index 387ad98..d626533 100644 (file)
@@ -2216,8 +2216,7 @@ retry:
                 * We have a transaction open.  All is sweetness.  It also sets
                 * i_size in generic_commit_write().
                 */
-               err = __page_symlink(inode, symname, l,
-                               mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
+               err = __page_symlink(inode, symname, l, 1);
                if (err) {
                        clear_nlink(inode);
                        ext4_mark_inode_dirty(handle, inode);
index 2bada6b..c8206db 100644 (file)
@@ -644,7 +644,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
 {
        pgoff_t index = pos >> PAGE_CACHE_SHIFT;
 
-       *pagep = __grab_cache_page(mapping, index);
+       *pagep = grab_cache_page_write_begin(mapping, index, flags);
        if (!*pagep)
                return -ENOMEM;
        return 0;
@@ -777,7 +777,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
                        break;
 
                err = -ENOMEM;
-               page = __grab_cache_page(mapping, index);
+               page = grab_cache_page_write_begin(mapping, index, 0);
                if (!page)
                        break;
 
index e64a1b0..c75df06 100644 (file)
@@ -675,7 +675,7 @@ static 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;
index d6ecabf..5e17cae 100644 (file)
@@ -500,7 +500,7 @@ int hostfs_write_begin(struct file *file, struct address_space *mapping,
 {
        pgoff_t index = pos >> PAGE_CACHE_SHIFT;
 
-       *pagep = __grab_cache_page(mapping, index);
+       *pagep = grab_cache_page_write_begin(mapping, index, flags);
        if (!*pagep)
                return -ENOMEM;
        return 0;
index 5a98aa8..5edc2bf 100644 (file)
@@ -132,7 +132,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
        uint32_t pageofs = index << PAGE_CACHE_SHIFT;
        int ret = 0;
 
-       pg = __grab_cache_page(mapping, index);
+       pg = grab_cache_page_write_begin(mapping, index, flags);
        if (!pg)
                return -ENOMEM;
        *pagep = pg;
index 1add676..8fc2407 100644 (file)
@@ -360,7 +360,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;
 
index 50c2193..2259d21 100644 (file)
@@ -2796,18 +2796,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;
 
@@ -2831,7 +2836,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));
 }
 
 const struct inode_operations page_symlink_inode_operations = {
index 7846065..30541f0 100644 (file)
@@ -351,7 +351,7 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
                file->f_path.dentry->d_name.name,
                mapping->host->i_ino, len, (long long) pos);
 
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
        *pagep = page;
index 5699171..8c2615e 100644 (file)
@@ -2565,7 +2565,7 @@ static int reiserfs_write_begin(struct file *file,
        }
 
        index = pos >> PAGE_CACHE_SHIFT;
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
        *pagep = page;
index e4f8d51..92d5e8f 100644 (file)
@@ -297,7 +297,7 @@ static int smb_write_begin(struct file *file, struct address_space *mapping,
                        struct page **pagep, void **fsdata)
 {
        pgoff_t index = pos >> PAGE_CACHE_SHIFT;
-       *pagep = __grab_cache_page(mapping, index);
+       *pagep = grab_cache_page_write_begin(mapping, index, flags);
        if (!*pagep)
                return -ENOMEM;
        return 0;
index 3d698e2..40033dc 100644 (file)
@@ -213,7 +213,8 @@ static void release_existing_page_budget(struct ubifs_info *c)
 }
 
 static int write_begin_slow(struct address_space *mapping,
-                           loff_t pos, unsigned len, struct page **pagep)
+                           loff_t pos, unsigned len, struct page **pagep,
+                           unsigned flags)
 {
        struct inode *inode = mapping->host;
        struct ubifs_info *c = inode->i_sb->s_fs_info;
@@ -241,7 +242,7 @@ static int write_begin_slow(struct address_space *mapping,
        if (unlikely(err))
                return err;
 
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (unlikely(!page)) {
                ubifs_release_budget(c, &req);
                return -ENOMEM;
@@ -432,7 +433,7 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping,
                return -EROFS;
 
        /* Try out the fast-path part first */
-       page = __grab_cache_page(mapping, index);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (unlikely(!page))
                return -ENOMEM;
 
@@ -477,7 +478,7 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping,
                unlock_page(page);
                page_cache_release(page);
 
-               return write_begin_slow(mapping, pos, len, pagep);
+               return write_begin_slow(mapping, pos, len, pagep, flags);
        }
 
        /*
index 580b513..d621217 100644 (file)
@@ -403,6 +403,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.
@@ -1959,7 +1962,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 const struct inode_operations page_symlink_inode_operations;
 extern int generic_readlink(struct dentry *, char __user *, int);
index 5da31c1..62efceb 100644 (file)
@@ -213,7 +213,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.
index 1cd0b87..f3033d0 100644 (file)
@@ -2033,7 +2033,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;
@@ -2197,19 +2197,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)
@@ -2218,7 +2223,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)
@@ -2263,7 +2268,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;