Whamcloud - gitweb
LU-13300 ldiskfs: port patches to improve extent status shrink
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / rhel7.6 / ext4-track-extent-status-tree-shrinker-delay-statict.patch
1 From f33e0fa5ab6cad962d3b88376f4611b9aba1d030 Mon Sep 17 00:00:00 2001
2 From: Wang Shilong <wshilong@ddn.com>
3 Date: Thu, 27 Feb 2020 17:08:04 +0800
4 Subject: [PATCH 1/7] ext4: track extent status tree shrinker delay statictics
5
6 This commit adds some statictics in extent status tree shrinker.  The
7 purpose to add these is that we want to collect more details when we
8 encounter a stall caused by extent status tree shrinker.  Here we count
9 the following statictics:
10   stats:
11     the number of all objects on all extent status trees
12     the number of reclaimable objects on lru list
13     cache hits/misses
14     the last sorted interval
15     the number of inodes on lru list
16   average:
17     scan time for shrinking some objects
18     the number of shrunk objects
19   maximum:
20     the inode that has max nr. of objects on lru list
21     the maximum scan time for shrinking some objects
22
23 The output looks like below:
24   $ cat /proc/fs/ext4/sda1/es_shrinker_info
25   stats:
26     28228 objects
27     6341 reclaimable objects
28     5281/631 cache hits/misses
29     586 ms last sorted interval
30     250 inodes on lru list
31   average:
32     153 us scan time
33     128 shrunk objects
34   maximum:
35     255 inode (255 objects, 198 reclaimable)
36     125723 us max scan time
37
38 If the lru list has never been sorted, the following line will not be
39 printed:
40     586ms last sorted interval
41 If there is an empty lru list, the following lines also will not be
42 printed:
43     250 inodes on lru list
44   ...
45   maximum:
46     255 inode (255 objects, 198 reclaimable)
47     0 us max scan time
48
49 Meanwhile in this commit a new trace point is defined to print some
50 details in __ext4_es_shrink().
51
52 [Shilong remove trace point parts of this patch]
53
54 Cc: Andreas Dilger <adilger.kernel@dilger.ca>
55 Cc: Jan Kara <jack@suse.cz>
56 Reviewed-by: Jan Kara <jack@suse.cz>
57 Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
58 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
59 ---
60  fs/ext4/ext4.h           |   4 +-
61  fs/ext4/extents_status.c | 179 +++++++++++++++++++++++++++++++++++++--
62  fs/ext4/extents_status.h |  13 ++-
63  fs/ext4/super.c          |  13 +--
64  4 files changed, 187 insertions(+), 22 deletions(-)
65
66 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
67 index 763276e2..cc5ba587 100644
68 --- a/fs/ext4/ext4.h
69 +++ b/fs/ext4/ext4.h
70 @@ -1018,6 +1018,7 @@ struct ext4_inode_info {
71         struct ext4_es_tree i_es_tree;
72         rwlock_t i_es_lock;
73         struct list_head i_es_lru;
74 +       unsigned int i_es_all_nr;       /* protected by i_es_lock */
75         unsigned int i_es_lru_nr;       /* protected by i_es_lock */
76         unsigned long i_touch_when;     /* jiffies of last accessing */
77  
78 @@ -1482,8 +1483,7 @@ struct ext4_sb_info {
79         /* Reclaim extents from extent status tree */
80         struct shrinker s_es_shrinker;
81         struct list_head s_es_lru;
82 -       unsigned long s_es_last_sorted;
83 -       struct percpu_counter s_extent_cache_cnt;
84 +       struct ext4_es_stats s_es_stats;
85         spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp;
86  
87         /* Ratelimit ext4 messages. */
88 diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
89 index 3ef7f932..7dfed27b 100644
90 --- a/fs/ext4/extents_status.c
91 +++ b/fs/ext4/extents_status.c
92 @@ -11,6 +11,8 @@
93   */
94  #include <linux/rbtree.h>
95  #include <linux/list_sort.h>
96 +#include <linux/proc_fs.h>
97 +#include <linux/seq_file.h>
98  #include "ext4.h"
99  #include "extents_status.h"
100  
101 @@ -313,19 +315,27 @@ ext4_es_alloc_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len,
102          */
103         if (!ext4_es_is_delayed(es)) {
104                 EXT4_I(inode)->i_es_lru_nr++;
105 -               percpu_counter_inc(&EXT4_SB(inode->i_sb)->s_extent_cache_cnt);
106 +               percpu_counter_inc(&EXT4_SB(inode->i_sb)->
107 +                                       s_es_stats.es_stats_lru_cnt);
108         }
109  
110 +       EXT4_I(inode)->i_es_all_nr++;
111 +       percpu_counter_inc(&EXT4_SB(inode->i_sb)->s_es_stats.es_stats_all_cnt);
112 +
113         return es;
114  }
115  
116  static void ext4_es_free_extent(struct inode *inode, struct extent_status *es)
117  {
118 +       EXT4_I(inode)->i_es_all_nr--;
119 +       percpu_counter_dec(&EXT4_SB(inode->i_sb)->s_es_stats.es_stats_all_cnt);
120 +
121         /* Decrease the lru counter when this es is not delayed */
122         if (!ext4_es_is_delayed(es)) {
123                 BUG_ON(EXT4_I(inode)->i_es_lru_nr == 0);
124                 EXT4_I(inode)->i_es_lru_nr--;
125 -               percpu_counter_dec(&EXT4_SB(inode->i_sb)->s_extent_cache_cnt);
126 +               percpu_counter_dec(&EXT4_SB(inode->i_sb)->
127 +                                       s_es_stats.es_stats_lru_cnt);
128         }
129  
130         kmem_cache_free(ext4_es_cachep, es);
131 @@ -739,6 +749,7 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
132                           struct extent_status *es)
133  {
134         struct ext4_es_tree *tree;
135 +       struct ext4_es_stats *stats;
136         struct extent_status *es1 = NULL;
137         struct rb_node *node;
138         int found = 0;
139 @@ -775,11 +786,15 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
140         }
141  
142  out:
143 +       stats = &EXT4_SB(inode->i_sb)->s_es_stats;
144         if (found) {
145                 BUG_ON(!es1);
146                 es->es_lblk = es1->es_lblk;
147                 es->es_len = es1->es_len;
148                 es->es_pblk = es1->es_pblk;
149 +               stats->es_stats_cache_hits++;
150 +       } else {
151 +               stats->es_stats_cache_misses++;
152         }
153  
154         read_unlock(&EXT4_I(inode)->i_es_lock);
155 @@ -941,11 +956,16 @@ static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan,
156                             struct ext4_inode_info *locked_ei)
157  {
158         struct ext4_inode_info *ei;
159 +       struct ext4_es_stats *es_stats;
160         struct list_head *cur, *tmp;
161         LIST_HEAD(skipped);
162 +       ktime_t start_time;
163 +       u64 scan_time;
164         int ret, nr_shrunk = 0;
165         int retried = 0, skip_precached = 1, nr_skipped = 0;
166  
167 +       es_stats = &sbi->s_es_stats;
168 +       start_time = ktime_get();
169         spin_lock(&sbi->s_es_lru_lock);
170  
171  retry:
172 @@ -954,7 +974,8 @@ retry:
173                  * If we have already reclaimed all extents from extent
174                  * status tree, just stop the loop immediately.
175                  */
176 -               if (percpu_counter_read_positive(&sbi->s_extent_cache_cnt) == 0)
177 +               if (percpu_counter_read_positive(
178 +                               &es_stats->es_stats_lru_cnt) == 0)
179                         break;
180  
181                 ei = list_entry(cur, struct ext4_inode_info, i_es_lru);
182 @@ -964,7 +985,7 @@ retry:
183                  * time.  Normally we try hard to avoid shrinking
184                  * precached inodes, but we will as a last resort.
185                  */
186 -               if ((sbi->s_es_last_sorted < ei->i_touch_when) ||
187 +               if ((es_stats->es_stats_last_sorted < ei->i_touch_when) ||
188                     (skip_precached && ext4_test_inode_state(&ei->vfs_inode,
189                                                 EXT4_STATE_EXT_PRECACHED))) {
190                         nr_skipped++;
191 @@ -998,7 +1019,7 @@ retry:
192         if ((nr_shrunk == 0) && nr_skipped && !retried) {
193                 retried++;
194                 list_sort(NULL, &sbi->s_es_lru, ext4_inode_touch_time_cmp);
195 -               sbi->s_es_last_sorted = jiffies;
196 +               es_stats->es_stats_last_sorted = jiffies;
197                 ei = list_first_entry(&sbi->s_es_lru, struct ext4_inode_info,
198                                       i_es_lru);
199                 /*
200 @@ -1016,6 +1037,20 @@ retry:
201         if (locked_ei && nr_shrunk == 0)
202                 nr_shrunk = __es_try_to_reclaim_extents(locked_ei, nr_to_scan);
203  
204 +       scan_time = ktime_to_ns(ktime_sub(ktime_get(), start_time));
205 +       if (likely(es_stats->es_stats_scan_time))
206 +               es_stats->es_stats_scan_time = (scan_time +
207 +                               es_stats->es_stats_scan_time*3) / 4;
208 +       else
209 +               es_stats->es_stats_scan_time = scan_time;
210 +       if (scan_time > es_stats->es_stats_max_scan_time)
211 +               es_stats->es_stats_max_scan_time = scan_time;
212 +       if (likely(es_stats->es_stats_shrunk))
213 +               es_stats->es_stats_shrunk = (nr_shrunk +
214 +                               es_stats->es_stats_shrunk*3) / 4;
215 +       else
216 +               es_stats->es_stats_shrunk = nr_shrunk;
217 +
218         return nr_shrunk;
219  }
220  
221 @@ -1026,7 +1061,7 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
222         int nr_to_scan = sc->nr_to_scan;
223         int ret, nr_shrunk;
224  
225 -       ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt);
226 +       ret = percpu_counter_read_positive(&sbi->s_es_stats.es_stats_lru_cnt);
227         trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan, ret);
228  
229         if (!nr_to_scan)
230 @@ -1034,23 +1069,149 @@ static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
231  
232         nr_shrunk = __ext4_es_shrink(sbi, nr_to_scan, NULL);
233  
234 -       ret = percpu_counter_read_positive(&sbi->s_extent_cache_cnt);
235 +       ret = percpu_counter_read_positive(&sbi->s_es_stats.es_stats_lru_cnt);
236         trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk, ret);
237         return ret;
238  }
239  
240 -void ext4_es_register_shrinker(struct ext4_sb_info *sbi)
241 +static void *ext4_es_seq_shrinker_info_start(struct seq_file *seq, loff_t *pos)
242 +{
243 +       return *pos ? NULL : SEQ_START_TOKEN;
244 +}
245 +
246 +static void *
247 +ext4_es_seq_shrinker_info_next(struct seq_file *seq, void *v, loff_t *pos)
248 +{
249 +       return NULL;
250 +}
251 +
252 +static int ext4_es_seq_shrinker_info_show(struct seq_file *seq, void *v)
253 +{
254 +       struct ext4_sb_info *sbi = seq->private;
255 +       struct ext4_es_stats *es_stats = &sbi->s_es_stats;
256 +       struct ext4_inode_info *ei, *max = NULL;
257 +       unsigned int inode_cnt = 0;
258 +
259 +       if (v != SEQ_START_TOKEN)
260 +               return 0;
261 +
262 +       /* here we just find an inode that has the max nr. of objects */
263 +       spin_lock(&sbi->s_es_lru_lock);
264 +       list_for_each_entry(ei, &sbi->s_es_lru, i_es_lru) {
265 +               inode_cnt++;
266 +               if (max && max->i_es_all_nr < ei->i_es_all_nr)
267 +                       max = ei;
268 +               else if (!max)
269 +                       max = ei;
270 +       }
271 +       spin_unlock(&sbi->s_es_lru_lock);
272 +
273 +       seq_printf(seq, "stats:\n  %lld objects\n  %lld reclaimable objects\n",
274 +                  percpu_counter_sum_positive(&es_stats->es_stats_all_cnt),
275 +                  percpu_counter_sum_positive(&es_stats->es_stats_lru_cnt));
276 +       seq_printf(seq, "  %lu/%lu cache hits/misses\n",
277 +                  es_stats->es_stats_cache_hits,
278 +                  es_stats->es_stats_cache_misses);
279 +       if (es_stats->es_stats_last_sorted != 0)
280 +               seq_printf(seq, "  %u ms last sorted interval\n",
281 +                          jiffies_to_msecs(jiffies -
282 +                                           es_stats->es_stats_last_sorted));
283 +       if (inode_cnt)
284 +               seq_printf(seq, "  %d inodes on lru list\n", inode_cnt);
285 +
286 +       seq_printf(seq, "average:\n  %llu us scan time\n",
287 +           div_u64(es_stats->es_stats_scan_time, 1000));
288 +       seq_printf(seq, "  %lu shrunk objects\n", es_stats->es_stats_shrunk);
289 +       if (inode_cnt)
290 +               seq_printf(seq,
291 +                   "maximum:\n  %lu inode (%u objects, %u reclaimable)\n"
292 +                   "  %llu us max scan time\n",
293 +                   max->vfs_inode.i_ino, max->i_es_all_nr, max->i_es_lru_nr,
294 +                   div_u64(es_stats->es_stats_max_scan_time, 1000));
295 +
296 +       return 0;
297 +}
298 +
299 +static void ext4_es_seq_shrinker_info_stop(struct seq_file *seq, void *v)
300 +{
301 +}
302 +
303 +static const struct seq_operations ext4_es_seq_shrinker_info_ops = {
304 +       .start = ext4_es_seq_shrinker_info_start,
305 +       .next  = ext4_es_seq_shrinker_info_next,
306 +       .stop  = ext4_es_seq_shrinker_info_stop,
307 +       .show  = ext4_es_seq_shrinker_info_show,
308 +};
309 +
310 +static int
311 +ext4_es_seq_shrinker_info_open(struct inode *inode, struct file *file)
312 +{
313 +       int ret;
314 +
315 +       ret = seq_open(file, &ext4_es_seq_shrinker_info_ops);
316 +       if (!ret) {
317 +               struct seq_file *m = file->private_data;
318 +               m->private = PDE_DATA(inode);
319 +       }
320 +
321 +       return ret;
322 +}
323 +
324 +static int
325 +ext4_es_seq_shrinker_info_release(struct inode *inode, struct file *file)
326  {
327 +       return seq_release(inode, file);
328 +}
329 +
330 +static const struct file_operations ext4_es_seq_shrinker_info_fops = {
331 +       .owner          = THIS_MODULE,
332 +       .open           = ext4_es_seq_shrinker_info_open,
333 +       .read           = seq_read,
334 +       .llseek         = seq_lseek,
335 +       .release        = ext4_es_seq_shrinker_info_release,
336 +};
337 +
338 +int ext4_es_register_shrinker(struct ext4_sb_info *sbi)
339 +{
340 +       int err;
341 +
342         INIT_LIST_HEAD(&sbi->s_es_lru);
343         spin_lock_init(&sbi->s_es_lru_lock);
344 -       sbi->s_es_last_sorted = 0;
345 +       sbi->s_es_stats.es_stats_last_sorted = 0;
346 +       sbi->s_es_stats.es_stats_shrunk = 0;
347 +       sbi->s_es_stats.es_stats_cache_hits = 0;
348 +       sbi->s_es_stats.es_stats_cache_misses = 0;
349 +       sbi->s_es_stats.es_stats_scan_time = 0;
350 +       sbi->s_es_stats.es_stats_max_scan_time = 0;
351 +       err = percpu_counter_init(&sbi->s_es_stats.es_stats_all_cnt,
352 +                                 0, GFP_KERNEL);
353 +       if (err)
354 +               return err;
355 +       err = percpu_counter_init(&sbi->s_es_stats.es_stats_lru_cnt,
356 +                                 0, GFP_KERNEL);
357 +       if (err)
358 +               goto err;
359         sbi->s_es_shrinker.shrink = ext4_es_shrink;
360         sbi->s_es_shrinker.seeks = DEFAULT_SEEKS;
361         register_shrinker(&sbi->s_es_shrinker);
362 +
363 +       if (sbi->s_proc)
364 +               proc_create_data("es_shrinker_info", S_IRUGO, sbi->s_proc,
365 +                                &ext4_es_seq_shrinker_info_fops, sbi);
366 +
367 +       return 0;
368 +
369 +err:
370 +       percpu_counter_destroy(&sbi->s_es_stats.es_stats_all_cnt);
371 +       return err;
372  }
373  
374  void ext4_es_unregister_shrinker(struct ext4_sb_info *sbi)
375  {
376 +       if (sbi->s_proc)
377 +               remove_proc_entry("es_shrinker_info", sbi->s_proc);
378 +       percpu_counter_destroy(&sbi->s_es_stats.es_stats_all_cnt);
379 +       percpu_counter_destroy(&sbi->s_es_stats.es_stats_lru_cnt);
380         unregister_shrinker(&sbi->s_es_shrinker);
381  }
382  
383 diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
384 index f1b62a41..efd5f970 100644
385 --- a/fs/ext4/extents_status.h
386 +++ b/fs/ext4/extents_status.h
387 @@ -64,6 +64,17 @@ struct ext4_es_tree {
388         struct extent_status *cache_es; /* recently accessed extent */
389  };
390  
391 +struct ext4_es_stats {
392 +       unsigned long es_stats_last_sorted;
393 +       unsigned long es_stats_shrunk;
394 +       unsigned long es_stats_cache_hits;
395 +       unsigned long es_stats_cache_misses;
396 +       u64 es_stats_scan_time;
397 +       u64 es_stats_max_scan_time;
398 +       struct percpu_counter es_stats_all_cnt;
399 +       struct percpu_counter es_stats_lru_cnt;
400 +};
401 +
402  extern int __init ext4_init_es(void);
403  extern void ext4_exit_es(void);
404  extern void ext4_es_init_tree(struct ext4_es_tree *tree);
405 @@ -138,7 +149,7 @@ static inline void ext4_es_store_pblock_status(struct extent_status *es,
406                        (pb & ~ES_MASK));
407  }
408  
409 -extern void ext4_es_register_shrinker(struct ext4_sb_info *sbi);
410 +extern int ext4_es_register_shrinker(struct ext4_sb_info *sbi);
411  extern void ext4_es_unregister_shrinker(struct ext4_sb_info *sbi);
412  extern void ext4_es_lru_add(struct inode *inode);
413  extern void ext4_es_lru_del(struct inode *inode);
414 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
415 index 18fe358c..bcdb48cf 100644
416 --- a/fs/ext4/super.c
417 +++ b/fs/ext4/super.c
418 @@ -880,7 +880,6 @@ static void ext4_put_super(struct super_block *sb)
419         percpu_counter_destroy(&sbi->s_freeinodes_counter);
420         percpu_counter_destroy(&sbi->s_dirs_counter);
421         percpu_counter_destroy(&sbi->s_dirtyclusters_counter);
422 -       percpu_counter_destroy(&sbi->s_extent_cache_cnt);
423  #ifdef CONFIG_QUOTA
424         for (i = 0; i < EXT4_MAXQUOTAS; i++)
425                 kfree(sbi->s_qf_names[i]);
426 @@ -944,6 +943,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
427         ext4_es_init_tree(&ei->i_es_tree);
428         rwlock_init(&ei->i_es_lock);
429         INIT_LIST_HEAD(&ei->i_es_lru);
430 +       ei->i_es_all_nr = 0;
431         ei->i_es_lru_nr = 0;
432         ei->i_touch_when = 0;
433         ei->i_reserved_data_blocks = 0;
434 @@ -4289,14 +4289,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
435         sbi->s_err_report.function = print_daily_error_info;
436         sbi->s_err_report.data = (unsigned long) sb;
437  
438 -       /* Register extent status tree shrinker */
439 -       ext4_es_register_shrinker(sbi);
440 -
441 -       err = percpu_counter_init(&sbi->s_extent_cache_cnt, 0, GFP_KERNEL);
442 -       if (err) {
443 -               ext4_msg(sb, KERN_ERR, "insufficient memory");
444 +       if (ext4_es_register_shrinker(sbi))
445                 goto failed_mount3;
446 -       }
447  
448         sbi->s_stripe = ext4_get_stripe_size(sbi);
449         sbi->s_extent_max_zeroout_kb = 32;
450 @@ -4641,10 +4635,9 @@ failed_mount_wq:
451                 jbd2_journal_destroy(sbi->s_journal);
452                 sbi->s_journal = NULL;
453         }
454 -failed_mount3:
455         ext4_es_unregister_shrinker(sbi);
456 +failed_mount3:
457         del_timer_sync(&sbi->s_err_report);
458 -       percpu_counter_destroy(&sbi->s_extent_cache_cnt);
459         if (sbi->s_mmp_tsk)
460                 kthread_stop(sbi->s_mmp_tsk);
461  failed_mount2:
462 -- 
463 2.24.1
464