From 1a994137f40c0c0bd9e2987efdadb2494fb33868 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 18 Aug 2020 10:20:50 -0700 Subject: [PATCH] scoutfs: add btree methods for item cache Add btree calls to call a callback for all items in a leaf, and to insert a list of items into their leaf blocks. These will be used by the item cache to populate the cache and to write dirty items into dirty btree blocks. Signed-off-by: Zach Brown --- kmod/src/btree.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ kmod/src/btree.h | 24 +++++++++++ 2 files changed, 128 insertions(+) diff --git a/kmod/src/btree.c b/kmod/src/btree.c index 99dede60..5c97e2e0 100644 --- a/kmod/src/btree.c +++ b/kmod/src/btree.c @@ -1780,3 +1780,107 @@ int scoutfs_btree_dirty(struct super_block *sb, return ret; } + +/* + * Call the users callback on all the items in the leaf that we find. + * We also set the caller's keys for the first and last possible keys + * that could exist in the leaf block. + */ +int scoutfs_btree_read_items(struct super_block *sb, + struct scoutfs_btree_root *root, + struct scoutfs_key *key, + struct scoutfs_key *start, + struct scoutfs_key *end, + scoutfs_btree_item_cb cb, void *arg) +{ + struct scoutfs_btree_item *item; + struct scoutfs_btree_block *bt; + struct scoutfs_avl_node *next_node; + struct scoutfs_avl_node *node; + struct btree_walk_key_range kr; + struct scoutfs_block *bl; + int ret; + + ret = btree_walk(sb, NULL, NULL, root, 0, key, 0, &bl, &kr); + if (ret < 0) + goto out; + bt = bl->data; + + if (scoutfs_key_compare(&kr.start, start) > 0) + *start = kr.start; + if (scoutfs_key_compare(&kr.end, end) < 0) + *end = kr.end; + + node = scoutfs_avl_search(&bt->item_root, cmp_key_item, start, NULL, + NULL, &next_node, NULL) ?: next_node; + while (node) { + item = node_item(node); + if (scoutfs_key_compare(&item->key, end) > 0) + break; + + ret = cb(sb, item_key(item), item_val(bt, item), + item_val_len(item), arg); + if (ret < 0) + break; + + node = scoutfs_avl_next(&bt->item_root, node); + } + + scoutfs_block_put(sb, bl); +out: + return ret; +} + +/* + * The caller has a sorted list of items to insert. We find the leaf + * block that contains each item and either overwrite or insert the + * caller's item. This has no mechanism for deleting items. + * + * This can make partial progress before returning an error, leaving + * dirty btree blocks with only some of the caller's items. It's up to + * the caller to resolve this. + */ +int scoutfs_btree_insert_list(struct super_block *sb, + struct scoutfs_radix_allocator *alloc, + struct scoutfs_block_writer *wri, + struct scoutfs_btree_root *root, + struct scoutfs_btree_item_list *lst) +{ + struct scoutfs_btree_item *item; + struct btree_walk_key_range kr; + struct scoutfs_btree_block *bt; + struct scoutfs_avl_node *par; + struct scoutfs_block *bl; + int cmp; + int ret = 0; + + while (lst) { + ret = btree_walk(sb, alloc, wri, root, BTW_DIRTY | BTW_INSERT, + &lst->key, lst->val_len, &bl, &kr); + if (ret < 0) + goto out; + bt = bl->data; + + do { + item = leaf_item_hash_search(sb, bt, &lst->key); + if (item) { + update_item_value(bt, item, lst->val, + lst->val_len); + } else { + scoutfs_avl_search(&bt->item_root, + cmp_key_item, &lst->key, + &cmp, &par, NULL, NULL); + create_item(bt, &lst->key, lst->val, + lst->val_len, par, cmp); + } + + lst = lst->next; + } while (lst && scoutfs_key_compare(&lst->key, &kr.end) <= 0 && + mid_free_item_room(bt, lst->val_len)); + + scoutfs_block_put(sb, bl); + } + +out: + return ret; +} diff --git a/kmod/src/btree.h b/kmod/src/btree.h index e86396ae..c9bd6478 100644 --- a/kmod/src/btree.h +++ b/kmod/src/btree.h @@ -18,6 +18,18 @@ struct scoutfs_btree_item_ref { #define SCOUTFS_BTREE_ITEM_REF(name) \ struct scoutfs_btree_item_ref name = {NULL,} +/* caller gives an item to the callback */ +typedef int (*scoutfs_btree_item_cb)(struct super_block *sb, + struct scoutfs_key *key, + void *val, int val_len, void *arg); + +/* simple singly-linked list of items */ +struct scoutfs_btree_item_list { + struct scoutfs_btree_item_list *next; + struct scoutfs_key key; + int val_len; + u8 val[0]; +}; int scoutfs_btree_lookup(struct super_block *sb, struct scoutfs_btree_root *root, @@ -58,6 +70,18 @@ int scoutfs_btree_dirty(struct super_block *sb, struct scoutfs_btree_root *root, struct scoutfs_key *key); +int scoutfs_btree_read_items(struct super_block *sb, + struct scoutfs_btree_root *root, + struct scoutfs_key *key, + struct scoutfs_key *start, + struct scoutfs_key *end, + scoutfs_btree_item_cb cb, void *arg); +int scoutfs_btree_insert_list(struct super_block *sb, + struct scoutfs_radix_allocator *alloc, + struct scoutfs_block_writer *wri, + struct scoutfs_btree_root *root, + struct scoutfs_btree_item_list *lst); + void scoutfs_btree_put_iref(struct scoutfs_btree_item_ref *iref); #endif