521 lines · 12.1 KB · C
RawDownload
/* * background writeback - scan btree for dirty data and write it to the backing * device * * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> * Copyright 2012 Google, Inc. */ #include "bcache.h"#include "btree.h"#include "debug.h"#include "writeback.h" #include <trace/events/bcache.h> static struct workqueue_struct *dirty_wq; static void read_dirty(struct closure *); struct dirty_io {	struct closure		cl;	struct cached_dev	*dc;	struct bio		bio;}; /* Rate limiting */ static void __update_writeback_rate(struct cached_dev *dc){	struct cache_set *c = dc->disk.c;	uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size;	uint64_t cache_dirty_target =		div_u64(cache_sectors * dc->writeback_percent, 100); 	int64_t target = div64_u64(cache_dirty_target * bdev_sectors(dc->bdev),				   c->cached_dev_sectors); 	/* PD controller */ 	int change = 0;	int64_t error;	int64_t dirty = bcache_dev_sectors_dirty(&dc->disk);	int64_t derivative = dirty - dc->disk.sectors_dirty_last; 	dc->disk.sectors_dirty_last = dirty; 	derivative *= dc->writeback_rate_d_term;	derivative = clamp(derivative, -dirty, dirty); 	derivative = ewma_add(dc->disk.sectors_dirty_derivative, derivative,			      dc->writeback_rate_d_smooth, 0); 	/* Avoid divide by zero */	if (!target)		goto out; 	error = div64_s64((dirty + derivative - target) << 8, target); 	change = div_s64((dc->writeback_rate.rate * error) >> 8,			 dc->writeback_rate_p_term_inverse); 	/* Don't increase writeback rate if the device isn't keeping up */	if (change > 0 &&	    time_after64(local_clock(),			 dc->writeback_rate.next + 10 * NSEC_PER_MSEC))		change = 0; 	dc->writeback_rate.rate =		clamp_t(int64_t, dc->writeback_rate.rate + change,			1, NSEC_PER_MSEC);out:	dc->writeback_rate_derivative = derivative;	dc->writeback_rate_change = change;	dc->writeback_rate_target = target; 	schedule_delayed_work(&dc->writeback_rate_update,			      dc->writeback_rate_update_seconds * HZ);} static void update_writeback_rate(struct work_struct *work){	struct cached_dev *dc = container_of(to_delayed_work(work),					     struct cached_dev,					     writeback_rate_update); 	down_read(&dc->writeback_lock); 	if (atomic_read(&dc->has_dirty) &&	    dc->writeback_percent)		__update_writeback_rate(dc); 	up_read(&dc->writeback_lock);} static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors){	if (atomic_read(&dc->disk.detaching) ||	    !dc->writeback_percent)		return 0; 	return bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);} /* Background writeback */ 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;	struct bio *bio = &io->bio; 	bio_init(bio);	if (!io->dc->writeback_percent)		bio_set_prio(bio, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); 	bio->bi_size		= KEY_SIZE(&w->key) << 9;	bio->bi_max_vecs	= DIV_ROUND_UP(KEY_SIZE(&w->key), PAGE_SECTORS);	bio->bi_private		= w;	bio->bi_io_vec		= bio->bi_inline_vecs;	bch_bio_map(bio, NULL);} static void refill_dirty(struct closure *cl){	struct cached_dev *dc = container_of(cl, struct cached_dev,					     writeback.cl);	struct keybuf *buf = &dc->writeback_keys;	bool searched_from_start = false;	struct bkey end = MAX_KEY;	SET_KEY_INODE(&end, dc->disk.id); 	if (!atomic_read(&dc->disk.detaching) &&	    !dc->writeback_running)		closure_return(cl); 	down_write(&dc->writeback_lock); 	if (!atomic_read(&dc->has_dirty)) {		SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);		bch_write_bdev_super(dc, NULL); 		up_write(&dc->writeback_lock);		closure_return(cl);	} 	if (bkey_cmp(&buf->last_scanned, &end) >= 0) {		buf->last_scanned = KEY(dc->disk.id, 0, 0);		searched_from_start = true;	} 	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 */ 		if (RB_EMPTY_ROOT(&buf->keys)) {			atomic_set(&dc->has_dirty, 0);			cached_dev_put(dc);		} 		if (!atomic_read(&dc->disk.detaching))			closure_delay(&dc->writeback, dc->writeback_delay * HZ);	} 	up_write(&dc->writeback_lock); 	ratelimit_reset(&dc->writeback_rate); 	/* Punt to workqueue only so we don't recurse and blow the stack */	continue_at(cl, read_dirty, dirty_wq);} void bch_writeback_queue(struct cached_dev *dc){	if (closure_trylock(&dc->writeback.cl, &dc->disk.cl)) {		if (!atomic_read(&dc->disk.detaching))			closure_delay(&dc->writeback, dc->writeback_delay * HZ); 		continue_at(&dc->writeback.cl, refill_dirty, dirty_wq);	}} void bch_writeback_add(struct cached_dev *dc){	if (!atomic_read(&dc->has_dirty) &&	    !atomic_xchg(&dc->has_dirty, 1)) {		atomic_inc(&dc->count); 		if (BDEV_STATE(&dc->sb) != BDEV_STATE_DIRTY) {			SET_BDEV_STATE(&dc->sb, BDEV_STATE_DIRTY);			/* XXX: should do this synchronously */			bch_write_bdev_super(dc, NULL);		} 		bch_writeback_queue(dc); 		if (dc->writeback_percent)			schedule_delayed_work(&dc->writeback_rate_update,				      dc->writeback_rate_update_seconds * HZ);	}} 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){	struct dirty_io *io = container_of(cl, struct dirty_io, cl);	kfree(io);} 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;	int i; 	bio_for_each_segment_all(bv, &io->bio, i)		__free_page(bv->bv_page); 	/* This is kind of a dumb way of signalling errors. */	if (KEY_DIRTY(&w->key)) {		unsigned i;		struct btree_op op;		bch_btree_op_init_stack(&op); 		op.type = BTREE_REPLACE;		bkey_copy(&op.replace, &w->key); 		SET_KEY_DIRTY(&w->key, false);		bch_keylist_add(&op.keys, &w->key); 		for (i = 0; i < KEY_PTRS(&w->key); i++)			atomic_inc(&PTR_BUCKET(dc->disk.c, &w->key, i)->pin); 		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);	} 	bch_keybuf_del(&dc->writeback_keys, w);	atomic_dec_bug(&dc->in_flight); 	closure_wake_up(&dc->writeback_wait); 	closure_return_with_destructor(cl, dirty_io_destructor);} static void dirty_endio(struct bio *bio, int error){	struct keybuf_key *w = bio->bi_private;	struct dirty_io *io = w->private; 	if (error)		SET_KEY_DIRTY(&w->key, false); 	closure_put(&io->cl);} static void write_dirty(struct closure *cl){	struct dirty_io *io = container_of(cl, struct dirty_io, cl);	struct keybuf_key *w = io->bio.bi_private; 	dirty_init(w);	io->bio.bi_rw		= WRITE;	io->bio.bi_sector	= KEY_START(&w->key);	io->bio.bi_bdev		= io->dc->bdev;	io->bio.bi_end_io	= dirty_endio; 	closure_bio_submit(&io->bio, cl, &io->dc->disk); 	continue_at(cl, write_dirty_finish, dirty_wq);} static void read_dirty_endio(struct bio *bio, int error){	struct keybuf_key *w = bio->bi_private;	struct dirty_io *io = w->private; 	bch_count_io_errors(PTR_CACHE(io->dc->disk.c, &w->key, 0),			    error, "reading dirty data from cache"); 	dirty_endio(bio, error);} static void read_dirty_submit(struct closure *cl){	struct dirty_io *io = container_of(cl, struct dirty_io, cl); 	closure_bio_submit(&io->bio, cl, &io->dc->disk); 	continue_at(cl, write_dirty, dirty_wq);} static void read_dirty(struct closure *cl){	struct cached_dev *dc = container_of(cl, struct cached_dev,					     writeback.cl);	unsigned delay = writeback_delay(dc, 0);	struct keybuf_key *w;	struct dirty_io *io; 	/*	 * XXX: if we error, background writeback just spins. Should use some	 * mempools.	 */ 	while (1) {		w = bch_keybuf_next(&dc->writeback_keys);		if (!w)			break; 		BUG_ON(ptr_stale(dc->disk.c, &w->key, 0)); 		if (delay > 0 &&		    (KEY_START(&w->key) != dc->last_read ||		     jiffies_to_msecs(delay) > 50)) {			w->private = NULL; 			closure_delay(&dc->writeback, delay);			continue_at(cl, read_dirty, dirty_wq);		} 		dc->last_read	= KEY_OFFSET(&w->key); 		io = kzalloc(sizeof(struct dirty_io) + sizeof(struct bio_vec)			     * DIV_ROUND_UP(KEY_SIZE(&w->key), PAGE_SECTORS),			     GFP_KERNEL);		if (!io)			goto err; 		w->private	= io;		io->dc		= dc; 		dirty_init(w);		io->bio.bi_sector	= PTR_OFFSET(&w->key, 0);		io->bio.bi_bdev		= PTR_CACHE(dc->disk.c,						    &w->key, 0)->bdev;		io->bio.bi_rw		= READ;		io->bio.bi_end_io	= read_dirty_endio; 		if (bio_alloc_pages(&io->bio, GFP_KERNEL))			goto err_free; 		trace_bcache_writeback(&w->key); 		closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl); 		delay = writeback_delay(dc, KEY_SIZE(&w->key)); 		atomic_inc(&dc->in_flight); 		if (!closure_wait_event(&dc->writeback_wait, cl,					atomic_read(&dc->in_flight) < 64))			continue_at(cl, read_dirty, dirty_wq);	} 	if (0) {err_free:		kfree(w->private);err:		bch_keybuf_del(&dc->writeback_keys, w);	} 	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); 	dc->writeback_metadata		= true;	dc->writeback_running		= true;	dc->writeback_percent		= 10;	dc->writeback_delay		= 30;	dc->writeback_rate.rate		= 1024; 	dc->writeback_rate_update_seconds = 30;	dc->writeback_rate_d_term	= 16;	dc->writeback_rate_p_term_inverse = 64;

Lines 1–500 of 521.