Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 208bca08 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'writeback-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/wfg/linux

* 'writeback-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/wfg/linux:
  writeback: Add a 'reason' to wb_writeback_work
  writeback: send work item to queue_io, move_expired_inodes
  writeback: trace event balance_dirty_pages
  writeback: trace event bdi_dirty_ratelimit
  writeback: fix ppc compile warnings on do_div(long long, unsigned long)
  writeback: per-bdi background threshold
  writeback: dirty position control - bdi reserve area
  writeback: control dirty pause time
  writeback: limit max dirty pause time
  writeback: IO-less balance_dirty_pages()
  writeback: per task dirty rate limit
  writeback: stabilize bdi->dirty_ratelimit
  writeback: dirty rate control
  writeback: add bg_threshold parameter to __bdi_update_bandwidth()
  writeback: dirty position control
  writeback: account per-bdi accumulated dirtied pages
parents 6aad3738 0e175a18
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -3340,7 +3340,8 @@ static int shrink_delalloc(struct btrfs_trans_handle *trans,
		smp_mb();
		nr_pages = min_t(unsigned long, nr_pages,
		       root->fs_info->delalloc_bytes >> PAGE_CACHE_SHIFT);
		writeback_inodes_sb_nr_if_idle(root->fs_info->sb, nr_pages);
		writeback_inodes_sb_nr_if_idle(root->fs_info->sb, nr_pages,
						WB_REASON_FS_FREE_SPACE);

		spin_lock(&space_info->lock);
		if (reserved > space_info->bytes_reserved)
+1 −1
Original line number Diff line number Diff line
@@ -288,7 +288,7 @@ static void free_more_memory(void)
	struct zone *zone;
	int nid;

	wakeup_flusher_threads(1024);
	wakeup_flusher_threads(1024, WB_REASON_FREE_MORE_MEM);
	yield();

	for_each_online_node(nid) {
+1 −1
Original line number Diff line number Diff line
@@ -2372,7 +2372,7 @@ static int ext4_nonda_switch(struct super_block *sb)
	 * start pushing delalloc when 1/2 of free blocks are dirty.
	 */
	if (free_blocks < 2 * dirty_blocks)
		writeback_inodes_sb_if_idle(sb);
		writeback_inodes_sb_if_idle(sb, WB_REASON_FS_FREE_SPACE);

	return 0;
}
+57 −27
Original line number Diff line number Diff line
@@ -41,11 +41,23 @@ struct wb_writeback_work {
	unsigned int for_kupdate:1;
	unsigned int range_cyclic:1;
	unsigned int for_background:1;
	enum wb_reason reason;		/* why was writeback initiated? */

	struct list_head list;		/* pending work list */
	struct completion *done;	/* set if the caller waits */
};

const char *wb_reason_name[] = {
	[WB_REASON_BACKGROUND]		= "background",
	[WB_REASON_TRY_TO_FREE_PAGES]	= "try_to_free_pages",
	[WB_REASON_SYNC]		= "sync",
	[WB_REASON_PERIODIC]		= "periodic",
	[WB_REASON_LAPTOP_TIMER]	= "laptop_timer",
	[WB_REASON_FREE_MORE_MEM]	= "free_more_memory",
	[WB_REASON_FS_FREE_SPACE]	= "fs_free_space",
	[WB_REASON_FORKER_THREAD]	= "forker_thread"
};

/*
 * Include the creation of the trace points after defining the
 * wb_writeback_work structure so that the definition remains local to this
@@ -115,7 +127,7 @@ static void bdi_queue_work(struct backing_dev_info *bdi,

static void
__bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
		      bool range_cyclic)
		      bool range_cyclic, enum wb_reason reason)
{
	struct wb_writeback_work *work;

@@ -135,6 +147,7 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
	work->sync_mode	= WB_SYNC_NONE;
	work->nr_pages	= nr_pages;
	work->range_cyclic = range_cyclic;
	work->reason	= reason;

	bdi_queue_work(bdi, work);
}
@@ -150,9 +163,10 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
 *   completion. Caller need not hold sb s_umount semaphore.
 *
 */
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages)
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
			enum wb_reason reason)
{
	__bdi_start_writeback(bdi, nr_pages, true);
	__bdi_start_writeback(bdi, nr_pages, true, reason);
}

/**
@@ -251,7 +265,7 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t)
 */
static int move_expired_inodes(struct list_head *delaying_queue,
			       struct list_head *dispatch_queue,
			       unsigned long *older_than_this)
			       struct wb_writeback_work *work)
{
	LIST_HEAD(tmp);
	struct list_head *pos, *node;
@@ -262,8 +276,8 @@ static int move_expired_inodes(struct list_head *delaying_queue,

	while (!list_empty(delaying_queue)) {
		inode = wb_inode(delaying_queue->prev);
		if (older_than_this &&
		    inode_dirtied_after(inode, *older_than_this))
		if (work->older_than_this &&
		    inode_dirtied_after(inode, *work->older_than_this))
			break;
		if (sb && sb != inode->i_sb)
			do_sb_sort = 1;
@@ -302,13 +316,13 @@ static int move_expired_inodes(struct list_head *delaying_queue,
 *                                           |
 *                                           +--> dequeue for IO
 */
static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this)
static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work)
{
	int moved;
	assert_spin_locked(&wb->list_lock);
	list_splice_init(&wb->b_more_io, &wb->b_io);
	moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this);
	trace_writeback_queue_io(wb, older_than_this, moved);
	moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work);
	trace_writeback_queue_io(wb, work, moved);
}

static int write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -641,31 +655,40 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
	return wrote;
}

long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages)
long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
				enum wb_reason reason)
{
	struct wb_writeback_work work = {
		.nr_pages	= nr_pages,
		.sync_mode	= WB_SYNC_NONE,
		.range_cyclic	= 1,
		.reason		= reason,
	};

	spin_lock(&wb->list_lock);
	if (list_empty(&wb->b_io))
		queue_io(wb, NULL);
		queue_io(wb, &work);
	__writeback_inodes_wb(wb, &work);
	spin_unlock(&wb->list_lock);

	return nr_pages - work.nr_pages;
}

static inline bool over_bground_thresh(void)
static bool over_bground_thresh(struct backing_dev_info *bdi)
{
	unsigned long background_thresh, dirty_thresh;

	global_dirty_limits(&background_thresh, &dirty_thresh);

	return (global_page_state(NR_FILE_DIRTY) +
		global_page_state(NR_UNSTABLE_NFS) > background_thresh);
	if (global_page_state(NR_FILE_DIRTY) +
	    global_page_state(NR_UNSTABLE_NFS) > background_thresh)
		return true;

	if (bdi_stat(bdi, BDI_RECLAIMABLE) >
				bdi_dirty_limit(bdi, background_thresh))
		return true;

	return false;
}

/*
@@ -675,7 +698,7 @@ static inline bool over_bground_thresh(void)
static void wb_update_bandwidth(struct bdi_writeback *wb,
				unsigned long start_time)
{
	__bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, start_time);
	__bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, 0, start_time);
}

/*
@@ -727,7 +750,7 @@ static long wb_writeback(struct bdi_writeback *wb,
		 * For background writeout, stop when we are below the
		 * background dirty threshold
		 */
		if (work->for_background && !over_bground_thresh())
		if (work->for_background && !over_bground_thresh(wb->bdi))
			break;

		if (work->for_kupdate) {
@@ -738,7 +761,7 @@ static long wb_writeback(struct bdi_writeback *wb,

		trace_writeback_start(wb->bdi, work);
		if (list_empty(&wb->b_io))
			queue_io(wb, work->older_than_this);
			queue_io(wb, work);
		if (work->sb)
			progress = writeback_sb_inodes(work->sb, wb, work);
		else
@@ -811,13 +834,14 @@ static unsigned long get_nr_dirty_pages(void)

static long wb_check_background_flush(struct bdi_writeback *wb)
{
	if (over_bground_thresh()) {
	if (over_bground_thresh(wb->bdi)) {

		struct wb_writeback_work work = {
			.nr_pages	= LONG_MAX,
			.sync_mode	= WB_SYNC_NONE,
			.for_background	= 1,
			.range_cyclic	= 1,
			.reason		= WB_REASON_BACKGROUND,
		};

		return wb_writeback(wb, &work);
@@ -851,6 +875,7 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
			.sync_mode	= WB_SYNC_NONE,
			.for_kupdate	= 1,
			.range_cyclic	= 1,
			.reason		= WB_REASON_PERIODIC,
		};

		return wb_writeback(wb, &work);
@@ -969,7 +994,7 @@ int bdi_writeback_thread(void *data)
 * Start writeback of `nr_pages' pages.  If `nr_pages' is zero, write back
 * the whole world.
 */
void wakeup_flusher_threads(long nr_pages)
void wakeup_flusher_threads(long nr_pages, enum wb_reason reason)
{
	struct backing_dev_info *bdi;

@@ -982,7 +1007,7 @@ void wakeup_flusher_threads(long nr_pages)
	list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
		if (!bdi_has_dirty_io(bdi))
			continue;
		__bdi_start_writeback(bdi, nr_pages, false);
		__bdi_start_writeback(bdi, nr_pages, false, reason);
	}
	rcu_read_unlock();
}
@@ -1203,7 +1228,9 @@ static void wait_sb_inodes(struct super_block *sb)
 * on how many (if any) will be written, and this function does not wait
 * for IO completion of submitted IO.
 */
void writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr)
void writeback_inodes_sb_nr(struct super_block *sb,
			    unsigned long nr,
			    enum wb_reason reason)
{
	DECLARE_COMPLETION_ONSTACK(done);
	struct wb_writeback_work work = {
@@ -1212,6 +1239,7 @@ void writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr)
		.tagged_writepages	= 1,
		.done			= &done,
		.nr_pages		= nr,
		.reason			= reason,
	};

	WARN_ON(!rwsem_is_locked(&sb->s_umount));
@@ -1228,9 +1256,9 @@ EXPORT_SYMBOL(writeback_inodes_sb_nr);
 * on how many (if any) will be written, and this function does not wait
 * for IO completion of submitted IO.
 */
void writeback_inodes_sb(struct super_block *sb)
void writeback_inodes_sb(struct super_block *sb, enum wb_reason reason)
{
	return writeback_inodes_sb_nr(sb, get_nr_dirty_pages());
	return writeback_inodes_sb_nr(sb, get_nr_dirty_pages(), reason);
}
EXPORT_SYMBOL(writeback_inodes_sb);

@@ -1241,11 +1269,11 @@ EXPORT_SYMBOL(writeback_inodes_sb);
 * Invoke writeback_inodes_sb if no writeback is currently underway.
 * Returns 1 if writeback was started, 0 if not.
 */
int writeback_inodes_sb_if_idle(struct super_block *sb)
int writeback_inodes_sb_if_idle(struct super_block *sb, enum wb_reason reason)
{
	if (!writeback_in_progress(sb->s_bdi)) {
		down_read(&sb->s_umount);
		writeback_inodes_sb(sb);
		writeback_inodes_sb(sb, reason);
		up_read(&sb->s_umount);
		return 1;
	} else
@@ -1262,11 +1290,12 @@ EXPORT_SYMBOL(writeback_inodes_sb_if_idle);
 * Returns 1 if writeback was started, 0 if not.
 */
int writeback_inodes_sb_nr_if_idle(struct super_block *sb,
				   unsigned long nr)
				   unsigned long nr,
				   enum wb_reason reason)
{
	if (!writeback_in_progress(sb->s_bdi)) {
		down_read(&sb->s_umount);
		writeback_inodes_sb_nr(sb, nr);
		writeback_inodes_sb_nr(sb, nr, reason);
		up_read(&sb->s_umount);
		return 1;
	} else
@@ -1290,6 +1319,7 @@ void sync_inodes_sb(struct super_block *sb)
		.nr_pages	= LONG_MAX,
		.range_cyclic	= 0,
		.done		= &done,
		.reason		= WB_REASON_SYNC,
	};

	WARN_ON(!rwsem_is_locked(&sb->s_umount));
+1 −1
Original line number Diff line number Diff line
@@ -286,7 +286,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
		/* caller already holds s_umount */
		if (sb->s_flags & MS_RDONLY)
			return -EROFS;
		writeback_inodes_sb(sb);
		writeback_inodes_sb(sb, WB_REASON_SYNC);
		return 0;
	default:
		return -EINVAL;
Loading