summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWaiman Long <longman@redhat.com>2019-01-30 13:52:38 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2019-01-30 14:02:11 -0500
commitaf0c9af1b3f66052c369d08be3f60fa9a9559e48 (patch)
tree94a1e127d6e4dd81382f62c0ea7822fb2f2ffd56
parent7d10f70fc198877b43d92bdcd7604279788b9568 (diff)
fs/dcache: Track & report number of negative dentries
The current dentry number tracking code doesn't distinguish between positive & negative dentries. It just reports the total number of dentries in the LRU lists. As excessive number of negative dentries can have an impact on system performance, it will be wise to track the number of positive and negative dentries separately. This patch adds tracking for the total number of negative dentries in the system LRU lists and reports it in the 5th field in the /proc/sys/fs/dentry-state file. The number, however, does not include negative dentries that are in flight but not in the LRU yet as well as those in the shrinker lists which are on the way out anyway. The number of positive dentries in the LRU lists can be roughly found by subtracting the number of negative dentries from the unused count. Matthew Wilcox had confirmed that since the introduction of the dentry_stat structure in 2.1.60, the dummy array was there, probably for future extension. They were not replacements of pre-existing fields. So no sane applications that read the value of /proc/sys/fs/dentry-state will do dummy thing if the last 2 fields of the sysctl parameter are not zero. IOW, it will be safe to use one of the dummy array entry for negative dentry count. Signed-off-by: Waiman Long <longman@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/sysctl/fs.txt26
-rw-r--r--fs/dcache.c32
-rw-r--r--include/linux/dcache.h7
3 files changed, 52 insertions, 13 deletions
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 819caf8ca05f..58649bd4fcfc 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -56,26 +56,32 @@ of any kernel data structures.
56 56
57dentry-state: 57dentry-state:
58 58
59From linux/fs/dentry.c: 59From linux/include/linux/dcache.h:
60-------------------------------------------------------------- 60--------------------------------------------------------------
61struct { 61struct dentry_stat_t dentry_stat {
62 int nr_dentry; 62 int nr_dentry;
63 int nr_unused; 63 int nr_unused;
64 int age_limit; /* age in seconds */ 64 int age_limit; /* age in seconds */
65 int want_pages; /* pages requested by system */ 65 int want_pages; /* pages requested by system */
66 int dummy[2]; 66 int nr_negative; /* # of unused negative dentries */
67} dentry_stat = {0, 0, 45, 0,}; 67 int dummy; /* Reserved for future use */
68-------------------------------------------------------------- 68};
69 69--------------------------------------------------------------
70Dentries are dynamically allocated and deallocated, and 70
71nr_dentry seems to be 0 all the time. Hence it's safe to 71Dentries are dynamically allocated and deallocated.
72assume that only nr_unused, age_limit and want_pages are 72
73used. Nr_unused seems to be exactly what its name says. 73nr_dentry shows the total number of dentries allocated (active
74+ unused). nr_unused shows the number of dentries that are not
75actively used, but are saved in the LRU list for future reuse.
76
74Age_limit is the age in seconds after which dcache entries 77Age_limit is the age in seconds after which dcache entries
75can be reclaimed when memory is short and want_pages is 78can be reclaimed when memory is short and want_pages is
76nonzero when shrink_dcache_pages() has been called and the 79nonzero when shrink_dcache_pages() has been called and the
77dcache isn't pruned yet. 80dcache isn't pruned yet.
78 81
82nr_negative shows the number of unused dentries that are also
83negative dentries which do not mapped to actual files.
84
79============================================================== 85==============================================================
80 86
81dquot-max & dquot-nr: 87dquot-max & dquot-nr:
diff --git a/fs/dcache.c b/fs/dcache.c
index 44e5652b2664..aac41adf4743 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -119,6 +119,7 @@ struct dentry_stat_t dentry_stat = {
119 119
120static DEFINE_PER_CPU(long, nr_dentry); 120static DEFINE_PER_CPU(long, nr_dentry);
121static DEFINE_PER_CPU(long, nr_dentry_unused); 121static DEFINE_PER_CPU(long, nr_dentry_unused);
122static DEFINE_PER_CPU(long, nr_dentry_negative);
122 123
123#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) 124#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
124 125
@@ -152,11 +153,22 @@ static long get_nr_dentry_unused(void)
152 return sum < 0 ? 0 : sum; 153 return sum < 0 ? 0 : sum;
153} 154}
154 155
156static long get_nr_dentry_negative(void)
157{
158 int i;
159 long sum = 0;
160
161 for_each_possible_cpu(i)
162 sum += per_cpu(nr_dentry_negative, i);
163 return sum < 0 ? 0 : sum;
164}
165
155int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer, 166int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer,
156 size_t *lenp, loff_t *ppos) 167 size_t *lenp, loff_t *ppos)
157{ 168{
158 dentry_stat.nr_dentry = get_nr_dentry(); 169 dentry_stat.nr_dentry = get_nr_dentry();
159 dentry_stat.nr_unused = get_nr_dentry_unused(); 170 dentry_stat.nr_unused = get_nr_dentry_unused();
171 dentry_stat.nr_negative = get_nr_dentry_negative();
160 return proc_doulongvec_minmax(table, write, buffer, lenp, ppos); 172 return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
161} 173}
162#endif 174#endif
@@ -317,6 +329,8 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
317 flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); 329 flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
318 WRITE_ONCE(dentry->d_flags, flags); 330 WRITE_ONCE(dentry->d_flags, flags);
319 dentry->d_inode = NULL; 331 dentry->d_inode = NULL;
332 if (dentry->d_flags & DCACHE_LRU_LIST)
333 this_cpu_inc(nr_dentry_negative);
320} 334}
321 335
322static void dentry_free(struct dentry *dentry) 336static void dentry_free(struct dentry *dentry)
@@ -371,6 +385,11 @@ static void dentry_unlink_inode(struct dentry * dentry)
371 * The per-cpu "nr_dentry_unused" counters are updated with 385 * The per-cpu "nr_dentry_unused" counters are updated with
372 * the DCACHE_LRU_LIST bit. 386 * the DCACHE_LRU_LIST bit.
373 * 387 *
388 * The per-cpu "nr_dentry_negative" counters are only updated
389 * when deleted from or added to the per-superblock LRU list, not
390 * from/to the shrink list. That is to avoid an unneeded dec/inc
391 * pair when moving from LRU to shrink list in select_collect().
392 *
374 * These helper functions make sure we always follow the 393 * These helper functions make sure we always follow the
375 * rules. d_lock must be held by the caller. 394 * rules. d_lock must be held by the caller.
376 */ 395 */
@@ -380,6 +399,8 @@ static void d_lru_add(struct dentry *dentry)
380 D_FLAG_VERIFY(dentry, 0); 399 D_FLAG_VERIFY(dentry, 0);
381 dentry->d_flags |= DCACHE_LRU_LIST; 400 dentry->d_flags |= DCACHE_LRU_LIST;
382 this_cpu_inc(nr_dentry_unused); 401 this_cpu_inc(nr_dentry_unused);
402 if (d_is_negative(dentry))
403 this_cpu_inc(nr_dentry_negative);
383 WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru)); 404 WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
384} 405}
385 406
@@ -388,6 +409,8 @@ static void d_lru_del(struct dentry *dentry)
388 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST); 409 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
389 dentry->d_flags &= ~DCACHE_LRU_LIST; 410 dentry->d_flags &= ~DCACHE_LRU_LIST;
390 this_cpu_dec(nr_dentry_unused); 411 this_cpu_dec(nr_dentry_unused);
412 if (d_is_negative(dentry))
413 this_cpu_dec(nr_dentry_negative);
391 WARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru, &dentry->d_lru)); 414 WARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
392} 415}
393 416
@@ -418,6 +441,8 @@ static void d_lru_isolate(struct list_lru_one *lru, struct dentry *dentry)
418 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST); 441 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
419 dentry->d_flags &= ~DCACHE_LRU_LIST; 442 dentry->d_flags &= ~DCACHE_LRU_LIST;
420 this_cpu_dec(nr_dentry_unused); 443 this_cpu_dec(nr_dentry_unused);
444 if (d_is_negative(dentry))
445 this_cpu_dec(nr_dentry_negative);
421 list_lru_isolate(lru, &dentry->d_lru); 446 list_lru_isolate(lru, &dentry->d_lru);
422} 447}
423 448
@@ -426,6 +451,8 @@ static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
426{ 451{
427 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST); 452 D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
428 dentry->d_flags |= DCACHE_SHRINK_LIST; 453 dentry->d_flags |= DCACHE_SHRINK_LIST;
454 if (d_is_negative(dentry))
455 this_cpu_dec(nr_dentry_negative);
429 list_lru_isolate_move(lru, &dentry->d_lru, list); 456 list_lru_isolate_move(lru, &dentry->d_lru, list);
430} 457}
431 458
@@ -1816,6 +1843,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
1816 WARN_ON(d_in_lookup(dentry)); 1843 WARN_ON(d_in_lookup(dentry));
1817 1844
1818 spin_lock(&dentry->d_lock); 1845 spin_lock(&dentry->d_lock);
1846 /*
1847 * Decrement negative dentry count if it was in the LRU list.
1848 */
1849 if (dentry->d_flags & DCACHE_LRU_LIST)
1850 this_cpu_dec(nr_dentry_negative);
1819 hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); 1851 hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
1820 raw_write_seqcount_begin(&dentry->d_seq); 1852 raw_write_seqcount_begin(&dentry->d_seq);
1821 __d_set_inode_and_type(dentry, inode, add_flags); 1853 __d_set_inode_and_type(dentry, inode, add_flags);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index ef4b70f64f33..60996e64c579 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -62,9 +62,10 @@ extern const struct qstr slash_name;
62struct dentry_stat_t { 62struct dentry_stat_t {
63 long nr_dentry; 63 long nr_dentry;
64 long nr_unused; 64 long nr_unused;
65 long age_limit; /* age in seconds */ 65 long age_limit; /* age in seconds */
66 long want_pages; /* pages requested by system */ 66 long want_pages; /* pages requested by system */
67 long dummy[2]; 67 long nr_negative; /* # of unused negative dentries */
68 long dummy; /* Reserved for future use */
68}; 69};
69extern struct dentry_stat_t dentry_stat; 70extern struct dentry_stat_t dentry_stat;
70 71