summaryrefslogtreecommitdiff
path: root/fs/dcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c60
1 files changed, 34 insertions, 26 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 85c9e2bff8e6..8086636bf796 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -218,7 +218,7 @@ static void __d_free(struct rcu_head *head)
{
struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu);
- WARN_ON(!list_empty(&dentry->d_alias));
+ WARN_ON(!hlist_unhashed(&dentry->d_alias));
if (dname_external(dentry))
kfree(dentry->d_name.name);
kmem_cache_free(dentry_cache, dentry);
@@ -267,7 +267,7 @@ static void dentry_iput(struct dentry * dentry)
struct inode *inode = dentry->d_inode;
if (inode) {
dentry->d_inode = NULL;
- list_del_init(&dentry->d_alias);
+ hlist_del_init(&dentry->d_alias);
spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock);
if (!inode->i_nlink)
@@ -291,7 +291,7 @@ static void dentry_unlink_inode(struct dentry * dentry)
{
struct inode *inode = dentry->d_inode;
dentry->d_inode = NULL;
- list_del_init(&dentry->d_alias);
+ hlist_del_init(&dentry->d_alias);
dentry_rcuwalk_barrier(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock);
@@ -683,6 +683,8 @@ EXPORT_SYMBOL(dget_parent);
/**
* d_find_alias - grab a hashed alias of inode
* @inode: inode in question
+ * @want_discon: flag, used by d_splice_alias, to request
+ * that only a DISCONNECTED alias be returned.
*
* If inode has a hashed alias, or is a directory and has any alias,
* acquire the reference to alias and return it. Otherwise return NULL.
@@ -691,21 +693,23 @@ EXPORT_SYMBOL(dget_parent);
* of a filesystem.
*
* If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
- * any other hashed alias over that.
+ * any other hashed alias over that one unless @want_discon is set,
+ * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
*/
-static struct dentry *__d_find_alias(struct inode *inode)
+static struct dentry *__d_find_alias(struct inode *inode, int want_discon)
{
struct dentry *alias, *discon_alias;
+ struct hlist_node *p;
again:
discon_alias = NULL;
- list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+ hlist_for_each_entry(alias, p, &inode->i_dentry, d_alias) {
spin_lock(&alias->d_lock);
if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
if (IS_ROOT(alias) &&
(alias->d_flags & DCACHE_DISCONNECTED)) {
discon_alias = alias;
- } else {
+ } else if (!want_discon) {
__dget_dlock(alias);
spin_unlock(&alias->d_lock);
return alias;
@@ -734,9 +738,9 @@ struct dentry *d_find_alias(struct inode *inode)
{
struct dentry *de = NULL;
- if (!list_empty(&inode->i_dentry)) {
+ if (!hlist_empty(&inode->i_dentry)) {
spin_lock(&inode->i_lock);
- de = __d_find_alias(inode);
+ de = __d_find_alias(inode, 0);
spin_unlock(&inode->i_lock);
}
return de;
@@ -750,9 +754,10 @@ EXPORT_SYMBOL(d_find_alias);
void d_prune_aliases(struct inode *inode)
{
struct dentry *dentry;
+ struct hlist_node *p;
restart:
spin_lock(&inode->i_lock);
- list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+ hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) {
spin_lock(&dentry->d_lock);
if (!dentry->d_count) {
__dget_dlock(dentry);
@@ -974,7 +979,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
inode = dentry->d_inode;
if (inode) {
dentry->d_inode = NULL;
- list_del_init(&dentry->d_alias);
+ hlist_del_init(&dentry->d_alias);
if (dentry->d_op && dentry->d_op->d_iput)
dentry->d_op->d_iput(dentry, inode);
else
@@ -1309,7 +1314,7 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
INIT_HLIST_BL_NODE(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_lru);
INIT_LIST_HEAD(&dentry->d_subdirs);
- INIT_LIST_HEAD(&dentry->d_alias);
+ INIT_HLIST_NODE(&dentry->d_alias);
INIT_LIST_HEAD(&dentry->d_u.d_child);
d_set_d_op(dentry, dentry->d_sb->s_d_op);
@@ -1397,7 +1402,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
if (inode) {
if (unlikely(IS_AUTOMOUNT(inode)))
dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
- list_add(&dentry->d_alias, &inode->i_dentry);
+ hlist_add_head(&dentry->d_alias, &inode->i_dentry);
}
dentry->d_inode = inode;
dentry_rcuwalk_barrier(dentry);
@@ -1422,7 +1427,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
void d_instantiate(struct dentry *entry, struct inode * inode)
{
- BUG_ON(!list_empty(&entry->d_alias));
+ BUG_ON(!hlist_unhashed(&entry->d_alias));
if (inode)
spin_lock(&inode->i_lock);
__d_instantiate(entry, inode);
@@ -1455,13 +1460,14 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
int len = entry->d_name.len;
const char *name = entry->d_name.name;
unsigned int hash = entry->d_name.hash;
+ struct hlist_node *p;
if (!inode) {
__d_instantiate(entry, NULL);
return NULL;
}
- list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+ hlist_for_each_entry(alias, p, &inode->i_dentry, d_alias) {
/*
* Don't need alias->d_lock here, because aliases with
* d_parent == entry->d_parent are not subject to name or
@@ -1487,7 +1493,7 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
{
struct dentry *result;
- BUG_ON(!list_empty(&entry->d_alias));
+ BUG_ON(!hlist_unhashed(&entry->d_alias));
if (inode)
spin_lock(&inode->i_lock);
@@ -1528,9 +1534,9 @@ static struct dentry * __d_find_any_alias(struct inode *inode)
{
struct dentry *alias;
- if (list_empty(&inode->i_dentry))
+ if (hlist_empty(&inode->i_dentry))
return NULL;
- alias = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+ alias = hlist_entry(inode->i_dentry.first, struct dentry, d_alias);
__dget(alias);
return alias;
}
@@ -1604,7 +1610,7 @@ struct dentry *d_obtain_alias(struct inode *inode)
spin_lock(&tmp->d_lock);
tmp->d_inode = inode;
tmp->d_flags |= DCACHE_DISCONNECTED;
- list_add(&tmp->d_alias, &inode->i_dentry);
+ hlist_add_head(&tmp->d_alias, &inode->i_dentry);
hlist_bl_lock(&tmp->d_sb->s_anon);
hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon);
hlist_bl_unlock(&tmp->d_sb->s_anon);
@@ -1647,8 +1653,9 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
if (inode && S_ISDIR(inode->i_mode)) {
spin_lock(&inode->i_lock);
- new = __d_find_any_alias(inode);
+ new = __d_find_alias(inode, 1);
if (new) {
+ BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
spin_unlock(&inode->i_lock);
security_d_instantiate(new, inode);
d_move(new, dentry);
@@ -2380,14 +2387,13 @@ static struct dentry *__d_unalias(struct inode *inode,
struct dentry *dentry, struct dentry *alias)
{
struct mutex *m1 = NULL, *m2 = NULL;
- struct dentry *ret;
+ struct dentry *ret = ERR_PTR(-EBUSY);
/* If alias and dentry share a parent, then no extra locks required */
if (alias->d_parent == dentry->d_parent)
goto out_unalias;
/* See lock_rename() */
- ret = ERR_PTR(-EBUSY);
if (!mutex_trylock(&dentry->d_sb->s_vfs_rename_mutex))
goto out_err;
m1 = &dentry->d_sb->s_vfs_rename_mutex;
@@ -2395,8 +2401,10 @@ static struct dentry *__d_unalias(struct inode *inode,
goto out_err;
m2 = &alias->d_parent->d_inode->i_mutex;
out_unalias:
- __d_move(alias, dentry);
- ret = alias;
+ if (likely(!d_mountpoint(alias))) {
+ __d_move(alias, dentry);
+ ret = alias;
+ }
out_err:
spin_unlock(&inode->i_lock);
if (m2)
@@ -2478,7 +2486,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
struct dentry *alias;
/* Does an aliased dentry already exist? */
- alias = __d_find_alias(inode);
+ alias = __d_find_alias(inode, 0);
if (alias) {
actual = alias;
write_seqlock(&rename_lock);
@@ -2618,7 +2626,7 @@ global_root:
if (!slash)
error = prepend(buffer, buflen, "/", 1);
if (!error)
- error = real_mount(vfsmnt)->mnt_ns ? 1 : 2;
+ error = is_mounted(vfsmnt) ? 1 : 2;
goto out;
}