From f611c769e2a6d4b04e0113f3e28b6fe35ffe89a8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 13 Jul 2017 10:02:36 -0700 Subject: [PATCH] scoutfs: add 'end' to item_next to limit reads Add an end key to the item_next calls to limit how many items will be read into the cache. Callers typically get this from the lock they hold that covers the iteration. We differentiate between iteration and caching so that a series of small iterations (listxattr on inodes, namespace walk in small dirs) can be satisfied by a single read of adjacent items from segments. Signed-off-by: Zach Brown --- kmod/src/data.c | 12 ++++++------ kmod/src/dir.c | 4 ++-- kmod/src/inode.c | 2 +- kmod/src/item.c | 26 ++++++++++++++------------ kmod/src/item.h | 9 ++++++--- kmod/src/xattr.c | 4 ++-- 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/kmod/src/data.c b/kmod/src/data.c index bc573bfc..ca437490 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -309,7 +309,7 @@ static int try_merge(struct super_block *sb, struct native_extent *cur, ext.flags = 0; init_extent_key(&key, key_bytes, &ext, arg, type); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret < 0) { if (ret == -ENOENT) ret = 0; @@ -455,7 +455,7 @@ static int remove_extent(struct super_block *sb, /* find outer existing extent that contains removal extent */ init_extent_key(&key, key_bytes, rem, arg, type); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret) goto out; @@ -552,7 +552,7 @@ int scoutfs_data_truncate_items(struct super_block *sb, u64 ino, u64 iblock, init_extent_key(&key, key_bytes, &rng, ino, SCOUTFS_FILE_EXTENT_TYPE); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret < 0) { if (ret == -ENOENT) ret = 0; @@ -797,7 +797,7 @@ retry: init_extent_key(&key, key_bytes, &ext, sbi->node_id, type); init_extent_key(&last, last_bytes, &last_ext, sbi->node_id, type); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret < 0) { if (ret == -ENOENT) { /* if the cursor's empty fall back to next large */ @@ -966,7 +966,7 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock, * item consistency. */ down_read(&datinf->alloc_rwsem); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); up_read(&datinf->alloc_rwsem); if (ret < 0) { if (ret == -ENOENT) @@ -1146,7 +1146,7 @@ int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ext.flags = 0; init_extent_key(&key, key_bytes, &ext, ino, type); - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret < 0) { if (ret != -ENOENT) break; diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 4dc5b3da..55909f8f 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -344,7 +344,7 @@ static int scoutfs_readdir(struct file *file, void *dirent, filldir_t filldir) scoutfs_kvec_init(val, dent, item_len); ret = scoutfs_item_next_same_min(sb, &key, &last_key, val, - offsetof(struct scoutfs_dirent, name[1])); + offsetof(struct scoutfs_dirent, name[1]), NULL); if (ret < 0) { if (ret == -ENOENT) ret = 0; @@ -878,7 +878,7 @@ static int add_next_linkref(struct super_block *sb, u64 ino, init_link_backref_key(&last, &last_lbkey, ino, U64_MAX, NULL, 0); /* next backref key is now in ent */ - ret = scoutfs_item_next(sb, &key, &last, NULL); + ret = scoutfs_item_next(sb, &key, &last, NULL, NULL); trace_printk("ino %llu dir_ino %llu ret %d key_len %u\n", ino, dir_ino, ret, key.key_len); if (ret < 0) diff --git a/kmod/src/inode.c b/kmod/src/inode.c index bd49c494..176bc306 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -933,7 +933,7 @@ int scoutfs_scan_orphans(struct super_block *sb) init_orphan_key(&last, &last_okey, sbi->node_id, ~0ULL); while (1) { - ret = scoutfs_item_next_same(sb, &key, &last, NULL); + ret = scoutfs_item_next_same(sb, &key, &last, NULL, NULL); if (ret == -ENOENT) /* No more orphan items */ break; if (ret < 0) diff --git a/kmod/src/item.c b/kmod/src/item.c index 2e9de37c..ac990cf6 100644 --- a/kmod/src/item.c +++ b/kmod/src/item.c @@ -844,6 +844,10 @@ static struct cached_item *item_for_next(struct rb_root *root, * Return the next item starting with the given key, returning the last * key at the most. * + * While iteration stops the last key we can cache up to the end key so + * that a sequence of small iterations covered by one lock are satisfied + * with a large read of items from segments into the cache. + * * -ENOENT is returned if there are no items between the given and last * keys. * @@ -855,12 +859,12 @@ static struct cached_item *item_for_next(struct rb_root *root, * by the caller's value buffer length. */ int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, - struct scoutfs_key_buf *last, struct kvec *val) + struct scoutfs_key_buf *last, struct kvec *val, + struct scoutfs_key_buf *end) { struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); struct item_cache *cac = sbi->item_cache; struct scoutfs_key_buf *read_start = NULL; - struct scoutfs_key_buf *read_end = NULL; struct scoutfs_key_buf *range_end = NULL; struct cached_item *item; unsigned long flags; @@ -874,9 +878,8 @@ int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, } read_start = scoutfs_key_alloc(sb, SCOUTFS_MAX_KEY_SIZE); - read_end = scoutfs_key_alloc(sb, SCOUTFS_MAX_KEY_SIZE); range_end = scoutfs_key_alloc(sb, SCOUTFS_MAX_KEY_SIZE); - if (!read_start || !read_end || !range_end) { + if (!read_start || !range_end) { ret = -ENOMEM; goto out; } @@ -902,12 +905,10 @@ int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, if (!cached) { /* missing cache starts at key */ scoutfs_key_copy(read_start, key); - scoutfs_key_copy(read_end, range_end); } else if (scoutfs_key_compare(range_end, last) < 0) { /* missing cache starts at range_end */ scoutfs_key_copy(read_start, range_end); - scoutfs_key_copy(read_end, last); } else { /* no items and we have cache between key and last */ @@ -917,7 +918,7 @@ int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, spin_unlock_irqrestore(&cac->lock, flags); - ret = scoutfs_manifest_read_items(sb, read_start, read_end); + ret = scoutfs_manifest_read_items(sb, read_start, end); spin_lock_irqsave(&cac->lock, flags); if (ret) @@ -927,7 +928,6 @@ int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, spin_unlock_irqrestore(&cac->lock, flags); out: scoutfs_key_free(sb, read_start); - scoutfs_key_free(sb, read_end); scoutfs_key_free(sb, range_end); trace_printk("ret %d\n", ret); @@ -943,7 +943,8 @@ out: int scoutfs_item_next_same_min(struct super_block *sb, struct scoutfs_key_buf *key, struct scoutfs_key_buf *last, - struct kvec *val, int len) + struct kvec *val, int len, + struct scoutfs_key_buf *end) { int key_len = key->key_len; int ret; @@ -953,7 +954,7 @@ int scoutfs_item_next_same_min(struct super_block *sb, if (WARN_ON_ONCE(!val || scoutfs_kvec_length(val) < len)) return -EINVAL; - ret = scoutfs_item_next(sb, key, last, val); + ret = scoutfs_item_next(sb, key, last, val, end); if (ret >= 0 && (key->key_len != key_len || ret < len)) ret = -EIO; @@ -967,14 +968,15 @@ int scoutfs_item_next_same_min(struct super_block *sb, * search key. It treats size mismatches as a sign of corruption. */ int scoutfs_item_next_same(struct super_block *sb, struct scoutfs_key_buf *key, - struct scoutfs_key_buf *last, struct kvec *val) + struct scoutfs_key_buf *last, struct kvec *val, + struct scoutfs_key_buf *end) { int key_len = key->key_len; int ret; trace_printk("key len %u\n", key_len); - ret = scoutfs_item_next(sb, key, last, val); + ret = scoutfs_item_next(sb, key, last, val, end); if (ret >= 0 && (key->key_len != key_len)) ret = -EIO; diff --git a/kmod/src/item.h b/kmod/src/item.h index 774f0ab7..924f2fda 100644 --- a/kmod/src/item.h +++ b/kmod/src/item.h @@ -18,13 +18,16 @@ int scoutfs_item_lookup_exact(struct super_block *sb, struct scoutfs_key_buf *key, struct kvec *val, int size, struct scoutfs_key_buf *end); int scoutfs_item_next(struct super_block *sb, struct scoutfs_key_buf *key, - struct scoutfs_key_buf *last, struct kvec *val); + struct scoutfs_key_buf *last, struct kvec *val, + struct scoutfs_key_buf *end); int scoutfs_item_next_same_min(struct super_block *sb, struct scoutfs_key_buf *key, struct scoutfs_key_buf *last, - struct kvec *val, int len); + struct kvec *val, int len, + struct scoutfs_key_buf *end); int scoutfs_item_next_same(struct super_block *sb, struct scoutfs_key_buf *key, - struct scoutfs_key_buf *last, struct kvec *val); + struct scoutfs_key_buf *last, struct kvec *val, + struct scoutfs_key_buf *end); int scoutfs_item_create(struct super_block *sb, struct scoutfs_key_buf *key, struct kvec *val); int scoutfs_item_dirty(struct super_block *sb, struct scoutfs_key_buf *key); diff --git a/kmod/src/xattr.c b/kmod/src/xattr.c index 19dbce6f..0fac057d 100644 --- a/kmod/src/xattr.c +++ b/kmod/src/xattr.c @@ -394,7 +394,7 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size) total = 0; for (;;) { - ret = scoutfs_item_next(sb, key, last, NULL); + ret = scoutfs_item_next(sb, key, last, NULL, lck->end); if (ret < 0) { if (ret == -ENOENT) ret = total; @@ -476,7 +476,7 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino) /* the inode is dead so we don't need the xattr sem */ for (;;) { - ret = scoutfs_item_next(sb, key, last, NULL); + ret = scoutfs_item_next(sb, key, last, NULL, lck->end); if (ret < 0) { if (ret == -ENOENT) ret = 0;