Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / fs / fs-writeback.c
index 7a8ea13..60d6fc2 100644 (file)
@@ -281,13 +281,15 @@ locked_inode_to_wb_and_lock_list(struct inode *inode)
                wb_get(wb);
                spin_unlock(&inode->i_lock);
                spin_lock(&wb->list_lock);
-               wb_put(wb);             /* not gonna deref it anymore */
 
                /* i_wb may have changed inbetween, can't use inode_to_wb() */
-               if (likely(wb == inode->i_wb))
-                       return wb;      /* @inode already has ref */
+               if (likely(wb == inode->i_wb)) {
+                       wb_put(wb);     /* @inode already has ref */
+                       return wb;
+               }
 
                spin_unlock(&wb->list_lock);
+               wb_put(wb);
                cpu_relax();
                spin_lock(&inode->i_lock);
        }
@@ -1339,10 +1341,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
  * we go e.g. from filesystem. Flusher thread uses __writeback_single_inode()
  * and does more profound writeback list handling in writeback_sb_inodes().
  */
-static int
-writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
-                      struct writeback_control *wbc)
+static int writeback_single_inode(struct inode *inode,
+                                 struct writeback_control *wbc)
 {
+       struct bdi_writeback *wb;
        int ret = 0;
 
        spin_lock(&inode->i_lock);
@@ -1380,7 +1382,8 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
        ret = __writeback_single_inode(inode, wbc);
 
        wbc_detach_inode(wbc);
-       spin_lock(&wb->list_lock);
+
+       wb = inode_to_wb_and_lock_list(inode);
        spin_lock(&inode->i_lock);
        /*
         * If inode is clean, remove it from writeback lists. Otherwise don't
@@ -1455,6 +1458,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 
        while (!list_empty(&wb->b_io)) {
                struct inode *inode = wb_inode(wb->b_io.prev);
+               struct bdi_writeback *tmp_wb;
 
                if (inode->i_sb != sb) {
                        if (work->sb) {
@@ -1545,15 +1549,23 @@ static long writeback_sb_inodes(struct super_block *sb,
                        cond_resched();
                }
 
-
-               spin_lock(&wb->list_lock);
+               /*
+                * Requeue @inode if still dirty.  Be careful as @inode may
+                * have been switched to another wb in the meantime.
+                */
+               tmp_wb = inode_to_wb_and_lock_list(inode);
                spin_lock(&inode->i_lock);
                if (!(inode->i_state & I_DIRTY_ALL))
                        wrote++;
-               requeue_inode(inode, wb, &wbc);
+               requeue_inode(inode, tmp_wb, &wbc);
                inode_sync_complete(inode);
                spin_unlock(&inode->i_lock);
 
+               if (unlikely(tmp_wb != wb)) {
+                       spin_unlock(&tmp_wb->list_lock);
+                       spin_lock(&wb->list_lock);
+               }
+
                /*
                 * bail out to wb_writeback() often enough to check
                 * background threshold and other termination conditions.
@@ -2340,7 +2352,6 @@ EXPORT_SYMBOL(sync_inodes_sb);
  */
 int write_inode_now(struct inode *inode, int sync)
 {
-       struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
        struct writeback_control wbc = {
                .nr_to_write = LONG_MAX,
                .sync_mode = sync ? WB_SYNC_ALL : WB_SYNC_NONE,
@@ -2352,7 +2363,7 @@ int write_inode_now(struct inode *inode, int sync)
                wbc.nr_to_write = 0;
 
        might_sleep();
-       return writeback_single_inode(inode, wb, &wbc);
+       return writeback_single_inode(inode, &wbc);
 }
 EXPORT_SYMBOL(write_inode_now);
 
@@ -2369,7 +2380,7 @@ EXPORT_SYMBOL(write_inode_now);
  */
 int sync_inode(struct inode *inode, struct writeback_control *wbc)
 {
-       return writeback_single_inode(inode, &inode_to_bdi(inode)->wb, wbc);
+       return writeback_single_inode(inode, wbc);
 }
 EXPORT_SYMBOL(sync_inode);