NFS: Add dentry materialisation op
The attached patch adds a new directory cache management function that prepares a disconnected anonymous function to be connected into the dentry tree. The anonymous dentry is transferred the name and parentage from another dentry. The following changes were made in [try #2]: (*) d_materialise_dentry() now switches the parentage of the two nodes around correctly when one or other of them is self-referential. The following changes were made in [try #7]: (*) d_instantiate_unique() has had the interior part split out as function __d_instantiate_unique(). Callers of this latter function must be holding the appropriate locks. (*) _d_rehash() has been added as a wrapper around __d_rehash() to call it with the most obvious hash list (the one from the name). d_rehash() now calls _d_rehash(). (*) d_materialise_dentry() is now __d_materialise_dentry() and is static. (*) d_materialise_unique() added to perform the combination of d_find_alias(), d_materialise_dentry() and d_add_unique() that the NFS client was doing twice, all within a single dcache_lock critical section. This reduces the number of times two different spinlocks were being accessed. The following further changes were made: (*) Add the dentries onto their parents d_subdirs lists. Signed-Off-By: David Howells <dhowells@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
979df72e6f
commit
770bfad846
160
fs/dcache.c
160
fs/dcache.c
@ -828,17 +828,19 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
|
|||||||
* (or otherwise set) by the caller to indicate that it is now
|
* (or otherwise set) by the caller to indicate that it is now
|
||||||
* in use by the dcache.
|
* in use by the dcache.
|
||||||
*/
|
*/
|
||||||
struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
|
static struct dentry *__d_instantiate_unique(struct dentry *entry,
|
||||||
|
struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
int len = entry->d_name.len;
|
int len = entry->d_name.len;
|
||||||
const char *name = entry->d_name.name;
|
const char *name = entry->d_name.name;
|
||||||
unsigned int hash = entry->d_name.hash;
|
unsigned int hash = entry->d_name.hash;
|
||||||
|
|
||||||
BUG_ON(!list_empty(&entry->d_alias));
|
if (!inode) {
|
||||||
spin_lock(&dcache_lock);
|
entry->d_inode = NULL;
|
||||||
if (!inode)
|
return NULL;
|
||||||
goto do_negative;
|
}
|
||||||
|
|
||||||
list_for_each_entry(alias, &inode->i_dentry, d_alias) {
|
list_for_each_entry(alias, &inode->i_dentry, d_alias) {
|
||||||
struct qstr *qstr = &alias->d_name;
|
struct qstr *qstr = &alias->d_name;
|
||||||
|
|
||||||
@ -851,19 +853,35 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
|
|||||||
if (memcmp(qstr->name, name, len))
|
if (memcmp(qstr->name, name, len))
|
||||||
continue;
|
continue;
|
||||||
dget_locked(alias);
|
dget_locked(alias);
|
||||||
spin_unlock(&dcache_lock);
|
|
||||||
BUG_ON(!d_unhashed(alias));
|
|
||||||
iput(inode);
|
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_add(&entry->d_alias, &inode->i_dentry);
|
list_add(&entry->d_alias, &inode->i_dentry);
|
||||||
do_negative:
|
|
||||||
entry->d_inode = inode;
|
entry->d_inode = inode;
|
||||||
fsnotify_d_instantiate(entry, inode);
|
fsnotify_d_instantiate(entry, inode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
|
||||||
|
{
|
||||||
|
struct dentry *result;
|
||||||
|
|
||||||
|
BUG_ON(!list_empty(&entry->d_alias));
|
||||||
|
|
||||||
|
spin_lock(&dcache_lock);
|
||||||
|
result = __d_instantiate_unique(entry, inode);
|
||||||
spin_unlock(&dcache_lock);
|
spin_unlock(&dcache_lock);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
security_d_instantiate(entry, inode);
|
security_d_instantiate(entry, inode);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BUG_ON(!d_unhashed(result));
|
||||||
|
iput(inode);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(d_instantiate_unique);
|
EXPORT_SYMBOL(d_instantiate_unique);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1235,6 +1253,11 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
|
|||||||
hlist_add_head_rcu(&entry->d_hash, list);
|
hlist_add_head_rcu(&entry->d_hash, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _d_rehash(struct dentry * entry)
|
||||||
|
{
|
||||||
|
__d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* d_rehash - add an entry back to the hash
|
* d_rehash - add an entry back to the hash
|
||||||
* @entry: dentry to add to the hash
|
* @entry: dentry to add to the hash
|
||||||
@ -1244,11 +1267,9 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
|
|||||||
|
|
||||||
void d_rehash(struct dentry * entry)
|
void d_rehash(struct dentry * entry)
|
||||||
{
|
{
|
||||||
struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
|
|
||||||
|
|
||||||
spin_lock(&dcache_lock);
|
spin_lock(&dcache_lock);
|
||||||
spin_lock(&entry->d_lock);
|
spin_lock(&entry->d_lock);
|
||||||
__d_rehash(entry, list);
|
_d_rehash(entry);
|
||||||
spin_unlock(&entry->d_lock);
|
spin_unlock(&entry->d_lock);
|
||||||
spin_unlock(&dcache_lock);
|
spin_unlock(&dcache_lock);
|
||||||
}
|
}
|
||||||
@ -1386,6 +1407,120 @@ already_unhashed:
|
|||||||
spin_unlock(&dcache_lock);
|
spin_unlock(&dcache_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare an anonymous dentry for life in the superblock's dentry tree as a
|
||||||
|
* named dentry in place of the dentry to be replaced.
|
||||||
|
*/
|
||||||
|
static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
|
||||||
|
{
|
||||||
|
struct dentry *dparent, *aparent;
|
||||||
|
|
||||||
|
switch_names(dentry, anon);
|
||||||
|
do_switch(dentry->d_name.len, anon->d_name.len);
|
||||||
|
do_switch(dentry->d_name.hash, anon->d_name.hash);
|
||||||
|
|
||||||
|
dparent = dentry->d_parent;
|
||||||
|
aparent = anon->d_parent;
|
||||||
|
|
||||||
|
dentry->d_parent = (aparent == anon) ? dentry : aparent;
|
||||||
|
list_del(&dentry->d_u.d_child);
|
||||||
|
if (!IS_ROOT(dentry))
|
||||||
|
list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
|
||||||
|
else
|
||||||
|
INIT_LIST_HEAD(&dentry->d_u.d_child);
|
||||||
|
|
||||||
|
anon->d_parent = (dparent == dentry) ? anon : dparent;
|
||||||
|
list_del(&anon->d_u.d_child);
|
||||||
|
if (!IS_ROOT(anon))
|
||||||
|
list_add(&anon->d_u.d_child, &anon->d_parent->d_subdirs);
|
||||||
|
else
|
||||||
|
INIT_LIST_HEAD(&anon->d_u.d_child);
|
||||||
|
|
||||||
|
anon->d_flags &= ~DCACHE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* d_materialise_unique - introduce an inode into the tree
|
||||||
|
* @dentry: candidate dentry
|
||||||
|
* @inode: inode to bind to the dentry, to which aliases may be attached
|
||||||
|
*
|
||||||
|
* Introduces an dentry into the tree, substituting an extant disconnected
|
||||||
|
* root directory alias in its place if there is one
|
||||||
|
*/
|
||||||
|
struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
|
||||||
|
{
|
||||||
|
struct dentry *alias, *actual;
|
||||||
|
|
||||||
|
BUG_ON(!d_unhashed(dentry));
|
||||||
|
|
||||||
|
spin_lock(&dcache_lock);
|
||||||
|
|
||||||
|
if (!inode) {
|
||||||
|
actual = dentry;
|
||||||
|
dentry->d_inode = NULL;
|
||||||
|
goto found_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if a disconnected directory already exists as an anonymous root
|
||||||
|
* that we should splice into the tree instead */
|
||||||
|
if (S_ISDIR(inode->i_mode) && (alias = __d_find_alias(inode, 1))) {
|
||||||
|
spin_lock(&alias->d_lock);
|
||||||
|
|
||||||
|
/* Is this a mountpoint that we could splice into our tree? */
|
||||||
|
if (IS_ROOT(alias))
|
||||||
|
goto connect_mountpoint;
|
||||||
|
|
||||||
|
if (alias->d_name.len == dentry->d_name.len &&
|
||||||
|
alias->d_parent == dentry->d_parent &&
|
||||||
|
memcmp(alias->d_name.name,
|
||||||
|
dentry->d_name.name,
|
||||||
|
dentry->d_name.len) == 0)
|
||||||
|
goto replace_with_alias;
|
||||||
|
|
||||||
|
spin_unlock(&alias->d_lock);
|
||||||
|
|
||||||
|
/* Doh! Seem to be aliasing directories for some reason... */
|
||||||
|
dput(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a unique reference */
|
||||||
|
actual = __d_instantiate_unique(dentry, inode);
|
||||||
|
if (!actual)
|
||||||
|
actual = dentry;
|
||||||
|
else if (unlikely(!d_unhashed(actual)))
|
||||||
|
goto shouldnt_be_hashed;
|
||||||
|
|
||||||
|
found_lock:
|
||||||
|
spin_lock(&actual->d_lock);
|
||||||
|
found:
|
||||||
|
_d_rehash(actual);
|
||||||
|
spin_unlock(&actual->d_lock);
|
||||||
|
spin_unlock(&dcache_lock);
|
||||||
|
|
||||||
|
if (actual == dentry) {
|
||||||
|
security_d_instantiate(dentry, inode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iput(inode);
|
||||||
|
return actual;
|
||||||
|
|
||||||
|
/* Convert the anonymous/root alias into an ordinary dentry */
|
||||||
|
connect_mountpoint:
|
||||||
|
__d_materialise_dentry(dentry, alias);
|
||||||
|
|
||||||
|
/* Replace the candidate dentry with the alias in the tree */
|
||||||
|
replace_with_alias:
|
||||||
|
__d_drop(alias);
|
||||||
|
actual = alias;
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
shouldnt_be_hashed:
|
||||||
|
spin_unlock(&dcache_lock);
|
||||||
|
BUG();
|
||||||
|
goto shouldnt_be_hashed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* d_path - return the path of a dentry
|
* d_path - return the path of a dentry
|
||||||
* @dentry: dentry to report
|
* @dentry: dentry to report
|
||||||
@ -1784,6 +1919,7 @@ EXPORT_SYMBOL(d_instantiate);
|
|||||||
EXPORT_SYMBOL(d_invalidate);
|
EXPORT_SYMBOL(d_invalidate);
|
||||||
EXPORT_SYMBOL(d_lookup);
|
EXPORT_SYMBOL(d_lookup);
|
||||||
EXPORT_SYMBOL(d_move);
|
EXPORT_SYMBOL(d_move);
|
||||||
|
EXPORT_SYMBOL_GPL(d_materialise_unique);
|
||||||
EXPORT_SYMBOL(d_path);
|
EXPORT_SYMBOL(d_path);
|
||||||
EXPORT_SYMBOL(d_prune_aliases);
|
EXPORT_SYMBOL(d_prune_aliases);
|
||||||
EXPORT_SYMBOL(d_rehash);
|
EXPORT_SYMBOL(d_rehash);
|
||||||
|
@ -221,6 +221,7 @@ static inline int dname_external(struct dentry *dentry)
|
|||||||
*/
|
*/
|
||||||
extern void d_instantiate(struct dentry *, struct inode *);
|
extern void d_instantiate(struct dentry *, struct inode *);
|
||||||
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
|
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
|
||||||
|
extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
|
||||||
extern void d_delete(struct dentry *);
|
extern void d_delete(struct dentry *);
|
||||||
|
|
||||||
/* allocate/de-allocate */
|
/* allocate/de-allocate */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user