diff options
-rw-r--r-- | Documentation/sysctl/fs.txt | 26 | ||||
-rw-r--r-- | fs/dcache.c | 32 | ||||
-rw-r--r-- | include/linux/dcache.h | 7 |
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 | ||
57 | dentry-state: | 57 | dentry-state: |
58 | 58 | ||
59 | From linux/fs/dentry.c: | 59 | From linux/include/linux/dcache.h: |
60 | -------------------------------------------------------------- | 60 | -------------------------------------------------------------- |
61 | struct { | 61 | struct 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 | -------------------------------------------------------------- | |
70 | Dentries are dynamically allocated and deallocated, and | 70 | |
71 | nr_dentry seems to be 0 all the time. Hence it's safe to | 71 | Dentries are dynamically allocated and deallocated. |
72 | assume that only nr_unused, age_limit and want_pages are | 72 | |
73 | used. Nr_unused seems to be exactly what its name says. | 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 | |||
74 | Age_limit is the age in seconds after which dcache entries | 77 | Age_limit is the age in seconds after which dcache entries |
75 | can be reclaimed when memory is short and want_pages is | 78 | can be reclaimed when memory is short and want_pages is |
76 | nonzero when shrink_dcache_pages() has been called and the | 79 | nonzero when shrink_dcache_pages() has been called and the |
77 | dcache isn't pruned yet. | 80 | dcache isn't pruned yet. |
78 | 81 | ||
82 | nr_negative shows the number of unused dentries that are also | ||
83 | negative dentries which do not mapped to actual files. | ||
84 | |||
79 | ============================================================== | 85 | ============================================================== |
80 | 86 | ||
81 | dquot-max & dquot-nr: | 87 | dquot-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 | ||
120 | static DEFINE_PER_CPU(long, nr_dentry); | 120 | static DEFINE_PER_CPU(long, nr_dentry); |
121 | static DEFINE_PER_CPU(long, nr_dentry_unused); | 121 | static DEFINE_PER_CPU(long, nr_dentry_unused); |
122 | static 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 | ||
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 | |||
155 | int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer, | 166 | int 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 | ||
322 | static void dentry_free(struct dentry *dentry) | 336 | static 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; | |||
62 | struct dentry_stat_t { | 62 | struct 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 | }; |
69 | extern struct dentry_stat_t dentry_stat; | 70 | extern struct dentry_stat_t dentry_stat; |
70 | 71 | ||