mirror of
https://github.com/versity/scoutfs.git
synced 2026-07-31 12:33:19 +00:00
Merge pull request #19 from versity/zab/block_crash_and_consistency
Zab/block crash and consistency
This commit is contained in:
+20
-101
@@ -358,31 +358,24 @@ static void list_block_sort(struct scoutfs_alloc_list_block *lblk)
|
||||
|
||||
/*
|
||||
* We're always reading blocks that we own, so we shouldn't see stale
|
||||
* references. But the cached block can be stale and we can need to
|
||||
* invalidate it.
|
||||
* references but we could retry reads after dropping stale cached
|
||||
* blocks. If we do see a stale error then we've hit persistent
|
||||
* corruption.
|
||||
*/
|
||||
static int read_list_block(struct super_block *sb,
|
||||
struct scoutfs_alloc_list_ref *ref,
|
||||
static int read_list_block(struct super_block *sb, struct scoutfs_block_ref *ref,
|
||||
struct scoutfs_block **bl_ret)
|
||||
{
|
||||
struct scoutfs_block *bl = NULL;
|
||||
int ret;
|
||||
|
||||
bl = scoutfs_block_read(sb, le64_to_cpu(ref->blkno));
|
||||
if (!IS_ERR_OR_NULL(bl) &&
|
||||
!scoutfs_block_consistent_ref(sb, bl, ref->seq, ref->blkno,
|
||||
SCOUTFS_BLOCK_MAGIC_ALLOC_LIST)) {
|
||||
scoutfs_inc_counter(sb, alloc_stale_cached_list_block);
|
||||
scoutfs_block_invalidate(sb, bl);
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = scoutfs_block_read(sb, le64_to_cpu(ref->blkno));
|
||||
}
|
||||
if (IS_ERR(bl)) {
|
||||
*bl_ret = NULL;
|
||||
return PTR_ERR(bl);
|
||||
}
|
||||
ret = scoutfs_block_read_ref(sb, ref, SCOUTFS_BLOCK_MAGIC_ALLOC_LIST, bl_ret);
|
||||
if (ret < 0) {
|
||||
if (ret == -ESTALE) {
|
||||
scoutfs_inc_counter(sb, alloc_stale_list_block);
|
||||
ret = -EIO;
|
||||
}
|
||||
};
|
||||
|
||||
*bl_ret = bl;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -396,86 +389,12 @@ static int read_list_block(struct super_block *sb,
|
||||
static int dirty_list_block(struct super_block *sb,
|
||||
struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri,
|
||||
struct scoutfs_alloc_list_ref *ref,
|
||||
struct scoutfs_block_ref *ref,
|
||||
u64 dirty, u64 *old,
|
||||
struct scoutfs_block **bl_ret)
|
||||
{
|
||||
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
|
||||
struct scoutfs_block *cow_bl = NULL;
|
||||
struct scoutfs_block *bl = NULL;
|
||||
struct scoutfs_alloc_list_block *lblk;
|
||||
bool undo_alloc = false;
|
||||
u64 blkno;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
blkno = le64_to_cpu(ref->blkno);
|
||||
if (blkno) {
|
||||
ret = read_list_block(sb, ref, &bl);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (scoutfs_block_writer_is_dirty(sb, bl)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty == 0) {
|
||||
ret = scoutfs_alloc_meta(sb, alloc, wri, &dirty);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
undo_alloc = true;
|
||||
}
|
||||
|
||||
cow_bl = scoutfs_block_create(sb, dirty);
|
||||
if (IS_ERR(cow_bl)) {
|
||||
ret = PTR_ERR(cow_bl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (old) {
|
||||
*old = blkno;
|
||||
} else if (blkno) {
|
||||
ret = scoutfs_free_meta(sb, alloc, wri, blkno);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bl)
|
||||
memcpy(cow_bl->data, bl->data, SCOUTFS_BLOCK_LG_SIZE);
|
||||
else
|
||||
memset(cow_bl->data, 0, SCOUTFS_BLOCK_LG_SIZE);
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = cow_bl;
|
||||
cow_bl = NULL;
|
||||
|
||||
lblk = bl->data;
|
||||
lblk->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_ALLOC_LIST);
|
||||
lblk->hdr.fsid = super->hdr.fsid;
|
||||
lblk->hdr.blkno = cpu_to_le64(bl->blkno);
|
||||
prandom_bytes(&lblk->hdr.seq, sizeof(lblk->hdr.seq));
|
||||
|
||||
ref->blkno = lblk->hdr.blkno;
|
||||
ref->seq = lblk->hdr.seq;
|
||||
|
||||
scoutfs_block_writer_mark_dirty(sb, wri, bl);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
scoutfs_block_put(sb, cow_bl);
|
||||
if (ret < 0 && undo_alloc) {
|
||||
err = scoutfs_free_meta(sb, alloc, wri, dirty);
|
||||
BUG_ON(err); /* inconsistent */
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
}
|
||||
*bl_ret = bl;
|
||||
|
||||
return ret;
|
||||
return scoutfs_block_dirty_ref(sb, alloc, wri, ref, SCOUTFS_BLOCK_MAGIC_ALLOC_LIST,
|
||||
bl_ret, dirty, old);
|
||||
}
|
||||
|
||||
/* Allocate a new dirty list block if we fill up more than 3/4 of the block. */
|
||||
@@ -497,7 +416,7 @@ static int dirty_alloc_blocks(struct super_block *sb,
|
||||
struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri)
|
||||
{
|
||||
struct scoutfs_alloc_list_ref orig_freed;
|
||||
struct scoutfs_block_ref orig_freed;
|
||||
struct scoutfs_alloc_list_block *lblk;
|
||||
struct scoutfs_block *av_bl = NULL;
|
||||
struct scoutfs_block *fr_bl = NULL;
|
||||
@@ -1106,7 +1025,7 @@ int scoutfs_alloc_splice_list(struct super_block *sb,
|
||||
struct scoutfs_alloc_list_head *src)
|
||||
{
|
||||
struct scoutfs_alloc_list_block *lblk;
|
||||
struct scoutfs_alloc_list_ref *ref;
|
||||
struct scoutfs_block_ref *ref;
|
||||
struct scoutfs_block *prev = NULL;
|
||||
struct scoutfs_block *bl = NULL;
|
||||
int ret = 0;
|
||||
@@ -1169,8 +1088,8 @@ bool scoutfs_alloc_meta_low(struct super_block *sb,
|
||||
int scoutfs_alloc_foreach(struct super_block *sb,
|
||||
scoutfs_alloc_foreach_cb_t cb, void *arg)
|
||||
{
|
||||
struct scoutfs_btree_ref stale_refs[2] = {{0,}};
|
||||
struct scoutfs_btree_ref refs[2] = {{0,}};
|
||||
struct scoutfs_block_ref stale_refs[2] = {{0,}};
|
||||
struct scoutfs_block_ref refs[2] = {{0,}};
|
||||
struct scoutfs_super_block *super = NULL;
|
||||
struct scoutfs_srch_compact *sc;
|
||||
struct scoutfs_log_trees lt;
|
||||
|
||||
+506
-250
File diff suppressed because it is too large
Load Diff
+6
-17
@@ -13,27 +13,16 @@ struct scoutfs_block {
|
||||
void *priv;
|
||||
};
|
||||
|
||||
__le32 scoutfs_block_calc_crc(struct scoutfs_block_header *hdr, u32 size);
|
||||
bool scoutfs_block_valid_crc(struct scoutfs_block_header *hdr, u32 size);
|
||||
bool scoutfs_block_valid_ref(struct super_block *sb,
|
||||
struct scoutfs_block_header *hdr,
|
||||
__le64 seq, __le64 blkno);
|
||||
|
||||
struct scoutfs_block *scoutfs_block_create(struct super_block *sb, u64 blkno);
|
||||
struct scoutfs_block *scoutfs_block_read(struct super_block *sb, u64 blkno);
|
||||
void scoutfs_block_invalidate(struct super_block *sb, struct scoutfs_block *bl);
|
||||
bool scoutfs_block_consistent_ref(struct super_block *sb,
|
||||
struct scoutfs_block *bl,
|
||||
__le64 seq, __le64 blkno, u32 magic);
|
||||
int scoutfs_block_read_ref(struct super_block *sb, struct scoutfs_block_ref *ref, u32 magic,
|
||||
struct scoutfs_block **bl_ret);
|
||||
void scoutfs_block_put(struct super_block *sb, struct scoutfs_block *bl);
|
||||
|
||||
void scoutfs_block_writer_init(struct super_block *sb,
|
||||
struct scoutfs_block_writer *wri);
|
||||
void scoutfs_block_writer_mark_dirty(struct super_block *sb,
|
||||
struct scoutfs_block_writer *wri,
|
||||
struct scoutfs_block *bl);
|
||||
bool scoutfs_block_writer_is_dirty(struct super_block *sb,
|
||||
struct scoutfs_block *bl);
|
||||
int scoutfs_block_dirty_ref(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, struct scoutfs_block_ref *ref,
|
||||
u32 magic, struct scoutfs_block **bl_ret,
|
||||
u64 dirty_blkno, u64 *ref_blkno);
|
||||
int scoutfs_block_writer_write(struct super_block *sb,
|
||||
struct scoutfs_block_writer *wri);
|
||||
void scoutfs_block_writer_forget_all(struct super_block *sb,
|
||||
|
||||
+26
-128
@@ -80,7 +80,7 @@ enum btree_walk_flags {
|
||||
BTW_NEXT = (1 << 0), /* return >= key */
|
||||
BTW_PREV = (1 << 1), /* return <= key */
|
||||
BTW_DIRTY = (1 << 2), /* cow stable blocks */
|
||||
BTW_ALLOC = (1 << 3), /* allocate a new block for 0 ref */
|
||||
BTW_ALLOC = (1 << 3), /* allocate a new block for 0 ref, requires dirty */
|
||||
BTW_INSERT = (1 << 4), /* walking to insert, try splitting */
|
||||
BTW_DELETE = (1 << 5), /* walking to delete, try joining */
|
||||
};
|
||||
@@ -619,140 +619,36 @@ static void move_items(struct scoutfs_btree_block *dst,
|
||||
* This is used to lookup cached blocks, read blocks, cow blocks for
|
||||
* dirtying, and allocate new blocks.
|
||||
*
|
||||
* Btree blocks don't have rigid cache consistency. We can be following
|
||||
* block references into cached blocks that are now stale or can be
|
||||
* following a stale root into blocks that have been overwritten. If we
|
||||
* hit a block that looks stale we first invalidate the cache and retry,
|
||||
* returning -ESTALE if it still looks wrong. The caller can retry the
|
||||
* read from a more current root or decide that this is a persistent
|
||||
* error.
|
||||
* If we read a stale block we return stale so the caller can retry with
|
||||
* a newer root or return an error.
|
||||
*/
|
||||
static int get_ref_block(struct super_block *sb,
|
||||
struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, int flags,
|
||||
struct scoutfs_btree_ref *ref,
|
||||
struct scoutfs_block_ref *ref,
|
||||
struct scoutfs_block **bl_ret)
|
||||
{
|
||||
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
|
||||
struct scoutfs_btree_block *bt = NULL;
|
||||
struct scoutfs_btree_block *new;
|
||||
struct scoutfs_block *new_bl = NULL;
|
||||
struct scoutfs_block *bl = NULL;
|
||||
bool retried = false;
|
||||
u64 blkno;
|
||||
u64 seq;
|
||||
int ret;
|
||||
|
||||
/* always get the current block, either to return or cow from */
|
||||
if (ref && ref->blkno) {
|
||||
retry:
|
||||
if (WARN_ON_ONCE((flags & BTW_ALLOC) && !(flags & BTW_DIRTY)))
|
||||
return -EINVAL;
|
||||
|
||||
bl = scoutfs_block_read(sb, le64_to_cpu(ref->blkno));
|
||||
if (IS_ERR(bl)) {
|
||||
trace_scoutfs_btree_read_error(sb, ref);
|
||||
scoutfs_inc_counter(sb, btree_read_error);
|
||||
ret = PTR_ERR(bl);
|
||||
goto out;
|
||||
}
|
||||
bt = (void *)bl->data;
|
||||
|
||||
if (!scoutfs_block_consistent_ref(sb, bl, ref->seq, ref->blkno,
|
||||
SCOUTFS_BLOCK_MAGIC_BTREE) ||
|
||||
scoutfs_trigger(sb, BTREE_STALE_READ)) {
|
||||
|
||||
scoutfs_inc_counter(sb, btree_stale_read);
|
||||
|
||||
scoutfs_block_invalidate(sb, bl);
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
|
||||
if (!retried) {
|
||||
retried = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to create a new dirty copy of the block if
|
||||
* the caller asked for it. If the block is already
|
||||
* dirty then we can return it.
|
||||
*/
|
||||
if (!(flags & BTW_DIRTY) ||
|
||||
scoutfs_block_writer_is_dirty(sb, bl)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else if (!(flags & BTW_ALLOC)) {
|
||||
if (ref->blkno == 0 && !(flags & BTW_ALLOC)) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = scoutfs_alloc_meta(sb, alloc, wri, &blkno);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prandom_bytes(&seq, sizeof(seq));
|
||||
|
||||
new_bl = scoutfs_block_create(sb, blkno);
|
||||
if (IS_ERR(new_bl)) {
|
||||
ret = scoutfs_free_meta(sb, alloc, wri, blkno);
|
||||
BUG_ON(ret);
|
||||
ret = PTR_ERR(new_bl);
|
||||
goto out;
|
||||
}
|
||||
new = (void *)new_bl->data;
|
||||
|
||||
/* free old stable blkno we're about to overwrite */
|
||||
if (ref && ref->blkno) {
|
||||
ret = scoutfs_free_meta(sb, alloc, wri,
|
||||
le64_to_cpu(ref->blkno));
|
||||
if (ret) {
|
||||
ret = scoutfs_free_meta(sb, alloc, wri, blkno);
|
||||
BUG_ON(ret);
|
||||
scoutfs_block_put(sb, new_bl);
|
||||
new_bl = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
scoutfs_block_writer_mark_dirty(sb, wri, new_bl);
|
||||
|
||||
trace_scoutfs_btree_dirty_block(sb, blkno, seq,
|
||||
bt ? le64_to_cpu(bt->hdr.blkno) : 0,
|
||||
bt ? le64_to_cpu(bt->hdr.seq) : 0);
|
||||
|
||||
if (bt) {
|
||||
/* returning a cow of an existing block */
|
||||
memcpy(new, bt, SCOUTFS_BLOCK_LG_SIZE);
|
||||
scoutfs_block_put(sb, bl);
|
||||
} else {
|
||||
/* returning a newly allocated block */
|
||||
memset(new, 0, SCOUTFS_BLOCK_LG_SIZE);
|
||||
new->hdr.fsid = super->hdr.fsid;
|
||||
}
|
||||
bl = new_bl;
|
||||
bt = new;
|
||||
|
||||
bt->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BTREE);
|
||||
bt->hdr.blkno = cpu_to_le64(blkno);
|
||||
bt->hdr.seq = cpu_to_le64(seq);
|
||||
if (ref) {
|
||||
ref->blkno = bt->hdr.blkno;
|
||||
ref->seq = bt->hdr.seq;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
if (flags & BTW_DIRTY)
|
||||
ret = scoutfs_block_dirty_ref(sb, alloc, wri, ref, SCOUTFS_BLOCK_MAGIC_BTREE,
|
||||
bl_ret, 0, NULL);
|
||||
else
|
||||
ret = scoutfs_block_read_ref(sb, ref, SCOUTFS_BLOCK_MAGIC_BTREE, bl_ret);
|
||||
out:
|
||||
if (ret) {
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
if (ret < 0) {
|
||||
if (ret == -ESTALE)
|
||||
scoutfs_inc_counter(sb, btree_stale_read);
|
||||
}
|
||||
|
||||
*bl_ret = bl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -766,7 +662,7 @@ static void create_parent_item(struct scoutfs_btree_block *parent,
|
||||
{
|
||||
struct scoutfs_avl_node *par;
|
||||
int cmp;
|
||||
struct scoutfs_btree_ref ref = {
|
||||
struct scoutfs_block_ref ref = {
|
||||
.blkno = child->hdr.blkno,
|
||||
.seq = child->hdr.seq,
|
||||
};
|
||||
@@ -784,7 +680,7 @@ static void update_parent_item(struct scoutfs_btree_block *parent,
|
||||
struct scoutfs_btree_item *par_item,
|
||||
struct scoutfs_btree_block *child)
|
||||
{
|
||||
struct scoutfs_btree_ref *ref = item_val(parent, par_item);
|
||||
struct scoutfs_block_ref *ref = item_val(parent, par_item);
|
||||
|
||||
par_item->key = *item_key(last_item(child));
|
||||
ref->blkno = child->hdr.blkno;
|
||||
@@ -832,12 +728,13 @@ static int try_split(struct super_block *sb,
|
||||
struct scoutfs_block *par_bl = NULL;
|
||||
struct scoutfs_btree_block *left;
|
||||
struct scoutfs_key max_key;
|
||||
struct scoutfs_block_ref zeros;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
/* parents need to leave room for child references */
|
||||
if (right->level)
|
||||
val_len = sizeof(struct scoutfs_btree_ref);
|
||||
val_len = sizeof(struct scoutfs_block_ref);
|
||||
|
||||
/* don't need to split if there's enough space for the item */
|
||||
if (mid_free_item_room(right, val_len))
|
||||
@@ -849,7 +746,8 @@ static int try_split(struct super_block *sb,
|
||||
scoutfs_inc_counter(sb, btree_split);
|
||||
|
||||
/* alloc split neighbour first to avoid unwinding tree growth */
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC, NULL, &left_bl);
|
||||
memset(&zeros, 0, sizeof(zeros));
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC | BTW_DIRTY, &zeros, &left_bl);
|
||||
if (ret)
|
||||
return ret;
|
||||
left = left_bl->data;
|
||||
@@ -857,7 +755,8 @@ static int try_split(struct super_block *sb,
|
||||
init_btree_block(left, right->level);
|
||||
|
||||
if (!parent) {
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC, NULL, &par_bl);
|
||||
memset(&zeros, 0, sizeof(zeros));
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC | BTW_DIRTY, &zeros, &par_bl);
|
||||
if (ret) {
|
||||
err = scoutfs_free_meta(sb, alloc, wri,
|
||||
le64_to_cpu(left->hdr.blkno));
|
||||
@@ -905,7 +804,7 @@ static int try_join(struct super_block *sb,
|
||||
struct scoutfs_btree_item *sib_par_item;
|
||||
struct scoutfs_btree_block *sib;
|
||||
struct scoutfs_block *sib_bl;
|
||||
struct scoutfs_btree_ref *ref;
|
||||
struct scoutfs_block_ref *ref;
|
||||
unsigned int sib_tot;
|
||||
bool move_right;
|
||||
int to_move;
|
||||
@@ -1194,7 +1093,7 @@ static int btree_walk(struct super_block *sb,
|
||||
struct scoutfs_btree_item *prev;
|
||||
struct scoutfs_avl_node *next_node;
|
||||
struct scoutfs_avl_node *node;
|
||||
struct scoutfs_btree_ref *ref;
|
||||
struct scoutfs_block_ref *ref;
|
||||
unsigned int level;
|
||||
unsigned int nr;
|
||||
int ret;
|
||||
@@ -1225,8 +1124,7 @@ restart:
|
||||
if (!(flags & BTW_INSERT)) {
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC,
|
||||
&root->ref, &bl);
|
||||
ret = get_ref_block(sb, alloc, wri, BTW_ALLOC | BTW_DIRTY, &root->ref, &bl);
|
||||
if (ret == 0) {
|
||||
bt = bl->data;
|
||||
init_btree_block(bt, 0);
|
||||
|
||||
+9
-6
@@ -20,17 +20,21 @@
|
||||
EXPAND_COUNTER(alloc_list_freed_hi) \
|
||||
EXPAND_COUNTER(alloc_move) \
|
||||
EXPAND_COUNTER(alloc_moved_extent) \
|
||||
EXPAND_COUNTER(alloc_stale_cached_list_block) \
|
||||
EXPAND_COUNTER(block_cache_access) \
|
||||
EXPAND_COUNTER(alloc_stale_list_block) \
|
||||
EXPAND_COUNTER(block_cache_access_update) \
|
||||
EXPAND_COUNTER(block_cache_alloc_failure) \
|
||||
EXPAND_COUNTER(block_cache_alloc_page_order) \
|
||||
EXPAND_COUNTER(block_cache_alloc_virt) \
|
||||
EXPAND_COUNTER(block_cache_end_io_error) \
|
||||
EXPAND_COUNTER(block_cache_forget) \
|
||||
EXPAND_COUNTER(block_cache_free) \
|
||||
EXPAND_COUNTER(block_cache_invalidate) \
|
||||
EXPAND_COUNTER(block_cache_lru_move) \
|
||||
EXPAND_COUNTER(block_cache_free_work) \
|
||||
EXPAND_COUNTER(block_cache_remove_stale) \
|
||||
EXPAND_COUNTER(block_cache_shrink) \
|
||||
EXPAND_COUNTER(block_cache_shrink_next) \
|
||||
EXPAND_COUNTER(block_cache_shrink_recent) \
|
||||
EXPAND_COUNTER(block_cache_shrink_remove) \
|
||||
EXPAND_COUNTER(block_cache_shrink_restart) \
|
||||
EXPAND_COUNTER(btree_compact_values) \
|
||||
EXPAND_COUNTER(btree_compact_values_enomem) \
|
||||
EXPAND_COUNTER(btree_delete) \
|
||||
@@ -42,7 +46,6 @@
|
||||
EXPAND_COUNTER(btree_lookup) \
|
||||
EXPAND_COUNTER(btree_next) \
|
||||
EXPAND_COUNTER(btree_prev) \
|
||||
EXPAND_COUNTER(btree_read_error) \
|
||||
EXPAND_COUNTER(btree_split) \
|
||||
EXPAND_COUNTER(btree_stale_read) \
|
||||
EXPAND_COUNTER(btree_update) \
|
||||
@@ -73,6 +76,7 @@
|
||||
EXPAND_COUNTER(ext_op_remove) \
|
||||
EXPAND_COUNTER(forest_bloom_fail) \
|
||||
EXPAND_COUNTER(forest_bloom_pass) \
|
||||
EXPAND_COUNTER(forest_bloom_stale) \
|
||||
EXPAND_COUNTER(forest_read_items) \
|
||||
EXPAND_COUNTER(forest_roots_next_hint) \
|
||||
EXPAND_COUNTER(forest_set_bloom_bits) \
|
||||
@@ -163,7 +167,6 @@
|
||||
EXPAND_COUNTER(srch_compact_flush) \
|
||||
EXPAND_COUNTER(srch_compact_log_page) \
|
||||
EXPAND_COUNTER(srch_compact_removed_entry) \
|
||||
EXPAND_COUNTER(srch_inconsistent_ref) \
|
||||
EXPAND_COUNTER(srch_rotate_log) \
|
||||
EXPAND_COUNTER(srch_search_log) \
|
||||
EXPAND_COUNTER(srch_search_log_block) \
|
||||
|
||||
+15
-65
@@ -66,8 +66,8 @@ struct forest_info {
|
||||
struct forest_info *name = SCOUTFS_SB(sb)->forest_info
|
||||
|
||||
struct forest_refs {
|
||||
struct scoutfs_btree_ref fs_ref;
|
||||
struct scoutfs_btree_ref logs_ref;
|
||||
struct scoutfs_block_ref fs_ref;
|
||||
struct scoutfs_block_ref logs_ref;
|
||||
};
|
||||
|
||||
/* initialize some refs that initially aren't equal */
|
||||
@@ -96,20 +96,16 @@ static void calc_bloom_nrs(struct forest_bloom_nrs *bloom,
|
||||
}
|
||||
}
|
||||
|
||||
static struct scoutfs_block *read_bloom_ref(struct super_block *sb,
|
||||
struct scoutfs_btree_ref *ref)
|
||||
static struct scoutfs_block *read_bloom_ref(struct super_block *sb, struct scoutfs_block_ref *ref)
|
||||
{
|
||||
struct scoutfs_block *bl;
|
||||
int ret;
|
||||
|
||||
bl = scoutfs_block_read(sb, le64_to_cpu(ref->blkno));
|
||||
if (IS_ERR(bl))
|
||||
return bl;
|
||||
|
||||
if (!scoutfs_block_consistent_ref(sb, bl, ref->seq, ref->blkno,
|
||||
SCOUTFS_BLOCK_MAGIC_BLOOM)) {
|
||||
scoutfs_block_invalidate(sb, bl);
|
||||
scoutfs_block_put(sb, bl);
|
||||
return ERR_PTR(-ESTALE);
|
||||
ret = scoutfs_block_read_ref(sb, ref, SCOUTFS_BLOCK_MAGIC_BLOOM, &bl);
|
||||
if (ret < 0) {
|
||||
if (ret == -ESTALE)
|
||||
scoutfs_inc_counter(sb, forest_bloom_stale);
|
||||
bl = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return bl;
|
||||
@@ -381,18 +377,14 @@ out:
|
||||
int scoutfs_forest_set_bloom_bits(struct super_block *sb,
|
||||
struct scoutfs_lock *lock)
|
||||
{
|
||||
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
|
||||
DECLARE_FOREST_INFO(sb, finf);
|
||||
struct scoutfs_block *new_bl = NULL;
|
||||
struct scoutfs_block *bl = NULL;
|
||||
struct scoutfs_bloom_block *bb;
|
||||
struct scoutfs_btree_ref *ref;
|
||||
struct scoutfs_block_ref *ref;
|
||||
struct forest_bloom_nrs bloom;
|
||||
int nr_set = 0;
|
||||
u64 blkno;
|
||||
u64 nr;
|
||||
int ret;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
nr = le64_to_cpu(finf->our_log.nr);
|
||||
@@ -410,53 +402,11 @@ int scoutfs_forest_set_bloom_bits(struct super_block *sb,
|
||||
|
||||
ref = &finf->our_log.bloom_ref;
|
||||
|
||||
if (ref->blkno) {
|
||||
bl = read_bloom_ref(sb, ref);
|
||||
if (IS_ERR(bl)) {
|
||||
ret = PTR_ERR(bl);
|
||||
goto unlock;
|
||||
}
|
||||
bb = bl->data;
|
||||
}
|
||||
|
||||
if (!ref->blkno || !scoutfs_block_writer_is_dirty(sb, bl)) {
|
||||
|
||||
ret = scoutfs_alloc_meta(sb, finf->alloc, finf->wri, &blkno);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
new_bl = scoutfs_block_create(sb, blkno);
|
||||
if (IS_ERR(new_bl)) {
|
||||
err = scoutfs_free_meta(sb, finf->alloc, finf->wri,
|
||||
blkno);
|
||||
BUG_ON(err); /* could have dirtied */
|
||||
ret = PTR_ERR(new_bl);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (bl) {
|
||||
err = scoutfs_free_meta(sb, finf->alloc, finf->wri,
|
||||
le64_to_cpu(ref->blkno));
|
||||
BUG_ON(err); /* could have dirtied */
|
||||
memcpy(new_bl->data, bl->data, SCOUTFS_BLOCK_LG_SIZE);
|
||||
} else {
|
||||
memset(new_bl->data, 0, SCOUTFS_BLOCK_LG_SIZE);
|
||||
}
|
||||
|
||||
scoutfs_block_writer_mark_dirty(sb, finf->wri, new_bl);
|
||||
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = new_bl;
|
||||
bb = bl->data;
|
||||
new_bl = NULL;
|
||||
|
||||
bb->hdr.magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_BLOOM);
|
||||
bb->hdr.fsid = super->hdr.fsid;
|
||||
bb->hdr.blkno = cpu_to_le64(blkno);
|
||||
prandom_bytes(&bb->hdr.seq, sizeof(bb->hdr.seq));
|
||||
ref->blkno = bb->hdr.blkno;
|
||||
ref->seq = bb->hdr.seq;
|
||||
}
|
||||
ret = scoutfs_block_dirty_ref(sb, finf->alloc, finf->wri, ref, SCOUTFS_BLOCK_MAGIC_BLOOM,
|
||||
&bl, 0, NULL);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
bb = bl->data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bloom.nrs); i++) {
|
||||
if (!test_and_set_bit_le(bloom.nrs[i], bb->bits)) {
|
||||
|
||||
+16
-22
@@ -106,6 +106,15 @@ struct scoutfs_block_header {
|
||||
__le64 blkno;
|
||||
};
|
||||
|
||||
/*
|
||||
* A reference to a block. The corresponding fields in the block_header
|
||||
* must match after having read the block contents.
|
||||
*/
|
||||
struct scoutfs_block_ref {
|
||||
__le64 blkno;
|
||||
__le64 seq;
|
||||
};
|
||||
|
||||
/*
|
||||
* scoutfs identifies all file system metadata items by a small key
|
||||
* struct.
|
||||
@@ -202,17 +211,12 @@ struct scoutfs_avl_node {
|
||||
*/
|
||||
#define SCOUTFS_BTREE_MAX_HEIGHT 20
|
||||
|
||||
struct scoutfs_btree_ref {
|
||||
__le64 blkno;
|
||||
__le64 seq;
|
||||
};
|
||||
|
||||
/*
|
||||
* A height of X means that the first block read will have level X-1 and
|
||||
* the leaves will have level 0.
|
||||
*/
|
||||
struct scoutfs_btree_root {
|
||||
struct scoutfs_btree_ref ref;
|
||||
struct scoutfs_block_ref ref;
|
||||
__u8 height;
|
||||
__u8 __pad[7];
|
||||
};
|
||||
@@ -253,18 +257,13 @@ struct scoutfs_btree_block {
|
||||
#define SCOUTFS_BTREE_LEAF_ITEM_HASH_BYTES \
|
||||
(SCOUTFS_BTREE_LEAF_ITEM_HASH_NR * sizeof(__le16))
|
||||
|
||||
struct scoutfs_alloc_list_ref {
|
||||
__le64 blkno;
|
||||
__le64 seq;
|
||||
};
|
||||
|
||||
/*
|
||||
* first_nr tracks the nr of the first block in the list and is used for
|
||||
* allocation sizing. total_nr is the sum of the nr of all the blocks in
|
||||
* the list and is used for calculating total free block counts.
|
||||
*/
|
||||
struct scoutfs_alloc_list_head {
|
||||
struct scoutfs_alloc_list_ref ref;
|
||||
struct scoutfs_block_ref ref;
|
||||
__le64 total_nr;
|
||||
__le32 first_nr;
|
||||
__u8 __pad[4];
|
||||
@@ -283,7 +282,7 @@ struct scoutfs_alloc_list_head {
|
||||
*/
|
||||
struct scoutfs_alloc_list_block {
|
||||
struct scoutfs_block_header hdr;
|
||||
struct scoutfs_alloc_list_ref next;
|
||||
struct scoutfs_block_ref next;
|
||||
__le32 start;
|
||||
__le32 nr;
|
||||
__le64 blknos[0]; /* naturally aligned for sorting */
|
||||
@@ -329,15 +328,10 @@ struct scoutfs_srch_entry {
|
||||
|
||||
#define SCOUTFS_SRCH_ENTRY_MAX_BYTES (2 + (sizeof(__u64) * 3))
|
||||
|
||||
struct scoutfs_srch_ref {
|
||||
__le64 blkno;
|
||||
__le64 seq;
|
||||
};
|
||||
|
||||
struct scoutfs_srch_file {
|
||||
struct scoutfs_srch_entry first;
|
||||
struct scoutfs_srch_entry last;
|
||||
struct scoutfs_srch_ref ref;
|
||||
struct scoutfs_block_ref ref;
|
||||
__le64 blocks;
|
||||
__le64 entries;
|
||||
__u8 height;
|
||||
@@ -346,13 +340,13 @@ struct scoutfs_srch_file {
|
||||
|
||||
struct scoutfs_srch_parent {
|
||||
struct scoutfs_block_header hdr;
|
||||
struct scoutfs_srch_ref refs[0];
|
||||
struct scoutfs_block_ref refs[0];
|
||||
};
|
||||
|
||||
#define SCOUTFS_SRCH_PARENT_REFS \
|
||||
((SCOUTFS_BLOCK_LG_SIZE - \
|
||||
offsetof(struct scoutfs_srch_parent, refs)) / \
|
||||
sizeof(struct scoutfs_srch_ref))
|
||||
sizeof(struct scoutfs_block_ref))
|
||||
|
||||
struct scoutfs_srch_block {
|
||||
struct scoutfs_block_header hdr;
|
||||
@@ -423,7 +417,7 @@ struct scoutfs_log_trees {
|
||||
struct scoutfs_alloc_list_head meta_avail;
|
||||
struct scoutfs_alloc_list_head meta_freed;
|
||||
struct scoutfs_btree_root item_root;
|
||||
struct scoutfs_btree_ref bloom_ref;
|
||||
struct scoutfs_block_ref bloom_ref;
|
||||
struct scoutfs_alloc_root data_avail;
|
||||
struct scoutfs_alloc_root data_freed;
|
||||
struct scoutfs_srch_file srch_file;
|
||||
|
||||
+73
-59
@@ -1611,7 +1611,7 @@ TRACE_EVENT(scoutfs_get_name,
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_btree_read_error,
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_btree_ref *ref),
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_block_ref *ref),
|
||||
|
||||
TP_ARGS(sb, ref),
|
||||
|
||||
@@ -1631,37 +1631,10 @@ TRACE_EVENT(scoutfs_btree_read_error,
|
||||
SCSB_TRACE_ARGS, __entry->blkno, __entry->seq)
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_btree_dirty_block,
|
||||
TP_PROTO(struct super_block *sb, u64 blkno, u64 seq,
|
||||
u64 bt_blkno, u64 bt_seq),
|
||||
|
||||
TP_ARGS(sb, blkno, seq, bt_blkno, bt_seq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
__field(__u64, blkno)
|
||||
__field(__u64, seq)
|
||||
__field(__u64, bt_blkno)
|
||||
__field(__u64, bt_seq)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
SCSB_TRACE_ASSIGN(sb);
|
||||
__entry->blkno = blkno;
|
||||
__entry->seq = seq;
|
||||
__entry->bt_blkno = bt_blkno;
|
||||
__entry->bt_seq = bt_seq;
|
||||
),
|
||||
|
||||
TP_printk(SCSBF" blkno %llu seq %llu bt_blkno %llu bt_seq %llu",
|
||||
SCSB_TRACE_ARGS, __entry->blkno, __entry->seq,
|
||||
__entry->bt_blkno, __entry->bt_seq)
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_btree_walk,
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_btree_root *root,
|
||||
struct scoutfs_key *key, int flags, int level,
|
||||
struct scoutfs_btree_ref *ref),
|
||||
struct scoutfs_block_ref *ref),
|
||||
|
||||
TP_ARGS(sb, root, key, flags, level, ref),
|
||||
|
||||
@@ -1989,8 +1962,8 @@ DEFINE_EVENT(scoutfs_forest_bloom_class, scoutfs_forest_bloom_search,
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_forest_prepare_commit,
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_btree_ref *item_ref,
|
||||
struct scoutfs_btree_ref *bloom_ref),
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_block_ref *item_ref,
|
||||
struct scoutfs_block_ref *bloom_ref),
|
||||
TP_ARGS(sb, item_ref, bloom_ref),
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
@@ -2056,18 +2029,45 @@ TRACE_EVENT(scoutfs_forest_init_our_log,
|
||||
__entry->blkno, __entry->seq)
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_block_dirty_ref,
|
||||
TP_PROTO(struct super_block *sb, u64 ref_blkno, u64 ref_seq,
|
||||
u64 block_blkno, u64 block_seq),
|
||||
|
||||
TP_ARGS(sb, ref_blkno, ref_seq, block_blkno, block_seq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
__field(__u64, ref_blkno)
|
||||
__field(__u64, ref_seq)
|
||||
__field(__u64, block_blkno)
|
||||
__field(__u64, block_seq)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
SCSB_TRACE_ASSIGN(sb);
|
||||
__entry->ref_blkno = ref_blkno;
|
||||
__entry->ref_seq = ref_seq;
|
||||
__entry->block_blkno = block_blkno;
|
||||
__entry->block_seq = block_seq;
|
||||
),
|
||||
|
||||
TP_printk(SCSBF" ref_blkno %llu ref_seq %llu block_blkno %llu block_seq %llu",
|
||||
SCSB_TRACE_ARGS, __entry->ref_blkno, __entry->ref_seq,
|
||||
__entry->block_blkno, __entry->block_seq)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(scoutfs_block_class,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved),
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno, int refcount, int io_count,
|
||||
unsigned long bits, __u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed),
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
__field(void *, bp)
|
||||
__field(__u64, blkno)
|
||||
__field(int, refcount)
|
||||
__field(int, io_count)
|
||||
__field(unsigned long, bits)
|
||||
__field(__u64, lru_moved)
|
||||
__field(long, bits)
|
||||
__field(__u64, accessed)
|
||||
),
|
||||
TP_fast_assign(
|
||||
SCSB_TRACE_ASSIGN(sb);
|
||||
@@ -2076,57 +2076,71 @@ DECLARE_EVENT_CLASS(scoutfs_block_class,
|
||||
__entry->refcount = refcount;
|
||||
__entry->io_count = io_count;
|
||||
__entry->bits = bits;
|
||||
__entry->lru_moved = lru_moved;
|
||||
__entry->accessed = accessed;
|
||||
),
|
||||
TP_printk(SCSBF" bp %p blkno %llu refcount %d io_count %d bits 0x%lx lru_moved %llu",
|
||||
SCSB_TRACE_ARGS, __entry->bp, __entry->blkno,
|
||||
__entry->refcount, __entry->io_count, __entry->bits,
|
||||
__entry->lru_moved)
|
||||
TP_printk(SCSBF" bp %p blkno %llu refcount %d io_count %d bits 0x%lx accessed %llu",
|
||||
SCSB_TRACE_ARGS, __entry->bp, __entry->blkno, __entry->refcount,
|
||||
__entry->io_count, __entry->bits, __entry->accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_allocate,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_free,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_insert,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_remove,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_end_io,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_submit,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_invalidate,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_mark_dirty,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_forget,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_block_class, scoutfs_block_shrink,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno,
|
||||
int refcount, int io_count, unsigned long bits, u64 lru_moved),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved)
|
||||
int refcount, int io_count, unsigned long bits,
|
||||
__u64 accessed),
|
||||
TP_ARGS(sb, bp, blkno, refcount, io_count, bits, accessed)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(scoutfs_ext_next_class,
|
||||
|
||||
+25
-121
@@ -255,24 +255,9 @@ static u8 height_for_blk(u64 blk)
|
||||
return hei;
|
||||
}
|
||||
|
||||
static void init_file_block(struct super_block *sb, struct scoutfs_block *bl,
|
||||
int level)
|
||||
static inline u32 srch_level_magic(int level)
|
||||
{
|
||||
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
|
||||
struct scoutfs_block_header *hdr;
|
||||
|
||||
/* don't leak uninit kernel mem.. block should do this for us? */
|
||||
memset(bl->data, 0, SCOUTFS_BLOCK_LG_SIZE);
|
||||
|
||||
hdr = bl->data;
|
||||
hdr->fsid = super->hdr.fsid;
|
||||
hdr->blkno = cpu_to_le64(bl->blkno);
|
||||
prandom_bytes(&hdr->seq, sizeof(hdr->seq));
|
||||
|
||||
if (level)
|
||||
hdr->magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_SRCH_PARENT);
|
||||
else
|
||||
hdr->magic = cpu_to_le32(SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK);
|
||||
return level ? SCOUTFS_BLOCK_MAGIC_SRCH_PARENT : SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -284,39 +269,15 @@ static void init_file_block(struct super_block *sb, struct scoutfs_block *bl,
|
||||
*/
|
||||
static int read_srch_block(struct super_block *sb,
|
||||
struct scoutfs_block_writer *wri, int level,
|
||||
struct scoutfs_srch_ref *ref,
|
||||
struct scoutfs_block_ref *ref,
|
||||
struct scoutfs_block **bl_ret)
|
||||
{
|
||||
struct scoutfs_block *bl;
|
||||
int retries = 0;
|
||||
int ret = 0;
|
||||
int mag;
|
||||
u32 magic = srch_level_magic(level);
|
||||
int ret;
|
||||
|
||||
mag = level ? SCOUTFS_BLOCK_MAGIC_SRCH_PARENT :
|
||||
SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK;
|
||||
retry:
|
||||
bl = scoutfs_block_read(sb, le64_to_cpu(ref->blkno));
|
||||
if (!IS_ERR_OR_NULL(bl) &&
|
||||
!scoutfs_block_consistent_ref(sb, bl, ref->seq, ref->blkno, mag)) {
|
||||
|
||||
scoutfs_inc_counter(sb, srch_inconsistent_ref);
|
||||
scoutfs_block_writer_forget(sb, wri, bl);
|
||||
scoutfs_block_invalidate(sb, bl);
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
|
||||
if (retries++ == 0)
|
||||
goto retry;
|
||||
|
||||
bl = ERR_PTR(-ESTALE);
|
||||
ret = scoutfs_block_read_ref(sb, ref, magic, bl_ret);
|
||||
if (ret == -ESTALE)
|
||||
scoutfs_inc_counter(sb, srch_read_stale);
|
||||
}
|
||||
if (IS_ERR(bl)) {
|
||||
ret = PTR_ERR(bl);
|
||||
bl = NULL;
|
||||
}
|
||||
|
||||
*bl_ret = bl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -333,7 +294,7 @@ static int read_path_block(struct super_block *sb,
|
||||
{
|
||||
struct scoutfs_block *bl = NULL;
|
||||
struct scoutfs_srch_parent *srp;
|
||||
struct scoutfs_srch_ref ref;
|
||||
struct scoutfs_block_ref ref;
|
||||
int level;
|
||||
int ind;
|
||||
int ret;
|
||||
@@ -392,12 +353,10 @@ static int get_file_block(struct super_block *sb,
|
||||
struct scoutfs_block_header *hdr;
|
||||
struct scoutfs_block *bl = NULL;
|
||||
struct scoutfs_srch_parent *srp;
|
||||
struct scoutfs_block *new_bl;
|
||||
struct scoutfs_srch_ref *ref;
|
||||
u64 blkno = 0;
|
||||
struct scoutfs_block_ref new_root_ref;
|
||||
struct scoutfs_block_ref *ref;
|
||||
int level;
|
||||
int ind;
|
||||
int err;
|
||||
int ret;
|
||||
u8 hei;
|
||||
|
||||
@@ -409,29 +368,21 @@ static int get_file_block(struct super_block *sb,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = scoutfs_alloc_meta(sb, alloc, wri, &blkno);
|
||||
memset(&new_root_ref, 0, sizeof(new_root_ref));
|
||||
level = sfl->height;
|
||||
|
||||
ret = scoutfs_block_dirty_ref(sb, alloc, wri, &new_root_ref,
|
||||
srch_level_magic(level), &bl, 0, NULL);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
bl = scoutfs_block_create(sb, blkno);
|
||||
if (IS_ERR(bl)) {
|
||||
ret = PTR_ERR(bl);
|
||||
goto out;
|
||||
}
|
||||
blkno = 0;
|
||||
|
||||
scoutfs_block_writer_mark_dirty(sb, wri, bl);
|
||||
|
||||
init_file_block(sb, bl, sfl->height);
|
||||
if (sfl->height) {
|
||||
if (level) {
|
||||
srp = bl->data;
|
||||
srp->refs[0].blkno = sfl->ref.blkno;
|
||||
srp->refs[0].seq = sfl->ref.seq;
|
||||
srp->refs[0] = sfl->ref;
|
||||
}
|
||||
|
||||
hdr = bl->data;
|
||||
sfl->ref.blkno = hdr->blkno;
|
||||
sfl->ref.seq = hdr->seq;
|
||||
sfl->ref = new_root_ref;
|
||||
sfl->height++;
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
@@ -447,54 +398,13 @@ static int get_file_block(struct super_block *sb,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read an existing block */
|
||||
if (ref->blkno) {
|
||||
ret = read_srch_block(sb, wri, level, ref, &bl);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate a new block if we need it */
|
||||
if (!ref->blkno || ((flags & GFB_DIRTY) &&
|
||||
!scoutfs_block_writer_is_dirty(sb, bl))) {
|
||||
ret = scoutfs_alloc_meta(sb, alloc, wri, &blkno);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
new_bl = scoutfs_block_create(sb, blkno);
|
||||
if (IS_ERR(new_bl)) {
|
||||
ret = PTR_ERR(new_bl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bl) {
|
||||
/* cow old block if we have one */
|
||||
ret = scoutfs_free_meta(sb, alloc, wri,
|
||||
bl->blkno);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
memcpy(new_bl->data, bl->data,
|
||||
SCOUTFS_BLOCK_LG_SIZE);
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = new_bl;
|
||||
hdr = bl->data;
|
||||
hdr->blkno = cpu_to_le64(bl->blkno);
|
||||
prandom_bytes(&hdr->seq, sizeof(hdr->seq));
|
||||
} else {
|
||||
/* init new allocated block */
|
||||
bl = new_bl;
|
||||
init_file_block(sb, bl, level);
|
||||
}
|
||||
|
||||
blkno = 0;
|
||||
scoutfs_block_writer_mark_dirty(sb, wri, bl);
|
||||
|
||||
/* update file or parent block ref */
|
||||
hdr = bl->data;
|
||||
ref->blkno = hdr->blkno;
|
||||
ref->seq = hdr->seq;
|
||||
}
|
||||
if (flags & GFB_DIRTY)
|
||||
ret = scoutfs_block_dirty_ref(sb, alloc, wri, ref, srch_level_magic(level),
|
||||
&bl, 0, NULL);
|
||||
else
|
||||
ret = scoutfs_block_read_ref(sb, ref, srch_level_magic(level), &bl);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (level == 0) {
|
||||
ret = 0;
|
||||
@@ -514,12 +424,6 @@ static int get_file_block(struct super_block *sb,
|
||||
out:
|
||||
scoutfs_block_put(sb, parent);
|
||||
|
||||
/* return allocated blkno on error */
|
||||
if (blkno > 0) {
|
||||
err = scoutfs_free_meta(sb, alloc, wri, blkno);
|
||||
BUG_ON(err); /* radix should have been dirty */
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
scoutfs_block_put(sb, bl);
|
||||
bl = NULL;
|
||||
|
||||
+1
-4
@@ -38,10 +38,7 @@ struct scoutfs_triggers {
|
||||
struct scoutfs_triggers *name = SCOUTFS_SB(sb)->triggers
|
||||
|
||||
static char *names[] = {
|
||||
[SCOUTFS_TRIGGER_BTREE_STALE_READ] = "btree_stale_read",
|
||||
[SCOUTFS_TRIGGER_BTREE_ADVANCE_RING_HALF] = "btree_advance_ring_half",
|
||||
[SCOUTFS_TRIGGER_HARD_STALE_ERROR] = "hard_stale_error",
|
||||
[SCOUTFS_TRIGGER_SEG_STALE_READ] = "seg_stale_read",
|
||||
[SCOUTFS_TRIGGER_BLOCK_REMOVE_STALE] = "block_remove_stale",
|
||||
[SCOUTFS_TRIGGER_STATFS_LOCK_PURGE] = "statfs_lock_purge",
|
||||
};
|
||||
|
||||
|
||||
+1
-4
@@ -2,10 +2,7 @@
|
||||
#define _SCOUTFS_TRIGGERS_H_
|
||||
|
||||
enum scoutfs_trigger {
|
||||
SCOUTFS_TRIGGER_BTREE_STALE_READ,
|
||||
SCOUTFS_TRIGGER_BTREE_ADVANCE_RING_HALF,
|
||||
SCOUTFS_TRIGGER_HARD_STALE_ERROR,
|
||||
SCOUTFS_TRIGGER_SEG_STALE_READ,
|
||||
SCOUTFS_TRIGGER_BLOCK_REMOVE_STALE,
|
||||
SCOUTFS_TRIGGER_STATFS_LOCK_PURGE,
|
||||
SCOUTFS_TRIGGER_NR,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
== create file for xattr ping pong
|
||||
# file: /mnt/test/test/block-stale-reads/file
|
||||
user.xat="initial"
|
||||
|
||||
== retry btree forest reads between mounts
|
||||
trigger block_remove_stale armed: 0
|
||||
# file: /mnt/test/test/block-stale-reads/file
|
||||
user.xat="1"
|
||||
|
||||
trigger block_remove_stale after: 0
|
||||
counter block_cache_remove_stale diff 1
|
||||
trigger block_remove_stale armed: 0
|
||||
# file: /mnt/test/test/block-stale-reads/file
|
||||
user.xat="2"
|
||||
|
||||
trigger block_remove_stale after: 0
|
||||
counter block_cache_remove_stale diff 2
|
||||
trigger block_remove_stale armed: 0
|
||||
# file: /mnt/test/test/block-stale-reads/file
|
||||
user.xat="3"
|
||||
|
||||
trigger block_remove_stale after: 0
|
||||
counter block_cache_remove_stale diff 3
|
||||
trigger block_remove_stale armed: 0
|
||||
# file: /mnt/test/test/block-stale-reads/file
|
||||
user.xat="4"
|
||||
|
||||
trigger block_remove_stale after: 0
|
||||
counter block_cache_remove_stale diff 4
|
||||
@@ -1,11 +0,0 @@
|
||||
== create file for xattr ping pong
|
||||
# file: /mnt/test/test/stale-btree-read/file
|
||||
user.xat="initial"
|
||||
|
||||
== retry btree block read
|
||||
trigger btree_stale_read armed: 1
|
||||
# file: /mnt/test/test/stale-btree-read/file
|
||||
user.xat="btree"
|
||||
|
||||
trigger btree_stale_read after: 0
|
||||
counter btree_stale_read diff 1
|
||||
+1
-1
@@ -28,5 +28,5 @@ setup-error-teardown.sh
|
||||
mount-unmount-race.sh
|
||||
createmany-parallel-mounts.sh
|
||||
archive-light-cycle.sh
|
||||
stale-btree-read.sh
|
||||
block-stale-reads.sh
|
||||
xfstests.sh
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# exercise stale block reading.
|
||||
#
|
||||
# It would be very difficult to manipulate the allocators, cache, and
|
||||
# persistent blocks to create stable block reading scenarios. Instead
|
||||
# we use triggers to exercise how readers encounter stale blocks.
|
||||
#
|
||||
|
||||
t_require_commands touch setfattr getfattr
|
||||
t_require_mounts 2
|
||||
|
||||
GETFATTR="getfattr --absolute-names"
|
||||
SETFATTR="setfattr"
|
||||
|
||||
#
|
||||
# force re-reading forest btree blocks as each mount reads the items
|
||||
# written by the other.
|
||||
#
|
||||
set_file="$T_D0/file"
|
||||
get_file="$T_D1/file"
|
||||
echo "== create file for xattr ping pong"
|
||||
touch "$set_file"
|
||||
$SETFATTR -n user.xat -v initial "$set_file"
|
||||
$GETFATTR -n user.xat "$get_file" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== retry btree forest reads between mounts"
|
||||
for i in $(seq 1 4); do
|
||||
tmp="$set_file"
|
||||
set_file="$get_file"
|
||||
get_file="$tmp"
|
||||
|
||||
$SETFATTR -n user.xat -v $i "$set_file"
|
||||
t_trigger_arm block_remove_stale $cl
|
||||
old=$(t_counter btree_stale_read $cl)
|
||||
$GETFATTR -n user.xat "$get_file" 2>&1 | t_filter_fs
|
||||
t_trigger_show block_remove_stale "after" $cl
|
||||
t_counter_diff block_cache_remove_stale $old $cl
|
||||
done
|
||||
|
||||
t_pass
|
||||
@@ -1,40 +0,0 @@
|
||||
#
|
||||
# verify stale btree block reading
|
||||
#
|
||||
|
||||
t_require_commands touch stat setfattr getfattr createmany
|
||||
t_require_mounts 2
|
||||
|
||||
GETFATTR="getfattr --absolute-names"
|
||||
SETFATTR="setfattr"
|
||||
|
||||
#
|
||||
# This exercises the soft retry of btree blocks when
|
||||
# inconsistent cached versions are found. It ensures that basic hard
|
||||
# error returning turns into EIO in the case where the persistent reread
|
||||
# blocks and segments really are inconsistent.
|
||||
#
|
||||
# The triggers apply across all execution in the file system. So to
|
||||
# trigger btree block retries in the client we make sure that the server
|
||||
# is running on the other node.
|
||||
#
|
||||
|
||||
cl=$(t_first_client_nr)
|
||||
sv=$(t_server_nr)
|
||||
eval cl_dir="\$T_D${cl}"
|
||||
eval sv_dir="\$T_D${sv}"
|
||||
|
||||
echo "== create file for xattr ping pong"
|
||||
touch "$sv_dir/file"
|
||||
$SETFATTR -n user.xat -v initial "$sv_dir/file"
|
||||
$GETFATTR -n user.xat "$sv_dir/file" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== retry btree block read"
|
||||
$SETFATTR -n user.xat -v btree "$sv_dir/file"
|
||||
t_trigger_arm btree_stale_read $cl
|
||||
old=$(t_counter btree_stale_read $cl)
|
||||
$GETFATTR -n user.xat "$cl_dir/file" 2>&1 | t_filter_fs
|
||||
t_trigger_show btree_stale_read "after" $cl
|
||||
t_counter_diff btree_stale_read $old $cl
|
||||
|
||||
t_pass
|
||||
+8
-9
@@ -388,10 +388,10 @@ static int print_alloc_item(struct scoutfs_key *key, void *val,
|
||||
typedef int (*print_item_func)(struct scoutfs_key *key, void *val,
|
||||
unsigned val_len, void *arg);
|
||||
|
||||
static int print_btree_ref(struct scoutfs_key *key, void *val,
|
||||
static int print_block_ref(struct scoutfs_key *key, void *val,
|
||||
unsigned val_len, print_item_func func, void *arg)
|
||||
{
|
||||
struct scoutfs_btree_ref *ref = val;
|
||||
struct scoutfs_block_ref *ref = val;
|
||||
|
||||
func(key, NULL, 0, arg);
|
||||
printf(" ref blkno %llu seq %llu\n",
|
||||
@@ -433,7 +433,7 @@ static void print_leaf_item_hash(struct scoutfs_btree_block *bt)
|
||||
}
|
||||
|
||||
static int print_btree_block(int fd, struct scoutfs_super_block *super,
|
||||
char *which, struct scoutfs_btree_ref *ref,
|
||||
char *which, struct scoutfs_block_ref *ref,
|
||||
print_item_func func, void *arg, u8 level)
|
||||
{
|
||||
struct scoutfs_btree_item *item;
|
||||
@@ -500,7 +500,7 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super,
|
||||
val_len);
|
||||
|
||||
if (level)
|
||||
print_btree_ref(key, val, val_len, func, arg);
|
||||
print_block_ref(key, val, val_len, func, arg);
|
||||
else
|
||||
func(key, val, val_len, arg);
|
||||
}
|
||||
@@ -531,11 +531,10 @@ static int print_btree(int fd, struct scoutfs_super_block *super, char *which,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_alloc_list_block(int fd, char *str,
|
||||
struct scoutfs_alloc_list_ref *ref)
|
||||
static int print_alloc_list_block(int fd, char *str, struct scoutfs_block_ref *ref)
|
||||
{
|
||||
struct scoutfs_alloc_list_block *lblk;
|
||||
struct scoutfs_alloc_list_ref next;
|
||||
struct scoutfs_block_ref next;
|
||||
u64 blkno;
|
||||
u64 start;
|
||||
u64 len;
|
||||
@@ -583,7 +582,7 @@ static int print_alloc_list_block(int fd, char *str,
|
||||
return print_alloc_list_block(fd, str, &next);
|
||||
}
|
||||
|
||||
static int print_srch_block(int fd, struct scoutfs_srch_ref *ref, int level)
|
||||
static int print_srch_block(int fd, struct scoutfs_block_ref *ref, int level)
|
||||
{
|
||||
struct scoutfs_srch_parent *srp;
|
||||
struct scoutfs_srch_block *srb;
|
||||
@@ -729,7 +728,7 @@ static int print_srch_root_files(struct scoutfs_key *key, void *val,
|
||||
}
|
||||
|
||||
static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super,
|
||||
struct scoutfs_btree_ref *ref,
|
||||
struct scoutfs_block_ref *ref,
|
||||
print_item_func func, void *arg)
|
||||
{
|
||||
struct scoutfs_btree_item *item;
|
||||
|
||||
Reference in New Issue
Block a user