|
|
@@ -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();
|