diff options
Diffstat (limited to 'fs/fs-writeback.c')
| -rw-r--r-- | fs/fs-writeback.c | 49 | 
1 files changed, 34 insertions, 15 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 9f4935b8f20..be568b7311d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -26,6 +26,7 @@  #include <linux/blkdev.h>  #include <linux/backing-dev.h>  #include <linux/tracepoint.h> +#include <linux/device.h>  #include "internal.h"  /* @@ -88,16 +89,31 @@ static inline struct inode *wb_inode(struct list_head *head)  #define CREATE_TRACE_POINTS  #include <trace/events/writeback.h> +EXPORT_TRACEPOINT_SYMBOL_GPL(wbc_writepage); + +static void bdi_wakeup_thread(struct backing_dev_info *bdi) +{ +	spin_lock_bh(&bdi->wb_lock); +	if (test_bit(BDI_registered, &bdi->state)) +		mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); +	spin_unlock_bh(&bdi->wb_lock); +} +  static void bdi_queue_work(struct backing_dev_info *bdi,  			   struct wb_writeback_work *work)  {  	trace_writeback_queue(bdi, work);  	spin_lock_bh(&bdi->wb_lock); +	if (!test_bit(BDI_registered, &bdi->state)) { +		if (work->done) +			complete(work->done); +		goto out_unlock; +	}  	list_add_tail(&work->list, &bdi->work_list); -	spin_unlock_bh(&bdi->wb_lock); -  	mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); +out_unlock: +	spin_unlock_bh(&bdi->wb_lock);  }  static void @@ -113,7 +129,7 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,  	work = kzalloc(sizeof(*work), GFP_ATOMIC);  	if (!work) {  		trace_writeback_nowork(bdi); -		mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); +		bdi_wakeup_thread(bdi);  		return;  	} @@ -160,7 +176,7 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi)  	 * writeback as soon as there is no other work to do.  	 */  	trace_writeback_wake_background(bdi); -	mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); +	bdi_wakeup_thread(bdi);  }  /* @@ -510,13 +526,16 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,  	}  	WARN_ON(inode->i_state & I_SYNC);  	/* -	 * Skip inode if it is clean. We don't want to mess with writeback -	 * lists in this function since flusher thread may be doing for example -	 * sync in parallel and if we move the inode, it could get skipped. So -	 * here we make sure inode is on some writeback list and leave it there -	 * unless we have completely cleaned the inode. +	 * Skip inode if it is clean and we have no outstanding writeback in +	 * WB_SYNC_ALL mode. We don't want to mess with writeback lists in this +	 * function since flusher thread may be doing for example sync in +	 * parallel and if we move the inode, it could get skipped. So here we +	 * make sure inode is on some writeback list and leave it there unless +	 * we have completely cleaned the inode.  	 */ -	if (!(inode->i_state & I_DIRTY)) +	if (!(inode->i_state & I_DIRTY) && +	    (wbc->sync_mode != WB_SYNC_ALL || +	     !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))  		goto out;  	inode->i_state |= I_SYNC;  	spin_unlock(&inode->i_lock); @@ -1013,7 +1032,7 @@ void bdi_writeback_workfn(struct work_struct *work)  	current->flags |= PF_SWAPWRITE;  	if (likely(!current_is_workqueue_rescuer() || -		   list_empty(&bdi->bdi_list))) { +		   !test_bit(BDI_registered, &bdi->state))) {  		/*  		 * The normal path.  Keep writing back @bdi until its  		 * work_list is empty.  Note that this path is also taken @@ -1035,10 +1054,10 @@ void bdi_writeback_workfn(struct work_struct *work)  		trace_writeback_pages_written(pages_written);  	} -	if (!list_empty(&bdi->work_list) || -	    (wb_has_dirty_io(wb) && dirty_writeback_interval)) -		queue_delayed_work(bdi_wq, &wb->dwork, -			msecs_to_jiffies(dirty_writeback_interval * 10)); +	if (!list_empty(&bdi->work_list)) +		mod_delayed_work(bdi_wq, &wb->dwork, 0); +	else if (wb_has_dirty_io(wb) && dirty_writeback_interval) +		bdi_wakeup_thread_delayed(bdi);  	current->flags &= ~PF_SWAPWRITE;  }  | 
