Commit 6e6ccc67 authored by Michael Lyle's avatar Michael Lyle Committed by Jens Axboe

bcache: writeback: properly order backing device IO

Writeback keys are presently iterated and dispatched for writeback in
order of the logical block address on the backing device.  Multiple may
be, in parallel, read from the cache device and then written back
(especially when there are contiguous I/O).

However-- there was no guarantee with the existing code that the writes
would be issued in LBA order, as the reads from the cache device are
often re-ordered.  In turn, when writing back quickly, the backing disk
often has to seek backwards-- this slows writeback and increases
utilization.

This patch introduces an ordering mechanism that guarantees that the
original order of issue is maintained for the write portion of the I/O.
Performance for writeback is significantly improved when there are
multiple contiguous keys or high writeback rates.
Signed-off-by: default avatarMichael Lyle <mlyle@lyle.org>
Reviewed-by: default avatarTang Junhui <tang.junhui@zte.com.cn>
Tested-by: default avatarTang Junhui <tang.junhui@zte.com.cn>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 539d39eb
...@@ -330,6 +330,14 @@ struct cached_dev { ...@@ -330,6 +330,14 @@ struct cached_dev {
struct keybuf writeback_keys; struct keybuf writeback_keys;
/*
* Order the write-half of writeback operations strongly in dispatch
* order. (Maintain LBA order; don't allow reads completing out of
* order to re-order the writes...)
*/
struct closure_waitlist writeback_ordering_wait;
atomic_t writeback_sequence_next;
/* For tracking sequential IO */ /* For tracking sequential IO */
#define RECENT_IO_BITS 7 #define RECENT_IO_BITS 7
#define RECENT_IO (1 << RECENT_IO_BITS) #define RECENT_IO (1 << RECENT_IO_BITS)
......
...@@ -116,6 +116,7 @@ static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors) ...@@ -116,6 +116,7 @@ static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
struct dirty_io { struct dirty_io {
struct closure cl; struct closure cl;
struct cached_dev *dc; struct cached_dev *dc;
uint16_t sequence;
struct bio bio; struct bio bio;
}; };
...@@ -194,6 +195,27 @@ static void write_dirty(struct closure *cl) ...@@ -194,6 +195,27 @@ static void write_dirty(struct closure *cl)
{ {
struct dirty_io *io = container_of(cl, struct dirty_io, cl); struct dirty_io *io = container_of(cl, struct dirty_io, cl);
struct keybuf_key *w = io->bio.bi_private; struct keybuf_key *w = io->bio.bi_private;
struct cached_dev *dc = io->dc;
uint16_t next_sequence;
if (atomic_read(&dc->writeback_sequence_next) != io->sequence) {
/* Not our turn to write; wait for a write to complete */
closure_wait(&dc->writeback_ordering_wait, cl);
if (atomic_read(&dc->writeback_sequence_next) == io->sequence) {
/*
* Edge case-- it happened in indeterminate order
* relative to when we were added to wait list..
*/
closure_wake_up(&dc->writeback_ordering_wait);
}
continue_at(cl, write_dirty, io->dc->writeback_write_wq);
return;
}
next_sequence = io->sequence + 1;
/* /*
* IO errors are signalled using the dirty bit on the key. * IO errors are signalled using the dirty bit on the key.
...@@ -211,6 +233,9 @@ static void write_dirty(struct closure *cl) ...@@ -211,6 +233,9 @@ static void write_dirty(struct closure *cl)
closure_bio_submit(&io->bio, cl); closure_bio_submit(&io->bio, cl);
} }
atomic_set(&dc->writeback_sequence_next, next_sequence);
closure_wake_up(&dc->writeback_ordering_wait);
continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq); continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq);
} }
...@@ -242,7 +267,10 @@ static void read_dirty(struct cached_dev *dc) ...@@ -242,7 +267,10 @@ static void read_dirty(struct cached_dev *dc)
int nk, i; int nk, i;
struct dirty_io *io; struct dirty_io *io;
struct closure cl; struct closure cl;
uint16_t sequence = 0;
BUG_ON(!llist_empty(&dc->writeback_ordering_wait.list));
atomic_set(&dc->writeback_sequence_next, sequence);
closure_init_stack(&cl); closure_init_stack(&cl);
/* /*
...@@ -303,6 +331,7 @@ static void read_dirty(struct cached_dev *dc) ...@@ -303,6 +331,7 @@ static void read_dirty(struct cached_dev *dc)
w->private = io; w->private = io;
io->dc = dc; io->dc = dc;
io->sequence = sequence++;
dirty_init(w); dirty_init(w);
bio_set_op_attrs(&io->bio, REQ_OP_READ, 0); bio_set_op_attrs(&io->bio, REQ_OP_READ, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment