Skip to content

Commit

Permalink
fs: dcache remove dcache_lock
Browse files Browse the repository at this point in the history
dcache_lock no longer protects anything. remove it.

Signed-off-by: Nick Piggin <[email protected]>
  • Loading branch information
Nick Piggin committed Jan 7, 2011
1 parent 949854d commit b5c84bf
Show file tree
Hide file tree
Showing 40 changed files with 109 additions and 307 deletions.
16 changes: 8 additions & 8 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ prototypes:
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);

locking rules:
dcache_lock rename_lock ->d_lock may block
d_revalidate: no no no yes
d_hash no no no no
d_compare: no yes no no
d_delete: yes no yes no
d_release: no no no yes
d_iput: no no no yes
d_dname: no no no no
rename_lock ->d_lock may block
d_revalidate: no no yes
d_hash no no no
d_compare: yes no no
d_delete: no yes no
d_release: no no yes
d_iput: no no yes
d_dname: no no no

--------------------------- inode_operations ---------------------------
prototypes:
Expand Down
40 changes: 19 additions & 21 deletions Documentation/filesystems/dentry-locking.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ significant change is the way d_lookup traverses the hash chain, it
doesn't acquire the dcache_lock for this and rely on RCU to ensure
that the dentry has not been *freed*.

dcache_lock no longer exists, dentry locking is explained in fs/dcache.c

Dcache locking details
======================
Expand All @@ -50,14 +51,12 @@ Safe lock-free look-up of dcache hash table

Dcache is a complex data structure with the hash table entries also
linked together in other lists. In 2.4 kernel, dcache_lock protected
all the lists. We applied RCU only on hash chain walking. The rest of
the lists are still protected by dcache_lock. Some of the important
changes are :
all the lists. RCU dentry hash walking works like this:

1. The deletion from hash chain is done using hlist_del_rcu() macro
which doesn't initialize next pointer of the deleted dentry and
this allows us to walk safely lock-free while a deletion is
happening.
happening. This is a standard hlist_rcu iteration.

2. Insertion of a dentry into the hash table is done using
hlist_add_head_rcu() which take care of ordering the writes - the
Expand All @@ -66,30 +65,29 @@ changes are :
which has since been replaced by hlist_for_each_entry_rcu(), while
walking the hash chain. The only requirement is that all
initialization to the dentry must be done before
hlist_add_head_rcu() since we don't have dcache_lock protection
while traversing the hash chain. This isn't different from the
existing code.

3. The dentry looked up without holding dcache_lock by cannot be
returned for walking if it is unhashed. It then may have a NULL
d_inode or other bogosity since RCU doesn't protect the other
fields in the dentry. We therefore use a flag DCACHE_UNHASHED to
indicate unhashed dentries and use this in conjunction with a
per-dentry lock (d_lock). Once looked up without the dcache_lock,
we acquire the per-dentry lock (d_lock) and check if the dentry is
unhashed. If so, the look-up is failed. If not, the reference count
of the dentry is increased and the dentry is returned.
hlist_add_head_rcu() since we don't have lock protection
while traversing the hash chain.

3. The dentry looked up without holding locks cannot be returned for
walking if it is unhashed. It then may have a NULL d_inode or other
bogosity since RCU doesn't protect the other fields in the dentry. We
therefore use a flag DCACHE_UNHASHED to indicate unhashed dentries
and use this in conjunction with a per-dentry lock (d_lock). Once
looked up without locks, we acquire the per-dentry lock (d_lock) and
check if the dentry is unhashed. If so, the look-up is failed. If not,
the reference count of the dentry is increased and the dentry is
returned.

4. Once a dentry is looked up, it must be ensured during the path walk
for that component it doesn't go away. In pre-2.5.10 code, this was
done holding a reference to the dentry. dcache_rcu does the same.
In some sense, dcache_rcu path walking looks like the pre-2.5.10
version.

5. All dentry hash chain updates must take the dcache_lock as well as
the per-dentry lock in that order. dput() does this to ensure that
a dentry that has just been looked up in another CPU doesn't get
deleted before dget() can be done on it.
5. All dentry hash chain updates must take the per-dentry lock (see
fs/dcache.c). This excludes dput() to ensure that a dentry that has
been looked up concurrently does not get deleted before dget() can
take a ref.

6. There are several ways to do reference counting of RCU protected
objects. One such example is in ipv4 route cache where deferred
Expand Down
8 changes: 7 additions & 1 deletion Documentation/filesystems/porting
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ had ->revalidate()) add calls in ->follow_link()/->readlink().
->d_parent changes are not protected by BKL anymore. Read access is safe
if at least one of the following is true:
* filesystem has no cross-directory rename()
* dcache_lock is held
* we know that parent had been locked (e.g. we are looking at
->d_parent of ->lookup() argument).
* we are called from ->rename().
Expand Down Expand Up @@ -340,3 +339,10 @@ look at examples of other filesystems) for guidance.
.d_hash() calling convention and locking rules are significantly
changed. Read updated documentation in Documentation/filesystems/vfs.txt (and
look at examples of other filesystems) for guidance.

---
[mandatory]
dcache_lock is gone, replaced by fine grained locks. See fs/dcache.c
for details of what locks to replace dcache_lock with in order to protect
particular things. Most of the time, a filesystem only needs ->d_lock, which
protects *all* the dcache state of a given dentry.
5 changes: 1 addition & 4 deletions arch/powerpc/platforms/cell/spufs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,18 @@ static void spufs_prune_dir(struct dentry *dir)

mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!(d_unhashed(dentry)) && dentry->d_inode) {
dget_locked_dlock(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
simple_unlink(dir->d_inode, dentry);
/* XXX: what is dcache_lock protecting here? Other
/* XXX: what was dcache_lock protecting here? Other
* filesystems (IB, configfs) release dcache_lock
* before unlink */
spin_unlock(&dcache_lock);
dput(dentry);
} else {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
}
}
shrink_dcache_parent(dir);
Expand Down
6 changes: 1 addition & 5 deletions drivers/infiniband/hw/ipath/ipath_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,18 +277,14 @@ static int remove_file(struct dentry *parent, char *name)
goto bail;
}

spin_lock(&dcache_lock);
spin_lock(&tmp->d_lock);
if (!(d_unhashed(tmp) && tmp->d_inode)) {
dget_locked_dlock(tmp);
__d_drop(tmp);
spin_unlock(&tmp->d_lock);
spin_unlock(&dcache_lock);
simple_unlink(parent->d_inode, tmp);
} else {
} else
spin_unlock(&tmp->d_lock);
spin_unlock(&dcache_lock);
}

ret = 0;
bail:
Expand Down
3 changes: 0 additions & 3 deletions drivers/infiniband/hw/qib/qib_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,17 +453,14 @@ static int remove_file(struct dentry *parent, char *name)
goto bail;
}

spin_lock(&dcache_lock);
spin_lock(&tmp->d_lock);
if (!(d_unhashed(tmp) && tmp->d_inode)) {
dget_locked_dlock(tmp);
__d_drop(tmp);
spin_unlock(&tmp->d_lock);
spin_unlock(&dcache_lock);
simple_unlink(parent->d_inode, tmp);
} else {
spin_unlock(&tmp->d_lock);
spin_unlock(&dcache_lock);
}

ret = 0;
Expand Down
2 changes: 0 additions & 2 deletions drivers/staging/pohmelfs/path_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
d = first;
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
spin_lock(&dcache_lock);

if (!IS_ROOT(d) && d_unhashed(d))
len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */
Expand All @@ -110,7 +109,6 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
len += d->d_name.len + 1; /* Plus slash */
d = d->d_parent;
}
spin_unlock(&dcache_lock);
rcu_read_unlock();
if (read_seqretry(&rename_lock, seq))
goto rename_retry;
Expand Down
4 changes: 0 additions & 4 deletions drivers/staging/smbfs/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ smb_invalidate_dircache_entries(struct dentry *parent)
struct list_head *next;
struct dentry *dentry;

spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) {
Expand All @@ -72,7 +71,6 @@ smb_invalidate_dircache_entries(struct dentry *parent)
next = next->next;
}
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock);
}

/*
Expand All @@ -98,7 +96,6 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
}

/* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock);
spin_lock(&parent->d_lock);
next = parent->d_subdirs.next;
while (next != &parent->d_subdirs) {
Expand All @@ -115,7 +112,6 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
dent = NULL;
out_unlock:
spin_unlock(&parent->d_lock);
spin_unlock(&dcache_lock);
return dent;
}

Expand Down
3 changes: 0 additions & 3 deletions drivers/usb/core/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,6 @@ static int usbfs_empty (struct dentry *dentry)
{
struct list_head *list;

spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
Expand All @@ -352,13 +351,11 @@ static int usbfs_empty (struct dentry *dentry)
if (usbfs_positive(de)) {
spin_unlock(&de->d_lock);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
return 0;
}
spin_unlock(&de->d_lock);
}
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
return 1;
}

Expand Down
2 changes: 0 additions & 2 deletions fs/9p/vfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,11 @@ static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
{
struct dentry *dentry;

spin_lock(&dcache_lock);
spin_lock(&dcache_inode_lock);
/* Directory should have only one entry. */
BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
spin_unlock(&dcache_inode_lock);
spin_unlock(&dcache_lock);
return dentry;
}

Expand Down
2 changes: 0 additions & 2 deletions fs/affs/amigaffs.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
void *data = dentry->d_fsdata;
struct list_head *head, *next;

spin_lock(&dcache_lock);
spin_lock(&dcache_inode_lock);
head = &inode->i_dentry;
next = head->next;
Expand All @@ -141,7 +140,6 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
next = next->next;
}
spin_unlock(&dcache_inode_lock);
spin_unlock(&dcache_lock);
}


Expand Down
3 changes: 3 additions & 0 deletions fs/autofs4/autofs_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/auto_fs4.h>
#include <linux/auto_dev-ioctl.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/list.h>

/* This is the range of ioctl() numbers we claim as ours */
Expand Down Expand Up @@ -60,6 +61,8 @@ do { \
current->pid, __func__, ##args); \
} while (0)

extern spinlock_t autofs4_lock;

/* Unified info structure. This is pointed to by both the dentry and
inode structures. Each file in the filesystem has an instance of this
structure. It holds a reference to the dentry, so dentries are never
Expand Down
10 changes: 5 additions & 5 deletions fs/autofs4/expire.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
if (prev == NULL)
return dget(prev);

spin_lock(&dcache_lock);
spin_lock(&autofs4_lock);
relock:
p = prev;
spin_lock(&p->d_lock);
Expand All @@ -114,7 +114,7 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,

if (p == root) {
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&autofs4_lock);
dput(prev);
return NULL;
}
Expand Down Expand Up @@ -144,7 +144,7 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
dget_dlock(ret);
spin_unlock(&ret->d_lock);
spin_unlock(&p->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&autofs4_lock);

dput(prev);

Expand Down Expand Up @@ -408,13 +408,13 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
ino->flags |= AUTOFS_INF_EXPIRING;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
spin_lock(&dcache_lock);
spin_lock(&autofs4_lock);
spin_lock(&expired->d_parent->d_lock);
spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
spin_unlock(&expired->d_lock);
spin_unlock(&expired->d_parent->d_lock);
spin_unlock(&dcache_lock);
spin_unlock(&autofs4_lock);
return expired;
}

Expand Down
Loading

0 comments on commit b5c84bf

Please sign in to comment.