Merge tag 'writeback-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/wfg...
[cascardo/linux.git] / fs / fs-writeback.c
index cabdece..9f4935b 100644 (file)
@@ -45,6 +45,7 @@ struct wb_writeback_work {
        unsigned int for_kupdate:1;
        unsigned int range_cyclic:1;
        unsigned int for_background:1;
+       unsigned int for_sync:1;        /* sync(2) WB_SYNC_ALL writeback */
        enum wb_reason reason;          /* why was writeback initiated? */
 
        struct list_head list;          /* pending work list */
@@ -445,9 +446,11 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
        /*
         * Make sure to wait on the data before writing out the metadata.
         * This is important for filesystems that modify metadata on data
-        * I/O completion.
+        * I/O completion. We don't do it for sync(2) writeback because it has a
+        * separate, external IO completion path and ->sync_fs for guaranteeing
+        * inode metadata is written back correctly.
         */
-       if (wbc->sync_mode == WB_SYNC_ALL) {
+       if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync) {
                int err = filemap_fdatawait(mapping);
                if (ret == 0)
                        ret = err;
@@ -580,6 +583,7 @@ static long writeback_sb_inodes(struct super_block *sb,
                .tagged_writepages      = work->tagged_writepages,
                .for_kupdate            = work->for_kupdate,
                .for_background         = work->for_background,
+               .for_sync               = work->for_sync,
                .range_cyclic           = work->range_cyclic,
                .range_start            = 0,
                .range_end              = LLONG_MAX,
@@ -721,7 +725,7 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
        return wrote;
 }
 
-long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
+static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
                                enum wb_reason reason)
 {
        struct wb_writeback_work work = {
@@ -961,7 +965,7 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
 /*
  * Retrieve work items and do the writeback they describe
  */
-long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
+static long wb_do_writeback(struct bdi_writeback *wb)
 {
        struct backing_dev_info *bdi = wb->bdi;
        struct wb_writeback_work *work;
@@ -969,12 +973,6 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
 
        set_bit(BDI_writeback_running, &wb->bdi->state);
        while ((work = get_next_work_item(bdi)) != NULL) {
-               /*
-                * Override sync mode, in case we must wait for completion
-                * because this thread is exiting now.
-                */
-               if (force_wait)
-                       work->sync_mode = WB_SYNC_ALL;
 
                trace_writeback_exec(bdi, work);
 
@@ -1023,7 +1021,7 @@ void bdi_writeback_workfn(struct work_struct *work)
                 * rescuer as work_list needs to be drained.
                 */
                do {
-                       pages_written = wb_do_writeback(wb, 0);
+                       pages_written = wb_do_writeback(wb);
                        trace_writeback_pages_written(pages_written);
                } while (!list_empty(&bdi->work_list));
        } else {
@@ -1053,10 +1051,8 @@ void wakeup_flusher_threads(long nr_pages, enum wb_reason reason)
 {
        struct backing_dev_info *bdi;
 
-       if (!nr_pages) {
-               nr_pages = global_page_state(NR_FILE_DIRTY) +
-                               global_page_state(NR_UNSTABLE_NFS);
-       }
+       if (!nr_pages)
+               nr_pages = get_nr_dirty_pages();
 
        rcu_read_lock();
        list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
@@ -1177,6 +1173,8 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                        bool wakeup_bdi = false;
                        bdi = inode_to_bdi(inode);
 
+                       spin_unlock(&inode->i_lock);
+                       spin_lock(&bdi->wb.list_lock);
                        if (bdi_cap_writeback_dirty(bdi)) {
                                WARN(!test_bit(BDI_registered, &bdi->state),
                                     "bdi-%s not registered\n", bdi->name);
@@ -1191,8 +1189,6 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                                        wakeup_bdi = true;
                        }
 
-                       spin_unlock(&inode->i_lock);
-                       spin_lock(&bdi->wb.list_lock);
                        inode->dirtied_when = jiffies;
                        list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
                        spin_unlock(&bdi->wb.list_lock);
@@ -1364,6 +1360,7 @@ void sync_inodes_sb(struct super_block *sb)
                .range_cyclic   = 0,
                .done           = &done,
                .reason         = WB_REASON_SYNC,
+               .for_sync       = 1,
        };
 
        /* Nothing to do? */