diff --git a/kmod/src/forest.c b/kmod/src/forest.c index fee22785..38681273 100644 --- a/kmod/src/forest.c +++ b/kmod/src/forest.c @@ -239,9 +239,9 @@ static int forest_read_items(struct super_block *sb, struct scoutfs_key *key, u6 * to reset their state and retry with a newer version of the btrees. */ int scoutfs_forest_read_items_roots(struct super_block *sb, struct scoutfs_net_roots *roots, - struct scoutfs_key *key, struct scoutfs_key *bloom_key, - struct scoutfs_key *start, struct scoutfs_key *end, - scoutfs_forest_item_cb cb, void *arg) + u64 merge_input_seq, struct scoutfs_key *key, + struct scoutfs_key *bloom_key, struct scoutfs_key *start, + struct scoutfs_key *end, scoutfs_forest_item_cb cb, void *arg) { struct forest_read_items_data rid = { .cb = cb, @@ -317,7 +317,9 @@ int scoutfs_forest_read_items_roots(struct super_block *sb, struct scoutfs_net_r scoutfs_inc_counter(sb, forest_bloom_pass); - if ((le64_to_cpu(lt.flags) & SCOUTFS_LOG_TREES_FINALIZED)) + if ((le64_to_cpu(lt.flags) & SCOUTFS_LOG_TREES_FINALIZED) && + (merge_input_seq == 0 || + le64_to_cpu(lt.finalize_seq) < merge_input_seq)) rid.fic |= FIC_MERGE_INPUT; ret = scoutfs_btree_read_items(sb, <.item_root, key, start, @@ -345,7 +347,7 @@ int scoutfs_forest_read_items(struct super_block *sb, ret = scoutfs_client_get_roots(sb, &roots); if (ret == 0) - ret = scoutfs_forest_read_items_roots(sb, &roots, key, bloom_key, start, end, + ret = scoutfs_forest_read_items_roots(sb, &roots, 0, key, bloom_key, start, end, cb, arg); return ret; } diff --git a/kmod/src/forest.h b/kmod/src/forest.h index 0a11a14b..a3d5e4b2 100644 --- a/kmod/src/forest.h +++ b/kmod/src/forest.h @@ -25,9 +25,9 @@ int scoutfs_forest_read_items(struct super_block *sb, struct scoutfs_key *end, scoutfs_forest_item_cb cb, void *arg); int scoutfs_forest_read_items_roots(struct super_block *sb, struct scoutfs_net_roots *roots, - struct scoutfs_key *key, struct scoutfs_key *bloom_key, - struct scoutfs_key *start, struct scoutfs_key *end, - scoutfs_forest_item_cb cb, void *arg); + u64 merge_input_seq, struct scoutfs_key *key, + struct scoutfs_key *bloom_key, struct scoutfs_key *start, + struct scoutfs_key *end, scoutfs_forest_item_cb cb, void *arg); int scoutfs_forest_set_bloom_bits(struct super_block *sb, struct scoutfs_lock *lock); void scoutfs_forest_set_max_seq(struct super_block *sb, u64 max_seq); diff --git a/kmod/src/wkic.c b/kmod/src/wkic.c index 58737c62..eee77f95 100644 --- a/kmod/src/wkic.c +++ b/kmod/src/wkic.c @@ -95,6 +95,7 @@ struct wkic_info { /* block reading slow path */ struct mutex roots_mutex; struct scoutfs_net_roots roots; + u64 merge_input_seq; u64 roots_read_seq; ktime_t roots_expire; @@ -805,29 +806,79 @@ static void free_page_list(struct super_block *sb, struct list_head *list) * read_seq number so that we can compare the age of the items in cached * pages. Only one request to refresh the roots is in progress at a * time. This is the slow path that's only used when the cache isn't - * populated and the roots aren't cached. The root request is fast - * enough, especially compared to the resulting item reading IO, that we - * don't mind hiding it behind a trivial mutex. + * populated and the roots aren't cached. + * + * We read roots directly from the on-disk superblock rather than + * requesting them from the server so that we can also read the + * log_merge btree from the same superblock. The merge status item + * seq tells us which finalized log trees are inputs to the current + * merge, which is needed to correctly resolve totl delta items. */ +static int refresh_roots(struct super_block *sb, struct wkic_info *winf) +{ + struct scoutfs_super_block *super; + struct scoutfs_log_merge_status *stat; + SCOUTFS_BTREE_ITEM_REF(iref); + struct scoutfs_key key; + int ret; + + super = kmalloc(sizeof(*super), GFP_NOFS); + if (!super) + return -ENOMEM; + + ret = scoutfs_read_super(sb, super); + if (ret < 0) + goto out; + + winf->roots = (struct scoutfs_net_roots){ + .fs_root = super->fs_root, + .logs_root = super->logs_root, + .srch_root = super->srch_root, + }; + + winf->merge_input_seq = 0; + if (super->log_merge.ref.blkno) { + scoutfs_key_set_zeros(&key); + key.sk_zone = SCOUTFS_LOG_MERGE_STATUS_ZONE; + ret = scoutfs_btree_lookup(sb, &super->log_merge, &key, &iref); + if (ret == 0) { + if (iref.val_len == sizeof(*stat)) { + stat = iref.val; + winf->merge_input_seq = le64_to_cpu(stat->seq); + } else { + ret = -EUCLEAN; + } + scoutfs_btree_put_iref(&iref); + } else if (ret == -ENOENT) { + ret = 0; + } + if (ret < 0) + goto out; + } + + winf->roots_read_seq++; + winf->roots_expire = ktime_add_ms(ktime_get_raw(), WKIC_CACHE_LIFETIME_MS); +out: + kfree(super); + return ret; +} + static int get_roots(struct super_block *sb, struct wkic_info *winf, - struct scoutfs_net_roots *roots_ret, u64 *read_seq, bool force_new) + struct scoutfs_net_roots *roots_ret, u64 *merge_input_seq, + u64 *read_seq, bool force_new) { - struct scoutfs_net_roots roots; int ret; mutex_lock(&winf->roots_mutex); if (force_new || ktime_before(winf->roots_expire, ktime_get_raw())) { - ret = scoutfs_client_get_roots(sb, &roots); + ret = refresh_roots(sb, winf); if (ret < 0) goto out; - - winf->roots = roots; - winf->roots_read_seq++; - winf->roots_expire = ktime_add_ms(ktime_get_raw(), WKIC_CACHE_LIFETIME_MS); } *roots_ret = winf->roots; + *merge_input_seq = winf->merge_input_seq; *read_seq = winf->roots_read_seq; ret = 0; out: @@ -870,20 +921,22 @@ static int insert_read_pages(struct super_block *sb, struct wkic_info *winf, struct scoutfs_key end; struct wkic_page *wpage; LIST_HEAD(pages); + u64 merge_input_seq; u64 read_seq; int ret; ret = 0; retry_stale: - ret = get_roots(sb, winf, &roots, &read_seq, ret == -ESTALE); + ret = get_roots(sb, winf, &roots, &merge_input_seq, &read_seq, ret == -ESTALE); if (ret < 0) - goto out; + goto check_stale; start = *range_start; end = *range_end; - ret = scoutfs_forest_read_items_roots(sb, &roots, key, range_start, &start, &end, - read_items_cb, &root); + ret = scoutfs_forest_read_items_roots(sb, &roots, merge_input_seq, key, range_start, + &start, &end, read_items_cb, &root); trace_scoutfs_wkic_read_items(sb, key, &start, &end); +check_stale: ret = scoutfs_block_check_stale(sb, ret, &saved, &roots.fs_root.ref, &roots.logs_root.ref); if (ret < 0) { if (ret == -ESTALE) {