fs: change d_compare for rcu-walk

Change d_compare so it may be called from lock-free RCU lookups. This
does put significant restrictions on what may be done from the callback,
however there don't seem to have been any problems with in-tree fses.
If some strange use case pops up that _really_ cannot cope with the
rcu-walk rules, we can just add new rcu-unaware callbacks, which would
cause name lookup to drop out of rcu-walk mode.

For in-tree filesystems, this is just a mechanical change.

Signed-off-by: Nick Piggin <[email protected]>
This commit is contained in:
Nick Piggin 2011-01-07 17:49:27 +11:00
commit 621e155a35
23 changed files with 242 additions and 157 deletions

View file

@ -164,16 +164,18 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
* Compare two msdos names. If either of the names are invalid,
* we fall back to doing the standard name comparison.
*/
static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
const struct dentry *dentry, const struct inode *inode,
unsigned int len, const char *str, const struct qstr *name)
{
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
int error;
error = msdos_format_name(a->name, a->len, a_msdos_name, options);
error = msdos_format_name(name->name, name->len, a_msdos_name, options);
if (error)
goto old_compare;
error = msdos_format_name(b->name, b->len, b_msdos_name, options);
error = msdos_format_name(str, len, b_msdos_name, options);
if (error)
goto old_compare;
error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@ -182,8 +184,8 @@ out:
old_compare:
error = 1;
if (a->len == b->len)
error = memcmp(a->name, b->name, a->len);
if (name->len == len)
error = memcmp(name->name, str, len);
goto out;
}

View file

@ -85,15 +85,18 @@ static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
}
/* returns the length of a struct qstr, ignoring trailing dots */
static unsigned int vfat_striptail_len(struct qstr *qstr)
static unsigned int __vfat_striptail_len(unsigned int len, const char *name)
{
unsigned int len = qstr->len;
while (len && qstr->name[len - 1] == '.')
while (len && name[len - 1] == '.')
len--;
return len;
}
static unsigned int vfat_striptail_len(const struct qstr *qstr)
{
return __vfat_striptail_len(qstr->len, qstr->name);
}
/*
* Compute the hash for the vfat name corresponding to the dentry.
* Note: if the name is invalid, we leave the hash code unchanged so
@ -133,16 +136,18 @@ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
/*
* Case insensitive compare of two vfat names.
*/
static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode,
const struct dentry *dentry, const struct inode *inode,
unsigned int len, const char *str, const struct qstr *name)
{
struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
unsigned int alen, blen;
/* A filename cannot end in '.' or we treat it like it has none */
alen = vfat_striptail_len(a);
blen = vfat_striptail_len(b);
alen = vfat_striptail_len(name);
blen = __vfat_striptail_len(len, str);
if (alen == blen) {
if (nls_strnicmp(t, a->name, b->name, alen) == 0)
if (nls_strnicmp(t, name->name, str, alen) == 0)
return 0;
}
return 1;
@ -151,15 +156,17 @@ static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
/*
* Case sensitive compare of two vfat names.
*/
static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
static int vfat_cmp(const struct dentry *parent, const struct inode *pinode,
const struct dentry *dentry, const struct inode *inode,
unsigned int len, const char *str, const struct qstr *name)
{
unsigned int alen, blen;
/* A filename cannot end in '.' or we treat it like it has none */
alen = vfat_striptail_len(a);
blen = vfat_striptail_len(b);
alen = vfat_striptail_len(name);
blen = __vfat_striptail_len(len, str);
if (alen == blen) {
if (strncmp(a->name, b->name, alen) == 0)
if (strncmp(name->name, str, alen) == 0)
return 0;
}
return 1;