]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - fs/f2fs/super.c
Merge tag 'for-f2fs-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[thirdparty/kernel/stable.git] / fs / f2fs / super.c
index 0b89b0b7b9f75701e3a5f85680761181ce64f3df..32e4c025e97e556eafdefb887eab06d4217f1ddb 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/random.h>
 #include <linux/exportfs.h>
 #include <linux/blkdev.h>
+#include <linux/quotaops.h>
 #include <linux/f2fs_fs.h>
 #include <linux/sysfs.h>
 
@@ -35,9 +36,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
 
@@ -108,6 +107,8 @@ enum {
        Opt_fault_injection,
        Opt_lazytime,
        Opt_nolazytime,
+       Opt_usrquota,
+       Opt_grpquota,
        Opt_err,
 };
 
@@ -143,212 +144,11 @@ static match_table_t f2fs_tokens = {
        {Opt_fault_injection, "fault_injection=%u"},
        {Opt_lazytime, "lazytime"},
        {Opt_nolazytime, "nolazytime"},
+       {Opt_usrquota, "usrquota"},
+       {Opt_grpquota, "grpquota"},
        {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(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_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(min_hot_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;
@@ -585,6 +385,20 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_nolazytime:
                        sb->s_flags &= ~MS_LAZYTIME;
                        break;
+#ifdef CONFIG_QUOTA
+               case Opt_usrquota:
+                       set_opt(sbi, USRQUOTA);
+                       break;
+               case Opt_grpquota:
+                       set_opt(sbi, GRPQUOTA);
+                       break;
+#else
+               case Opt_usrquota:
+               case Opt_grpquota:
+                       f2fs_msg(sb, KERN_INFO,
+                                       "quota operations not supported");
+                       break;
+#endif
                default:
                        f2fs_msg(sb, KERN_ERR,
                                "Unrecognized mount option \"%s\" or missing value",
@@ -624,7 +438,12 @@ 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);
 
+#ifdef CONFIG_QUOTA
+       memset(&fi->i_dquot, 0, sizeof(fi->i_dquot));
+       fi->i_reserved_quota = 0;
+#endif
        /* Will be used by directory only */
        fi->i_dir_level = F2FS_SB(sb)->dir_level;
        return &fi->vfs_inode;
@@ -765,18 +584,13 @@ static void destroy_device_list(struct f2fs_sb_info *sbi)
        kfree(sbi->devs);
 }
 
+static void f2fs_quota_off_umount(struct super_block *sb);
 static void f2fs_put_super(struct super_block *sb)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
+       int i;
 
-       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);
-
-       stop_gc_thread(sbi);
+       f2fs_quota_off_umount(sb);
 
        /* prevent remaining shrinker jobs */
        mutex_lock(&sbi->umount_mutex);
@@ -797,7 +611,7 @@ static void f2fs_put_super(struct super_block *sb)
        /* be sure to wait for any on-going discard commands */
        f2fs_wait_discard_bios(sbi);
 
-       if (!sbi->discard_blks) {
+       if (f2fs_discard_en(sbi) && !sbi->discard_blks) {
                struct cp_control cpc = {
                        .reason = CP_UMOUNT | CP_TRIMMED,
                };
@@ -817,7 +631,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);
@@ -827,8 +641,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)
@@ -838,6 +652,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);
 }
 
@@ -888,6 +704,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
        u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
        block_t total_count, user_block_count, start_count, ovp_count;
+       u64 avail_node_count;
 
        total_count = le64_to_cpu(sbi->raw_super->block_count);
        user_block_count = sbi->user_block_count;
@@ -898,11 +715,19 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
        buf->f_blocks = total_count - start_count;
        buf->f_bfree = user_block_count - valid_user_blocks(sbi) + ovp_count;
-       buf->f_bavail = user_block_count - valid_user_blocks(sbi);
+       buf->f_bavail = user_block_count - valid_user_blocks(sbi) -
+                                               sbi->reserved_blocks;
 
-       buf->f_files = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
-       buf->f_ffree = min(buf->f_files - valid_node_count(sbi),
-                                                       buf->f_bavail);
+       avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
+
+       if (avail_node_count > user_block_count) {
+               buf->f_files = user_block_count;
+               buf->f_ffree = buf->f_bavail;
+       } else {
+               buf->f_files = avail_node_count;
+               buf->f_ffree = min(avail_node_count - valid_node_count(sbi),
+                                       buf->f_bavail);
+       }
 
        buf->f_namelen = F2FS_NAME_LEN;
        buf->f_fsid.val[0] = (u32)id;
@@ -980,79 +805,19 @@ 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
+#ifdef CONFIG_QUOTA
+       if (test_opt(sbi, USRQUOTA))
+               seq_puts(seq, ",usrquota");
+       if (test_opt(sbi, GRPQUOTA))
+               seq_puts(seq, ",grpquota");
 #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, false));
-               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, false));
-               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 */
@@ -1089,6 +854,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
        struct f2fs_mount_info org_mount_opt;
+       unsigned long old_sb_flags;
        int err, active_logs;
        bool need_restart_gc = false;
        bool need_stop_gc = false;
@@ -1102,6 +868,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
         * need to restore them.
         */
        org_mount_opt = sbi->mount_opt;
+       old_sb_flags = sb->s_flags;
        active_logs = sbi->active_logs;
 
        /* recover superblocks we couldn't write due to previous RO mount */
@@ -1113,7 +880,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
                        clear_sbi_flag(sbi, SBI_NEED_SB_WRITE);
        }
 
-       sbi->mount_opt.opt = 0;
        default_options(sbi);
 
        /* parse mount options */
@@ -1128,6 +894,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
        if (f2fs_readonly(sb) && (*flags & MS_RDONLY))
                goto skip;
 
+       if (!f2fs_readonly(sb) && (*flags & MS_RDONLY)) {
+               err = dquot_suspend(sb, -1);
+               if (err < 0)
+                       goto restore_opts;
+       } else {
+               /* dquot_resume needs RW */
+               sb->s_flags &= ~MS_RDONLY;
+               dquot_resume(sb, -1);
+       }
+
        /* disallow enable/disable extent_cache dynamically */
        if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) {
                err = -EINVAL;
@@ -1192,12 +968,237 @@ restore_gc:
 restore_opts:
        sbi->mount_opt = org_mount_opt;
        sbi->active_logs = active_logs;
+       sb->s_flags = old_sb_flags;
 #ifdef CONFIG_F2FS_FAULT_INJECTION
        sbi->fault_info = ffi;
 #endif
        return err;
 }
 
+#ifdef CONFIG_QUOTA
+/* Read data from quotafile */
+static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data,
+                              size_t len, loff_t off)
+{
+       struct inode *inode = sb_dqopt(sb)->files[type];
+       struct address_space *mapping = inode->i_mapping;
+       block_t blkidx = F2FS_BYTES_TO_BLK(off);
+       int offset = off & (sb->s_blocksize - 1);
+       int tocopy;
+       size_t toread;
+       loff_t i_size = i_size_read(inode);
+       struct page *page;
+       char *kaddr;
+
+       if (off > i_size)
+               return 0;
+
+       if (off + len > i_size)
+               len = i_size - off;
+       toread = len;
+       while (toread > 0) {
+               tocopy = min_t(unsigned long, sb->s_blocksize - offset, toread);
+repeat:
+               page = read_mapping_page(mapping, blkidx, NULL);
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+
+               lock_page(page);
+
+               if (unlikely(page->mapping != mapping)) {
+                       f2fs_put_page(page, 1);
+                       goto repeat;
+               }
+               if (unlikely(!PageUptodate(page))) {
+                       f2fs_put_page(page, 1);
+                       return -EIO;
+               }
+
+               kaddr = kmap_atomic(page);
+               memcpy(data, kaddr + offset, tocopy);
+               kunmap_atomic(kaddr);
+               f2fs_put_page(page, 1);
+
+               offset = 0;
+               toread -= tocopy;
+               data += tocopy;
+               blkidx++;
+       }
+       return len;
+}
+
+/* Write to quotafile */
+static ssize_t f2fs_quota_write(struct super_block *sb, int type,
+                               const char *data, size_t len, loff_t off)
+{
+       struct inode *inode = sb_dqopt(sb)->files[type];
+       struct address_space *mapping = inode->i_mapping;
+       const struct address_space_operations *a_ops = mapping->a_ops;
+       int offset = off & (sb->s_blocksize - 1);
+       size_t towrite = len;
+       struct page *page;
+       char *kaddr;
+       int err = 0;
+       int tocopy;
+
+       while (towrite > 0) {
+               tocopy = min_t(unsigned long, sb->s_blocksize - offset,
+                                                               towrite);
+
+               err = a_ops->write_begin(NULL, mapping, off, tocopy, 0,
+                                                       &page, NULL);
+               if (unlikely(err))
+                       break;
+
+               kaddr = kmap_atomic(page);
+               memcpy(kaddr + offset, data, tocopy);
+               kunmap_atomic(kaddr);
+               flush_dcache_page(page);
+
+               a_ops->write_end(NULL, mapping, off, tocopy, tocopy,
+                                               page, NULL);
+               offset = 0;
+               towrite -= tocopy;
+               off += tocopy;
+               data += tocopy;
+               cond_resched();
+       }
+
+       if (len == towrite)
+               return err;
+       inode->i_version++;
+       inode->i_mtime = inode->i_ctime = current_time(inode);
+       f2fs_mark_inode_dirty_sync(inode, false);
+       return len - towrite;
+}
+
+static struct dquot **f2fs_get_dquots(struct inode *inode)
+{
+       return F2FS_I(inode)->i_dquot;
+}
+
+static qsize_t *f2fs_get_reserved_space(struct inode *inode)
+{
+       return &F2FS_I(inode)->i_reserved_quota;
+}
+
+static int f2fs_quota_sync(struct super_block *sb, int type)
+{
+       struct quota_info *dqopt = sb_dqopt(sb);
+       int cnt;
+       int ret;
+
+       ret = dquot_writeback_dquots(sb, type);
+       if (ret)
+               return ret;
+
+       /*
+        * Now when everything is written we can discard the pagecache so
+        * that userspace sees the changes.
+        */
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (type != -1 && cnt != type)
+                       continue;
+               if (!sb_has_quota_active(sb, cnt))
+                       continue;
+
+               ret = filemap_write_and_wait(dqopt->files[cnt]->i_mapping);
+               if (ret)
+                       return ret;
+
+               inode_lock(dqopt->files[cnt]);
+               truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
+               inode_unlock(dqopt->files[cnt]);
+       }
+       return 0;
+}
+
+static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
+                                                       const struct path *path)
+{
+       struct inode *inode;
+       int err;
+
+       err = f2fs_quota_sync(sb, -1);
+       if (err)
+               return err;
+
+       err = dquot_quota_on(sb, type, format_id, path);
+       if (err)
+               return err;
+
+       inode = d_inode(path->dentry);
+
+       inode_lock(inode);
+       F2FS_I(inode)->i_flags |= FS_NOATIME_FL | FS_IMMUTABLE_FL;
+       inode_set_flags(inode, S_NOATIME | S_IMMUTABLE,
+                                       S_NOATIME | S_IMMUTABLE);
+       inode_unlock(inode);
+       f2fs_mark_inode_dirty_sync(inode, false);
+
+       return 0;
+}
+
+static int f2fs_quota_off(struct super_block *sb, int type)
+{
+       struct inode *inode = sb_dqopt(sb)->files[type];
+       int err;
+
+       if (!inode || !igrab(inode))
+               return dquot_quota_off(sb, type);
+
+       f2fs_quota_sync(sb, -1);
+
+       err = dquot_quota_off(sb, type);
+       if (err)
+               goto out_put;
+
+       inode_lock(inode);
+       F2FS_I(inode)->i_flags &= ~(FS_NOATIME_FL | FS_IMMUTABLE_FL);
+       inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE);
+       inode_unlock(inode);
+       f2fs_mark_inode_dirty_sync(inode, false);
+out_put:
+       iput(inode);
+       return err;
+}
+
+static void f2fs_quota_off_umount(struct super_block *sb)
+{
+       int type;
+
+       for (type = 0; type < MAXQUOTAS; type++)
+               f2fs_quota_off(sb, type);
+}
+
+static const struct dquot_operations f2fs_quota_operations = {
+       .get_reserved_space = f2fs_get_reserved_space,
+       .write_dquot    = dquot_commit,
+       .acquire_dquot  = dquot_acquire,
+       .release_dquot  = dquot_release,
+       .mark_dirty     = dquot_mark_dquot_dirty,
+       .write_info     = dquot_commit_info,
+       .alloc_dquot    = dquot_alloc,
+       .destroy_dquot  = dquot_destroy,
+       .get_next_id    = dquot_get_next_id,
+};
+
+static const struct quotactl_ops f2fs_quotactl_ops = {
+       .quota_on       = f2fs_quota_on,
+       .quota_off      = f2fs_quota_off,
+       .quota_sync     = f2fs_quota_sync,
+       .get_state      = dquot_get_state,
+       .set_info       = dquot_set_dqinfo,
+       .get_dqblk      = dquot_get_dqblk,
+       .set_dqblk      = dquot_set_dqblk,
+       .get_nextdqblk  = dquot_get_next_dqblk,
+};
+#else
+static inline void f2fs_quota_off_umount(struct super_block *sb)
+{
+}
+#endif
+
 static struct super_operations f2fs_sops = {
        .alloc_inode    = f2fs_alloc_inode,
        .drop_inode     = f2fs_drop_inode,
@@ -1205,6 +1206,11 @@ static struct super_operations f2fs_sops = {
        .write_inode    = f2fs_write_inode,
        .dirty_inode    = f2fs_dirty_inode,
        .show_options   = f2fs_show_options,
+#ifdef CONFIG_QUOTA
+       .quota_read     = f2fs_quota_read,
+       .quota_write    = f2fs_quota_write,
+       .get_dquots     = f2fs_get_dquots,
+#endif
        .evict_inode    = f2fs_evict_inode,
        .put_super      = f2fs_put_super,
        .sync_fs        = f2fs_sync_fs,
@@ -1521,6 +1527,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);
@@ -1542,6 +1550,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;
@@ -1552,7 +1574,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);
@@ -1584,8 +1606,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
 
        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);
 }
 
@@ -1908,6 +1931,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
@@ -1929,6 +1953,12 @@ try_onemore:
        sb->s_max_links = F2FS_LINK_MAX;
        get_random_bytes(&sbi->s_next_generation, sizeof(u32));
 
+#ifdef CONFIG_QUOTA
+       sb->dq_op = &f2fs_quota_operations;
+       sb->s_qcop = &f2fs_quotactl_ops;
+       sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
+#endif
+
        sb->s_op = &f2fs_sops;
        sb->s_cop = &f2fs_cryptops;
        sb->s_xattr = f2fs_xattr_handlers;
@@ -1950,13 +1980,24 @@ try_onemore:
        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);
@@ -1970,8 +2011,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 */
@@ -2003,6 +2046,7 @@ try_onemore:
        sbi->total_valid_block_count =
                                le64_to_cpu(sbi->ckpt->valid_block_count);
        sbi->last_valid_block_count = sbi->total_valid_block_count;
+       sbi->reserved_blocks = 0;
 
        for (i = 0; i < NR_INODE_TYPE; i++) {
                INIT_LIST_HEAD(&sbi->inode_list[i]);
@@ -2078,22 +2122,9 @@ try_onemore:
                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);
+       err = f2fs_init_sysfs(sbi);
        if (err)
-               goto free_proc;
+               goto free_root_inode;
 
        /* recover fsynced data */
        if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
@@ -2104,7 +2135,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)
@@ -2118,7 +2149,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);
@@ -2127,7 +2158,7 @@ try_onemore:
                        err = -EINVAL;
                        f2fs_msg(sb, KERN_ERR,
                                "Need to recover fsync data");
-                       goto free_kobj;
+                       goto free_sysfs;
                }
        }
 skip_recovery:
@@ -2142,7 +2173,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);
 
@@ -2160,17 +2191,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_exit_sysfs(sbi);
 free_root_inode:
        dput(sb->s_root);
        sb->s_root = NULL;
@@ -2202,6 +2225,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:
@@ -2228,8 +2253,11 @@ static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags,
 
 static void kill_f2fs_super(struct super_block *sb)
 {
-       if (sb->s_root)
+       if (sb->s_root) {
                set_sbi_flag(F2FS_SB(sb), SBI_IS_CLOSE);
+               stop_gc_thread(F2FS_SB(sb));
+               stop_discard_thread(F2FS_SB(sb));
+       }
        kill_block_super(sb);
 }
 
@@ -2283,30 +2311,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:
@@ -2323,11 +2347,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();