mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
misc bcache fixes from ejt
This commit is contained in:
parent
0da296003d
commit
8b26a007b1
@ -134,7 +134,6 @@ struct async_engine {
|
|||||||
struct io_engine e;
|
struct io_engine e;
|
||||||
io_context_t aio_context;
|
io_context_t aio_context;
|
||||||
struct cb_set *cbs;
|
struct cb_set *cbs;
|
||||||
unsigned max_io;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct async_engine *_to_async(struct io_engine *e)
|
static struct async_engine *_to_async(struct io_engine *e)
|
||||||
@ -185,7 +184,10 @@ static bool _async_issue(struct io_engine *ioe, enum dir d, int fd,
|
|||||||
cb->cb.aio_lio_opcode = (d == DIR_READ) ? IO_CMD_PREAD : IO_CMD_PWRITE;
|
cb->cb.aio_lio_opcode = (d == DIR_READ) ? IO_CMD_PREAD : IO_CMD_PWRITE;
|
||||||
|
|
||||||
cb_array[0] = &cb->cb;
|
cb_array[0] = &cb->cb;
|
||||||
|
do {
|
||||||
r = io_submit(e->aio_context, 1, cb_array);
|
r = io_submit(e->aio_context, 1, cb_array);
|
||||||
|
} while (r == -EAGAIN);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_sys_warn("io_submit");
|
log_sys_warn("io_submit");
|
||||||
_cb_free(e->cbs, cb);
|
_cb_free(e->cbs, cb);
|
||||||
@ -206,7 +208,10 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
|
|||||||
struct async_engine *e = _to_async(ioe);
|
struct async_engine *e = _to_async(ioe);
|
||||||
|
|
||||||
memset(&event, 0, sizeof(event));
|
memset(&event, 0, sizeof(event));
|
||||||
|
do {
|
||||||
r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
|
r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
|
||||||
|
} while (r == -EINTR);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_sys_warn("io_getevents");
|
log_sys_warn("io_getevents");
|
||||||
return false;
|
return false;
|
||||||
@ -223,6 +228,7 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
|
|||||||
else if ((int) ev->res < 0)
|
else if ((int) ev->res < 0)
|
||||||
fn(cb->context, (int) ev->res);
|
fn(cb->context, (int) ev->res);
|
||||||
|
|
||||||
|
// FIXME: dct added this. a short read is ok?!
|
||||||
else if (ev->res >= (1 << SECTOR_SHIFT)) {
|
else if (ev->res >= (1 << SECTOR_SHIFT)) {
|
||||||
/* minimum acceptable read is 1 sector */
|
/* minimum acceptable read is 1 sector */
|
||||||
fn((void *) cb->context, 0);
|
fn((void *) cb->context, 0);
|
||||||
@ -238,13 +244,12 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned _async_max_io(struct io_engine *ioe)
|
static unsigned _async_max_io(struct io_engine *e)
|
||||||
{
|
{
|
||||||
struct async_engine *e = _to_async(ioe);
|
return MAX_IO;
|
||||||
return e->max_io;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct io_engine *create_async_io_engine(unsigned max_io)
|
struct io_engine *create_async_io_engine(void)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct async_engine *e = dm_malloc(sizeof(*e));
|
struct async_engine *e = dm_malloc(sizeof(*e));
|
||||||
@ -252,22 +257,20 @@ struct io_engine *create_async_io_engine(unsigned max_io)
|
|||||||
if (!e)
|
if (!e)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
e->max_io = max_io;
|
|
||||||
|
|
||||||
e->e.destroy = _async_destroy;
|
e->e.destroy = _async_destroy;
|
||||||
e->e.issue = _async_issue;
|
e->e.issue = _async_issue;
|
||||||
e->e.wait = _async_wait;
|
e->e.wait = _async_wait;
|
||||||
e->e.max_io = _async_max_io;
|
e->e.max_io = _async_max_io;
|
||||||
|
|
||||||
e->aio_context = 0;
|
e->aio_context = 0;
|
||||||
r = io_setup(max_io, &e->aio_context);
|
r = io_setup(MAX_IO, &e->aio_context);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_warn("io_setup failed");
|
log_warn("io_setup failed");
|
||||||
dm_free(e);
|
dm_free(e);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->cbs = _cb_set_create(max_io);
|
e->cbs = _cb_set_create(MAX_IO);
|
||||||
if (!e->cbs) {
|
if (!e->cbs) {
|
||||||
log_warn("couldn't create control block set");
|
log_warn("couldn't create control block set");
|
||||||
dm_free(e);
|
dm_free(e);
|
||||||
@ -535,10 +538,17 @@ static void _complete_io(void *context, int err)
|
|||||||
*/
|
*/
|
||||||
dm_list_del(&b->list);
|
dm_list_del(&b->list);
|
||||||
|
|
||||||
if (b->error)
|
if (b->error) {
|
||||||
|
if (b->io_dir == DIR_READ) {
|
||||||
|
// We can just forget about this block, since there's
|
||||||
|
// no dirty data to be written back.
|
||||||
|
_hash_remove(b);
|
||||||
|
dm_list_add(&cache->free, &b->list);
|
||||||
|
|
||||||
|
} else
|
||||||
dm_list_add(&cache->errored, &b->list);
|
dm_list_add(&cache->errored, &b->list);
|
||||||
|
|
||||||
else {
|
} else {
|
||||||
_clear_flags(b, BF_DIRTY);
|
_clear_flags(b, BF_DIRTY);
|
||||||
_link_block(b);
|
_link_block(b);
|
||||||
}
|
}
|
||||||
@ -548,35 +558,34 @@ static void _complete_io(void *context, int err)
|
|||||||
* |b->list| should be valid (either pointing to itself, on one of the other
|
* |b->list| should be valid (either pointing to itself, on one of the other
|
||||||
* lists.
|
* lists.
|
||||||
*/
|
*/
|
||||||
static bool _issue_low_level(struct block *b, enum dir d)
|
static void _issue_low_level(struct block *b, enum dir d)
|
||||||
{
|
{
|
||||||
struct bcache *cache = b->cache;
|
struct bcache *cache = b->cache;
|
||||||
sector_t sb = b->index * cache->block_sectors;
|
sector_t sb = b->index * cache->block_sectors;
|
||||||
sector_t se = sb + cache->block_sectors;
|
sector_t se = sb + cache->block_sectors;
|
||||||
|
|
||||||
if (_test_flags(b, BF_IO_PENDING))
|
if (_test_flags(b, BF_IO_PENDING))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
|
b->io_dir = d;
|
||||||
_set_flags(b, BF_IO_PENDING);
|
_set_flags(b, BF_IO_PENDING);
|
||||||
dm_list_add(&cache->io_pending, &b->list);
|
dm_list_add(&cache->io_pending, &b->list);
|
||||||
|
|
||||||
if (!cache->engine->issue(cache->engine, d, b->fd, sb, se, b->data, b)) {
|
if (!cache->engine->issue(cache->engine, d, b->fd, sb, se, b->data, b)) {
|
||||||
_complete_io(b, -EIO);
|
_complete_io(b, -EIO);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool _issue_read(struct block *b)
|
static inline void _issue_read(struct block *b)
|
||||||
{
|
{
|
||||||
return _issue_low_level(b, DIR_READ);
|
_issue_low_level(b, DIR_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool _issue_write(struct block *b)
|
static inline void _issue_write(struct block *b)
|
||||||
{
|
{
|
||||||
return _issue_low_level(b, DIR_WRITE);
|
_issue_low_level(b, DIR_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _wait_io(struct bcache *cache)
|
static bool _wait_io(struct bcache *cache)
|
||||||
@ -903,8 +912,13 @@ void bcache_put(struct block *b)
|
|||||||
_preemptive_writeback(b->cache);
|
_preemptive_writeback(b->cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bcache_flush(struct bcache *cache)
|
bool bcache_flush(struct bcache *cache)
|
||||||
{
|
{
|
||||||
|
// Only dirty data is on the errored list, since bad read blocks get
|
||||||
|
// recycled straight away. So we put these back on the dirty list, and
|
||||||
|
// try and rewrite everything.
|
||||||
|
dm_list_splice(&cache->dirty, &cache->errored);
|
||||||
|
|
||||||
while (!dm_list_empty(&cache->dirty)) {
|
while (!dm_list_empty(&cache->dirty)) {
|
||||||
struct block *b = dm_list_item(_list_pop(&cache->dirty), struct block);
|
struct block *b = dm_list_item(_list_pop(&cache->dirty), struct block);
|
||||||
if (b->ref_count || _test_flags(b, BF_IO_PENDING)) {
|
if (b->ref_count || _test_flags(b, BF_IO_PENDING)) {
|
||||||
@ -917,7 +931,7 @@ int bcache_flush(struct bcache *cache)
|
|||||||
|
|
||||||
_wait_all(cache);
|
_wait_all(cache);
|
||||||
|
|
||||||
return dm_list_empty(&cache->errored) ? 0 : -EIO;
|
return dm_list_empty(&cache->errored);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _recycle_block(struct bcache *cache, struct block *b)
|
static void _recycle_block(struct bcache *cache, struct block *b)
|
||||||
|
@ -47,7 +47,7 @@ struct io_engine {
|
|||||||
unsigned (*max_io)(struct io_engine *e);
|
unsigned (*max_io)(struct io_engine *e);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct io_engine *create_async_io_engine(unsigned max_io);
|
struct io_engine *create_async_io_engine(void);
|
||||||
|
|
||||||
/*----------------------------------------------------------------*/
|
/*----------------------------------------------------------------*/
|
||||||
|
|
||||||
@ -65,6 +65,7 @@ struct block {
|
|||||||
unsigned flags;
|
unsigned flags;
|
||||||
unsigned ref_count;
|
unsigned ref_count;
|
||||||
int error;
|
int error;
|
||||||
|
enum dir io_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -122,7 +123,11 @@ bool bcache_get(struct bcache *cache, int fd, block_address index,
|
|||||||
unsigned flags, struct block **result);
|
unsigned flags, struct block **result);
|
||||||
void bcache_put(struct block *b);
|
void bcache_put(struct block *b);
|
||||||
|
|
||||||
int bcache_flush(struct bcache *cache);
|
/*
|
||||||
|
* flush() does not attempt to writeback locked blocks. flush will fail
|
||||||
|
* (return false), if any unlocked dirty data cannot be written back.
|
||||||
|
*/
|
||||||
|
bool bcache_flush(struct bcache *cache);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Removes a block from the cache. If the block is dirty it will be written
|
* Removes a block from the cache. If the block is dirty it will be written
|
||||||
|
@ -587,7 +587,7 @@ static int _setup_bcache(int cache_blocks)
|
|||||||
* possible, i.e, the number of devices that can be read at
|
* possible, i.e, the number of devices that can be read at
|
||||||
* once. Should this be configurable?
|
* once. Should this be configurable?
|
||||||
*/
|
*/
|
||||||
if (!(ioe = create_async_io_engine(100))) {
|
if (!(ioe = create_async_io_engine())) {
|
||||||
log_error("Failed to create bcache io engine.");
|
log_error("Failed to create bcache io engine.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user