diff options
author | Christoph Hellwig <hch@infradead.org> | 2010-10-10 05:36:23 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-10-25 21:26:12 -0400 |
commit | 312d3ca856d369bb04d0443846b85b4cdde6fa8a (patch) | |
tree | cf95d01cffaf02bf53c2bb0f7c2c924279ec6eeb /fs/dcache.c | |
parent | 9c82ab9c9e16cb9edf17bd0d31f3d6904afce04f (diff) |
fs: use percpu counter for nr_dentry and nr_dentry_unused
The nr_dentry stat is a globally touched cacheline and atomic operation
twice over the lifetime of a dentry. It is used for the benfit of userspace
only. Turn it into a per-cpu counter and always decrement it in d_free instead
of doing various batching operations to reduce lock hold times in the callers.
Based on an earlier patch from Nick Piggin <npiggin@suse.de>.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 51 |
1 files changed, 32 insertions, 19 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 028753951e95..c37a656802b0 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -67,6 +67,19 @@ struct dentry_stat_t dentry_stat = { | |||
67 | .age_limit = 45, | 67 | .age_limit = 45, |
68 | }; | 68 | }; |
69 | 69 | ||
70 | static struct percpu_counter nr_dentry __cacheline_aligned_in_smp; | ||
71 | static struct percpu_counter nr_dentry_unused __cacheline_aligned_in_smp; | ||
72 | |||
73 | #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) | ||
74 | int proc_nr_dentry(ctl_table *table, int write, void __user *buffer, | ||
75 | size_t *lenp, loff_t *ppos) | ||
76 | { | ||
77 | dentry_stat.nr_dentry = percpu_counter_sum_positive(&nr_dentry); | ||
78 | dentry_stat.nr_unused = percpu_counter_sum_positive(&nr_dentry_unused); | ||
79 | return proc_dointvec(table, write, buffer, lenp, ppos); | ||
80 | } | ||
81 | #endif | ||
82 | |||
70 | static void __d_free(struct rcu_head *head) | 83 | static void __d_free(struct rcu_head *head) |
71 | { | 84 | { |
72 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); | 85 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); |
@@ -78,13 +91,14 @@ static void __d_free(struct rcu_head *head) | |||
78 | } | 91 | } |
79 | 92 | ||
80 | /* | 93 | /* |
81 | * no dcache_lock, please. The caller must decrement dentry_stat.nr_dentry | 94 | * no dcache_lock, please. |
82 | * inside dcache_lock. | ||
83 | */ | 95 | */ |
84 | static void d_free(struct dentry *dentry) | 96 | static void d_free(struct dentry *dentry) |
85 | { | 97 | { |
98 | percpu_counter_dec(&nr_dentry); | ||
86 | if (dentry->d_op && dentry->d_op->d_release) | 99 | if (dentry->d_op && dentry->d_op->d_release) |
87 | dentry->d_op->d_release(dentry); | 100 | dentry->d_op->d_release(dentry); |
101 | |||
88 | /* if dentry was never inserted into hash, immediate free is OK */ | 102 | /* if dentry was never inserted into hash, immediate free is OK */ |
89 | if (hlist_unhashed(&dentry->d_hash)) | 103 | if (hlist_unhashed(&dentry->d_hash)) |
90 | __d_free(&dentry->d_u.d_rcu); | 104 | __d_free(&dentry->d_u.d_rcu); |
@@ -125,14 +139,14 @@ static void dentry_lru_add(struct dentry *dentry) | |||
125 | { | 139 | { |
126 | list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); | 140 | list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); |
127 | dentry->d_sb->s_nr_dentry_unused++; | 141 | dentry->d_sb->s_nr_dentry_unused++; |
128 | dentry_stat.nr_unused++; | 142 | percpu_counter_inc(&nr_dentry_unused); |
129 | } | 143 | } |
130 | 144 | ||
131 | static void dentry_lru_add_tail(struct dentry *dentry) | 145 | static void dentry_lru_add_tail(struct dentry *dentry) |
132 | { | 146 | { |
133 | list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); | 147 | list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); |
134 | dentry->d_sb->s_nr_dentry_unused++; | 148 | dentry->d_sb->s_nr_dentry_unused++; |
135 | dentry_stat.nr_unused++; | 149 | percpu_counter_inc(&nr_dentry_unused); |
136 | } | 150 | } |
137 | 151 | ||
138 | static void dentry_lru_del(struct dentry *dentry) | 152 | static void dentry_lru_del(struct dentry *dentry) |
@@ -140,7 +154,7 @@ static void dentry_lru_del(struct dentry *dentry) | |||
140 | if (!list_empty(&dentry->d_lru)) { | 154 | if (!list_empty(&dentry->d_lru)) { |
141 | list_del(&dentry->d_lru); | 155 | list_del(&dentry->d_lru); |
142 | dentry->d_sb->s_nr_dentry_unused--; | 156 | dentry->d_sb->s_nr_dentry_unused--; |
143 | dentry_stat.nr_unused--; | 157 | percpu_counter_dec(&nr_dentry_unused); |
144 | } | 158 | } |
145 | } | 159 | } |
146 | 160 | ||
@@ -149,7 +163,7 @@ static void dentry_lru_del_init(struct dentry *dentry) | |||
149 | if (likely(!list_empty(&dentry->d_lru))) { | 163 | if (likely(!list_empty(&dentry->d_lru))) { |
150 | list_del_init(&dentry->d_lru); | 164 | list_del_init(&dentry->d_lru); |
151 | dentry->d_sb->s_nr_dentry_unused--; | 165 | dentry->d_sb->s_nr_dentry_unused--; |
152 | dentry_stat.nr_unused--; | 166 | percpu_counter_dec(&nr_dentry_unused); |
153 | } | 167 | } |
154 | } | 168 | } |
155 | 169 | ||
@@ -168,7 +182,6 @@ static struct dentry *d_kill(struct dentry *dentry) | |||
168 | struct dentry *parent; | 182 | struct dentry *parent; |
169 | 183 | ||
170 | list_del(&dentry->d_u.d_child); | 184 | list_del(&dentry->d_u.d_child); |
171 | dentry_stat.nr_dentry--; /* For d_free, below */ | ||
172 | /*drops the locks, at that point nobody can reach this dentry */ | 185 | /*drops the locks, at that point nobody can reach this dentry */ |
173 | dentry_iput(dentry); | 186 | dentry_iput(dentry); |
174 | if (IS_ROOT(dentry)) | 187 | if (IS_ROOT(dentry)) |
@@ -314,7 +327,6 @@ int d_invalidate(struct dentry * dentry) | |||
314 | EXPORT_SYMBOL(d_invalidate); | 327 | EXPORT_SYMBOL(d_invalidate); |
315 | 328 | ||
316 | /* This should be called _only_ with dcache_lock held */ | 329 | /* This should be called _only_ with dcache_lock held */ |
317 | |||
318 | static inline struct dentry * __dget_locked(struct dentry *dentry) | 330 | static inline struct dentry * __dget_locked(struct dentry *dentry) |
319 | { | 331 | { |
320 | atomic_inc(&dentry->d_count); | 332 | atomic_inc(&dentry->d_count); |
@@ -534,7 +546,7 @@ static void prune_dcache(int count) | |||
534 | { | 546 | { |
535 | struct super_block *sb, *p = NULL; | 547 | struct super_block *sb, *p = NULL; |
536 | int w_count; | 548 | int w_count; |
537 | int unused = dentry_stat.nr_unused; | 549 | int unused = percpu_counter_sum_positive(&nr_dentry_unused); |
538 | int prune_ratio; | 550 | int prune_ratio; |
539 | int pruned; | 551 | int pruned; |
540 | 552 | ||
@@ -699,20 +711,13 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
699 | * otherwise we ascend to the parent and move to the | 711 | * otherwise we ascend to the parent and move to the |
700 | * next sibling if there is one */ | 712 | * next sibling if there is one */ |
701 | if (!parent) | 713 | if (!parent) |
702 | goto out; | 714 | return; |
703 | |||
704 | dentry = parent; | 715 | dentry = parent; |
705 | |||
706 | } while (list_empty(&dentry->d_subdirs)); | 716 | } while (list_empty(&dentry->d_subdirs)); |
707 | 717 | ||
708 | dentry = list_entry(dentry->d_subdirs.next, | 718 | dentry = list_entry(dentry->d_subdirs.next, |
709 | struct dentry, d_u.d_child); | 719 | struct dentry, d_u.d_child); |
710 | } | 720 | } |
711 | out: | ||
712 | /* several dentries were freed, need to correct nr_dentry */ | ||
713 | spin_lock(&dcache_lock); | ||
714 | dentry_stat.nr_dentry -= detached; | ||
715 | spin_unlock(&dcache_lock); | ||
716 | } | 721 | } |
717 | 722 | ||
718 | /* | 723 | /* |
@@ -896,12 +901,16 @@ EXPORT_SYMBOL(shrink_dcache_parent); | |||
896 | */ | 901 | */ |
897 | static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) | 902 | static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) |
898 | { | 903 | { |
904 | int nr_unused; | ||
905 | |||
899 | if (nr) { | 906 | if (nr) { |
900 | if (!(gfp_mask & __GFP_FS)) | 907 | if (!(gfp_mask & __GFP_FS)) |
901 | return -1; | 908 | return -1; |
902 | prune_dcache(nr); | 909 | prune_dcache(nr); |
903 | } | 910 | } |
904 | return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; | 911 | |
912 | nr_unused = percpu_counter_sum_positive(&nr_dentry_unused); | ||
913 | return (nr_unused / 100) * sysctl_vfs_cache_pressure; | ||
905 | } | 914 | } |
906 | 915 | ||
907 | static struct shrinker dcache_shrinker = { | 916 | static struct shrinker dcache_shrinker = { |
@@ -968,9 +977,10 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) | |||
968 | spin_lock(&dcache_lock); | 977 | spin_lock(&dcache_lock); |
969 | if (parent) | 978 | if (parent) |
970 | list_add(&dentry->d_u.d_child, &parent->d_subdirs); | 979 | list_add(&dentry->d_u.d_child, &parent->d_subdirs); |
971 | dentry_stat.nr_dentry++; | ||
972 | spin_unlock(&dcache_lock); | 980 | spin_unlock(&dcache_lock); |
973 | 981 | ||
982 | percpu_counter_inc(&nr_dentry); | ||
983 | |||
974 | return dentry; | 984 | return dentry; |
975 | } | 985 | } |
976 | EXPORT_SYMBOL(d_alloc); | 986 | EXPORT_SYMBOL(d_alloc); |
@@ -2417,6 +2427,9 @@ static void __init dcache_init(void) | |||
2417 | { | 2427 | { |
2418 | int loop; | 2428 | int loop; |
2419 | 2429 | ||
2430 | percpu_counter_init(&nr_dentry, 0); | ||
2431 | percpu_counter_init(&nr_dentry_unused, 0); | ||
2432 | |||
2420 | /* | 2433 | /* |
2421 | * A constructor could be added for stable state like the lists, | 2434 | * A constructor could be added for stable state like the lists, |
2422 | * but it is probably not worth it because of the cache nature | 2435 | * but it is probably not worth it because of the cache nature |