diff options
Diffstat (limited to 'fs/fs-writeback.c')
| -rw-r--r-- | fs/fs-writeback.c | 35 | 
1 files changed, 24 insertions, 11 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 091a36444972..29e4599f6fc1 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -778,19 +778,24 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,  				  struct wb_writeback_work *base_work,  				  bool skip_if_busy)  { -	int next_memcg_id = 0; -	struct bdi_writeback *wb; -	struct wb_iter iter; +	struct bdi_writeback *last_wb = NULL; +	struct bdi_writeback *wb = list_entry_rcu(&bdi->wb_list, +						struct bdi_writeback, bdi_node);  	might_sleep();  restart:  	rcu_read_lock(); -	bdi_for_each_wb(wb, bdi, &iter, next_memcg_id) { +	list_for_each_entry_continue_rcu(wb, &bdi->wb_list, bdi_node) {  		DEFINE_WB_COMPLETION_ONSTACK(fallback_work_done);  		struct wb_writeback_work fallback_work;  		struct wb_writeback_work *work;  		long nr_pages; +		if (last_wb) { +			wb_put(last_wb); +			last_wb = NULL; +		} +  		/* SYNC_ALL writes out I_DIRTY_TIME too */  		if (!wb_has_dirty_io(wb) &&  		    (base_work->sync_mode == WB_SYNC_NONE || @@ -819,12 +824,22 @@ restart:  		wb_queue_work(wb, work); -		next_memcg_id = wb->memcg_css->id + 1; +		/* +		 * Pin @wb so that it stays on @bdi->wb_list.  This allows +		 * continuing iteration from @wb after dropping and +		 * regrabbing rcu read lock. +		 */ +		wb_get(wb); +		last_wb = wb; +  		rcu_read_unlock();  		wb_wait_for_completion(bdi, &fallback_work_done);  		goto restart;  	}  	rcu_read_unlock(); + +	if (last_wb) +		wb_put(last_wb);  }  #else	/* CONFIG_CGROUP_WRITEBACK */ @@ -1857,12 +1872,11 @@ void wakeup_flusher_threads(long nr_pages, enum wb_reason reason)  	rcu_read_lock();  	list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {  		struct bdi_writeback *wb; -		struct wb_iter iter;  		if (!bdi_has_dirty_io(bdi))  			continue; -		bdi_for_each_wb(wb, bdi, &iter, 0) +		list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node)  			wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages),  					   false, reason);  	} @@ -1894,11 +1908,10 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)  	rcu_read_lock();  	list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {  		struct bdi_writeback *wb; -		struct wb_iter iter; -		bdi_for_each_wb(wb, bdi, &iter, 0) -			if (!list_empty(&bdi->wb.b_dirty_time)) -				wb_wakeup(&bdi->wb); +		list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) +			if (!list_empty(&wb->b_dirty_time)) +				wb_wakeup(wb);  	}  	rcu_read_unlock();  	schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);  | 
