]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - fs/f2fs/super.c
f2fs: move sysfs code from super.c to fs/f2fs/sysfs.c
[mirror_ubuntu-jammy-kernel.git] / fs / f2fs / super.c
index 96fe8ed7310001c666c5f370ea735b5aecdc2518..9081570bc616b0a6f0d30088845ad35f141b2883 100644 (file)
@@ -35,9 +35,7 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/f2fs.h>
 
-static struct proc_dir_entry *f2fs_proc_root;
 static struct kmem_cache *f2fs_inode_cachep;
-static struct kset *f2fs_kset;
 
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 
@@ -49,6 +47,7 @@ char *fault_name[FAULT_MAX] = {
        [FAULT_BLOCK]           = "no more block",
        [FAULT_DIR_DEPTH]       = "too big dir depth",
        [FAULT_EVICT_INODE]     = "evict_inode fail",
+       [FAULT_TRUNCATE]        = "truncate fail",
        [FAULT_IO]              = "IO error",
        [FAULT_CHECKPOINT]      = "checkpoint error",
 };
@@ -82,6 +81,7 @@ enum {
        Opt_discard,
        Opt_nodiscard,
        Opt_noheap,
+       Opt_heap,
        Opt_user_xattr,
        Opt_nouser_xattr,
        Opt_acl,
@@ -116,6 +116,7 @@ static match_table_t f2fs_tokens = {
        {Opt_discard, "discard"},
        {Opt_nodiscard, "nodiscard"},
        {Opt_noheap, "no_heap"},
+       {Opt_heap, "heap"},
        {Opt_user_xattr, "user_xattr"},
        {Opt_nouser_xattr, "nouser_xattr"},
        {Opt_acl, "acl"},
@@ -143,207 +144,6 @@ static match_table_t f2fs_tokens = {
        {Opt_err, NULL},
 };
 
-/* Sysfs support for f2fs */
-enum {
-       GC_THREAD,      /* struct f2fs_gc_thread */
-       SM_INFO,        /* struct f2fs_sm_info */
-       DCC_INFO,       /* struct discard_cmd_control */
-       NM_INFO,        /* struct f2fs_nm_info */
-       F2FS_SBI,       /* struct f2fs_sb_info */
-#ifdef CONFIG_F2FS_FAULT_INJECTION
-       FAULT_INFO_RATE,        /* struct f2fs_fault_info */
-       FAULT_INFO_TYPE,        /* struct f2fs_fault_info */
-#endif
-};
-
-struct f2fs_attr {
-       struct attribute attr;
-       ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *);
-       ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *,
-                        const char *, size_t);
-       int struct_type;
-       int offset;
-};
-
-static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
-{
-       if (struct_type == GC_THREAD)
-               return (unsigned char *)sbi->gc_thread;
-       else if (struct_type == SM_INFO)
-               return (unsigned char *)SM_I(sbi);
-       else if (struct_type == DCC_INFO)
-               return (unsigned char *)SM_I(sbi)->dcc_info;
-       else if (struct_type == NM_INFO)
-               return (unsigned char *)NM_I(sbi);
-       else if (struct_type == F2FS_SBI)
-               return (unsigned char *)sbi;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
-       else if (struct_type == FAULT_INFO_RATE ||
-                                       struct_type == FAULT_INFO_TYPE)
-               return (unsigned char *)&sbi->fault_info;
-#endif
-       return NULL;
-}
-
-static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
-               struct f2fs_sb_info *sbi, char *buf)
-{
-       struct super_block *sb = sbi->sb;
-
-       if (!sb->s_bdev->bd_part)
-               return snprintf(buf, PAGE_SIZE, "0\n");
-
-       return snprintf(buf, PAGE_SIZE, "%llu\n",
-               (unsigned long long)(sbi->kbytes_written +
-                       BD_PART_WRITTEN(sbi)));
-}
-
-static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
-                       struct f2fs_sb_info *sbi, char *buf)
-{
-       unsigned char *ptr = NULL;
-       unsigned int *ui;
-
-       ptr = __struct_ptr(sbi, a->struct_type);
-       if (!ptr)
-               return -EINVAL;
-
-       ui = (unsigned int *)(ptr + a->offset);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", *ui);
-}
-
-static ssize_t f2fs_sbi_store(struct f2fs_attr *a,
-                       struct f2fs_sb_info *sbi,
-                       const char *buf, size_t count)
-{
-       unsigned char *ptr;
-       unsigned long t;
-       unsigned int *ui;
-       ssize_t ret;
-
-       ptr = __struct_ptr(sbi, a->struct_type);
-       if (!ptr)
-               return -EINVAL;
-
-       ui = (unsigned int *)(ptr + a->offset);
-
-       ret = kstrtoul(skip_spaces(buf), 0, &t);
-       if (ret < 0)
-               return ret;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
-       if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX))
-               return -EINVAL;
-#endif
-       *ui = t;
-       return count;
-}
-
-static ssize_t f2fs_attr_show(struct kobject *kobj,
-                               struct attribute *attr, char *buf)
-{
-       struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
-                                                               s_kobj);
-       struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
-
-       return a->show ? a->show(a, sbi, buf) : 0;
-}
-
-static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr,
-                                               const char *buf, size_t len)
-{
-       struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
-                                                                       s_kobj);
-       struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
-
-       return a->store ? a->store(a, sbi, buf, len) : 0;
-}
-
-static void f2fs_sb_release(struct kobject *kobj)
-{
-       struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
-                                                               s_kobj);
-       complete(&sbi->s_kobj_unregister);
-}
-
-#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
-static struct f2fs_attr f2fs_attr_##_name = {                  \
-       .attr = {.name = __stringify(_name), .mode = _mode },   \
-       .show   = _show,                                        \
-       .store  = _store,                                       \
-       .struct_type = _struct_type,                            \
-       .offset = _offset                                       \
-}
-
-#define F2FS_RW_ATTR(struct_type, struct_name, name, elname)   \
-       F2FS_ATTR_OFFSET(struct_type, name, 0644,               \
-               f2fs_sbi_show, f2fs_sbi_store,                  \
-               offsetof(struct struct_name, elname))
-
-#define F2FS_GENERAL_RO_ATTR(name) \
-static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
-
-F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time);
-F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time);
-F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time);
-F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle);
-F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments);
-F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards);
-F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections);
-F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy);
-F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
-F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks);
-F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
-F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages);
-F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
-#ifdef CONFIG_F2FS_FAULT_INJECTION
-F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
-F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
-#endif
-F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
-
-#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
-static struct attribute *f2fs_attrs[] = {
-       ATTR_LIST(gc_min_sleep_time),
-       ATTR_LIST(gc_max_sleep_time),
-       ATTR_LIST(gc_no_gc_sleep_time),
-       ATTR_LIST(gc_idle),
-       ATTR_LIST(reclaim_segments),
-       ATTR_LIST(max_small_discards),
-       ATTR_LIST(batched_trim_sections),
-       ATTR_LIST(ipu_policy),
-       ATTR_LIST(min_ipu_util),
-       ATTR_LIST(min_fsync_blocks),
-       ATTR_LIST(max_victim_search),
-       ATTR_LIST(dir_level),
-       ATTR_LIST(ram_thresh),
-       ATTR_LIST(ra_nid_pages),
-       ATTR_LIST(dirty_nats_ratio),
-       ATTR_LIST(cp_interval),
-       ATTR_LIST(idle_interval),
-#ifdef CONFIG_F2FS_FAULT_INJECTION
-       ATTR_LIST(inject_rate),
-       ATTR_LIST(inject_type),
-#endif
-       ATTR_LIST(lifetime_write_kbytes),
-       NULL,
-};
-
-static const struct sysfs_ops f2fs_attr_ops = {
-       .show   = f2fs_attr_show,
-       .store  = f2fs_attr_store,
-};
-
-static struct kobj_type f2fs_ktype = {
-       .default_attrs  = f2fs_attrs,
-       .sysfs_ops      = &f2fs_attr_ops,
-       .release        = f2fs_sb_release,
-};
-
 void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...)
 {
        struct va_format vaf;
@@ -436,6 +236,9 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_noheap:
                        set_opt(sbi, NOHEAP);
                        break;
+               case Opt_heap:
+                       clear_opt(sbi, NOHEAP);
+                       break;
 #ifdef CONFIG_F2FS_FS_XATTR
                case Opt_user_xattr:
                        set_opt(sbi, XATTR_USER);
@@ -616,6 +419,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
        mutex_init(&fi->inmem_lock);
        init_rwsem(&fi->dio_rwsem[READ]);
        init_rwsem(&fi->dio_rwsem[WRITE]);
+       init_rwsem(&fi->i_mmap_sem);
 
        /* Will be used by directory only */
        fi->i_dir_level = F2FS_SB(sb)->dir_level;
@@ -760,13 +564,7 @@ static void destroy_device_list(struct f2fs_sb_info *sbi)
 static void f2fs_put_super(struct super_block *sb)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
-
-       if (sbi->s_proc) {
-               remove_proc_entry("segment_info", sbi->s_proc);
-               remove_proc_entry("segment_bits", sbi->s_proc);
-               remove_proc_entry(sb->s_id, f2fs_proc_root);
-       }
-       kobject_del(&sbi->s_kobj);
+       int i;
 
        stop_gc_thread(sbi);
 
@@ -787,7 +585,14 @@ static void f2fs_put_super(struct super_block *sb)
        }
 
        /* be sure to wait for any on-going discard commands */
-       f2fs_wait_discard_bio(sbi, NULL_ADDR);
+       f2fs_wait_discard_bios(sbi);
+
+       if (!sbi->discard_blks) {
+               struct cp_control cpc = {
+                       .reason = CP_UMOUNT | CP_TRIMMED,
+               };
+               write_checkpoint(sbi, &cpc);
+       }
 
        /* write_checkpoint can update stat informaion */
        f2fs_destroy_stats(sbi);
@@ -802,7 +607,7 @@ static void f2fs_put_super(struct super_block *sb)
        mutex_unlock(&sbi->umount_mutex);
 
        /* our cp_error case, we can wait for any writeback page */
-       f2fs_flush_merged_bios(sbi);
+       f2fs_flush_merged_writes(sbi);
 
        iput(sbi->node_inode);
        iput(sbi->meta_inode);
@@ -812,8 +617,8 @@ static void f2fs_put_super(struct super_block *sb)
        destroy_segment_manager(sbi);
 
        kfree(sbi->ckpt);
-       kobject_put(&sbi->s_kobj);
-       wait_for_completion(&sbi->s_kobj_unregister);
+
+       f2fs_exit_sysfs(sbi);
 
        sb->s_fs_info = NULL;
        if (sbi->s_chksum_driver)
@@ -823,6 +628,8 @@ static void f2fs_put_super(struct super_block *sb)
        destroy_device_list(sbi);
        mempool_destroy(sbi->write_io_dummy);
        destroy_percpu_info(sbi);
+       for (i = 0; i < NR_PAGE_TYPE; i++)
+               kfree(sbi->write_io[i]);
        kfree(sbi);
 }
 
@@ -913,7 +720,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
        if (test_opt(sbi, DISCARD))
                seq_puts(seq, ",discard");
        if (test_opt(sbi, NOHEAP))
-               seq_puts(seq, ",no_heap_alloc");
+               seq_puts(seq, ",no_heap");
+       else
+               seq_puts(seq, ",heap");
 #ifdef CONFIG_F2FS_FS_XATTR
        if (test_opt(sbi, XATTR_USER))
                seq_puts(seq, ",user_xattr");
@@ -963,79 +772,13 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi));
 #ifdef CONFIG_F2FS_FAULT_INJECTION
        if (test_opt(sbi, FAULT_INJECTION))
-               seq_puts(seq, ",fault_injection");
+               seq_printf(seq, ",fault_injection=%u",
+                               sbi->fault_info.inject_rate);
 #endif
 
        return 0;
 }
 
-static int segment_info_seq_show(struct seq_file *seq, void *offset)
-{
-       struct super_block *sb = seq->private;
-       struct f2fs_sb_info *sbi = F2FS_SB(sb);
-       unsigned int total_segs =
-                       le32_to_cpu(sbi->raw_super->segment_count_main);
-       int i;
-
-       seq_puts(seq, "format: segment_type|valid_blocks\n"
-               "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n");
-
-       for (i = 0; i < total_segs; i++) {
-               struct seg_entry *se = get_seg_entry(sbi, i);
-
-               if ((i % 10) == 0)
-                       seq_printf(seq, "%-10d", i);
-               seq_printf(seq, "%d|%-3u", se->type,
-                                       get_valid_blocks(sbi, i, 1));
-               if ((i % 10) == 9 || i == (total_segs - 1))
-                       seq_putc(seq, '\n');
-               else
-                       seq_putc(seq, ' ');
-       }
-
-       return 0;
-}
-
-static int segment_bits_seq_show(struct seq_file *seq, void *offset)
-{
-       struct super_block *sb = seq->private;
-       struct f2fs_sb_info *sbi = F2FS_SB(sb);
-       unsigned int total_segs =
-                       le32_to_cpu(sbi->raw_super->segment_count_main);
-       int i, j;
-
-       seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n"
-               "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n");
-
-       for (i = 0; i < total_segs; i++) {
-               struct seg_entry *se = get_seg_entry(sbi, i);
-
-               seq_printf(seq, "%-10d", i);
-               seq_printf(seq, "%d|%-3u|", se->type,
-                                       get_valid_blocks(sbi, i, 1));
-               for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++)
-                       seq_printf(seq, " %.2x", se->cur_valid_map[j]);
-               seq_putc(seq, '\n');
-       }
-       return 0;
-}
-
-#define F2FS_PROC_FILE_DEF(_name)                                      \
-static int _name##_open_fs(struct inode *inode, struct file *file)     \
-{                                                                      \
-       return single_open(file, _name##_seq_show, PDE_DATA(inode));    \
-}                                                                      \
-                                                                       \
-static const struct file_operations f2fs_seq_##_name##_fops = {                \
-       .open = _name##_open_fs,                                        \
-       .read = seq_read,                                               \
-       .llseek = seq_lseek,                                            \
-       .release = single_release,                                      \
-};
-
-F2FS_PROC_FILE_DEF(segment_info);
-F2FS_PROC_FILE_DEF(segment_bits);
-
 static void default_options(struct f2fs_sb_info *sbi)
 {
        /* init some FS parameters */
@@ -1046,6 +789,7 @@ static void default_options(struct f2fs_sb_info *sbi)
        set_opt(sbi, INLINE_DATA);
        set_opt(sbi, INLINE_DENTRY);
        set_opt(sbi, EXTENT_CACHE);
+       set_opt(sbi, NOHEAP);
        sbi->sb->s_flags |= MS_LAZYTIME;
        set_opt(sbi, FLUSH_MERGE);
        if (f2fs_sb_mounted_blkzoned(sbi->sb)) {
@@ -1307,7 +1051,7 @@ static int __f2fs_commit_super(struct buffer_head *bh,
        unlock_buffer(bh);
 
        /* it's rare case, we can do fua all the time */
-       return __sync_dirty_buffer(bh, REQ_PREFLUSH | REQ_FUA);
+       return __sync_dirty_buffer(bh, REQ_SYNC | REQ_PREFLUSH | REQ_FUA);
 }
 
 static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
@@ -1483,6 +1227,13 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
                return 1;
        }
 
+       if (le32_to_cpu(raw_super->segment_count) > F2FS_MAX_SEGMENT) {
+               f2fs_msg(sb, KERN_INFO,
+                       "Invalid segment count (%u)",
+                       le32_to_cpu(raw_super->segment_count));
+               return 1;
+       }
+
        /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
        if (sanity_check_area_boundary(sbi, bh))
                return 1;
@@ -1496,6 +1247,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
        unsigned int ovp_segments, reserved_segments;
+       unsigned int main_segs, blocks_per_seg;
+       int i;
 
        total = le32_to_cpu(raw_super->segment_count);
        fsmeta = le32_to_cpu(raw_super->segment_count_ckpt);
@@ -1517,6 +1270,20 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
                return 1;
        }
 
+       main_segs = le32_to_cpu(raw_super->segment_count_main);
+       blocks_per_seg = sbi->blocks_per_seg;
+
+       for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) {
+               if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs ||
+                       le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg)
+                       return 1;
+       }
+       for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) {
+               if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs ||
+                       le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg)
+                       return 1;
+       }
+
        if (unlikely(f2fs_cp_error(sbi))) {
                f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck");
                return 1;
@@ -1527,7 +1294,7 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
 static void init_sb_info(struct f2fs_sb_info *sbi)
 {
        struct f2fs_super_block *raw_super = sbi->raw_super;
-       int i;
+       int i, j;
 
        sbi->log_sectors_per_block =
                le32_to_cpu(raw_super->log_sectors_per_block);
@@ -1555,10 +1322,13 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
        for (i = 0; i < NR_COUNT_TYPE; i++)
                atomic_set(&sbi->nr_pages[i], 0);
 
+       atomic_set(&sbi->wb_sync_req, 0);
+
        INIT_LIST_HEAD(&sbi->s_list);
        mutex_init(&sbi->umount_mutex);
-       mutex_init(&sbi->wio_mutex[NODE]);
-       mutex_init(&sbi->wio_mutex[DATA]);
+       for (i = 0; i < NR_PAGE_TYPE - 1; i++)
+               for (j = HOT; j < NR_TEMP_TYPE; j++)
+                       mutex_init(&sbi->wio_mutex[i][j]);
        spin_lock_init(&sbi->cp_lock);
 }
 
@@ -1881,6 +1651,7 @@ try_onemore:
        if (f2fs_sb_mounted_blkzoned(sb)) {
                f2fs_msg(sb, KERN_ERR,
                         "Zoned block device support is not enabled\n");
+               err = -EOPNOTSUPP;
                goto free_sb_buf;
        }
 #endif
@@ -1917,18 +1688,30 @@ try_onemore:
        mutex_init(&sbi->gc_mutex);
        mutex_init(&sbi->cp_mutex);
        init_rwsem(&sbi->node_write);
+       init_rwsem(&sbi->node_change);
 
        /* disallow all the data/node/meta page writes */
        set_sbi_flag(sbi, SBI_POR_DOING);
        spin_lock_init(&sbi->stat_lock);
 
-       init_rwsem(&sbi->read_io.io_rwsem);
-       sbi->read_io.sbi = sbi;
-       sbi->read_io.bio = NULL;
        for (i = 0; i < NR_PAGE_TYPE; i++) {
-               init_rwsem(&sbi->write_io[i].io_rwsem);
-               sbi->write_io[i].sbi = sbi;
-               sbi->write_io[i].bio = NULL;
+               int n = (i == META) ? 1: NR_TEMP_TYPE;
+               int j;
+
+               sbi->write_io[i] = kmalloc(n * sizeof(struct f2fs_bio_info),
+                                                               GFP_KERNEL);
+               if (!sbi->write_io[i]) {
+                       err = -ENOMEM;
+                       goto free_options;
+               }
+
+               for (j = HOT; j < n; j++) {
+                       init_rwsem(&sbi->write_io[i][j].io_rwsem);
+                       sbi->write_io[i][j].sbi = sbi;
+                       sbi->write_io[i][j].bio = NULL;
+                       spin_lock_init(&sbi->write_io[i][j].io_lock);
+                       INIT_LIST_HEAD(&sbi->write_io[i][j].io_list);
+               }
        }
 
        init_rwsem(&sbi->cp_rwsem);
@@ -1942,8 +1725,10 @@ try_onemore:
        if (F2FS_IO_SIZE(sbi) > 1) {
                sbi->write_io_dummy =
                        mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0);
-               if (!sbi->write_io_dummy)
+               if (!sbi->write_io_dummy) {
+                       err = -ENOMEM;
                        goto free_options;
+               }
        }
 
        /* get an inode for meta space */
@@ -2022,6 +1807,10 @@ try_onemore:
 
        f2fs_join_shrinker(sbi);
 
+       err = f2fs_build_stats(sbi);
+       if (err)
+               goto free_nm;
+
        /* if there are nt orphan nodes free them */
        err = recover_orphan_inodes(sbi);
        if (err)
@@ -2046,27 +1835,10 @@ try_onemore:
                goto free_root_inode;
        }
 
-       err = f2fs_build_stats(sbi);
+       err = f2fs_init_sysfs(sbi);
        if (err)
                goto free_root_inode;
 
-       if (f2fs_proc_root)
-               sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root);
-
-       if (sbi->s_proc) {
-               proc_create_data("segment_info", S_IRUGO, sbi->s_proc,
-                                &f2fs_seq_segment_info_fops, sb);
-               proc_create_data("segment_bits", S_IRUGO, sbi->s_proc,
-                                &f2fs_seq_segment_bits_fops, sb);
-       }
-
-       sbi->s_kobj.kset = f2fs_kset;
-       init_completion(&sbi->s_kobj_unregister);
-       err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL,
-                                                       "%s", sb->s_id);
-       if (err)
-               goto free_proc;
-
        /* recover fsynced data */
        if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
                /*
@@ -2076,7 +1848,7 @@ try_onemore:
                if (bdev_read_only(sb->s_bdev) &&
                                !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
                        err = -EROFS;
-                       goto free_kobj;
+                       goto free_sysfs;
                }
 
                if (need_fsck)
@@ -2090,7 +1862,7 @@ try_onemore:
                        need_fsck = true;
                        f2fs_msg(sb, KERN_ERR,
                                "Cannot recover all fsync data errno=%d", err);
-                       goto free_kobj;
+                       goto free_sysfs;
                }
        } else {
                err = recover_fsync_data(sbi, true);
@@ -2099,7 +1871,7 @@ try_onemore:
                        err = -EINVAL;
                        f2fs_msg(sb, KERN_ERR,
                                "Need to recover fsync data");
-                       goto free_kobj;
+                       goto free_sysfs;
                }
        }
 skip_recovery:
@@ -2114,7 +1886,7 @@ skip_recovery:
                /* After POR, we can run background GC thread.*/
                err = start_gc_thread(sbi);
                if (err)
-                       goto free_kobj;
+                       goto free_sysfs;
        }
        kfree(options);
 
@@ -2132,18 +1904,9 @@ skip_recovery:
        f2fs_update_time(sbi, REQ_TIME);
        return 0;
 
-free_kobj:
+free_sysfs:
        f2fs_sync_inode_meta(sbi);
-       kobject_del(&sbi->s_kobj);
-       kobject_put(&sbi->s_kobj);
-       wait_for_completion(&sbi->s_kobj_unregister);
-free_proc:
-       if (sbi->s_proc) {
-               remove_proc_entry("segment_info", sbi->s_proc);
-               remove_proc_entry("segment_bits", sbi->s_proc);
-               remove_proc_entry(sb->s_id, f2fs_proc_root);
-       }
-       f2fs_destroy_stats(sbi);
+       f2fs_exit_sysfs(sbi);
 free_root_inode:
        dput(sb->s_root);
        sb->s_root = NULL;
@@ -2161,6 +1924,7 @@ free_node_inode:
        truncate_inode_pages_final(META_MAPPING(sbi));
        iput(sbi->node_inode);
        mutex_unlock(&sbi->umount_mutex);
+       f2fs_destroy_stats(sbi);
 free_nm:
        destroy_node_manager(sbi);
 free_sm:
@@ -2174,6 +1938,8 @@ free_meta_inode:
 free_io_dummy:
        mempool_destroy(sbi->write_io_dummy);
 free_options:
+       for (i = 0; i < NR_PAGE_TYPE; i++)
+               kfree(sbi->write_io[i]);
        destroy_percpu_info(sbi);
        kfree(options);
 free_sb_buf:
@@ -2255,30 +2021,26 @@ static int __init init_f2fs_fs(void)
        err = create_extent_cache();
        if (err)
                goto free_checkpoint_caches;
-       f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj);
-       if (!f2fs_kset) {
-               err = -ENOMEM;
+       err = f2fs_register_sysfs();
+       if (err)
                goto free_extent_cache;
-       }
        err = register_shrinker(&f2fs_shrinker_info);
        if (err)
-               goto free_kset;
-
+               goto free_sysfs;
        err = register_filesystem(&f2fs_fs_type);
        if (err)
                goto free_shrinker;
        err = f2fs_create_root_stats();
        if (err)
                goto free_filesystem;
-       f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
        return 0;
 
 free_filesystem:
        unregister_filesystem(&f2fs_fs_type);
 free_shrinker:
        unregister_shrinker(&f2fs_shrinker_info);
-free_kset:
-       kset_unregister(f2fs_kset);
+free_sysfs:
+       f2fs_unregister_sysfs();
 free_extent_cache:
        destroy_extent_cache();
 free_checkpoint_caches:
@@ -2295,11 +2057,10 @@ fail:
 
 static void __exit exit_f2fs_fs(void)
 {
-       remove_proc_entry("fs/f2fs", NULL);
        f2fs_destroy_root_stats();
        unregister_filesystem(&f2fs_fs_type);
        unregister_shrinker(&f2fs_shrinker_info);
-       kset_unregister(f2fs_kset);
+       f2fs_unregister_sysfs();
        destroy_extent_cache();
        destroy_checkpoint_caches();
        destroy_segment_manager_caches();