The complex patch with which MohammadAG had his success
[h-e-n] / fs / fs-writeback.c
index ae45f77..4ce9b26 100644 (file)
@@ -8,7 +8,7 @@
  * pages against inodes.  ie: data writeback.  Writeout of the
  * inode itself is not handled here.
  *
- * 10Apr2002   akpm@zip.com.au
+ * 10Apr2002   Andrew Morton
  *             Split out of fs/inode.c
  *             Additions for address_space-based writeback
  */
@@ -65,6 +65,24 @@ static void writeback_release(struct backing_dev_info *bdi)
 }
 
 /**
+ * enable_pwb - enable periodic write-back after an inode was marked as dirty.
+ * @inode: the inode which was marked as dirty
+ *
+ * This is a helper function for '__mark_inode_dirty()' which enables the
+ * periodic write-back, unless:
+ *   * the backing device @inode belongs to does not support write-back;
+ *   * periodic write-back is already enabled.
+ */
+static void enable_pwb(struct inode *inode)
+{
+       struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info;
+
+       if (bdi_cap_writeback_dirty(bdi) &&
+           atomic_add_unless(&periodic_wb_enabled, 1, 1))
+               enable_periodic_wb();
+}
+
+/**
  *     __mark_inode_dirty -    internal function
  *     @inode: inode to mark
  *     @flags: what kind of dirty (i.e. I_DIRTY_SYNC)
@@ -164,6 +182,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                if (!was_dirty) {
                        inode->dirtied_when = jiffies;
                        list_move(&inode->i_list, &sb->s_dirty);
+                       enable_pwb(inode);
                }
        }
 out:
@@ -172,6 +191,28 @@ out:
 
 EXPORT_SYMBOL(__mark_inode_dirty);
 
+/**
+ * mark_sb_dirty - mark super block as dirty.
+ * @sb: the super block to mark as dirty
+ *
+ * This function marks super block @sb as dirty and enables the periodic
+ * write-back, unless it is already enabled. Note, VFS does not serialize the
+ * super block clean/dirty (@sb->s_dirt) state changes, and each FS is
+ * responsible for doing its own serialization.
+ */
+void mark_sb_dirty(struct super_block *sb)
+{
+       sb->s_dirt = 1;
+       /*
+        * If 'periodic_wb_enabled' is 0, set it to 1 and enable the periodic
+        * write-back.
+        */
+       if (atomic_add_unless(&periodic_wb_enabled, 1, 1))
+               enable_periodic_wb();
+}
+
+EXPORT_SYMBOL(mark_sb_dirty);
+
 static int write_inode(struct inode *inode, int sync)
 {
        if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
@@ -424,8 +465,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
  * WB_SYNC_HOLD is a hack for sys_sync(): reattach the inode to sb->s_dirty so
  * that it can be located for waiting on in __writeback_single_inode().
  *
- * Called under inode_lock.
- *
  * If `bdi' is non-zero then we're being asked to writeback a specific queue.
  * This function assumes that the blockdev superblock's inodes are backed by
  * a variety of queues, so all inodes are searched.  For other superblocks,
@@ -441,11 +480,12 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
  * on the writer throttling path, and we get decent balancing between many
  * throttled threads: we don't want them all piling up on inode_sync_wait.
  */
-static void
-sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc)
+void generic_sync_sb_inodes(struct super_block *sb,
+                               struct writeback_control *wbc)
 {
        const unsigned long start = jiffies;    /* livelock avoidance */
 
+       spin_lock(&inode_lock);
        if (!wbc->for_kupdate || list_empty(&sb->s_io))
                queue_io(sb, wbc->older_than_this);
 
@@ -524,8 +564,16 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc)
                if (!list_empty(&sb->s_more_io))
                        wbc->more_io = 1;
        }
+       spin_unlock(&inode_lock);
        return;         /* Leave any unwritten inodes on s_io */
 }
+EXPORT_SYMBOL_GPL(generic_sync_sb_inodes);
+
+static void sync_sb_inodes(struct super_block *sb,
+                               struct writeback_control *wbc)
+{
+       generic_sync_sb_inodes(sb, wbc);
+}
 
 /*
  * Start writeback of dirty pagecache data against all unlocked inodes.
@@ -565,11 +613,8 @@ restart:
                         * be unmounted by the time it is released.
                         */
                        if (down_read_trylock(&sb->s_umount)) {
-                               if (sb->s_root) {
-                                       spin_lock(&inode_lock);
+                               if (sb->s_root)
                                        sync_sb_inodes(sb, wbc);
-                                       spin_unlock(&inode_lock);
-                               }
                                up_read(&sb->s_umount);
                        }
                        spin_lock(&sb_lock);
@@ -607,9 +652,7 @@ void sync_inodes_sb(struct super_block *sb, int wait)
                        (inodes_stat.nr_inodes - inodes_stat.nr_unused) +
                        nr_dirty + nr_unstable;
        wbc.nr_to_write += wbc.nr_to_write / 2;         /* Bit more for luck */
-       spin_lock(&inode_lock);
        sync_sb_inodes(sb, &wbc);
-       spin_unlock(&inode_lock);
 }
 
 /*