Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[sfrench/cifs-2.6.git] / fs / fs-writeback.c
index e3fe9918faafb710446f5bd73445eeca5ce5b0d7..eed4806399020bbf8d100d1e309b420612a1a093 100644 (file)
@@ -196,7 +196,7 @@ static void redirty_tail(struct inode *inode)
                struct inode *tail_inode;
 
                tail_inode = list_entry(sb->s_dirty.next, struct inode, i_list);
-               if (!time_after_eq(inode->dirtied_when,
+               if (time_before(inode->dirtied_when,
                                tail_inode->dirtied_when))
                        inode->dirtied_when = jiffies;
        }
@@ -220,6 +220,21 @@ static void inode_sync_complete(struct inode *inode)
        wake_up_bit(&inode->i_state, __I_SYNC);
 }
 
+static bool inode_dirtied_after(struct inode *inode, unsigned long t)
+{
+       bool ret = time_after(inode->dirtied_when, t);
+#ifndef CONFIG_64BIT
+       /*
+        * For inodes being constantly redirtied, dirtied_when can get stuck.
+        * It _appears_ to be in the future, but is actually in distant past.
+        * This test is necessary to prevent such wrapped-around relative times
+        * from permanently stopping the whole pdflush writeback.
+        */
+       ret = ret && time_before_eq(inode->dirtied_when, jiffies);
+#endif
+       return ret;
+}
+
 /*
  * Move expired dirty inodes from @delaying_queue to @dispatch_queue.
  */
@@ -231,7 +246,7 @@ static void move_expired_inodes(struct list_head *delaying_queue,
                struct inode *inode = list_entry(delaying_queue->prev,
                                                struct inode, i_list);
                if (older_than_this &&
-                       time_after(inode->dirtied_when, *older_than_this))
+                   inode_dirtied_after(inode, *older_than_this))
                        break;
                list_move(&inode->i_list, dispatch_queue);
        }
@@ -492,8 +507,11 @@ void generic_sync_sb_inodes(struct super_block *sb,
                        continue;               /* blockdev has wrong queue */
                }
 
-               /* Was this inode dirtied after sync_sb_inodes was called? */
-               if (time_after(inode->dirtied_when, start))
+               /*
+                * Was this inode dirtied after sync_sb_inodes was called?
+                * This keeps sync from extra jobs and livelock.
+                */
+               if (inode_dirtied_after(inode, start))
                        break;
 
                /* Is another pdflush already flushing this queue? */
@@ -538,7 +556,8 @@ void generic_sync_sb_inodes(struct super_block *sb,
                list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
                        struct address_space *mapping;
 
-                       if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
+                       if (inode->i_state &
+                                       (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW))
                                continue;
                        mapping = inode->i_mapping;
                        if (mapping->nrpages == 0)