summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2003-08-18 18:32:54 -0700
committerLinus Torvalds <torvalds@home.osdl.org>2003-08-18 18:32:54 -0700
commitfcad2b42fc2e15a94ba1a1ba8535681a735bfd16 (patch)
treefc973782eac7c4c1ea377831c499c0a3492b0656
parentfe7e689f489459a99e9f66f9f0a81aa62491b646 (diff)
[PATCH] async write errors: use flags in address space
From: Oliver Xymoron <oxymoron@waste.org> This patch just saves a few bytes in the inode by turning mapping->gfp_mask into an unsigned long mapping->flags. The mapping's gfp mask is placed in the 16 high bits of mapping->flags and two of the remaining 16 bits are used for tracking EIO and ENOSPC errors. This leaves 14 bits in the mapping for future use. They should be accessed with the atomic bitops.
-rw-r--r--drivers/block/loop.c7
-rw-r--r--fs/block_dev.c2
-rw-r--r--fs/buffer.c4
-rw-r--r--fs/inode.c5
-rw-r--r--fs/jfs/inode.c3
-rw-r--r--fs/jfs/jfs_imap.c3
-rw-r--r--fs/jfs/namei.c2
-rw-r--r--fs/mpage.c18
-rw-r--r--fs/open.c9
-rw-r--r--include/linux/fs.h3
-rw-r--r--include/linux/gfp.h3
-rw-r--r--include/linux/pagemap.h29
-rw-r--r--mm/filemap.c11
-rw-r--r--mm/shmem.c2
-rw-r--r--mm/vmscan.c8
15 files changed, 76 insertions, 33 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index e16189060172..5b2b2174b371 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -728,8 +728,9 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
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);
@@ -845,7 +846,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
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);
/* This is safe: open() is still holding a reference. */
diff --git a/fs/block_dev.c b/fs/block_dev.c
index b88ff14498d2..a200cc3420c6 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -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 --git a/fs/buffer.c b/fs/buffer.c
index 52be4645bee3..414b8388120a 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -579,7 +579,7 @@ void end_buffer_async_write(struct buffer_head *bh, int uptodate)
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 buffer_head **buffers_to_free)
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 --git a/fs/inode.c b/fs/inode.c
index ed6a18ab24ea..bd116dc1fadc 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -18,6 +18,7 @@
#include <linux/hash.h>
#include <linux/swap.h>
#include <linux/security.h>
+#include <linux/pagemap.h>
#include <linux/cdev.h>
/*
@@ -141,11 +142,11 @@ static struct inode *alloc_inode(struct super_block *sb)
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 --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index f0379c24b734..4ad780e83e73 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -20,6 +20,7 @@
#include <linux/fs.h>
#include <linux/mpage.h>
#include <linux/buffer_head.h>
+#include <linux/pagemap.h>
#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 --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index 11738b01ca8c..847afd3030d8 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -43,6 +43,7 @@
#include <linux/fs.h>
#include <linux/buffer_head.h>
+#include <linux/pagemap.h>
#include "jfs_incore.h"
#include "jfs_filsys.h"
@@ -504,7 +505,7 @@ struct inode *diReadSpecial(struct super_block *sb, ino_t inum, int secondary)
}
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 --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index a9be68ac192c..5062dc50b02d 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -258,7 +258,7 @@ int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
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 --git a/fs/mpage.c b/fs/mpage.c
index fdd7381753b6..630d6a0f7e7b 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -566,8 +566,12 @@ confused:
/*
* The caller has a ref on the inode, so *mapping is stable
*/
- if (*ret < 0)
- mapping->error = *ret;
+ if (*ret) {
+ if (*ret == -ENOSPC)
+ set_bit(AS_ENOSPC, &mapping->flags);
+ else
+ set_bit(AS_EIO, &mapping->flags);
+ }
out:
return bio;
}
@@ -669,8 +673,14 @@ mpage_writepages(struct address_space *mapping,
test_clear_page_dirty(page)) {
if (writepage) {
ret = (*writepage)(page, wbc);
- if (ret < 0)
- mapping->error = ret;
+ if (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 --git a/fs/open.c b/fs/open.c
index 2c9c345aed41..ca20549d5023 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -21,6 +21,7 @@
#include <linux/vfs.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
+#include <linux/pagemap.h>
int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
{
@@ -954,10 +955,10 @@ int filp_close(struct file *filp, fl_owner_t id)
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 --git a/include/linux/fs.h b/include/linux/fs.h
index 3d97b77c5bc6..8df205c7ba14 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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 --git a/include/linux/gfp.h b/include/linux/gfp.h
index aa3705a9a21e..c9695427a435 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -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 --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index c1edd5e720e1..8fc118dc90cc 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -8,7 +8,30 @@
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/highmem.h>
+#include <linux/pagemap.h>
#include <asm/uaccess.h>
+#include <linux/gfp.h>
+
+/*
+ * 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, int nr, int cold);
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(struct address_space *mapping,
*/
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 --git a/mm/filemap.c b/mm/filemap.c
index 597db82a32c0..fcc9fc2735e2 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -205,11 +205,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;
}
@@ -532,7 +531,7 @@ grab_cache_page_nowait(struct address_space *mapping, unsigned long index)
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 --git a/mm/shmem.c b/mm/shmem.c
index 9b943298b0fd..a106bfec7709 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -320,7 +320,7 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long
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 --git a/mm/vmscan.c b/mm/vmscan.c
index 7fad2bf77bba..bec66b42ee75 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -251,8 +251,12 @@ static void handle_write_error(struct address_space *mapping,
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);
}