vfs: make it possible to access the dentry hash/len as one 64-bit entry

This allows comparing hash and len in one operation on 64-bit
architectures.  Right now only __d_lookup_rcu() takes advantage of this,
since that is the case we care most about.

The use of anonymous struct/unions hides the alternate 64-bit approach
from most users, the exception being a few cases where we initialize a
'struct qstr' with a static initializer.  This makes the problematic
cases use a new QSTR_INIT() helper function for that (but initializing
just the name pointer with a "{ .name = xyzzy }" initializer remains
valid, as does just copying another qstr structure).

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2012-05-10 13:14:12 -07:00
parent ee983e8967
commit 26fe575028
18 changed files with 44 additions and 48 deletions

View File

@ -1502,7 +1502,7 @@ struct dentry *d_make_root(struct inode *root_inode)
struct dentry *res = NULL; struct dentry *res = NULL;
if (root_inode) { if (root_inode) {
static const struct qstr name = { .name = "/", .len = 1 }; static const struct qstr name = QSTR_INIT("/", 1);
res = __d_alloc(root_inode->i_sb, &name); res = __d_alloc(root_inode->i_sb, &name);
if (res) if (res)
@ -1816,10 +1816,9 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
const struct qstr *name, const struct qstr *name,
unsigned *seqp, struct inode *inode) unsigned *seqp, struct inode *inode)
{ {
unsigned int len = name->len; u64 hashlen = name->hash_len;
unsigned int hash = name->hash;
const unsigned char *str = name->name; const unsigned char *str = name->name;
struct hlist_bl_head *b = d_hash(parent, hash); struct hlist_bl_head *b = d_hash(parent, hashlen_hash(hashlen));
struct hlist_bl_node *node; struct hlist_bl_node *node;
struct dentry *dentry; struct dentry *dentry;
@ -1846,9 +1845,6 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) { hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
unsigned seq; unsigned seq;
if (dentry->d_name.hash != hash)
continue;
seqretry: seqretry:
/* /*
* The dentry sequence count protects us from concurrent * The dentry sequence count protects us from concurrent
@ -1871,6 +1867,8 @@ seqretry:
*seqp = seq; *seqp = seq;
if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) { if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
if (dentry->d_name.hash != hashlen_hash(hashlen))
continue;
switch (slow_dentry_cmp(parent, inode, dentry, seq, name)) { switch (slow_dentry_cmp(parent, inode, dentry, seq, name)) {
case D_COMP_OK: case D_COMP_OK:
return dentry; return dentry;
@ -1881,9 +1879,9 @@ seqretry:
} }
} }
if (dentry->d_name.len != len) if (dentry->d_name.hash_len != hashlen)
continue; continue;
if (!dentry_cmp(dentry, str, len)) if (!dentry_cmp(dentry, str, hashlen_len(hashlen)))
return dentry; return dentry;
} }
return NULL; return NULL;

View File

@ -79,7 +79,7 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str
struct dentry *ext2_get_parent(struct dentry *child) struct dentry *ext2_get_parent(struct dentry *child)
{ {
struct qstr dotdot = {.name = "..", .len = 2}; struct qstr dotdot = QSTR_INIT("..", 2);
unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot); unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot);
if (!ino) if (!ino)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);

View File

@ -1045,7 +1045,7 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str
struct dentry *ext3_get_parent(struct dentry *child) struct dentry *ext3_get_parent(struct dentry *child)
{ {
unsigned long ino; unsigned long ino;
struct qstr dotdot = {.name = "..", .len = 2}; struct qstr dotdot = QSTR_INIT("..", 2);
struct ext3_dir_entry_2 * de; struct ext3_dir_entry_2 * de;
struct buffer_head *bh; struct buffer_head *bh;

View File

@ -1052,10 +1052,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
struct dentry *ext4_get_parent(struct dentry *child) struct dentry *ext4_get_parent(struct dentry *child)
{ {
__u32 ino; __u32 ino;
static const struct qstr dotdot = { static const struct qstr dotdot = QSTR_INIT("..", 2);
.name = "..",
.len = 2,
};
struct ext4_dir_entry_2 * de; struct ext4_dir_entry_2 * de;
struct buffer_head *bh; struct buffer_head *bh;

View File

@ -821,7 +821,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
struct buffer_head *bh; struct buffer_head *bh;
struct gfs2_leaf *leaf; struct gfs2_leaf *leaf;
struct gfs2_dirent *dent; struct gfs2_dirent *dent;
struct qstr name = { .name = "", .len = 0, .hash = 0 }; struct qstr name = { .name = "" };
error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL); error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
if (error) if (error)

View File

@ -68,7 +68,7 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, struct na
int dcache_dir_open(struct inode *inode, struct file *file) int dcache_dir_open(struct inode *inode, struct file *file)
{ {
static struct qstr cursor_name = {.len = 1, .name = "."}; static struct qstr cursor_name = QSTR_INIT(".", 1);
file->private_data = d_alloc(file->f_path.dentry, &cursor_name); file->private_data = d_alloc(file->f_path.dentry, &cursor_name);
@ -225,7 +225,7 @@ struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name,
struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
struct dentry *dentry; struct dentry *dentry;
struct inode *root; struct inode *root;
struct qstr d_name = {.name = name, .len = strlen(name)}; struct qstr d_name = QSTR_INIT(name, strlen(name));
if (IS_ERR(s)) if (IS_ERR(s))
return ERR_CAST(s); return ERR_CAST(s);

View File

@ -477,10 +477,7 @@ different:
static static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{ {
struct qstr filename = { struct qstr filename = QSTR_INIT(entry->name, entry->len);
.len = entry->len,
.name = entry->name,
};
struct dentry *dentry; struct dentry *dentry;
struct dentry *alias; struct dentry *alias;
struct inode *dir = parent->d_inode; struct inode *dir = parent->d_inode;

View File

@ -398,8 +398,7 @@ nfs3_proc_remove(struct inode *dir, struct qstr *name)
{ {
struct nfs_removeargs arg = { struct nfs_removeargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name.len = name->len, .name = *name,
.name.name = name->name,
}; };
struct nfs_removeres res; struct nfs_removeres res;
struct rpc_message msg = { struct rpc_message msg = {

View File

@ -2782,8 +2782,7 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct nfs_removeargs args = { struct nfs_removeargs args = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name.len = name->len, .name = *name,
.name.name = name->name,
.bitmask = server->attr_bitmask, .bitmask = server->attr_bitmask,
}; };
struct nfs_removeres res = { struct nfs_removeres res = {

View File

@ -335,8 +335,7 @@ nfs_proc_remove(struct inode *dir, struct qstr *name)
{ {
struct nfs_removeargs arg = { struct nfs_removeargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name.len = name->len, .name = *name,
.name.name = name->name,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_REMOVE], .rpc_proc = &nfs_procedures[NFSPROC_REMOVE],

View File

@ -441,7 +441,7 @@ static struct dentry *nilfs_get_parent(struct dentry *child)
{ {
unsigned long ino; unsigned long ino;
struct inode *inode; struct inode *inode;
struct qstr dotdot = {.name = "..", .len = 2}; struct qstr dotdot = QSTR_INIT("..", 2);
struct nilfs_root *root; struct nilfs_root *root;
ino = nilfs_inode_by_name(child->d_inode, &dotdot); ino = nilfs_inode_by_name(child->d_inode, &dotdot);

View File

@ -2361,7 +2361,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
* by passing 'ubifs_tnc_remove_nm()' the same key but * by passing 'ubifs_tnc_remove_nm()' the same key but
* an unmatchable name. * an unmatchable name.
*/ */
struct qstr noname = { .len = 0, .name = "" }; struct qstr noname = { .name = "" };
err = dbg_check_tnc(c, 0); err = dbg_check_tnc(c, 0);
mutex_unlock(&c->tnc_mutex); mutex_unlock(&c->tnc_mutex);

View File

@ -298,7 +298,7 @@ int ubifs_setxattr(struct dentry *dentry, const char *name,
{ {
struct inode *inode, *host = dentry->d_inode; struct inode *inode, *host = dentry->d_inode;
struct ubifs_info *c = host->i_sb->s_fs_info; struct ubifs_info *c = host->i_sb->s_fs_info;
struct qstr nm = { .name = name, .len = strlen(name) }; struct qstr nm = QSTR_INIT(name, strlen(name));
struct ubifs_dent_node *xent; struct ubifs_dent_node *xent;
union ubifs_key key; union ubifs_key key;
int err, type; int err, type;
@ -361,7 +361,7 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf,
{ {
struct inode *inode, *host = dentry->d_inode; struct inode *inode, *host = dentry->d_inode;
struct ubifs_info *c = host->i_sb->s_fs_info; struct ubifs_info *c = host->i_sb->s_fs_info;
struct qstr nm = { .name = name, .len = strlen(name) }; struct qstr nm = QSTR_INIT(name, strlen(name));
struct ubifs_inode *ui; struct ubifs_inode *ui;
struct ubifs_dent_node *xent; struct ubifs_dent_node *xent;
union ubifs_key key; union ubifs_key key;
@ -524,7 +524,7 @@ int ubifs_removexattr(struct dentry *dentry, const char *name)
{ {
struct inode *inode, *host = dentry->d_inode; struct inode *inode, *host = dentry->d_inode;
struct ubifs_info *c = host->i_sb->s_fs_info; struct ubifs_info *c = host->i_sb->s_fs_info;
struct qstr nm = { .name = name, .len = strlen(name) }; struct qstr nm = QSTR_INIT(name, strlen(name));
struct ubifs_dent_node *xent; struct ubifs_dent_node *xent;
union ubifs_key key; union ubifs_key key;
int err; int err;

View File

@ -1193,7 +1193,7 @@ static struct dentry *udf_get_parent(struct dentry *child)
{ {
struct kernel_lb_addr tloc; struct kernel_lb_addr tloc;
struct inode *inode = NULL; struct inode *inode = NULL;
struct qstr dotdot = {.name = "..", .len = 2}; struct qstr dotdot = QSTR_INIT("..", 2);
struct fileIdentDesc cfi; struct fileIdentDesc cfi;
struct udf_fileident_bh fibh; struct udf_fileident_bh fibh;

View File

@ -146,10 +146,7 @@ static struct dentry *ufs_fh_to_parent(struct super_block *sb, struct fid *fid,
static struct dentry *ufs_get_parent(struct dentry *child) static struct dentry *ufs_get_parent(struct dentry *child)
{ {
struct qstr dot_dot = { struct qstr dot_dot = QSTR_INIT("..", 2);
.name = "..",
.len = 2,
};
ino_t ino; ino_t ino;
ino = ufs_inode_by_name(child->d_inode, &dot_dot); ino = ufs_inode_by_name(child->d_inode, &dot_dot);

View File

@ -25,6 +25,13 @@ struct vfsmount;
#define IS_ROOT(x) ((x) == (x)->d_parent) #define IS_ROOT(x) ((x) == (x)->d_parent)
/* The hash is always the low bits of hash_len */
#ifdef __LITTLE_ENDIAN
#define HASH_LEN_DECLARE u32 hash; u32 len;
#else
#define HASH_LEN_DECLARE u32 len; u32 hash;
#endif
/* /*
* "quick string" -- eases parameter passing, but more importantly * "quick string" -- eases parameter passing, but more importantly
* saves "metadata" about the string (ie length and the hash). * saves "metadata" about the string (ie length and the hash).
@ -33,11 +40,19 @@ struct vfsmount;
* dentry. * dentry.
*/ */
struct qstr { struct qstr {
unsigned int hash; union {
unsigned int len; struct {
HASH_LEN_DECLARE;
};
u64 hash_len;
};
const unsigned char *name; const unsigned char *name;
}; };
#define QSTR_INIT(n,l) { { { .len = l } }, .name = n }
#define hashlen_hash(hashlen) ((u32) (hashlen))
#define hashlen_len(hashlen) ((u32)((hashlen) >> 32))
struct dentry_stat_t { struct dentry_stat_t {
int nr_dentry; int nr_dentry;
int nr_unused; int nr_unused;

View File

@ -127,9 +127,7 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
{ {
static uint32_t clntid; static uint32_t clntid;
char name[15]; char name[15];
struct qstr q = { struct qstr q = { .name = name };
.name = name,
};
struct dentry *dir, *dentry; struct dentry *dir, *dentry;
int error; int error;

View File

@ -1059,12 +1059,9 @@ static const struct rpc_filelist files[] = {
struct dentry *rpc_d_lookup_sb(const struct super_block *sb, struct dentry *rpc_d_lookup_sb(const struct super_block *sb,
const unsigned char *dir_name) const unsigned char *dir_name)
{ {
struct qstr dir = { struct qstr dir = QSTR_INIT(dir_name, strlen(dir_name));
.name = dir_name,
.len = strlen(dir_name),
.hash = full_name_hash(dir_name, strlen(dir_name)),
};
dir.hash = full_name_hash(dir.name, dir.len);
return d_lookup(sb->s_root, &dir); return d_lookup(sb->s_root, &dir);
} }
EXPORT_SYMBOL_GPL(rpc_d_lookup_sb); EXPORT_SYMBOL_GPL(rpc_d_lookup_sb);