Commit b4aff1f8 authored by Josef Bacik's avatar Josef Bacik Committed by Al Viro

Btrfs: load the key from the dir item in readdir into a fake dentry

In btrfs we have 2 indexes for inodes.  One is for readdir, it's in this nice
sequential order and works out brilliantly for readdir.  However if you use ls,
it usually stat's each file it gets from readdir.  This is where the second
index comes in, which is based on a hash of the name of the file.  So then the
lookup has to lookup this index, and then lookup the inode.  The index lookup is
going to be in random order (since its based on the name hash), which gives us
less than stellar performance.  Since we know the inode location from the
readdir index, I create a dummy dentry and copy the location key into
dentry->d_fsdata.  Then on lookup if we have d_fsdata we use that location to
lookup the inode, avoiding looking up the other directory index.  Thanks,
Signed-off-by: default avatarJosef Bacik <josef@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 9d108d25
...@@ -4016,12 +4016,19 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -4016,12 +4016,19 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
struct btrfs_root *sub_root = root; struct btrfs_root *sub_root = root;
struct btrfs_key location; struct btrfs_key location;
int index; int index;
int ret; int ret = 0;
if (dentry->d_name.len > BTRFS_NAME_LEN) if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
if (unlikely(d_need_lookup(dentry))) {
memcpy(&location, dentry->d_fsdata, sizeof(struct btrfs_key));
kfree(dentry->d_fsdata);
dentry->d_fsdata = NULL;
d_clear_need_lookup(dentry);
} else {
ret = btrfs_inode_by_name(dir, dentry, &location); ret = btrfs_inode_by_name(dir, dentry, &location);
}
if (ret < 0) if (ret < 0)
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -4076,6 +4083,12 @@ static int btrfs_dentry_delete(const struct dentry *dentry) ...@@ -4076,6 +4083,12 @@ static int btrfs_dentry_delete(const struct dentry *dentry)
return 0; return 0;
} }
static void btrfs_dentry_release(struct dentry *dentry)
{
if (dentry->d_fsdata)
kfree(dentry->d_fsdata);
}
static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd) struct nameidata *nd)
{ {
...@@ -4098,6 +4111,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, ...@@ -4098,6 +4111,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
struct btrfs_path *path; struct btrfs_path *path;
struct list_head ins_list; struct list_head ins_list;
struct list_head del_list; struct list_head del_list;
struct qstr q;
int ret; int ret;
struct extent_buffer *leaf; struct extent_buffer *leaf;
int slot; int slot;
...@@ -4187,6 +4201,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, ...@@ -4187,6 +4201,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
while (di_cur < di_total) { while (di_cur < di_total) {
struct btrfs_key location; struct btrfs_key location;
struct dentry *tmp;
if (verify_dir_item(root, leaf, di)) if (verify_dir_item(root, leaf, di))
break; break;
...@@ -4207,6 +4222,33 @@ static int btrfs_real_readdir(struct file *filp, void *dirent, ...@@ -4207,6 +4222,33 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)]; d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)];
btrfs_dir_item_key_to_cpu(leaf, di, &location); btrfs_dir_item_key_to_cpu(leaf, di, &location);
q.name = name_ptr;
q.len = name_len;
q.hash = full_name_hash(q.name, q.len);
tmp = d_lookup(filp->f_dentry, &q);
if (!tmp) {
struct btrfs_key *newkey;
newkey = kzalloc(sizeof(struct btrfs_key),
GFP_NOFS);
if (!newkey)
goto no_dentry;
tmp = d_alloc(filp->f_dentry, &q);
if (!tmp) {
kfree(newkey);
dput(tmp);
goto no_dentry;
}
memcpy(newkey, &location,
sizeof(struct btrfs_key));
tmp->d_fsdata = newkey;
tmp->d_flags |= DCACHE_NEED_LOOKUP;
d_rehash(tmp);
dput(tmp);
} else {
dput(tmp);
}
no_dentry:
/* is this a reference to our own snapshot? If so /* is this a reference to our own snapshot? If so
* skip it * skip it
*/ */
...@@ -7452,4 +7494,5 @@ static const struct inode_operations btrfs_symlink_inode_operations = { ...@@ -7452,4 +7494,5 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
const struct dentry_operations btrfs_dentry_operations = { const struct dentry_operations btrfs_dentry_operations = {
.d_delete = btrfs_dentry_delete, .d_delete = btrfs_dentry_delete,
.d_release = btrfs_dentry_release,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment