diff options
author | Gao Xiang <gaoxiang25@huawei.com> | 2018-07-26 08:22:04 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-27 11:24:10 -0400 |
commit | a15813126272e5f81311e5e1330162baa40e5b0a (patch) | |
tree | 98ad3b2a8b90878e7635a8ed0e1ab54484ff2266 | |
parent | 2497ee41295c769dc74cb8bac7e03842bc51d331 (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.h | 7 | ||||
-rw-r--r-- | drivers/staging/erofs/super.c | 15 | ||||
-rw-r--r-- | drivers/staging/erofs/utils.c | 85 |
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; | |||
66 | struct erofs_sb_info { | 66 | struct 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); | |||
416 | extern void erofs_register_super(struct super_block *sb); | 418 | extern void erofs_register_super(struct super_block *sb); |
417 | extern void erofs_unregister_super(struct super_block *sb); | 419 | extern void erofs_unregister_super(struct super_block *sb); |
418 | 420 | ||
421 | extern unsigned long erofs_shrink_count(struct shrinker *shrink, | ||
422 | struct shrink_control *sc); | ||
423 | extern 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 | ||
420 | static struct shrinker erofs_shrinker_info = { | ||
421 | .scan_objects = erofs_shrink_scan, | ||
422 | .count_objects = erofs_shrink_count, | ||
423 | .seeks = DEFAULT_SEEKS, | ||
424 | }; | ||
425 | |||
418 | static struct file_system_type erofs_fs_type = { | 426 | static 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 | ||
445 | fs_err: | 457 | fs_err: |
458 | unregister_shrinker(&erofs_shrinker_info); | ||
459 | shrinker_err: | ||
446 | erofs_exit_inode_cache(); | 460 | erofs_exit_inode_cache(); |
447 | icache_err: | 461 | icache_err: |
448 | return err; | 462 | return err; |
@@ -451,6 +465,7 @@ icache_err: | |||
451 | static void __exit erofs_module_exit(void) | 465 | static 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 | ||
32 | static DEFINE_MUTEX(erofs_sb_list_lock); | 32 | |
33 | /* protected by 'erofs_sb_list_lock' */ | ||
34 | static unsigned int shrinker_run_no; | ||
35 | |||
36 | /* protects the mounted 'erofs_sb_list' */ | ||
37 | static DEFINE_SPINLOCK(erofs_sb_list_lock); | ||
33 | static LIST_HEAD(erofs_sb_list); | 38 | static LIST_HEAD(erofs_sb_list); |
34 | 39 | ||
40 | /* global shrink count (for all mounted EROFS instances) */ | ||
41 | static atomic_long_t erofs_global_shrink_cnt; | ||
42 | |||
35 | void erofs_register_super(struct super_block *sb) | 43 | void 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 | ||
42 | void erofs_unregister_super(struct super_block *sb) | 54 | void 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 | |||
61 | unsigned long erofs_shrink_count(struct shrinker *shrink, | ||
62 | struct shrink_control *sc) | ||
63 | { | ||
64 | return atomic_long_read(&erofs_global_shrink_cnt); | ||
65 | } | ||
66 | |||
67 | unsigned 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 | ||