mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-06 05:02:31 +00:00
vfs: make the dentry cache use the lockref infrastructure
This just replaces the dentry count/lock combination with the lockref structure that contains both a count and a spinlock, and does the mechanical conversion to use the lockref infrastructure. There are no semantic changes here, it's purely syntactic. The reference lockref implementation uses the spinlock exactly the same way that the old dcache code did, and the bulk of this patch is just expanding the internal "d_count" use in the dcache code to use "d_lockref.count" instead. This is purely preparation for the real change to make the reference count updates be lockless during the 3.12 merge window. [ As with the previous commit, this is a rewritten version of a concept originally from Waiman, so credit goes to him, blame for any errors goes to me. Waiman's patch had some semantic differences for taking advantage of the lockless update in dget_parent(), while this patch is intentionally a pure search-and-replace change with no semantic changes. - Linus ] Signed-off-by: Waiman Long <Waiman.Long@hp.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
0f8f2aaaab
commit
98474236f7
57
fs/dcache.c
57
fs/dcache.c
@ -229,7 +229,7 @@ static void __d_free(struct rcu_head *head)
|
|||||||
*/
|
*/
|
||||||
static void d_free(struct dentry *dentry)
|
static void d_free(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
BUG_ON(dentry->d_count);
|
BUG_ON(dentry->d_lockref.count);
|
||||||
this_cpu_dec(nr_dentry);
|
this_cpu_dec(nr_dentry);
|
||||||
if (dentry->d_op && dentry->d_op->d_release)
|
if (dentry->d_op && dentry->d_op->d_release)
|
||||||
dentry->d_op->d_release(dentry);
|
dentry->d_op->d_release(dentry);
|
||||||
@ -467,7 +467,7 @@ static inline struct dentry *dentry_kill(struct dentry *dentry, int ref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ref)
|
if (ref)
|
||||||
dentry->d_count--;
|
dentry->d_lockref.count--;
|
||||||
/*
|
/*
|
||||||
* inform the fs via d_prune that this dentry is about to be
|
* inform the fs via d_prune that this dentry is about to be
|
||||||
* unhashed and destroyed.
|
* unhashed and destroyed.
|
||||||
@ -513,15 +513,10 @@ void dput(struct dentry *dentry)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
repeat:
|
repeat:
|
||||||
if (dentry->d_count == 1)
|
if (dentry->d_lockref.count == 1)
|
||||||
might_sleep();
|
might_sleep();
|
||||||
spin_lock(&dentry->d_lock);
|
if (lockref_put_or_lock(&dentry->d_lockref))
|
||||||
BUG_ON(!dentry->d_count);
|
|
||||||
if (dentry->d_count > 1) {
|
|
||||||
dentry->d_count--;
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (dentry->d_flags & DCACHE_OP_DELETE) {
|
if (dentry->d_flags & DCACHE_OP_DELETE) {
|
||||||
if (dentry->d_op->d_delete(dentry))
|
if (dentry->d_op->d_delete(dentry))
|
||||||
@ -535,7 +530,7 @@ void dput(struct dentry *dentry)
|
|||||||
dentry->d_flags |= DCACHE_REFERENCED;
|
dentry->d_flags |= DCACHE_REFERENCED;
|
||||||
dentry_lru_add(dentry);
|
dentry_lru_add(dentry);
|
||||||
|
|
||||||
dentry->d_count--;
|
dentry->d_lockref.count--;
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -590,7 +585,7 @@ int d_invalidate(struct dentry * dentry)
|
|||||||
* We also need to leave mountpoints alone,
|
* We also need to leave mountpoints alone,
|
||||||
* directory or not.
|
* directory or not.
|
||||||
*/
|
*/
|
||||||
if (dentry->d_count > 1 && dentry->d_inode) {
|
if (dentry->d_lockref.count > 1 && dentry->d_inode) {
|
||||||
if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
|
if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
@ -606,14 +601,12 @@ EXPORT_SYMBOL(d_invalidate);
|
|||||||
/* This must be called with d_lock held */
|
/* This must be called with d_lock held */
|
||||||
static inline void __dget_dlock(struct dentry *dentry)
|
static inline void __dget_dlock(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
dentry->d_count++;
|
dentry->d_lockref.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void __dget(struct dentry *dentry)
|
static inline void __dget(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
spin_lock(&dentry->d_lock);
|
lockref_get(&dentry->d_lockref);
|
||||||
__dget_dlock(dentry);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dentry *dget_parent(struct dentry *dentry)
|
struct dentry *dget_parent(struct dentry *dentry)
|
||||||
@ -634,8 +627,8 @@ struct dentry *dget_parent(struct dentry *dentry)
|
|||||||
goto repeat;
|
goto repeat;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
BUG_ON(!ret->d_count);
|
BUG_ON(!ret->d_lockref.count);
|
||||||
ret->d_count++;
|
ret->d_lockref.count++;
|
||||||
spin_unlock(&ret->d_lock);
|
spin_unlock(&ret->d_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -718,7 +711,7 @@ void d_prune_aliases(struct inode *inode)
|
|||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
|
hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
if (!dentry->d_count) {
|
if (!dentry->d_lockref.count) {
|
||||||
__dget_dlock(dentry);
|
__dget_dlock(dentry);
|
||||||
__d_drop(dentry);
|
__d_drop(dentry);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
@ -763,12 +756,8 @@ static void try_prune_one_dentry(struct dentry *dentry)
|
|||||||
/* Prune ancestors. */
|
/* Prune ancestors. */
|
||||||
dentry = parent;
|
dentry = parent;
|
||||||
while (dentry) {
|
while (dentry) {
|
||||||
spin_lock(&dentry->d_lock);
|
if (lockref_put_or_lock(&dentry->d_lockref))
|
||||||
if (dentry->d_count > 1) {
|
|
||||||
dentry->d_count--;
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
dentry = dentry_kill(dentry, 1);
|
dentry = dentry_kill(dentry, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -793,7 +782,7 @@ static void shrink_dentry_list(struct list_head *list)
|
|||||||
* the LRU because of laziness during lookup. Do not free
|
* the LRU because of laziness during lookup. Do not free
|
||||||
* it - just keep it off the LRU list.
|
* it - just keep it off the LRU list.
|
||||||
*/
|
*/
|
||||||
if (dentry->d_count) {
|
if (dentry->d_lockref.count) {
|
||||||
dentry_lru_del(dentry);
|
dentry_lru_del(dentry);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
continue;
|
continue;
|
||||||
@ -913,7 +902,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
|
|||||||
dentry_lru_del(dentry);
|
dentry_lru_del(dentry);
|
||||||
__d_shrink(dentry);
|
__d_shrink(dentry);
|
||||||
|
|
||||||
if (dentry->d_count != 0) {
|
if (dentry->d_lockref.count != 0) {
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
"BUG: Dentry %p{i=%lx,n=%s}"
|
"BUG: Dentry %p{i=%lx,n=%s}"
|
||||||
" still in use (%d)"
|
" still in use (%d)"
|
||||||
@ -922,7 +911,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
|
|||||||
dentry->d_inode ?
|
dentry->d_inode ?
|
||||||
dentry->d_inode->i_ino : 0UL,
|
dentry->d_inode->i_ino : 0UL,
|
||||||
dentry->d_name.name,
|
dentry->d_name.name,
|
||||||
dentry->d_count,
|
dentry->d_lockref.count,
|
||||||
dentry->d_sb->s_type->name,
|
dentry->d_sb->s_type->name,
|
||||||
dentry->d_sb->s_id);
|
dentry->d_sb->s_id);
|
||||||
BUG();
|
BUG();
|
||||||
@ -933,7 +922,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
|
|||||||
list_del(&dentry->d_u.d_child);
|
list_del(&dentry->d_u.d_child);
|
||||||
} else {
|
} else {
|
||||||
parent = dentry->d_parent;
|
parent = dentry->d_parent;
|
||||||
parent->d_count--;
|
parent->d_lockref.count--;
|
||||||
list_del(&dentry->d_u.d_child);
|
list_del(&dentry->d_u.d_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,7 +970,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
|
|||||||
|
|
||||||
dentry = sb->s_root;
|
dentry = sb->s_root;
|
||||||
sb->s_root = NULL;
|
sb->s_root = NULL;
|
||||||
dentry->d_count--;
|
dentry->d_lockref.count--;
|
||||||
shrink_dcache_for_umount_subtree(dentry);
|
shrink_dcache_for_umount_subtree(dentry);
|
||||||
|
|
||||||
while (!hlist_bl_empty(&sb->s_anon)) {
|
while (!hlist_bl_empty(&sb->s_anon)) {
|
||||||
@ -1147,7 +1136,7 @@ static int select_parent(struct dentry *parent, struct list_head *dispose)
|
|||||||
* loop in shrink_dcache_parent() might not make any progress
|
* loop in shrink_dcache_parent() might not make any progress
|
||||||
* and loop forever.
|
* and loop forever.
|
||||||
*/
|
*/
|
||||||
if (dentry->d_count) {
|
if (dentry->d_lockref.count) {
|
||||||
dentry_lru_del(dentry);
|
dentry_lru_del(dentry);
|
||||||
} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
|
} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
|
||||||
dentry_lru_move_list(dentry, dispose);
|
dentry_lru_move_list(dentry, dispose);
|
||||||
@ -1269,7 +1258,7 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
|||||||
smp_wmb();
|
smp_wmb();
|
||||||
dentry->d_name.name = dname;
|
dentry->d_name.name = dname;
|
||||||
|
|
||||||
dentry->d_count = 1;
|
dentry->d_lockref.count = 1;
|
||||||
dentry->d_flags = 0;
|
dentry->d_flags = 0;
|
||||||
spin_lock_init(&dentry->d_lock);
|
spin_lock_init(&dentry->d_lock);
|
||||||
seqcount_init(&dentry->d_seq);
|
seqcount_init(&dentry->d_seq);
|
||||||
@ -1970,7 +1959,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
|
|||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
dentry->d_count++;
|
dentry->d_lockref.count++;
|
||||||
found = dentry;
|
found = dentry;
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
break;
|
break;
|
||||||
@ -2069,7 +2058,7 @@ void d_delete(struct dentry * dentry)
|
|||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
inode = dentry->d_inode;
|
inode = dentry->d_inode;
|
||||||
isdir = S_ISDIR(inode->i_mode);
|
isdir = S_ISDIR(inode->i_mode);
|
||||||
if (dentry->d_count == 1) {
|
if (dentry->d_lockref.count == 1) {
|
||||||
if (!spin_trylock(&inode->i_lock)) {
|
if (!spin_trylock(&inode->i_lock)) {
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
@ -2948,7 +2937,7 @@ void d_genocide(struct dentry *root)
|
|||||||
}
|
}
|
||||||
if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
|
if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
|
||||||
dentry->d_flags |= DCACHE_GENOCIDE;
|
dentry->d_flags |= DCACHE_GENOCIDE;
|
||||||
dentry->d_count--;
|
dentry->d_lockref.count--;
|
||||||
}
|
}
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
@ -2956,7 +2945,7 @@ void d_genocide(struct dentry *root)
|
|||||||
struct dentry *child = this_parent;
|
struct dentry *child = this_parent;
|
||||||
if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
|
if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
|
||||||
this_parent->d_flags |= DCACHE_GENOCIDE;
|
this_parent->d_flags |= DCACHE_GENOCIDE;
|
||||||
this_parent->d_count--;
|
this_parent->d_lockref.count--;
|
||||||
}
|
}
|
||||||
this_parent = try_to_ascend(this_parent, locked, seq);
|
this_parent = try_to_ascend(this_parent, locked, seq);
|
||||||
if (!this_parent)
|
if (!this_parent)
|
||||||
|
@ -536,8 +536,8 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
|
|||||||
* a reference at this point.
|
* a reference at this point.
|
||||||
*/
|
*/
|
||||||
BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
|
BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
|
||||||
BUG_ON(!parent->d_count);
|
BUG_ON(!parent->d_lockref.count);
|
||||||
parent->d_count++;
|
parent->d_lockref.count++;
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&parent->d_lock);
|
spin_unlock(&parent->d_lock);
|
||||||
@ -3327,7 +3327,7 @@ void dentry_unhash(struct dentry *dentry)
|
|||||||
{
|
{
|
||||||
shrink_dcache_parent(dentry);
|
shrink_dcache_parent(dentry);
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
if (dentry->d_count == 1)
|
if (dentry->d_lockref.count == 1)
|
||||||
__d_drop(dentry);
|
__d_drop(dentry);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <linux/seqlock.h>
|
#include <linux/seqlock.h>
|
||||||
#include <linux/cache.h>
|
#include <linux/cache.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
#include <linux/lockref.h>
|
||||||
|
|
||||||
struct nameidata;
|
struct nameidata;
|
||||||
struct path;
|
struct path;
|
||||||
@ -100,6 +101,8 @@ extern unsigned int full_name_hash(const unsigned char *, unsigned int);
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define d_lock d_lockref.lock
|
||||||
|
|
||||||
struct dentry {
|
struct dentry {
|
||||||
/* RCU lookup touched fields */
|
/* RCU lookup touched fields */
|
||||||
unsigned int d_flags; /* protected by d_lock */
|
unsigned int d_flags; /* protected by d_lock */
|
||||||
@ -112,8 +115,7 @@ struct dentry {
|
|||||||
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
|
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
|
||||||
|
|
||||||
/* Ref lookup also touches following */
|
/* Ref lookup also touches following */
|
||||||
unsigned int d_count; /* protected by d_lock */
|
struct lockref d_lockref; /* per-dentry lock and refcount */
|
||||||
spinlock_t d_lock; /* per dentry lock */
|
|
||||||
const struct dentry_operations *d_op;
|
const struct dentry_operations *d_op;
|
||||||
struct super_block *d_sb; /* The root of the dentry tree */
|
struct super_block *d_sb; /* The root of the dentry tree */
|
||||||
unsigned long d_time; /* used by d_revalidate */
|
unsigned long d_time; /* used by d_revalidate */
|
||||||
@ -318,7 +320,7 @@ static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq)
|
|||||||
assert_spin_locked(&dentry->d_lock);
|
assert_spin_locked(&dentry->d_lock);
|
||||||
if (!read_seqcount_retry(&dentry->d_seq, seq)) {
|
if (!read_seqcount_retry(&dentry->d_seq, seq)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
dentry->d_count++;
|
dentry->d_lockref.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -326,7 +328,7 @@ static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq)
|
|||||||
|
|
||||||
static inline unsigned d_count(const struct dentry *dentry)
|
static inline unsigned d_count(const struct dentry *dentry)
|
||||||
{
|
{
|
||||||
return dentry->d_count;
|
return dentry->d_lockref.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* validate "insecure" dentry pointer */
|
/* validate "insecure" dentry pointer */
|
||||||
@ -357,17 +359,14 @@ extern char *dentry_path(struct dentry *, char *, int);
|
|||||||
static inline struct dentry *dget_dlock(struct dentry *dentry)
|
static inline struct dentry *dget_dlock(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
if (dentry)
|
if (dentry)
|
||||||
dentry->d_count++;
|
dentry->d_lockref.count++;
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct dentry *dget(struct dentry *dentry)
|
static inline struct dentry *dget(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
if (dentry) {
|
if (dentry)
|
||||||
spin_lock(&dentry->d_lock);
|
lockref_get(&dentry->d_lockref);
|
||||||
dget_dlock(dentry);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user