diff options
Diffstat (limited to 'drivers/md/bcache/writeback.c')
| -rw-r--r-- | drivers/md/bcache/writeback.c | 133 | 
1 files changed, 120 insertions, 13 deletions
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 2714ed3991d1..22cbff551628 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -9,6 +9,9 @@  #include "bcache.h"  #include "btree.h"  #include "debug.h" +#include "writeback.h" + +#include <trace/events/bcache.h>  static struct workqueue_struct *dirty_wq; @@ -36,7 +39,7 @@ static void __update_writeback_rate(struct cached_dev *dc)  	int change = 0;  	int64_t error; -	int64_t dirty = atomic_long_read(&dc->disk.sectors_dirty); +	int64_t dirty = bcache_dev_sectors_dirty(&dc->disk);  	int64_t derivative = dirty - dc->disk.sectors_dirty_last;  	dc->disk.sectors_dirty_last = dirty; @@ -105,6 +108,31 @@ static bool dirty_pred(struct keybuf *buf, struct bkey *k)  	return KEY_DIRTY(k);  } +static bool dirty_full_stripe_pred(struct keybuf *buf, struct bkey *k) +{ +	uint64_t stripe; +	unsigned nr_sectors = KEY_SIZE(k); +	struct cached_dev *dc = container_of(buf, struct cached_dev, +					     writeback_keys); +	unsigned stripe_size = 1 << dc->disk.stripe_size_bits; + +	if (!KEY_DIRTY(k)) +		return false; + +	stripe = KEY_START(k) >> dc->disk.stripe_size_bits; +	while (1) { +		if (atomic_read(dc->disk.stripe_sectors_dirty + stripe) != +		    stripe_size) +			return false; + +		if (nr_sectors <= stripe_size) +			return true; + +		nr_sectors -= stripe_size; +		stripe++; +	} +} +  static void dirty_init(struct keybuf_key *w)  {  	struct dirty_io *io = w->private; @@ -149,7 +177,22 @@ static void refill_dirty(struct closure *cl)  		searched_from_start = true;  	} -	bch_refill_keybuf(dc->disk.c, buf, &end); +	if (dc->partial_stripes_expensive) { +		uint64_t i; + +		for (i = 0; i < dc->disk.nr_stripes; i++) +			if (atomic_read(dc->disk.stripe_sectors_dirty + i) == +			    1 << dc->disk.stripe_size_bits) +				goto full_stripes; + +		goto normal_refill; +full_stripes: +		bch_refill_keybuf(dc->disk.c, buf, &end, +				  dirty_full_stripe_pred); +	} else { +normal_refill: +		bch_refill_keybuf(dc->disk.c, buf, &end, dirty_pred); +	}  	if (bkey_cmp(&buf->last_scanned, &end) >= 0 && searched_from_start) {  		/* Searched the entire btree  - delay awhile */ @@ -181,10 +224,8 @@ void bch_writeback_queue(struct cached_dev *dc)  	}  } -void bch_writeback_add(struct cached_dev *dc, unsigned sectors) +void bch_writeback_add(struct cached_dev *dc)  { -	atomic_long_add(sectors, &dc->disk.sectors_dirty); -  	if (!atomic_read(&dc->has_dirty) &&  	    !atomic_xchg(&dc->has_dirty, 1)) {  		atomic_inc(&dc->count); @@ -203,6 +244,34 @@ void bch_writeback_add(struct cached_dev *dc, unsigned sectors)  	}  } +void bcache_dev_sectors_dirty_add(struct cache_set *c, unsigned inode, +				  uint64_t offset, int nr_sectors) +{ +	struct bcache_device *d = c->devices[inode]; +	unsigned stripe_size, stripe_offset; +	uint64_t stripe; + +	if (!d) +		return; + +	stripe_size = 1 << d->stripe_size_bits; +	stripe = offset >> d->stripe_size_bits; +	stripe_offset = offset & (stripe_size - 1); + +	while (nr_sectors) { +		int s = min_t(unsigned, abs(nr_sectors), +			      stripe_size - stripe_offset); + +		if (nr_sectors < 0) +			s = -s; + +		atomic_add(s, d->stripe_sectors_dirty + stripe); +		nr_sectors -= s; +		stripe_offset = 0; +		stripe++; +	} +} +  /* Background writeback - IO loop */  static void dirty_io_destructor(struct closure *cl) @@ -216,9 +285,10 @@ static void write_dirty_finish(struct closure *cl)  	struct dirty_io *io = container_of(cl, struct dirty_io, cl);  	struct keybuf_key *w = io->bio.bi_private;  	struct cached_dev *dc = io->dc; -	struct bio_vec *bv = bio_iovec_idx(&io->bio, io->bio.bi_vcnt); +	struct bio_vec *bv; +	int i; -	while (bv-- != io->bio.bi_io_vec) +	bio_for_each_segment_all(bv, &io->bio, i)  		__free_page(bv->bv_page);  	/* This is kind of a dumb way of signalling errors. */ @@ -236,10 +306,12 @@ static void write_dirty_finish(struct closure *cl)  		for (i = 0; i < KEY_PTRS(&w->key); i++)  			atomic_inc(&PTR_BUCKET(dc->disk.c, &w->key, i)->pin); -		pr_debug("clearing %s", pkey(&w->key));  		bch_btree_insert(&op, dc->disk.c);  		closure_sync(&op.cl); +		if (op.insert_collision) +			trace_bcache_writeback_collision(&w->key); +  		atomic_long_inc(op.insert_collision  				? &dc->disk.c->writeback_keys_failed  				: &dc->disk.c->writeback_keys_done); @@ -275,7 +347,6 @@ static void write_dirty(struct closure *cl)  	io->bio.bi_bdev		= io->dc->bdev;  	io->bio.bi_end_io	= dirty_endio; -	trace_bcache_write_dirty(&io->bio);  	closure_bio_submit(&io->bio, cl, &io->dc->disk);  	continue_at(cl, write_dirty_finish, dirty_wq); @@ -296,7 +367,6 @@ static void read_dirty_submit(struct closure *cl)  {  	struct dirty_io *io = container_of(cl, struct dirty_io, cl); -	trace_bcache_read_dirty(&io->bio);  	closure_bio_submit(&io->bio, cl, &io->dc->disk);  	continue_at(cl, write_dirty, dirty_wq); @@ -349,10 +419,10 @@ static void read_dirty(struct closure *cl)  		io->bio.bi_rw		= READ;  		io->bio.bi_end_io	= read_dirty_endio; -		if (bch_bio_alloc_pages(&io->bio, GFP_KERNEL)) +		if (bio_alloc_pages(&io->bio, GFP_KERNEL))  			goto err_free; -		pr_debug("%s", pkey(&w->key)); +		trace_bcache_writeback(&w->key);  		closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl); @@ -375,12 +445,49 @@ err:  	refill_dirty(cl);  } +/* Init */ + +static int bch_btree_sectors_dirty_init(struct btree *b, struct btree_op *op, +					struct cached_dev *dc) +{ +	struct bkey *k; +	struct btree_iter iter; + +	bch_btree_iter_init(b, &iter, &KEY(dc->disk.id, 0, 0)); +	while ((k = bch_btree_iter_next_filter(&iter, b, bch_ptr_bad))) +		if (!b->level) { +			if (KEY_INODE(k) > dc->disk.id) +				break; + +			if (KEY_DIRTY(k)) +				bcache_dev_sectors_dirty_add(b->c, dc->disk.id, +							     KEY_START(k), +							     KEY_SIZE(k)); +		} else { +			btree(sectors_dirty_init, k, b, op, dc); +			if (KEY_INODE(k) > dc->disk.id) +				break; + +			cond_resched(); +		} + +	return 0; +} + +void bch_sectors_dirty_init(struct cached_dev *dc) +{ +	struct btree_op op; + +	bch_btree_op_init_stack(&op); +	btree_root(sectors_dirty_init, dc->disk.c, &op, dc); +} +  void bch_cached_dev_writeback_init(struct cached_dev *dc)  {  	closure_init_unlocked(&dc->writeback);  	init_rwsem(&dc->writeback_lock); -	bch_keybuf_init(&dc->writeback_keys, dirty_pred); +	bch_keybuf_init(&dc->writeback_keys);  	dc->writeback_metadata		= true;  	dc->writeback_running		= true;  | 
