mirror of
git://sourceware.org/git/lvm2.git
synced 2024-10-27 10:25:13 +03:00
[bcache] add bcache_abort()
This gives us a way to cope with write failures.
This commit is contained in:
parent
6163b733e1
commit
2938b4dcca
@ -1382,6 +1382,39 @@ bool bcache_invalidate_fd(struct bcache *cache, int fd)
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _abort_v(struct radix_tree_iterator *it,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value v)
|
||||
{
|
||||
struct block *b = v.ptr;
|
||||
|
||||
if (b->ref_count) {
|
||||
log_fatal("bcache_abort: block (%d, %llu) still held",
|
||||
b->fd, (unsigned long long) b->index);
|
||||
return true;
|
||||
}
|
||||
|
||||
_unlink_block(b);
|
||||
_free_block(b);
|
||||
|
||||
// We can't remove the block from the radix tree yet because
|
||||
// we're in the middle of an iteration.
|
||||
return true;
|
||||
}
|
||||
|
||||
void bcache_abort_fd(struct bcache *cache, int fd)
|
||||
{
|
||||
union key k;
|
||||
struct radix_tree_iterator it;
|
||||
|
||||
k.parts.fd = fd;
|
||||
|
||||
it.visit = _abort_v;
|
||||
radix_tree_iterate(cache->rtree, k.bytes, k.bytes + sizeof(k.parts.fd), &it);
|
||||
radix_tree_remove_prefix(cache->rtree, k.bytes, k.bytes + sizeof(k.parts.fd));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
void bcache_set_last_byte(struct bcache *cache, int fd, uint64_t offset, int sector_size)
|
||||
{
|
||||
_last_byte_fd = fd;
|
||||
|
@ -144,6 +144,13 @@ bool bcache_invalidate(struct bcache *cache, int fd, block_address index);
|
||||
*/
|
||||
bool bcache_invalidate_fd(struct bcache *cache, int fd);
|
||||
|
||||
/*
|
||||
* Call this function if flush, or invalidate fail and you do not
|
||||
* wish to retry the writes. This will throw away any dirty data
|
||||
* not written. If any blocks for fd are held, then it will call
|
||||
* abort().
|
||||
*/
|
||||
void bcache_abort_fd(struct bcache *cache, int fd);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// The next four functions are utilities written in terms of the above api.
|
||||
|
@ -793,7 +793,6 @@ static void test_invalidate_after_write_error(void *context)
|
||||
|
||||
static void test_invalidate_held_block(void *context)
|
||||
{
|
||||
|
||||
struct fixture *f = context;
|
||||
struct mock_engine *me = f->me;
|
||||
struct bcache *cache = f->cache;
|
||||
@ -809,6 +808,67 @@ static void test_invalidate_held_block(void *context)
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// abort tests
|
||||
|
||||
static void test_abort_no_blocks(void *context)
|
||||
{
|
||||
struct fixture *f = context;
|
||||
struct bcache *cache = f->cache;
|
||||
int fd = 17;
|
||||
|
||||
// We have no expectations
|
||||
bcache_abort_fd(cache, fd);
|
||||
}
|
||||
|
||||
static void test_abort_single_block(void *context)
|
||||
{
|
||||
struct fixture *f = context;
|
||||
struct bcache *cache = f->cache;
|
||||
struct block *b;
|
||||
int fd = 17;
|
||||
|
||||
T_ASSERT(bcache_get(cache, fd, 0, GF_ZERO, &b));
|
||||
bcache_put(b);
|
||||
|
||||
bcache_abort_fd(cache, fd);
|
||||
|
||||
// no write should be issued
|
||||
T_ASSERT(bcache_flush(cache));
|
||||
}
|
||||
|
||||
static void test_abort_only_specific_fd(void *context)
|
||||
{
|
||||
struct fixture *f = context;
|
||||
struct mock_engine *me = f->me;
|
||||
struct bcache *cache = f->cache;
|
||||
struct block *b;
|
||||
int fd1 = 17, fd2 = 18;
|
||||
|
||||
T_ASSERT(bcache_get(cache, fd1, 0, GF_ZERO, &b));
|
||||
bcache_put(b);
|
||||
|
||||
T_ASSERT(bcache_get(cache, fd1, 1, GF_ZERO, &b));
|
||||
bcache_put(b);
|
||||
|
||||
T_ASSERT(bcache_get(cache, fd2, 0, GF_ZERO, &b));
|
||||
bcache_put(b);
|
||||
|
||||
T_ASSERT(bcache_get(cache, fd2, 1, GF_ZERO, &b));
|
||||
bcache_put(b);
|
||||
|
||||
bcache_abort_fd(cache, fd2);
|
||||
|
||||
// writes for fd1 should still be issued
|
||||
_expect_write(me, fd1, 0);
|
||||
_expect_write(me, fd1, 1);
|
||||
|
||||
_expect(me, E_WAIT);
|
||||
_expect(me, E_WAIT);
|
||||
|
||||
T_ASSERT(bcache_flush(cache));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Chasing a bug reported by dct
|
||||
|
||||
@ -897,6 +957,11 @@ static struct test_suite *_small_tests(void)
|
||||
T("invalidate-read-error", "invalidate a block that errored", test_invalidate_after_read_error);
|
||||
T("invalidate-write-error", "invalidate a block that errored", test_invalidate_after_write_error);
|
||||
T("invalidate-fails-in-held", "invalidating a held block fails", test_invalidate_held_block);
|
||||
|
||||
T("abort-with-no-blocks", "you can call abort, even if there are no blocks in the cache", test_abort_no_blocks);
|
||||
T("abort-single-block", "single block get silently discarded", test_abort_single_block);
|
||||
T("abort-specific-fd", "abort doesn't effect other fds", test_abort_only_specific_fd);
|
||||
|
||||
T("concurrent-reads-after-invalidate", "prefetch should still issue concurrent reads after invalidate",
|
||||
test_concurrent_reads_after_invalidate);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user