aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGao Xiang <gaoxiang25@huawei.com>2018-07-26 08:22:04 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-07-27 11:24:10 -0400
commita15813126272e5f81311e5e1330162baa40e5b0a (patch)
tree98ad3b2a8b90878e7635a8ed0e1ab54484ff2266
parent2497ee41295c769dc74cb8bac7e03842bc51d331 (diff)
staging: erofs: introduce erofs shrinker
This patch adds a dedicated shrinker targeting to free unneeded memory consumed by a number of erofs in-memory data structures. Like F2FS and UBIFS, it also adds: - sbi->umount_mutex to avoid races on shrinker and put_super - sbi->shrinker_run_no to not revisit recently scaned objects Signed-off-by: Gao Xiang <gaoxiang25@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/staging/erofs/internal.h7
-rw-r--r--drivers/staging/erofs/super.c15
-rw-r--r--drivers/staging/erofs/utils.c85
3 files changed, 101 insertions, 6 deletions
diff --git a/drivers/staging/erofs/internal.h b/drivers/staging/erofs/internal.h
index 18e7b9c2aaa0..42455f01c421 100644
--- a/drivers/staging/erofs/internal.h
+++ b/drivers/staging/erofs/internal.h
@@ -66,6 +66,7 @@ typedef u64 erofs_nid_t;
66struct erofs_sb_info { 66struct erofs_sb_info {
67 /* list for all registered superblocks, mainly for shrinker */ 67 /* list for all registered superblocks, mainly for shrinker */
68 struct list_head list; 68 struct list_head list;
69 struct mutex umount_mutex;
69 70
70 u32 blocks; 71 u32 blocks;
71 u32 meta_blkaddr; 72 u32 meta_blkaddr;
@@ -93,6 +94,7 @@ struct erofs_sb_info {
93 char *dev_name; 94 char *dev_name;
94 95
95 unsigned int mount_opt; 96 unsigned int mount_opt;
97 unsigned int shrinker_run_no;
96 98
97#ifdef CONFIG_EROFS_FAULT_INJECTION 99#ifdef CONFIG_EROFS_FAULT_INJECTION
98 struct erofs_fault_info fault_info; /* For fault injection */ 100 struct erofs_fault_info fault_info; /* For fault injection */
@@ -416,6 +418,11 @@ extern struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp);
416extern void erofs_register_super(struct super_block *sb); 418extern void erofs_register_super(struct super_block *sb);
417extern void erofs_unregister_super(struct super_block *sb); 419extern void erofs_unregister_super(struct super_block *sb);
418 420
421extern unsigned long erofs_shrink_count(struct shrinker *shrink,
422 struct shrink_control *sc);
423extern unsigned long erofs_shrink_scan(struct shrinker *shrink,
424 struct shrink_control *sc);
425
419#ifndef lru_to_page 426#ifndef lru_to_page
420#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) 427#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
421#endif 428#endif
diff --git a/drivers/staging/erofs/super.c b/drivers/staging/erofs/super.c
index f455d7135599..ef85884d47fb 100644
--- a/drivers/staging/erofs/super.c
+++ b/drivers/staging/erofs/super.c
@@ -375,7 +375,9 @@ static void erofs_put_super(struct super_block *sb)
375 infoln("unmounted for %s", sbi->dev_name); 375 infoln("unmounted for %s", sbi->dev_name);
376 __putname(sbi->dev_name); 376 __putname(sbi->dev_name);
377 377
378 mutex_lock(&sbi->umount_mutex);
378 erofs_unregister_super(sb); 379 erofs_unregister_super(sb);
380 mutex_unlock(&sbi->umount_mutex);
379 381
380 kfree(sbi); 382 kfree(sbi);
381 sb->s_fs_info = NULL; 383 sb->s_fs_info = NULL;
@@ -415,6 +417,12 @@ static void erofs_kill_sb(struct super_block *sb)
415 kill_block_super(sb); 417 kill_block_super(sb);
416} 418}
417 419
420static struct shrinker erofs_shrinker_info = {
421 .scan_objects = erofs_shrink_scan,
422 .count_objects = erofs_shrink_count,
423 .seeks = DEFAULT_SEEKS,
424};
425
418static struct file_system_type erofs_fs_type = { 426static struct file_system_type erofs_fs_type = {
419 .owner = THIS_MODULE, 427 .owner = THIS_MODULE,
420 .name = "erofs", 428 .name = "erofs",
@@ -435,6 +443,10 @@ static int __init erofs_module_init(void)
435 if (err) 443 if (err)
436 goto icache_err; 444 goto icache_err;
437 445
446 err = register_shrinker(&erofs_shrinker_info);
447 if (err)
448 goto shrinker_err;
449
438 err = register_filesystem(&erofs_fs_type); 450 err = register_filesystem(&erofs_fs_type);
439 if (err) 451 if (err)
440 goto fs_err; 452 goto fs_err;
@@ -443,6 +455,8 @@ static int __init erofs_module_init(void)
443 return 0; 455 return 0;
444 456
445fs_err: 457fs_err:
458 unregister_shrinker(&erofs_shrinker_info);
459shrinker_err:
446 erofs_exit_inode_cache(); 460 erofs_exit_inode_cache();
447icache_err: 461icache_err:
448 return err; 462 return err;
@@ -451,6 +465,7 @@ icache_err:
451static void __exit erofs_module_exit(void) 465static void __exit erofs_module_exit(void)
452{ 466{
453 unregister_filesystem(&erofs_fs_type); 467 unregister_filesystem(&erofs_fs_type);
468 unregister_shrinker(&erofs_shrinker_info);
454 erofs_exit_inode_cache(); 469 erofs_exit_inode_cache();
455 infoln("successfully finalize erofs"); 470 infoln("successfully finalize erofs");
456} 471}
diff --git a/drivers/staging/erofs/utils.c b/drivers/staging/erofs/utils.c
index 6748def67e74..c1d83cecfec6 100644
--- a/drivers/staging/erofs/utils.c
+++ b/drivers/staging/erofs/utils.c
@@ -29,20 +29,93 @@ struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp)
29 return page; 29 return page;
30} 30}
31 31
32static DEFINE_MUTEX(erofs_sb_list_lock); 32
33/* protected by 'erofs_sb_list_lock' */
34static unsigned int shrinker_run_no;
35
36/* protects the mounted 'erofs_sb_list' */
37static DEFINE_SPINLOCK(erofs_sb_list_lock);
33static LIST_HEAD(erofs_sb_list); 38static LIST_HEAD(erofs_sb_list);
34 39
40/* global shrink count (for all mounted EROFS instances) */
41static atomic_long_t erofs_global_shrink_cnt;
42
35void erofs_register_super(struct super_block *sb) 43void erofs_register_super(struct super_block *sb)
36{ 44{
37 mutex_lock(&erofs_sb_list_lock); 45 struct erofs_sb_info *sbi = EROFS_SB(sb);
38 list_add(&EROFS_SB(sb)->list, &erofs_sb_list); 46
39 mutex_unlock(&erofs_sb_list_lock); 47 mutex_init(&sbi->umount_mutex);
48
49 spin_lock(&erofs_sb_list_lock);
50 list_add(&sbi->list, &erofs_sb_list);
51 spin_unlock(&erofs_sb_list_lock);
40} 52}
41 53
42void erofs_unregister_super(struct super_block *sb) 54void erofs_unregister_super(struct super_block *sb)
43{ 55{
44 mutex_lock(&erofs_sb_list_lock); 56 spin_lock(&erofs_sb_list_lock);
45 list_del(&EROFS_SB(sb)->list); 57 list_del(&EROFS_SB(sb)->list);
46 mutex_unlock(&erofs_sb_list_lock); 58 spin_unlock(&erofs_sb_list_lock);
59}
60
61unsigned long erofs_shrink_count(struct shrinker *shrink,
62 struct shrink_control *sc)
63{
64 return atomic_long_read(&erofs_global_shrink_cnt);
65}
66
67unsigned long erofs_shrink_scan(struct shrinker *shrink,
68 struct shrink_control *sc)
69{
70 struct erofs_sb_info *sbi;
71 struct list_head *p;
72
73 unsigned long nr = sc->nr_to_scan;
74 unsigned int run_no;
75 unsigned long freed = 0;
76
77 spin_lock(&erofs_sb_list_lock);
78 do
79 run_no = ++shrinker_run_no;
80 while (run_no == 0);
81
82 /* Iterate over all mounted superblocks and try to shrink them */
83 p = erofs_sb_list.next;
84 while (p != &erofs_sb_list) {
85 sbi = list_entry(p, struct erofs_sb_info, list);
86
87 /*
88 * We move the ones we do to the end of the list, so we stop
89 * when we see one we have already done.
90 */
91 if (sbi->shrinker_run_no == run_no)
92 break;
93
94 if (!mutex_trylock(&sbi->umount_mutex)) {
95 p = p->next;
96 continue;
97 }
98
99 spin_unlock(&erofs_sb_list_lock);
100 sbi->shrinker_run_no = run_no;
101
102 /* add scan handlers here */
103
104 spin_lock(&erofs_sb_list_lock);
105 /* Get the next list element before we move this one */
106 p = p->next;
107
108 /*
109 * Move this one to the end of the list to provide some
110 * fairness.
111 */
112 list_move_tail(&sbi->list, &erofs_sb_list);
113 mutex_unlock(&sbi->umount_mutex);
114
115 if (freed >= nr)
116 break;
117 }
118 spin_unlock(&erofs_sb_list_lock);
119 return freed;
47} 120}
48 121