f2fs: introduce a shrinker for mounted fs
authorJaegeuk Kim <jaegeuk@kernel.org>
Fri, 19 Jun 2015 19:01:21 +0000 (12:01 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Tue, 4 Aug 2015 21:09:54 +0000 (14:09 -0700)
This patch introduces a shrinker targeting to reduce memory footprint consumed
by a number of in-memory f2fs data structures.

In addition, it newly adds:
 - sbi->umount_mutex to avoid data races on shrinker and put_super
 - sbi->shruinker_run_no to not revisit objects

Note that the basic implementation was copied from fs/ubifs/shrinker.c

Reviewed-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/Makefile
fs/f2fs/f2fs.h
fs/f2fs/shrinker.c [new file with mode: 0644]
fs/f2fs/super.c

index 396be1a39e554c172e89e7600db577f4da0141ff..005251b8d459f212334a4015041648e75bd68dd9 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o
 
 f2fs-y         := dir.o file.o inode.o namei.o hash.o super.o inline.o
 f2fs-y         += checkpoint.o gc.o data.o node.o segment.o recovery.o
+f2fs-y         += shrinker.o
 f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
 f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
 f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
index 3aaa4b99050a94515bd9c33d81e6208c29b36cb0..e82af8c7ee8cd86996b4baaced924767f5e4b6fd 100644 (file)
@@ -791,6 +791,11 @@ struct f2fs_sb_info {
        /* For sysfs suppport */
        struct kobject s_kobj;
        struct completion s_kobj_unregister;
+
+       /* For shrinker support */
+       struct list_head s_list;
+       struct mutex umount_mutex;
+       unsigned int shrinker_run_no;
 };
 
 /*
@@ -1951,6 +1956,14 @@ bool f2fs_empty_inline_dir(struct inode *);
 int f2fs_read_inline_dir(struct file *, struct dir_context *,
                                                struct f2fs_str *);
 
+/*
+ * shrinker.c
+ */
+unsigned long f2fs_shrink_count(struct shrinker *, struct shrink_control *);
+unsigned long f2fs_shrink_scan(struct shrinker *, struct shrink_control *);
+void f2fs_join_shrinker(struct f2fs_sb_info *);
+void f2fs_leave_shrinker(struct f2fs_sb_info *);
+
 /*
  * crypto support
  */
diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c
new file mode 100644 (file)
index 0000000..16e9b43
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * f2fs shrinker support
+ *   the basic infra was copied from fs/ubifs/shrinker.c
+ *
+ * Copyright (c) 2015 Motorola Mobility
+ * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/fs.h>
+#include <linux/f2fs_fs.h>
+
+#include "f2fs.h"
+
+static LIST_HEAD(f2fs_list);
+static DEFINE_SPINLOCK(f2fs_list_lock);
+static unsigned int shrinker_run_no;
+
+unsigned long f2fs_shrink_count(struct shrinker *shrink,
+                               struct shrink_control *sc)
+{
+       struct f2fs_sb_info *sbi;
+       struct list_head *p;
+       unsigned long count = 0;
+
+       spin_lock(&f2fs_list_lock);
+       p = f2fs_list.next;
+       while (p != &f2fs_list) {
+               sbi = list_entry(p, struct f2fs_sb_info, s_list);
+
+               /* stop f2fs_put_super */
+               if (!mutex_trylock(&sbi->umount_mutex)) {
+                       p = p->next;
+                       continue;
+               }
+               spin_unlock(&f2fs_list_lock);
+
+               /* TODO: count # of objects */
+
+               spin_lock(&f2fs_list_lock);
+               p = p->next;
+               mutex_unlock(&sbi->umount_mutex);
+       }
+       spin_unlock(&f2fs_list_lock);
+       return count;
+}
+
+unsigned long f2fs_shrink_scan(struct shrinker *shrink,
+                               struct shrink_control *sc)
+{
+       unsigned long nr = sc->nr_to_scan;
+       struct f2fs_sb_info *sbi;
+       struct list_head *p;
+       unsigned int run_no;
+       unsigned long freed = 0;
+
+       spin_lock(&f2fs_list_lock);
+       do {
+               run_no = ++shrinker_run_no;
+       } while (run_no == 0);
+       p = f2fs_list.next;
+       while (p != &f2fs_list) {
+               sbi = list_entry(p, struct f2fs_sb_info, s_list);
+
+               if (sbi->shrinker_run_no == run_no)
+                       break;
+
+               /* stop f2fs_put_super */
+               if (!mutex_trylock(&sbi->umount_mutex)) {
+                       p = p->next;
+                       continue;
+               }
+               spin_unlock(&f2fs_list_lock);
+
+               sbi->shrinker_run_no = run_no;
+
+               /* TODO: shrink caches */
+
+               spin_lock(&f2fs_list_lock);
+               p = p->next;
+               list_move_tail(&sbi->s_list, &f2fs_list);
+               mutex_unlock(&sbi->umount_mutex);
+               if (freed >= nr)
+                       break;
+       }
+       spin_unlock(&f2fs_list_lock);
+       return freed;
+}
+
+void f2fs_join_shrinker(struct f2fs_sb_info *sbi)
+{
+       spin_lock(&f2fs_list_lock);
+       list_add_tail(&sbi->s_list, &f2fs_list);
+       spin_unlock(&f2fs_list_lock);
+}
+
+void f2fs_leave_shrinker(struct f2fs_sb_info *sbi)
+{
+       spin_lock(&f2fs_list_lock);
+       list_del(&sbi->s_list);
+       spin_unlock(&f2fs_list_lock);
+}
index da277100dc90931cdaba3fcceae226accb7a8aab..bc7684b6d57a77aa7f74d1777252ae2f361f51cb 100644 (file)
@@ -39,6 +39,13 @@ static struct proc_dir_entry *f2fs_proc_root;
 static struct kmem_cache *f2fs_inode_cachep;
 static struct kset *f2fs_kset;
 
+/* f2fs-wide shrinker description */
+static struct shrinker f2fs_shrinker_info = {
+       .scan_objects = f2fs_shrink_scan,
+       .count_objects = f2fs_shrink_count,
+       .seeks = DEFAULT_SEEKS,
+};
+
 enum {
        Opt_gc_background,
        Opt_disable_roll_forward,
@@ -500,6 +507,9 @@ static void f2fs_put_super(struct super_block *sb)
 
        stop_gc_thread(sbi);
 
+       /* prevent remaining shrinker jobs */
+       mutex_lock(&sbi->umount_mutex);
+
        /*
         * We don't need to do checkpoint when superblock is clean.
         * But, the previous checkpoint was not done by umount, it needs to do
@@ -523,6 +533,9 @@ static void f2fs_put_super(struct super_block *sb)
        release_dirty_inode(sbi);
        release_discard_addrs(sbi);
 
+       f2fs_leave_shrinker(sbi);
+       mutex_unlock(&sbi->umount_mutex);
+
        iput(sbi->node_inode);
        iput(sbi->meta_inode);
 
@@ -972,6 +985,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
 
        sbi->dir_level = DEF_DIR_LEVEL;
        clear_sbi_flag(sbi, SBI_NEED_FSCK);
+
+       INIT_LIST_HEAD(&sbi->s_list);
+       mutex_init(&sbi->umount_mutex);
 }
 
 /*
@@ -1214,6 +1230,8 @@ try_onemore:
                goto free_nm;
        }
 
+       f2fs_join_shrinker(sbi);
+
        /* if there are nt orphan nodes free them */
        recover_orphan_inodes(sbi);
 
@@ -1310,7 +1328,10 @@ free_root_inode:
        dput(sb->s_root);
        sb->s_root = NULL;
 free_node_inode:
+       mutex_lock(&sbi->umount_mutex);
+       f2fs_leave_shrinker(sbi);
        iput(sbi->node_inode);
+       mutex_unlock(&sbi->umount_mutex);
 free_nm:
        destroy_node_manager(sbi);
 free_sm:
@@ -1406,13 +1427,20 @@ static int __init init_f2fs_fs(void)
        err = f2fs_init_crypto();
        if (err)
                goto free_kset;
-       err = register_filesystem(&f2fs_fs_type);
+
+       err = register_shrinker(&f2fs_shrinker_info);
        if (err)
                goto free_crypto;
+
+       err = register_filesystem(&f2fs_fs_type);
+       if (err)
+               goto free_shrinker;
        f2fs_create_root_stats();
        f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
        return 0;
 
+free_shrinker:
+       unregister_shrinker(&f2fs_shrinker_info);
 free_crypto:
        f2fs_exit_crypto();
 free_kset:
@@ -1435,6 +1463,7 @@ static void __exit exit_f2fs_fs(void)
 {
        remove_proc_entry("fs/f2fs", NULL);
        f2fs_destroy_root_stats();
+       unregister_shrinker(&f2fs_shrinker_info);
        unregister_filesystem(&f2fs_fs_type);
        f2fs_exit_crypto();
        destroy_extent_cache();