GFS2: Make writeback more responsive to system conditions
authorSteven Whitehouse <swhiteho@redhat.com>
Mon, 18 Apr 2011 13:18:09 +0000 (14:18 +0100)
committerSteven Whitehouse <swhiteho@redhat.com>
Wed, 20 Apr 2011 08:01:37 +0000 (09:01 +0100)
This patch adds writeback_control to writing back the AIL
list. This means that we can then take advantage of the
information we get in ->write_inode() in order to set off
some pre-emptive writeback.

In addition, the AIL code is cleaned up a bit to make it
a bit simpler to understand.

There is still more which can usefully be done in this area,
but this is a good start at least.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
fs/gfs2/export.c
fs/gfs2/glock.c
fs/gfs2/incore.h
fs/gfs2/inode.c
fs/gfs2/inode.h
fs/gfs2/log.c
fs/gfs2/log.h
fs/gfs2/super.c

index b5a5e60df0d5294ea237da99a231416fe800e5b4..fe9945f2ff72945bd2e4d3d4a6d7fc183b43f137 100644 (file)
@@ -139,7 +139,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
        struct gfs2_sbd *sdp = sb->s_fs_info;
        struct inode *inode;
 
-       inode = gfs2_ilookup(sb, inum->no_addr);
+       inode = gfs2_ilookup(sb, inum->no_addr, 0);
        if (inode) {
                if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) {
                        iput(inode);
index cb8776f0102e29adcfd1a299bbdcd99267c0d1d8..eed4b6855614019b90bb514f4f28dfe38b8c6a0d 100644 (file)
@@ -649,7 +649,7 @@ static void delete_work_func(struct work_struct *work)
        /* Note: Unsafe to dereference ip as we don't hold right refs/locks */
 
        if (ip)
-               inode = gfs2_ilookup(sdp->sd_vfs, no_addr);
+               inode = gfs2_ilookup(sdp->sd_vfs, no_addr, 1);
        else
                inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED);
        if (inode && !IS_ERR(inode)) {
index 5067beaffa6864ea01609a179f40f9f7457f5bb7..69a63823f7c51daa35c5a0525ae4d410f2efcaa4 100644 (file)
@@ -20,7 +20,6 @@
 
 #define DIO_WAIT       0x00000010
 #define DIO_METADATA   0x00000020
-#define DIO_ALL                0x00000100
 
 struct gfs2_log_operations;
 struct gfs2_log_element;
@@ -377,8 +376,6 @@ struct gfs2_ail {
        unsigned int ai_first;
        struct list_head ai_ail1_list;
        struct list_head ai_ail2_list;
-
-       u64 ai_sync_gen;
 };
 
 struct gfs2_journal_extent {
@@ -657,7 +654,6 @@ struct gfs2_sbd {
        spinlock_t sd_ail_lock;
        struct list_head sd_ail1_list;
        struct list_head sd_ail2_list;
-       u64 sd_ail_sync_gen;
 
        /* Replay stuff */
 
index 9b7b9e40073bc439df6f0c43255c25f75a7dfda5..94c3a7db11163d97f37de2137dfecb1d3e83acaf 100644 (file)
@@ -74,14 +74,14 @@ static int iget_set(struct inode *inode, void *opaque)
        return 0;
 }
 
-struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr)
+struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block)
 {
        unsigned long hash = (unsigned long)no_addr;
        struct gfs2_skip_data data;
 
        data.no_addr = no_addr;
        data.skipped = 0;
-       data.non_block = 0;
+       data.non_block = non_block;
        return ilookup5(sb, hash, iget_test, &data);
 }
 
index 842346eae836dd8e2e96ac146e058c86b07bc6b2..8d1344a4e6739740e700dc1554da1fa2489ac6da 100644 (file)
@@ -102,7 +102,7 @@ extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
 extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
                                         u64 *no_formal_ino,
                                         unsigned int blktype);
-extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr);
+extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int nonblock);
 
 extern int gfs2_inode_refresh(struct gfs2_inode *ip);
 
index 3ebafa1efad0fe5f44346d26aebbf70ff6dbff61..03e00417061b79d23d903630219726fbe42ddc86 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 #include <linux/bio.h>
+#include <linux/writeback.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -83,50 +84,90 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
 /**
  * gfs2_ail1_start_one - Start I/O on a part of the AIL
  * @sdp: the filesystem
- * @tr: the part of the AIL
+ * @wbc: The writeback control structure
+ * @ai: The ail structure
  *
  */
 
-static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
+static void gfs2_ail1_start_one(struct gfs2_sbd *sdp,
+                               struct writeback_control *wbc,
+                               struct gfs2_ail *ai)
 __releases(&sdp->sd_ail_lock)
 __acquires(&sdp->sd_ail_lock)
 {
        struct gfs2_glock *gl = NULL;
+       struct address_space *mapping;
        struct gfs2_bufdata *bd, *s;
        struct buffer_head *bh;
-       int retry;
 
-       do {
-               retry = 0;
+restart:
+       list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, bd_ail_st_list) {
+               bh = bd->bd_bh;
 
-               list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
-                                                bd_ail_st_list) {
-                       bh = bd->bd_bh;
+               gfs2_assert(sdp, bd->bd_ail == ai);
 
-                       gfs2_assert(sdp, bd->bd_ail == ai);
+               if (!buffer_busy(bh)) {
+                       if (!buffer_uptodate(bh))
+                               gfs2_io_error_bh(sdp, bh);
+                       list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
+                       continue;
+               }
+
+               if (!buffer_dirty(bh))
+                       continue;
+               if (gl == bd->bd_gl)
+                       continue;
+               gl = bd->bd_gl;
+               list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
+               mapping = bh->b_page->mapping;
+               spin_unlock(&sdp->sd_ail_lock);
+               generic_writepages(mapping, wbc);
+               spin_lock(&sdp->sd_ail_lock);
+               if (wbc->nr_to_write <= 0)
+                       break;
+               goto restart;
+       }
+}
 
-                       if (!buffer_busy(bh)) {
-                               if (!buffer_uptodate(bh))
-                                       gfs2_io_error_bh(sdp, bh);
-                               list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
-                               continue;
-                       }
 
-                       if (!buffer_dirty(bh))
-                               continue;
-                       if (gl == bd->bd_gl)
-                               continue;
-                       gl = bd->bd_gl;
-                       list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
+/**
+ * gfs2_ail1_flush - start writeback of some ail1 entries 
+ * @sdp: The super block
+ * @wbc: The writeback control structure
+ *
+ * Writes back some ail1 entries, according to the limits in the
+ * writeback control structure
+ */
 
-                       spin_unlock(&sdp->sd_ail_lock);
-                       filemap_fdatawrite(gfs2_glock2aspace(gl));
-                       spin_lock(&sdp->sd_ail_lock);
+void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
+{
+       struct list_head *head = &sdp->sd_ail1_list;
+       struct gfs2_ail *ai;
 
-                       retry = 1;
+       spin_lock(&sdp->sd_ail_lock);
+       list_for_each_entry_reverse(ai, head, ai_list) {
+               if (wbc->nr_to_write <= 0)
                        break;
-               }
-       } while (retry);
+               gfs2_ail1_start_one(sdp, wbc, ai); /* This may drop ail lock */
+       }
+       spin_unlock(&sdp->sd_ail_lock);
+}
+
+/**
+ * gfs2_ail1_start - start writeback of all ail1 entries
+ * @sdp: The superblock
+ */
+
+static void gfs2_ail1_start(struct gfs2_sbd *sdp)
+{
+       struct writeback_control wbc = {
+               .sync_mode = WB_SYNC_NONE,
+               .nr_to_write = LONG_MAX,
+               .range_start = 0,
+               .range_end = LLONG_MAX,
+       };
+
+       return gfs2_ail1_flush(sdp, &wbc);
 }
 
 /**
@@ -136,7 +177,7 @@ __acquires(&sdp->sd_ail_lock)
  *
  */
 
-static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags)
+static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
 {
        struct gfs2_bufdata *bd, *s;
        struct buffer_head *bh;
@@ -144,71 +185,37 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
        list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
                                         bd_ail_st_list) {
                bh = bd->bd_bh;
-
                gfs2_assert(sdp, bd->bd_ail == ai);
-
-               if (buffer_busy(bh)) {
-                       if (flags & DIO_ALL)
-                               continue;
-                       else
-                               break;
-               }
-
+               if (buffer_busy(bh))
+                       continue;
                if (!buffer_uptodate(bh))
                        gfs2_io_error_bh(sdp, bh);
-
                list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
        }
 
-       return list_empty(&ai->ai_ail1_list);
 }
 
-static void gfs2_ail1_start(struct gfs2_sbd *sdp)
-{
-       struct list_head *head;
-       u64 sync_gen;
-       struct gfs2_ail *ai;
-       int done = 0;
-
-       spin_lock(&sdp->sd_ail_lock);
-       head = &sdp->sd_ail1_list;
-       if (list_empty(head)) {
-               spin_unlock(&sdp->sd_ail_lock);
-               return;
-       }
-       sync_gen = sdp->sd_ail_sync_gen++;
-
-       while(!done) {
-               done = 1;
-               list_for_each_entry_reverse(ai, head, ai_list) {
-                       if (ai->ai_sync_gen >= sync_gen)
-                               continue;
-                       ai->ai_sync_gen = sync_gen;
-                       gfs2_ail1_start_one(sdp, ai); /* This may drop ail lock */
-                       done = 0;
-                       break;
-               }
-       }
-
-       spin_unlock(&sdp->sd_ail_lock);
-}
+/**
+ * gfs2_ail1_empty - Try to empty the ail1 lists
+ * @sdp: The superblock
+ *
+ * Tries to empty the ail1 lists, starting with the oldest first
+ */
 
-static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags)
+static int gfs2_ail1_empty(struct gfs2_sbd *sdp)
 {
        struct gfs2_ail *ai, *s;
        int ret;
 
        spin_lock(&sdp->sd_ail_lock);
-
        list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) {
-               if (gfs2_ail1_empty_one(sdp, ai, flags))
+               gfs2_ail1_empty_one(sdp, ai);
+               if (list_empty(&ai->ai_ail1_list))
                        list_move(&ai->ai_list, &sdp->sd_ail2_list);
-               else if (!(flags & DIO_ALL))
+               else
                        break;
        }
-
        ret = list_empty(&sdp->sd_ail1_list);
-
        spin_unlock(&sdp->sd_ail_lock);
 
        return ret;
@@ -569,7 +576,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
        set_buffer_uptodate(bh);
        clear_buffer_dirty(bh);
 
-       gfs2_ail1_empty(sdp, 0);
+       gfs2_ail1_empty(sdp);
        tail = current_tail(sdp);
 
        lh = (struct gfs2_log_header *)bh->b_data;
@@ -864,7 +871,7 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
        gfs2_log_flush(sdp, NULL);
        for (;;) {
                gfs2_ail1_start(sdp);
-               if (gfs2_ail1_empty(sdp, DIO_ALL))
+               if (gfs2_ail1_empty(sdp))
                        break;
                msleep(10);
        }
@@ -900,17 +907,15 @@ int gfs2_logd(void *data)
 
                preflush = atomic_read(&sdp->sd_log_pinned);
                if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
-                       gfs2_ail1_empty(sdp, DIO_ALL);
+                       gfs2_ail1_empty(sdp);
                        gfs2_log_flush(sdp, NULL);
-                       gfs2_ail1_empty(sdp, DIO_ALL);
                }
 
                if (gfs2_ail_flush_reqd(sdp)) {
                        gfs2_ail1_start(sdp);
                        io_schedule();
-                       gfs2_ail1_empty(sdp, 0);
+                       gfs2_ail1_empty(sdp);
                        gfs2_log_flush(sdp, NULL);
-                       gfs2_ail1_empty(sdp, DIO_ALL);
                }
 
                wake_up(&sdp->sd_log_waitq);
index 0d007f92023411b00015a74d247095e8f7354f37..ab0621698b73f1969b8642222dfeb9bd820e4635 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/writeback.h>
 #include "incore.h"
 
 /**
@@ -59,6 +60,7 @@ extern struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
 extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
 extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
 extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
+extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
 
 extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
 extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp);
index 215c37bfc2a4bb053f682840bc54fc4e537da838..58fe3a4ac8291d559f4c62685e84c27567c8c0dd 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <linux/writeback.h>
+#include <linux/backing-dev.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -714,6 +715,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
        struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
+       struct backing_dev_info *bdi = metamapping->backing_dev_info;
        struct gfs2_holder gh;
        struct buffer_head *bh;
        struct timespec atime;
@@ -747,6 +749,8 @@ do_flush:
        if (wbc->sync_mode == WB_SYNC_ALL)
                gfs2_log_flush(GFS2_SB(inode), ip->i_gl);
        filemap_fdatawrite(metamapping);
+       if (bdi->dirty_exceeded)
+               gfs2_ail1_flush(sdp, wbc);
        if (!ret && (wbc->sync_mode == WB_SYNC_ALL))
                ret = filemap_fdatawait(metamapping);
        if (ret)
@@ -1366,7 +1370,8 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip)
        if (error)
                goto out_rindex_relse;
 
-       error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA, 1);
+       error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA,
+                                sdp->sd_jdesc->jd_blocks);
        if (error)
                goto out_rg_gunlock;
 
This page took 0.045078 seconds and 5 git commands to generate.