Skip to content

Commit af0c9af

Browse files
Waiman-Longtorvalds
authored andcommitted
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>
1 parent 7d10f70 commit af0c9af

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

Documentation/sysctl/fs.txt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,32 @@ of any kernel data structures.
5656

5757
dentry-state:
5858

59-
From linux/fs/dentry.c:
59+
From linux/include/linux/dcache.h:
6060
--------------------------------------------------------------
61-
struct {
61+
struct dentry_stat_t dentry_stat {
6262
int nr_dentry;
6363
int nr_unused;
6464
int age_limit; /* age in seconds */
6565
int want_pages; /* pages requested by system */
66-
int dummy[2];
67-
} dentry_stat = {0, 0, 45, 0,};
68-
--------------------------------------------------------------
69-
70-
Dentries are dynamically allocated and deallocated, and
71-
nr_dentry seems to be 0 all the time. Hence it's safe to
72-
assume that only nr_unused, age_limit and want_pages are
73-
used. Nr_unused seems to be exactly what its name says.
66+
int nr_negative; /* # of unused negative dentries */
67+
int dummy; /* Reserved for future use */
68+
};
69+
--------------------------------------------------------------
70+
71+
Dentries are dynamically allocated and deallocated.
72+
73+
nr_dentry shows the total number of dentries allocated (active
74+
+ unused). nr_unused shows the number of dentries that are not
75+
actively used, but are saved in the LRU list for future reuse.
76+
7477
Age_limit is the age in seconds after which dcache entries
7578
can be reclaimed when memory is short and want_pages is
7679
nonzero when shrink_dcache_pages() has been called and the
7780
dcache isn't pruned yet.
7881

82+
nr_negative shows the number of unused dentries that are also
83+
negative dentries which do not mapped to actual files.
84+
7985
==============================================================
8086

8187
dquot-max & dquot-nr:

fs/dcache.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct dentry_stat_t dentry_stat = {
119119

120120
static DEFINE_PER_CPU(long, nr_dentry);
121121
static DEFINE_PER_CPU(long, nr_dentry_unused);
122+
static DEFINE_PER_CPU(long, nr_dentry_negative);
122123

123124
#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
124125

@@ -152,11 +153,22 @@ static long get_nr_dentry_unused(void)
152153
return sum < 0 ? 0 : sum;
153154
}
154155

156+
static 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+
155166
int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer,
156167
size_t *lenp, loff_t *ppos)
157168
{
158169
dentry_stat.nr_dentry = get_nr_dentry();
159170
dentry_stat.nr_unused = get_nr_dentry_unused();
171+
dentry_stat.nr_negative = get_nr_dentry_negative();
160172
return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
161173
}
162174
#endif
@@ -317,6 +329,8 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
317329
flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
318330
WRITE_ONCE(dentry->d_flags, flags);
319331
dentry->d_inode = NULL;
332+
if (dentry->d_flags & DCACHE_LRU_LIST)
333+
this_cpu_inc(nr_dentry_negative);
320334
}
321335

322336
static void dentry_free(struct dentry *dentry)
@@ -371,6 +385,11 @@ static void dentry_unlink_inode(struct dentry * dentry)
371385
* The per-cpu "nr_dentry_unused" counters are updated with
372386
* the DCACHE_LRU_LIST bit.
373387
*
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+
*
374393
* These helper functions make sure we always follow the
375394
* rules. d_lock must be held by the caller.
376395
*/
@@ -380,6 +399,8 @@ static void d_lru_add(struct dentry *dentry)
380399
D_FLAG_VERIFY(dentry, 0);
381400
dentry->d_flags |= DCACHE_LRU_LIST;
382401
this_cpu_inc(nr_dentry_unused);
402+
if (d_is_negative(dentry))
403+
this_cpu_inc(nr_dentry_negative);
383404
WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
384405
}
385406

@@ -388,6 +409,8 @@ static void d_lru_del(struct dentry *dentry)
388409
D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
389410
dentry->d_flags &= ~DCACHE_LRU_LIST;
390411
this_cpu_dec(nr_dentry_unused);
412+
if (d_is_negative(dentry))
413+
this_cpu_dec(nr_dentry_negative);
391414
WARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
392415
}
393416

@@ -418,6 +441,8 @@ static void d_lru_isolate(struct list_lru_one *lru, struct dentry *dentry)
418441
D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
419442
dentry->d_flags &= ~DCACHE_LRU_LIST;
420443
this_cpu_dec(nr_dentry_unused);
444+
if (d_is_negative(dentry))
445+
this_cpu_dec(nr_dentry_negative);
421446
list_lru_isolate(lru, &dentry->d_lru);
422447
}
423448

@@ -426,6 +451,8 @@ static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
426451
{
427452
D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
428453
dentry->d_flags |= DCACHE_SHRINK_LIST;
454+
if (d_is_negative(dentry))
455+
this_cpu_dec(nr_dentry_negative);
429456
list_lru_isolate_move(lru, &dentry->d_lru, list);
430457
}
431458

@@ -1816,6 +1843,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
18161843
WARN_ON(d_in_lookup(dentry));
18171844

18181845
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);
18191851
hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
18201852
raw_write_seqcount_begin(&dentry->d_seq);
18211853
__d_set_inode_and_type(dentry, inode, add_flags);

include/linux/dcache.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ extern const struct qstr slash_name;
6262
struct dentry_stat_t {
6363
long nr_dentry;
6464
long nr_unused;
65-
long age_limit; /* age in seconds */
66-
long want_pages; /* pages requested by system */
67-
long dummy[2];
65+
long age_limit; /* age in seconds */
66+
long want_pages; /* pages requested by system */
67+
long nr_negative; /* # of unused negative dentries */
68+
long dummy; /* Reserved for future use */
6869
};
6970
extern struct dentry_stat_t dentry_stat;
7071

0 commit comments

Comments
 (0)