Skip to content

Commit

Permalink
[bcache] add bcache_abort()
Browse files Browse the repository at this point in the history
This gives us a way to cope with write failures.
  • Loading branch information
jthornber committed Oct 28, 2019
1 parent 6163b73 commit 2938b4d
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 1 deletion.
33 changes: 33 additions & 0 deletions lib/device/bcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions lib/device/bcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
67 changes: 66 additions & 1 deletion test/unit/bcache_t.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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

Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 2938b4d

Please sign in to comment.