Use contiguous key struct instead of kvecs

Using kvecs for keys seemed like a good idea because there were a few
uses that had keys in fragmented memory: dirent keys made up of an
on-stack struct and the file name in the dentry, and keys straddling the
pages that make up a cached segment.

But it hasn't worked out very well.  The code to perform ops on keys
by iterating over vectors is pretty fiddly.  And the raw kvecs only
describe the actively referenced key, they know nothing about the total
size of the buffer that the key resides in.  Some ops can't check that
they're not clobbering things, they're relying on callers not to mess
up.

And critically, the kvec iteration's become a bottleneck.  It turns out
that comparing keys is a very hot path in the item cache.  All the code
to initialize and iterate over two key vectors adds up when each high
level fs operation is a few tree descents and each tree descent is a
bunch of compares.

So let's back off and have a specific struct for tracking keys that are
stored in contiguous memory regions.  Users ensure that keys are
contiguous.  The code ends up being a lot clearer, code now can see how
big the full key buffer is, and the rbtree node comparison fast path is
now just a memcmp.

Almost all of the changes in the patch are mechanical semantic changes
involving types, function names, args, and occasionaly slightly
different return conventions.

A slightly more involved change is that now dirent key users have to
manage an allocated contiguous key with a copy of the path from the
dentry.

Item reading is now a little more clever about calculating the greatest
range it can cache by initially walking all the segments instead of
trying to do it as it runs out of items in each segment.

The largest meaningful change is that now keys can't straddle page
boundaries in memory which means they can't cross block boundaries in
the segment.  We align key offsets to the next block as we write keys to
segments that would have straddled a block.

We then also have to account for that padding when building segments.
We add a helper that calculates if a given number of items will fit in a
segment which is used by item dirtying, segment writing, and compaction.

I left the tracepoint formatting for another patch.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2017-04-18 13:44:54 -07:00
parent 8a302609f2
commit 2bc1617280
14 changed files with 794 additions and 459 deletions
+16 -17
View File
@@ -127,26 +127,28 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
ci->data_version = le64_to_cpu(cinode->data_version);
}
static void set_inode_key(struct scoutfs_inode_key *ikey, u64 ino)
static void init_inode_key(struct scoutfs_key_buf *key,
struct scoutfs_inode_key *ikey, u64 ino)
{
ikey->type = SCOUTFS_INODE_KEY;
ikey->ino = cpu_to_be64(ino);
scoutfs_key_init(key, ikey, sizeof(struct scoutfs_inode_key));
}
static int scoutfs_read_locked_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct scoutfs_inode_key ikey;
struct scoutfs_key_buf key;
struct scoutfs_inode sinode;
SCOUTFS_DECLARE_KVEC(key);
SCOUTFS_DECLARE_KVEC(val);
int ret;
set_inode_key(&ikey, scoutfs_ino(inode));
scoutfs_kvec_init(key, &ikey, sizeof(ikey));
init_inode_key(&key, &ikey, scoutfs_ino(inode));
scoutfs_kvec_init(val, &sinode, sizeof(sinode));
ret = scoutfs_item_lookup_exact(sb, key, val, sizeof(sinode));
ret = scoutfs_item_lookup_exact(sb, &key, val, sizeof(sinode));
if (ret == 0)
load_inode(inode, &sinode);
@@ -269,16 +271,15 @@ int scoutfs_dirty_inode_item(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct scoutfs_inode_key ikey;
struct scoutfs_key_buf key;
struct scoutfs_inode sinode;
SCOUTFS_DECLARE_KVEC(key);
int ret;
store_inode(&sinode, inode);
set_inode_key(&ikey, scoutfs_ino(inode));
scoutfs_kvec_init(key, &ikey, sizeof(ikey));
init_inode_key(&key, &ikey, scoutfs_ino(inode));
ret = scoutfs_item_dirty(sb, key);
ret = scoutfs_item_dirty(sb, &key);
if (!ret)
trace_scoutfs_dirty_inode(inode);
return ret;
@@ -297,18 +298,17 @@ void scoutfs_update_inode_item(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct scoutfs_inode_key ikey;
struct scoutfs_key_buf key;
struct scoutfs_inode sinode;
SCOUTFS_DECLARE_KVEC(key);
SCOUTFS_DECLARE_KVEC(val);
int err;
store_inode(&sinode, inode);
set_inode_key(&ikey, scoutfs_ino(inode));
scoutfs_kvec_init(key, &ikey, sizeof(ikey));
init_inode_key(&key, &ikey, scoutfs_ino(inode));
scoutfs_kvec_init(val, &sinode, sizeof(sinode));
err = scoutfs_item_update(sb, key, val);
err = scoutfs_item_update(sb, &key, val);
BUG_ON(err);
trace_scoutfs_update_inode(inode);
@@ -388,8 +388,8 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
{
struct scoutfs_inode_info *ci;
struct scoutfs_inode_key ikey;
struct scoutfs_key_buf key;
struct scoutfs_inode sinode;
SCOUTFS_DECLARE_KVEC(key);
SCOUTFS_DECLARE_KVEC(val);
struct inode *inode;
u64 ino;
@@ -419,11 +419,10 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
set_inode_ops(inode);
store_inode(&sinode, inode);
set_inode_key(&ikey, scoutfs_ino(inode));
scoutfs_kvec_init(key, &ikey, sizeof(ikey));
init_inode_key(&key, &ikey, scoutfs_ino(inode));
scoutfs_kvec_init(val, &sinode, sizeof(sinode));
ret = scoutfs_item_create(sb, key, val);
ret = scoutfs_item_create(sb, &key, val);
if (ret) {
iput(inode);
return ERR_PTR(ret);