From: Oliver Xymoron Combine mapping->error and ->gfp_mask into ->flags DESC async write errors: mapping->flags fixes EDESC - Robustify the logic a bit. At present if writepage() returns an error which is not -EIO or -ENOSPC we lose track of it. So instead, treat all unknown errors as -EIO. drivers/block/loop.c | 7 ++++--- fs/block_dev.c | 2 +- fs/buffer.c | 4 ++-- fs/inode.c | 5 +++-- fs/jfs/inode.c | 3 ++- fs/jfs/jfs_imap.c | 3 ++- fs/jfs/namei.c | 2 +- fs/mpage.c | 12 ++++++++---- fs/open.c | 9 +++++---- include/linux/fs.h | 3 +-- include/linux/gfp.h | 3 +++ include/linux/pagemap.h | 29 ++++++++++++++++++++++++++--- mm/filemap.c | 11 +++++------ mm/shmem.c | 2 +- mm/vmscan.c | 8 ++++++-- 15 files changed, 70 insertions(+), 33 deletions(-) diff -puN drivers/block/loop.c~awe-use-gfp_flags drivers/block/loop.c --- 25/drivers/block/loop.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/drivers/block/loop.c 2003-08-07 15:39:28.000000000 -0700 @@ -727,8 +727,9 @@ static int loop_set_fd(struct loop_devic fput(file); goto out_putf; } - lo->old_gfp_mask = inode->i_mapping->gfp_mask; - inode->i_mapping->gfp_mask &= ~(__GFP_IO|__GFP_FS); + lo->old_gfp_mask = mapping_gfp_mask(inode->i_mapping); + mapping_set_gfp_mask(inode->i_mapping, + lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); set_blocksize(bdev, lo_blocksize); @@ -853,7 +854,7 @@ static int loop_clr_fd(struct loop_devic memset(lo->lo_file_name, 0, LO_NAME_SIZE); invalidate_bdev(bdev, 0); set_capacity(disks[lo->lo_number], 0); - filp->f_dentry->d_inode->i_mapping->gfp_mask = gfp; + mapping_set_gfp_mask(filp->f_dentry->d_inode->i_mapping, gfp); lo->lo_state = Lo_unbound; fput(filp); blk_put_queue(lo->lo_queue); diff -puN fs/block_dev.c~awe-use-gfp_flags fs/block_dev.c --- 25/fs/block_dev.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/block_dev.c 2003-08-07 15:39:28.000000000 -0700 @@ -320,7 +320,7 @@ struct block_device *bdget(dev_t dev) inode->i_rdev = kdev; inode->i_bdev = new_bdev; inode->i_data.a_ops = &def_blk_aops; - inode->i_data.gfp_mask = GFP_USER; + mapping_set_gfp_mask(&inode->i_data, GFP_USER); inode->i_data.backing_dev_info = &default_backing_dev_info; spin_lock(&bdev_lock); bdev = bdfind(dev, head); diff -puN fs/buffer.c~awe-use-gfp_flags fs/buffer.c --- 25/fs/buffer.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/buffer.c 2003-08-07 15:39:28.000000000 -0700 @@ -579,7 +579,7 @@ void end_buffer_async_write(struct buffe buffer_io_error(bh); printk(KERN_WARNING "lost page write due to I/O error on %s\n", bdevname(bh->b_bdev, b)); - page->mapping->error = -EIO; + set_bit(AS_EIO, &page->mapping->flags); clear_buffer_uptodate(bh); SetPageError(page); } @@ -2815,7 +2815,7 @@ drop_buffers(struct page *page, struct b do { check_ttfb_buffer(page, bh); if (buffer_write_io_error(bh)) - page->mapping->error = -EIO; + set_bit(AS_EIO, &page->mapping->flags); if (buffer_busy(bh)) goto failed; if (!buffer_uptodate(bh) && !buffer_req(bh)) diff -puN fs/inode.c~awe-use-gfp_flags fs/inode.c --- 25/fs/inode.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/inode.c 2003-08-07 15:39:28.000000000 -0700 @@ -18,6 +18,7 @@ #include #include #include +#include #include /* @@ -141,11 +142,11 @@ static struct inode *alloc_inode(struct mapping->a_ops = &empty_aops; mapping->host = inode; - mapping->gfp_mask = GFP_HIGHUSER; + mapping->flags = 0; + mapping_set_gfp_mask(mapping, GFP_HIGHUSER); mapping->dirtied_when = 0; mapping->assoc_mapping = NULL; mapping->backing_dev_info = &default_backing_dev_info; - mapping->error = 0; if (sb->s_bdev) mapping->backing_dev_info = sb->s_bdev->bd_inode->i_mapping->backing_dev_info; memset(&inode->u, 0, sizeof(inode->u)); diff -puN fs/jfs/inode.c~awe-use-gfp_flags fs/jfs/inode.c --- 25/fs/jfs/inode.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/jfs/inode.c 2003-08-07 15:39:28.000000000 -0700 @@ -20,6 +20,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_imap.h" @@ -51,7 +52,7 @@ void jfs_read_inode(struct inode *inode) inode->i_op = &jfs_dir_inode_operations; inode->i_fop = &jfs_dir_operations; inode->i_mapping->a_ops = &jfs_aops; - inode->i_mapping->gfp_mask = GFP_NOFS; + mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); } else if (S_ISLNK(inode->i_mode)) { if (inode->i_size >= IDATASIZE) { inode->i_op = &page_symlink_inode_operations; diff -puN fs/jfs/jfs_imap.c~awe-use-gfp_flags fs/jfs/jfs_imap.c --- 25/fs/jfs/jfs_imap.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/jfs/jfs_imap.c 2003-08-07 15:39:28.000000000 -0700 @@ -43,6 +43,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" @@ -504,7 +505,7 @@ struct inode *diReadSpecial(struct super } ip->i_mapping->a_ops = &jfs_aops; - ip->i_mapping->gfp_mask = GFP_NOFS; + mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS); if ((inum == FILESYSTEM_I) && (JFS_IP(ip)->ipimap == sbi->ipaimap)) { sbi->gengen = le32_to_cpu(dp->di_gengen); diff -puN fs/jfs/namei.c~awe-use-gfp_flags fs/jfs/namei.c --- 25/fs/jfs/namei.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/jfs/namei.c 2003-08-07 15:39:28.000000000 -0700 @@ -258,7 +258,7 @@ int jfs_mkdir(struct inode *dip, struct ip->i_op = &jfs_dir_inode_operations; ip->i_fop = &jfs_dir_operations; ip->i_mapping->a_ops = &jfs_aops; - ip->i_mapping->gfp_mask = GFP_NOFS; + mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS); insert_inode_hash(ip); mark_inode_dirty(ip); diff -puN fs/mpage.c~awe-use-gfp_flags fs/mpage.c --- 25/fs/mpage.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/mpage.c 2003-08-07 15:39:28.000000000 -0700 @@ -566,8 +566,10 @@ confused: /* * The caller has a ref on the inode, so *mapping is stable */ - if (*ret < 0) - mapping->error = *ret; + if (*ret == -ENOSPC) + set_bit(AS_ENOSPC, &mapping->flags); + else + set_bit(AS_EIO, &mapping->flags); out: return bio; } @@ -669,8 +671,10 @@ mpage_writepages(struct address_space *m test_clear_page_dirty(page)) { if (writepage) { ret = (*writepage)(page, wbc); - if (ret < 0) - mapping->error = ret; + if (ret == -ENOSPC) + set_bit(AS_ENOSPC, &mapping->flags); + else + set_bit(AS_EIO, &mapping->flags); } else { bio = mpage_writepage(bio, page, get_block, &last_block_in_bio, &ret, wbc); diff -puN fs/open.c~awe-use-gfp_flags fs/open.c --- 25/fs/open.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/fs/open.c 2003-08-07 15:39:28.000000000 -0700 @@ -21,6 +21,7 @@ #include #include #include +#include int vfs_statfs(struct super_block *sb, struct kstatfs *buf) { @@ -954,10 +955,10 @@ int filp_close(struct file *filp, fl_own retval = err; } - err = mapping->error; - if (!retval) - retval = err; - mapping->error = 0; + if (test_and_clear_bit(AS_ENOSPC, &mapping->flags)) + retval = -ENOSPC; + if (test_and_clear_bit(AS_EIO, &mapping->flags)) + retval = -EIO; if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); diff -puN include/linux/fs.h~awe-use-gfp_flags include/linux/fs.h --- 25/include/linux/fs.h~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/include/linux/fs.h 2003-08-07 15:39:28.000000000 -0700 @@ -327,12 +327,11 @@ struct address_space { struct semaphore i_shared_sem; /* protect both above lists */ atomic_t truncate_count; /* Cover race condition with truncate */ unsigned long dirtied_when; /* jiffies of first page dirtying */ - int gfp_mask; /* how to allocate the pages */ + unsigned long flags; /* error bits/gfp mask */ struct backing_dev_info *backing_dev_info; /* device readahead, etc */ spinlock_t private_lock; /* for use by the address_space */ struct list_head private_list; /* ditto */ struct address_space *assoc_mapping; /* ditto */ - int error; /* write error for fsync */ }; struct block_device { diff -puN include/linux/gfp.h~awe-use-gfp_flags include/linux/gfp.h --- 25/include/linux/gfp.h~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/include/linux/gfp.h 2003-08-07 15:39:28.000000000 -0700 @@ -33,6 +33,9 @@ #define __GFP_NORETRY 0x1000 /* Do not retry. Might fail */ #define __GFP_NO_GROW 0x2000 /* Slab internal usage */ +#define __GFP_BITS_SHIFT 16 /* Room for 16 __GFP_FOO bits */ +#define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1) + #define GFP_ATOMIC (__GFP_HIGH) #define GFP_NOIO (__GFP_WAIT) #define GFP_NOFS (__GFP_WAIT | __GFP_IO) diff -puN include/linux/pagemap.h~awe-use-gfp_flags include/linux/pagemap.h --- 25/include/linux/pagemap.h~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/include/linux/pagemap.h 2003-08-07 15:39:28.000000000 -0700 @@ -8,7 +8,30 @@ #include #include #include +#include #include +#include + +/* + * Bits in mapping->flags. The lower __GFP_BITS_SHIFT bits are the page + * allocation mode flags. + */ +#define AS_EIO (__GFP_BITS_SHIFT + 0) /* IO error on async write */ +#define AS_ENOSPC (__GFP_BITS_SHIFT + 1) /* ENOSPC on async write */ + +static inline int mapping_gfp_mask(struct address_space * mapping) +{ + return mapping->flags & __GFP_BITS_MASK; +} + +/* + * This is non-atomic. Only to be used before the mapping is activated. + * Probably needs a barrier... + */ +static inline void mapping_set_gfp_mask(struct address_space *m, int mask) +{ + m->flags = (m->flags & ~__GFP_BITS_MASK) | mask; +} /* * The page cache can done in larger chunks than @@ -29,12 +52,12 @@ void release_pages(struct page **pages, static inline struct page *page_cache_alloc(struct address_space *x) { - return alloc_pages(x->gfp_mask, 0); + return alloc_pages(mapping_gfp_mask(x), 0); } static inline struct page *page_cache_alloc_cold(struct address_space *x) { - return alloc_pages(x->gfp_mask|__GFP_COLD, 0); + return alloc_pages(mapping_gfp_mask(x)|__GFP_COLD, 0); } typedef int filler_t(void *, struct page *); @@ -56,7 +79,7 @@ extern unsigned int find_get_pages(struc */ static inline struct page *grab_cache_page(struct address_space *mapping, unsigned long index) { - return find_or_create_page(mapping, index, mapping->gfp_mask); + return find_or_create_page(mapping, index, mapping_gfp_mask(mapping)); } extern struct page * grab_cache_page_nowait(struct address_space *mapping, diff -puN mm/filemap.c~awe-use-gfp_flags mm/filemap.c --- 25/mm/filemap.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/mm/filemap.c 2003-08-07 15:39:28.000000000 -0700 @@ -199,11 +199,10 @@ restart: spin_unlock(&mapping->page_lock); /* Check for outstanding write errors */ - if (mapping->error) { - if (!ret) - ret = mapping->error; - mapping->error = 0; - } + if (test_and_clear_bit(AS_ENOSPC, &mapping->flags)) + ret = -ENOSPC; + if (test_and_clear_bit(AS_EIO, &mapping->flags)) + ret = -EIO; return ret; } @@ -526,7 +525,7 @@ grab_cache_page_nowait(struct address_sp page_cache_release(page); return NULL; } - gfp_mask = mapping->gfp_mask & ~__GFP_FS; + gfp_mask = mapping_gfp_mask(mapping) & ~__GFP_FS; page = alloc_pages(gfp_mask, 0); if (page && add_to_page_cache_lru(page, mapping, index, gfp_mask)) { page_cache_release(page); diff -puN mm/shmem.c~awe-use-gfp_flags mm/shmem.c --- 25/mm/shmem.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/mm/shmem.c 2003-08-07 15:39:28.000000000 -0700 @@ -320,7 +320,7 @@ static swp_entry_t *shmem_swp_alloc(stru spin_unlock(&sbinfo->stat_lock); spin_unlock(&info->lock); - page = shmem_dir_alloc(inode->i_mapping->gfp_mask); + page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping)); if (page) { clear_highpage(page); page->nr_swapped = 0; diff -puN mm/vmscan.c~awe-use-gfp_flags mm/vmscan.c --- 25/mm/vmscan.c~awe-use-gfp_flags 2003-08-07 15:39:28.000000000 -0700 +++ 25-akpm/mm/vmscan.c 2003-08-07 15:39:28.000000000 -0700 @@ -251,8 +251,12 @@ static void handle_write_error(struct ad struct page *page, int error) { lock_page(page); - if (page->mapping == mapping) - mapping->error = error; + if (page->mapping == mapping) { + if (error == -ENOSPC) + set_bit(AS_ENOSPC, &mapping->flags); + else + set_bit(AS_EIO, &mapping->flags); + } unlock_page(page); } _