linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 RESEND] f2fs: support journalled quota
@ 2017-08-08  2:54 Chao Yu
  2017-08-11  1:51 ` Jaegeuk Kim
  0 siblings, 1 reply; 5+ messages in thread
From: Chao Yu @ 2017-08-08  2:54 UTC (permalink / raw)
  To: jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, chao, Chao Yu

This patch supports to enable f2fs to accept quota information through
mount option:
- {usr,grp,prj}jquota=<quota file path>
- jqfmt=<quota type>

Then, in ->mount flow, we can recover quota file during log replaying,
by this, journelled quota can be supported.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 Documentation/filesystems/f2fs.txt |   9 +
 fs/f2fs/checkpoint.c               |  26 ++-
 fs/f2fs/f2fs.h                     |   9 +
 fs/f2fs/recovery.c                 |  72 +++++++-
 fs/f2fs/super.c                    | 327 ++++++++++++++++++++++++++++++++++---
 5 files changed, 413 insertions(+), 30 deletions(-)

diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index b8f495a8b67d..deafeff7795b 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -165,6 +165,15 @@ io_bits=%u             Set the bit size of write IO requests. It should be set
 usrquota               Enable plain user disk quota accounting.
 grpquota               Enable plain group disk quota accounting.
 prjquota               Enable plain project quota accounting.
+usrjquota=<file>       Appoint specified file and type during mount, so that quota
+grpjquota=<file>       information can be properly updated during recovery flow,
+prjjquota=<file>       <quota file>: must be in root directory;
+jqfmt=<quota type>     <quota type>: [vfsold,vfsv0,vfsv1].
+offusrjquota           Turn off user journelled quota.
+offgrpjquota           Turn off group journelled quota.
+offprjjquota           Turn off project journelled quota.
+quota                  Enable plain user disk quota accounting.
+noquota                Disable all plain disk quota option.
 
 ================================================================================
 DEBUGFS ENTRIES
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index da5b49183e09..04fe1df052b2 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -588,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
 int recover_orphan_inodes(struct f2fs_sb_info *sbi)
 {
 	block_t start_blk, orphan_blocks, i, j;
-	int err;
+	unsigned int s_flags = sbi->sb->s_flags;
+	int err = 0;
 
 	if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
 		return 0;
 
+	if (s_flags & MS_RDONLY) {
+		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
+		sbi->sb->s_flags &= ~MS_RDONLY;
+	}
+
+#ifdef CONFIG_QUOTA
+	/* Needed for iput() to work correctly and not trash data */
+	sbi->sb->s_flags |= MS_ACTIVE;
+	/* Turn on quotas so that they are updated correctly */
+	f2fs_enable_quota_files(sbi);
+#endif
+
 	start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
 	orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
 
@@ -608,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
 			err = recover_orphan_inode(sbi, ino);
 			if (err) {
 				f2fs_put_page(page, 1);
-				return err;
+				goto out;
 			}
 		}
 		f2fs_put_page(page, 1);
 	}
 	/* clear Orphan Flag */
 	clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG);
-	return 0;
+out:
+#ifdef CONFIG_QUOTA
+	/* Turn quotas off */
+	f2fs_quota_off_umount(sbi->sb);
+#endif
+	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
+
+	return err;
 }
 
 static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1bcaa93bfed7..cea329f75068 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX];
 #define F2FS_MOUNT_USRQUOTA		0x00080000
 #define F2FS_MOUNT_GRPQUOTA		0x00100000
 #define F2FS_MOUNT_PRJQUOTA		0x00200000
+#define F2FS_MOUNT_QUOTA		0x00400000
 
 #define clear_opt(sbi, option)	((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
 #define set_opt(sbi, option)	((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -1121,6 +1122,12 @@ struct f2fs_sb_info {
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	struct f2fs_fault_info fault_info;
 #endif
+
+#ifdef CONFIG_QUOTA
+	/* Names of quota files with journalled quota */
+	char *s_qf_names[MAXQUOTAS];
+	int s_jquota_fmt;			/* Format of quota to use */
+#endif
 };
 
 #ifdef CONFIG_F2FS_FAULT_INJECTION
@@ -2433,6 +2440,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
  */
 int f2fs_inode_dirtied(struct inode *inode, bool sync);
 void f2fs_inode_synced(struct inode *inode);
+void f2fs_enable_quota_files(struct f2fs_sb_info *sbi);
+void f2fs_quota_off_umount(struct super_block *sb);
 int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
 int f2fs_sync_fs(struct super_block *sb, int sync);
 extern __printf(3, 4)
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 2d9b8182691f..a3d02613934a 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
 }
 
 static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
-					struct list_head *head, nid_t ino)
+			struct list_head *head, nid_t ino, bool quota_inode)
 {
 	struct inode *inode;
 	struct fsync_inode_entry *entry;
+	int err;
 
 	inode = f2fs_iget_retry(sbi->sb, ino);
 	if (IS_ERR(inode))
 		return ERR_CAST(inode);
 
+	err = dquot_initialize(inode);
+	if (err)
+		goto err_out;
+
+	if (quota_inode) {
+		err = dquot_alloc_inode(inode);
+		if (err)
+			goto err_out;
+	}
+
 	entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
 	entry->inode = inode;
 	list_add_tail(&entry->list, head);
 
 	return entry;
+err_out:
+	iput(inode);
+	return ERR_PTR(err);
 }
 
 static void del_fsync_inode(struct fsync_inode_entry *entry)
@@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
 
 	entry = get_fsync_inode(dir_list, pino);
 	if (!entry) {
-		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
+		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
+							pino, false);
 		if (IS_ERR(entry)) {
 			dir = ERR_CAST(entry);
 			err = PTR_ERR(entry);
@@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
 				err = -EEXIST;
 			goto out_unmap_put;
 		}
+
+		err = dquot_initialize(einode);
+		if (err) {
+			iput(einode);
+			goto out_unmap_put;
+		}
+
 		err = acquire_orphan_inode(F2FS_I_SB(inode));
 		if (err) {
 			iput(einode);
@@ -226,18 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
 
 		entry = get_fsync_inode(head, ino_of_node(page));
 		if (!entry) {
+			bool quota_inode = false;
+
 			if (!check_only &&
 					IS_INODE(page) && is_dent_dnode(page)) {
 				err = recover_inode_page(sbi, page);
 				if (err)
 					break;
+				quota_inode = true;
 			}
 
 			/*
 			 * CP | dnode(F) | inode(DF)
 			 * For this case, we should not give up now.
 			 */
-			entry = add_fsync_inode(sbi, head, ino_of_node(page));
+			entry = add_fsync_inode(sbi, head, ino_of_node(page),
+								quota_inode);
 			if (IS_ERR(entry)) {
 				err = PTR_ERR(entry);
 				if (err == -ENOENT) {
@@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
 	f2fs_put_page(node_page, 1);
 
 	if (ino != dn->inode->i_ino) {
+		int ret;
+
 		/* Deallocate previous index in the node page */
 		inode = f2fs_iget_retry(sbi->sb, ino);
 		if (IS_ERR(inode))
 			return PTR_ERR(inode);
+
+		ret = dquot_initialize(inode);
+		if (ret) {
+			iput(inode);
+			return ret;
+		}
 	} else {
 		inode = dn->inode;
 	}
@@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
 	struct list_head dir_list;
 	int err;
 	int ret = 0;
+	unsigned long s_flags = sbi->sb->s_flags;
 	bool need_writecp = false;
 
+	if (s_flags & MS_RDONLY) {
+		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
+		sbi->sb->s_flags &= ~MS_RDONLY;
+	}
+
+#ifdef CONFIG_QUOTA
+	/* Needed for iput() to work correctly and not trash data */
+	sbi->sb->s_flags |= MS_ACTIVE;
+	/* Turn on quotas so that they are updated correctly */
+	f2fs_enable_quota_files(sbi);
+#endif
+
 	fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
 			sizeof(struct fsync_inode_entry));
-	if (!fsync_entry_slab)
-		return -ENOMEM;
+	if (!fsync_entry_slab) {
+		err = -ENOMEM;
+		goto out;
+	}
 
 	INIT_LIST_HEAD(&inode_list);
 	INIT_LIST_HEAD(&dir_list);
@@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
 	/* step #1: find fsynced inode numbers */
 	err = find_fsync_dnodes(sbi, &inode_list, check_only);
 	if (err || list_empty(&inode_list))
-		goto out;
+		goto skip;
 
 	if (check_only) {
 		ret = 1;
-		goto out;
+		goto skip;
 	}
 
 	need_writecp = true;
@@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
 	err = recover_data(sbi, &inode_list, &dir_list);
 	if (!err)
 		f2fs_bug_on(sbi, !list_empty(&inode_list));
-out:
+skip:
 	destroy_fsync_dnodes(&inode_list);
 
 	/* truncate meta pages to be used by the recovery */
@@ -615,5 +664,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
 	}
 
 	kmem_cache_destroy(fsync_entry_slab);
+out:
+#ifdef CONFIG_QUOTA
+	/* Turn quotas off */
+	f2fs_quota_off_umount(sbi->sb);
+#endif
+	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
+
 	return ret ? ret: err;
 }
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 54b8ff4c6fc9..4c1bdcb94133 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -25,6 +25,7 @@
 #include <linux/quotaops.h>
 #include <linux/f2fs_fs.h>
 #include <linux/sysfs.h>
+#include <linux/quota.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -107,9 +108,20 @@ enum {
 	Opt_fault_injection,
 	Opt_lazytime,
 	Opt_nolazytime,
+	Opt_quota,
+	Opt_noquota,
 	Opt_usrquota,
 	Opt_grpquota,
 	Opt_prjquota,
+	Opt_usrjquota,
+	Opt_grpjquota,
+	Opt_prjjquota,
+	Opt_offusrjquota,
+	Opt_offgrpjquota,
+	Opt_offprjjquota,
+	Opt_jqfmt_vfsold,
+	Opt_jqfmt_vfsv0,
+	Opt_jqfmt_vfsv1,
 	Opt_err,
 };
 
@@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = {
 	{Opt_fault_injection, "fault_injection=%u"},
 	{Opt_lazytime, "lazytime"},
 	{Opt_nolazytime, "nolazytime"},
+	{Opt_quota, "quota"},
+	{Opt_noquota, "noquota"},
 	{Opt_usrquota, "usrquota"},
 	{Opt_grpquota, "grpquota"},
 	{Opt_prjquota, "prjquota"},
+	{Opt_usrjquota, "usrjquota=%s"},
+	{Opt_grpjquota, "grpjquota=%s"},
+	{Opt_prjjquota, "prjjquota=%s"},
+	{Opt_offusrjquota, "usrjquota="},
+	{Opt_offgrpjquota, "grpjquota="},
+	{Opt_offprjjquota, "prjjquota="},
+	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
+	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
+	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
 	{Opt_err, NULL},
 };
 
@@ -170,6 +193,105 @@ static void init_once(void *foo)
 	inode_init_once(&fi->vfs_inode);
 }
 
+#ifdef CONFIG_QUOTA
+static const char * const quotatypes[] = INITQFNAMES;
+#define QTYPE2NAME(t) (quotatypes[t])
+static int f2fs_set_qf_name(struct super_block *sb, int qtype,
+							substring_t *args)
+{
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	char *qname;
+	int ret = -1;
+
+	if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) {
+		f2fs_msg(sb, KERN_ERR,
+			"Cannot change journaled "
+			"quota options when quota turned on");
+		return -1;
+	}
+	qname = match_strdup(args);
+	if (!qname) {
+		f2fs_msg(sb, KERN_ERR,
+			"Not enough memory for storing quotafile name");
+		return -1;
+	}
+	if (sbi->s_qf_names[qtype]) {
+		if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
+			ret = 1;
+		else
+			f2fs_msg(sb, KERN_ERR,
+				 "%s quota file already specified",
+				 QTYPE2NAME(qtype));
+		goto errout;
+	}
+	if (strchr(qname, '/')) {
+		f2fs_msg(sb, KERN_ERR,
+			"quotafile must be on filesystem root");
+		goto errout;
+	}
+	sbi->s_qf_names[qtype] = qname;
+	set_opt(sbi, QUOTA);
+	return 0;
+errout:
+	kfree(qname);
+	return ret;
+}
+
+static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
+{
+
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
+	if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) {
+		f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options"
+			" when quota turned on");
+		return -1;
+	}
+	kfree(sbi->s_qf_names[qtype]);
+	sbi->s_qf_names[qtype] = NULL;
+	return 1;
+}
+
+static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
+{
+	/*
+	 * We do the test below only for project quotas. 'usrquota' and
+	 * 'grpquota' mount options are allowed even without quota feature
+	 * to support legacy quotas in quota files.
+	 */
+	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi->sb)) {
+		f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. "
+			 "Cannot enable project quota enforcement.");
+		return -1;
+	}
+	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
+			sbi->s_qf_names[PRJQUOTA]) {
+		if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
+			clear_opt(sbi, USRQUOTA);
+
+		if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
+			clear_opt(sbi, GRPQUOTA);
+
+		if (test_opt(sbi, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
+			clear_opt(sbi, PRJQUOTA);
+
+		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
+				test_opt(sbi, PRJQUOTA)) {
+			f2fs_msg(sbi->sb, KERN_ERR, "old and new quota "
+					"format mixing");
+			return -1;
+		}
+
+		if (!sbi->s_jquota_fmt) {
+			f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format "
+					"not specified");
+			return -1;
+		}
+	}
+	return 0;
+}
+#endif
+
 static int parse_options(struct super_block *sb, char *options)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
@@ -177,6 +299,9 @@ static int parse_options(struct super_block *sb, char *options)
 	substring_t args[MAX_OPT_ARGS];
 	char *p, *name;
 	int arg = 0;
+#ifdef CONFIG_QUOTA
+	int ret;
+#endif
 
 	if (!options)
 		return 0;
@@ -388,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options)
 			sb->s_flags &= ~MS_LAZYTIME;
 			break;
 #ifdef CONFIG_QUOTA
+		case Opt_quota:
 		case Opt_usrquota:
 			set_opt(sbi, USRQUOTA);
 			break;
@@ -397,10 +523,66 @@ static int parse_options(struct super_block *sb, char *options)
 		case Opt_prjquota:
 			set_opt(sbi, PRJQUOTA);
 			break;
+		case Opt_usrjquota:
+			ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
+			if (ret)
+				return ret;
+			break;
+		case Opt_grpjquota:
+			ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
+			if (ret)
+				return ret;
+			break;
+		case Opt_prjjquota:
+			ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
+			if (ret)
+				return ret;
+			break;
+		case Opt_offusrjquota:
+			ret = f2fs_clear_qf_name(sb, USRQUOTA);
+			if (ret)
+				return ret;
+			break;
+		case Opt_offgrpjquota:
+			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
+			if (ret)
+				return ret;
+			break;
+		case Opt_offprjjquota:
+			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
+			if (ret)
+				return ret;
+			break;
+		case Opt_jqfmt_vfsold:
+			sbi->s_jquota_fmt = QFMT_VFS_OLD;
+			break;
+		case Opt_jqfmt_vfsv0:
+			sbi->s_jquota_fmt = QFMT_VFS_V0;
+			break;
+		case Opt_jqfmt_vfsv1:
+			sbi->s_jquota_fmt = QFMT_VFS_V1;
+			break;
+		case Opt_noquota:
+			clear_opt(sbi, QUOTA);
+			clear_opt(sbi, USRQUOTA);
+			clear_opt(sbi, GRPQUOTA);
+			clear_opt(sbi, PRJQUOTA);
+			break;
 #else
+		case Opt_quota:
 		case Opt_usrquota:
 		case Opt_grpquota:
 		case Opt_prjquota:
+		case Opt_usrjquota:
+		case Opt_grpjquota:
+		case Opt_prjjquota:
+		case Opt_offusrjquota:
+		case Opt_offgrpjquota:
+		case Opt_offprjjquota:
+		case Opt_jqfmt_vfsold:
+		case Opt_jqfmt_vfsv0:
+		case Opt_jqfmt_vfsv1:
+		case Opt_noquota:
 			f2fs_msg(sb, KERN_INFO,
 					"quota operations not supported");
 			break;
@@ -412,6 +594,10 @@ static int parse_options(struct super_block *sb, char *options)
 			return -EINVAL;
 		}
 	}
+#ifdef CONFIG_QUOTA
+	if (f2fs_check_quota_options(sbi))
+		return -EINVAL;
+#endif
 
 	if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
 		f2fs_msg(sb, KERN_ERR,
@@ -591,7 +777,6 @@ 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);
@@ -658,6 +843,10 @@ static void f2fs_put_super(struct super_block *sb)
 
 	destroy_device_list(sbi);
 	mempool_destroy(sbi->write_io_dummy);
+#ifdef CONFIG_QUOTA
+	for (i = 0; i < MAXQUOTAS; i++)
+		kfree(sbi->s_qf_names[i]);
+#endif
 	destroy_percpu_info(sbi);
 	for (i = 0; i < NR_PAGE_TYPE; i++)
 		kfree(sbi->write_io[i]);
@@ -671,6 +860,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
 
 	trace_f2fs_sync_fs(sb, sync);
 
+	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
+		return -EAGAIN;
+
 	if (sync) {
 		struct cp_control cpc;
 
@@ -791,6 +983,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
+static inline void f2fs_show_quota_options(struct seq_file *seq,
+					   struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
+	if (sbi->s_jquota_fmt) {
+		char *fmtname = "";
+
+		switch (sbi->s_jquota_fmt) {
+		case QFMT_VFS_OLD:
+			fmtname = "vfsold";
+			break;
+		case QFMT_VFS_V0:
+			fmtname = "vfsv0";
+			break;
+		case QFMT_VFS_V1:
+			fmtname = "vfsv1";
+			break;
+		}
+		seq_printf(seq, ",jqfmt=%s", fmtname);
+	}
+
+	if (sbi->s_qf_names[USRQUOTA])
+		seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]);
+
+	if (sbi->s_qf_names[GRPQUOTA])
+		seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]);
+
+	if (sbi->s_qf_names[PRJQUOTA])
+		seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]);
+#endif
+}
+
 static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
@@ -864,6 +1090,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
 				sbi->fault_info.inject_rate);
 #endif
 #ifdef CONFIG_QUOTA
+	if (test_opt(sbi, QUOTA))
+		seq_puts(seq, ",quota");
 	if (test_opt(sbi, USRQUOTA))
 		seq_puts(seq, ",usrquota");
 	if (test_opt(sbi, GRPQUOTA))
@@ -871,6 +1099,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
 	if (test_opt(sbi, PRJQUOTA))
 		seq_puts(seq, ",prjquota");
 #endif
+	f2fs_show_quota_options(seq, sbi->sb);
 
 	return 0;
 }
@@ -919,6 +1148,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	struct f2fs_fault_info ffi = sbi->fault_info;
 #endif
+#ifdef CONFIG_QUOTA
+	int s_jquota_fmt;
+	char *s_qf_names[MAXQUOTAS];
+	int i, j;
+#endif
 
 	/*
 	 * Save the old mount options in case we
@@ -928,6 +1162,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	old_sb_flags = sb->s_flags;
 	active_logs = sbi->active_logs;
 
+#ifdef CONFIG_QUOTA
+	s_jquota_fmt = sbi->s_jquota_fmt;
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if (sbi->s_qf_names[i]) {
+			s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
+							 GFP_KERNEL);
+			if (!s_qf_names[i]) {
+				for (j = 0; j < i; j++)
+					kfree(s_qf_names[j]);
+				return -ENOMEM;
+			}
+		} else {
+			s_qf_names[i] = NULL;
+		}
+	}
+#endif
+
 	/* recover superblocks we couldn't write due to previous RO mount */
 	if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
 		err = f2fs_commit_super(sbi, false);
@@ -1009,6 +1260,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 			goto restore_gc;
 	}
 skip:
+#ifdef CONFIG_QUOTA
+	/* Release old quota file names */
+	for (i = 0; i < MAXQUOTAS; i++)
+		kfree(s_qf_names[i]);
+#endif
 	/* Update the POSIXACL Flag */
 	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
 		(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
@@ -1023,6 +1279,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 		stop_gc_thread(sbi);
 	}
 restore_opts:
+#ifdef CONFIG_QUOTA
+	sbi->s_jquota_fmt = s_jquota_fmt;
+	for (i = 0; i < MAXQUOTAS; i++) {
+		kfree(sbi->s_qf_names[i]);
+		sbi->s_qf_names[i] = s_qf_names[i];
+	}
+#endif
 	sbi->mount_opt = org_mount_opt;
 	sbi->active_logs = active_logs;
 	sb->s_flags = old_sb_flags;
@@ -1139,6 +1402,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
 	return &F2FS_I(inode)->i_reserved_quota;
 }
 
+static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
+{
+	return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type],
+						sbi->s_jquota_fmt, type);
+}
+
+void f2fs_enable_quota_files(struct f2fs_sb_info *sbi)
+{
+	int i, ret;
+
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if (sbi->s_qf_names[i]) {
+			ret = f2fs_quota_on_mount(sbi, i);
+			if (ret < 0)
+				f2fs_msg(sbi->sb, KERN_ERR,
+					"Cannot turn on journaled "
+					"quota: error %d", ret);
+		}
+	}
+}
+
 static int f2fs_quota_sync(struct super_block *sb, int type)
 {
 	struct quota_info *dqopt = sb_dqopt(sb);
@@ -1220,7 +1504,7 @@ static int f2fs_quota_off(struct super_block *sb, int type)
 	return err;
 }
 
-static void f2fs_quota_off_umount(struct super_block *sb)
+void f2fs_quota_off_umount(struct super_block *sb)
 {
 	int type;
 
@@ -1258,7 +1542,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = {
 	.get_nextdqblk	= dquot_get_next_dqblk,
 };
 #else
-static inline void f2fs_quota_off_umount(struct super_block *sb)
+void f2fs_quota_off_umount(struct super_block *sb)
 {
 }
 #endif
@@ -2178,11 +2462,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	if (err)
 		goto free_nm;
 
-	/* if there are nt orphan nodes free them */
-	err = recover_orphan_inodes(sbi);
-	if (err)
-		goto free_node_inode;
-
 	/* read root inode and dentry */
 	root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
 	if (IS_ERR(root)) {
@@ -2206,6 +2485,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	if (err)
 		goto free_root_inode;
 
+	/* if there are nt orphan nodes free them */
+	err = recover_orphan_inodes(sbi);
+	if (err)
+		goto free_sysfs;
+
 	/* recover fsynced data */
 	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
 		/*
@@ -2215,7 +2499,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		if (bdev_read_only(sb->s_bdev) &&
 				!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
 			err = -EROFS;
-			goto free_sysfs;
+			goto free_meta;
 		}
 
 		if (need_fsck)
@@ -2229,7 +2513,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 			need_fsck = true;
 			f2fs_msg(sb, KERN_ERR,
 				"Cannot recover all fsync data errno=%d", err);
-			goto free_sysfs;
+			goto free_meta;
 		}
 	} else {
 		err = recover_fsync_data(sbi, true);
@@ -2253,7 +2537,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		/* After POR, we can run background GC thread.*/
 		err = start_gc_thread(sbi);
 		if (err)
-			goto free_sysfs;
+			goto free_meta;
 	}
 	kfree(options);
 
@@ -2271,8 +2555,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	f2fs_update_time(sbi, REQ_TIME);
 	return 0;
 
-free_sysfs:
+free_meta:
 	f2fs_sync_inode_meta(sbi);
+	/*
+	 * Some dirty meta pages can be produced by recover_orphan_inodes()
+	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
+	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
+	 * falls into an infinite loop in sync_meta_pages().
+	 */
+	truncate_inode_pages_final(META_MAPPING(sbi));
+free_sysfs:
 	f2fs_unregister_sysfs(sbi);
 free_root_inode:
 	dput(sb->s_root);
@@ -2282,13 +2574,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	mutex_lock(&sbi->umount_mutex);
 	release_ino_entry(sbi, true);
 	f2fs_leave_shrinker(sbi);
-	/*
-	 * Some dirty meta pages can be produced by recover_orphan_inodes()
-	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
-	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
-	 * falls into an infinite loop in sync_meta_pages().
-	 */
-	truncate_inode_pages_final(META_MAPPING(sbi));
 	iput(sbi->node_inode);
 	mutex_unlock(&sbi->umount_mutex);
 	f2fs_destroy_stats(sbi);
@@ -2308,6 +2593,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	for (i = 0; i < NR_PAGE_TYPE; i++)
 		kfree(sbi->write_io[i]);
 	destroy_percpu_info(sbi);
+#ifdef CONFIG_QUOTA
+	for (i = 0; i < MAXQUOTAS; i++)
+		kfree(sbi->s_qf_names[i]);
+#endif
 	kfree(options);
 free_sb_buf:
 	kfree(raw_super);
-- 
2.13.1.388.g69e6b9b4f4a9

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 RESEND] f2fs: support journalled quota
  2017-08-08  2:54 [PATCH v3 RESEND] f2fs: support journalled quota Chao Yu
@ 2017-08-11  1:51 ` Jaegeuk Kim
  2017-08-11 10:14   ` Chao Yu
  0 siblings, 1 reply; 5+ messages in thread
From: Jaegeuk Kim @ 2017-08-11  1:51 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-f2fs-devel, linux-kernel, chao

Hi Chao,

Do we have to add .get_inode_usage ?

I hit a kernel panic on __dquot_transfer() -> check_idq() saying
"BUG: unable to handle kernel paging request at 0xfff..."

Thanks,

On 08/08, Chao Yu wrote:
> This patch supports to enable f2fs to accept quota information through
> mount option:
> - {usr,grp,prj}jquota=<quota file path>
> - jqfmt=<quota type>
> 
> Then, in ->mount flow, we can recover quota file during log replaying,
> by this, journelled quota can be supported.
> 
> Signed-off-by: Chao Yu <yuchao0@huawei.com>
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>  Documentation/filesystems/f2fs.txt |   9 +
>  fs/f2fs/checkpoint.c               |  26 ++-
>  fs/f2fs/f2fs.h                     |   9 +
>  fs/f2fs/recovery.c                 |  72 +++++++-
>  fs/f2fs/super.c                    | 327 ++++++++++++++++++++++++++++++++++---
>  5 files changed, 413 insertions(+), 30 deletions(-)
> 
> diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
> index b8f495a8b67d..deafeff7795b 100644
> --- a/Documentation/filesystems/f2fs.txt
> +++ b/Documentation/filesystems/f2fs.txt
> @@ -165,6 +165,15 @@ io_bits=%u             Set the bit size of write IO requests. It should be set
>  usrquota               Enable plain user disk quota accounting.
>  grpquota               Enable plain group disk quota accounting.
>  prjquota               Enable plain project quota accounting.
> +usrjquota=<file>       Appoint specified file and type during mount, so that quota
> +grpjquota=<file>       information can be properly updated during recovery flow,
> +prjjquota=<file>       <quota file>: must be in root directory;
> +jqfmt=<quota type>     <quota type>: [vfsold,vfsv0,vfsv1].
> +offusrjquota           Turn off user journelled quota.
> +offgrpjquota           Turn off group journelled quota.
> +offprjjquota           Turn off project journelled quota.
> +quota                  Enable plain user disk quota accounting.
> +noquota                Disable all plain disk quota option.
>  
>  ================================================================================
>  DEBUGFS ENTRIES
> diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
> index da5b49183e09..04fe1df052b2 100644
> --- a/fs/f2fs/checkpoint.c
> +++ b/fs/f2fs/checkpoint.c
> @@ -588,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
>  int recover_orphan_inodes(struct f2fs_sb_info *sbi)
>  {
>  	block_t start_blk, orphan_blocks, i, j;
> -	int err;
> +	unsigned int s_flags = sbi->sb->s_flags;
> +	int err = 0;
>  
>  	if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
>  		return 0;
>  
> +	if (s_flags & MS_RDONLY) {
> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
> +		sbi->sb->s_flags &= ~MS_RDONLY;
> +	}
> +
> +#ifdef CONFIG_QUOTA
> +	/* Needed for iput() to work correctly and not trash data */
> +	sbi->sb->s_flags |= MS_ACTIVE;
> +	/* Turn on quotas so that they are updated correctly */
> +	f2fs_enable_quota_files(sbi);
> +#endif
> +
>  	start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
>  	orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
>  
> @@ -608,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
>  			err = recover_orphan_inode(sbi, ino);
>  			if (err) {
>  				f2fs_put_page(page, 1);
> -				return err;
> +				goto out;
>  			}
>  		}
>  		f2fs_put_page(page, 1);
>  	}
>  	/* clear Orphan Flag */
>  	clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG);
> -	return 0;
> +out:
> +#ifdef CONFIG_QUOTA
> +	/* Turn quotas off */
> +	f2fs_quota_off_umount(sbi->sb);
> +#endif
> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
> +
> +	return err;
>  }
>  
>  static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 1bcaa93bfed7..cea329f75068 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX];
>  #define F2FS_MOUNT_USRQUOTA		0x00080000
>  #define F2FS_MOUNT_GRPQUOTA		0x00100000
>  #define F2FS_MOUNT_PRJQUOTA		0x00200000
> +#define F2FS_MOUNT_QUOTA		0x00400000
>  
>  #define clear_opt(sbi, option)	((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
>  #define set_opt(sbi, option)	((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
> @@ -1121,6 +1122,12 @@ struct f2fs_sb_info {
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	struct f2fs_fault_info fault_info;
>  #endif
> +
> +#ifdef CONFIG_QUOTA
> +	/* Names of quota files with journalled quota */
> +	char *s_qf_names[MAXQUOTAS];
> +	int s_jquota_fmt;			/* Format of quota to use */
> +#endif
>  };
>  
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> @@ -2433,6 +2440,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
>   */
>  int f2fs_inode_dirtied(struct inode *inode, bool sync);
>  void f2fs_inode_synced(struct inode *inode);
> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi);
> +void f2fs_quota_off_umount(struct super_block *sb);
>  int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
>  int f2fs_sync_fs(struct super_block *sb, int sync);
>  extern __printf(3, 4)
> diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
> index 2d9b8182691f..a3d02613934a 100644
> --- a/fs/f2fs/recovery.c
> +++ b/fs/f2fs/recovery.c
> @@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
>  }
>  
>  static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
> -					struct list_head *head, nid_t ino)
> +			struct list_head *head, nid_t ino, bool quota_inode)
>  {
>  	struct inode *inode;
>  	struct fsync_inode_entry *entry;
> +	int err;
>  
>  	inode = f2fs_iget_retry(sbi->sb, ino);
>  	if (IS_ERR(inode))
>  		return ERR_CAST(inode);
>  
> +	err = dquot_initialize(inode);
> +	if (err)
> +		goto err_out;
> +
> +	if (quota_inode) {
> +		err = dquot_alloc_inode(inode);
> +		if (err)
> +			goto err_out;
> +	}
> +
>  	entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
>  	entry->inode = inode;
>  	list_add_tail(&entry->list, head);
>  
>  	return entry;
> +err_out:
> +	iput(inode);
> +	return ERR_PTR(err);
>  }
>  
>  static void del_fsync_inode(struct fsync_inode_entry *entry)
> @@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
>  
>  	entry = get_fsync_inode(dir_list, pino);
>  	if (!entry) {
> -		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
> +		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
> +							pino, false);
>  		if (IS_ERR(entry)) {
>  			dir = ERR_CAST(entry);
>  			err = PTR_ERR(entry);
> @@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
>  				err = -EEXIST;
>  			goto out_unmap_put;
>  		}
> +
> +		err = dquot_initialize(einode);
> +		if (err) {
> +			iput(einode);
> +			goto out_unmap_put;
> +		}
> +
>  		err = acquire_orphan_inode(F2FS_I_SB(inode));
>  		if (err) {
>  			iput(einode);
> @@ -226,18 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
>  
>  		entry = get_fsync_inode(head, ino_of_node(page));
>  		if (!entry) {
> +			bool quota_inode = false;
> +
>  			if (!check_only &&
>  					IS_INODE(page) && is_dent_dnode(page)) {
>  				err = recover_inode_page(sbi, page);
>  				if (err)
>  					break;
> +				quota_inode = true;
>  			}
>  
>  			/*
>  			 * CP | dnode(F) | inode(DF)
>  			 * For this case, we should not give up now.
>  			 */
> -			entry = add_fsync_inode(sbi, head, ino_of_node(page));
> +			entry = add_fsync_inode(sbi, head, ino_of_node(page),
> +								quota_inode);
>  			if (IS_ERR(entry)) {
>  				err = PTR_ERR(entry);
>  				if (err == -ENOENT) {
> @@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
>  	f2fs_put_page(node_page, 1);
>  
>  	if (ino != dn->inode->i_ino) {
> +		int ret;
> +
>  		/* Deallocate previous index in the node page */
>  		inode = f2fs_iget_retry(sbi->sb, ino);
>  		if (IS_ERR(inode))
>  			return PTR_ERR(inode);
> +
> +		ret = dquot_initialize(inode);
> +		if (ret) {
> +			iput(inode);
> +			return ret;
> +		}
>  	} else {
>  		inode = dn->inode;
>  	}
> @@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>  	struct list_head dir_list;
>  	int err;
>  	int ret = 0;
> +	unsigned long s_flags = sbi->sb->s_flags;
>  	bool need_writecp = false;
>  
> +	if (s_flags & MS_RDONLY) {
> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
> +		sbi->sb->s_flags &= ~MS_RDONLY;
> +	}
> +
> +#ifdef CONFIG_QUOTA
> +	/* Needed for iput() to work correctly and not trash data */
> +	sbi->sb->s_flags |= MS_ACTIVE;
> +	/* Turn on quotas so that they are updated correctly */
> +	f2fs_enable_quota_files(sbi);
> +#endif
> +
>  	fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
>  			sizeof(struct fsync_inode_entry));
> -	if (!fsync_entry_slab)
> -		return -ENOMEM;
> +	if (!fsync_entry_slab) {
> +		err = -ENOMEM;
> +		goto out;
> +	}
>  
>  	INIT_LIST_HEAD(&inode_list);
>  	INIT_LIST_HEAD(&dir_list);
> @@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>  	/* step #1: find fsynced inode numbers */
>  	err = find_fsync_dnodes(sbi, &inode_list, check_only);
>  	if (err || list_empty(&inode_list))
> -		goto out;
> +		goto skip;
>  
>  	if (check_only) {
>  		ret = 1;
> -		goto out;
> +		goto skip;
>  	}
>  
>  	need_writecp = true;
> @@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>  	err = recover_data(sbi, &inode_list, &dir_list);
>  	if (!err)
>  		f2fs_bug_on(sbi, !list_empty(&inode_list));
> -out:
> +skip:
>  	destroy_fsync_dnodes(&inode_list);
>  
>  	/* truncate meta pages to be used by the recovery */
> @@ -615,5 +664,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>  	}
>  
>  	kmem_cache_destroy(fsync_entry_slab);
> +out:
> +#ifdef CONFIG_QUOTA
> +	/* Turn quotas off */
> +	f2fs_quota_off_umount(sbi->sb);
> +#endif
> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
> +
>  	return ret ? ret: err;
>  }
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 54b8ff4c6fc9..4c1bdcb94133 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -25,6 +25,7 @@
>  #include <linux/quotaops.h>
>  #include <linux/f2fs_fs.h>
>  #include <linux/sysfs.h>
> +#include <linux/quota.h>
>  
>  #include "f2fs.h"
>  #include "node.h"
> @@ -107,9 +108,20 @@ enum {
>  	Opt_fault_injection,
>  	Opt_lazytime,
>  	Opt_nolazytime,
> +	Opt_quota,
> +	Opt_noquota,
>  	Opt_usrquota,
>  	Opt_grpquota,
>  	Opt_prjquota,
> +	Opt_usrjquota,
> +	Opt_grpjquota,
> +	Opt_prjjquota,
> +	Opt_offusrjquota,
> +	Opt_offgrpjquota,
> +	Opt_offprjjquota,
> +	Opt_jqfmt_vfsold,
> +	Opt_jqfmt_vfsv0,
> +	Opt_jqfmt_vfsv1,
>  	Opt_err,
>  };
>  
> @@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = {
>  	{Opt_fault_injection, "fault_injection=%u"},
>  	{Opt_lazytime, "lazytime"},
>  	{Opt_nolazytime, "nolazytime"},
> +	{Opt_quota, "quota"},
> +	{Opt_noquota, "noquota"},
>  	{Opt_usrquota, "usrquota"},
>  	{Opt_grpquota, "grpquota"},
>  	{Opt_prjquota, "prjquota"},
> +	{Opt_usrjquota, "usrjquota=%s"},
> +	{Opt_grpjquota, "grpjquota=%s"},
> +	{Opt_prjjquota, "prjjquota=%s"},
> +	{Opt_offusrjquota, "usrjquota="},
> +	{Opt_offgrpjquota, "grpjquota="},
> +	{Opt_offprjjquota, "prjjquota="},
> +	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
> +	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
> +	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
>  	{Opt_err, NULL},
>  };
>  
> @@ -170,6 +193,105 @@ static void init_once(void *foo)
>  	inode_init_once(&fi->vfs_inode);
>  }
>  
> +#ifdef CONFIG_QUOTA
> +static const char * const quotatypes[] = INITQFNAMES;
> +#define QTYPE2NAME(t) (quotatypes[t])
> +static int f2fs_set_qf_name(struct super_block *sb, int qtype,
> +							substring_t *args)
> +{
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +	char *qname;
> +	int ret = -1;
> +
> +	if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) {
> +		f2fs_msg(sb, KERN_ERR,
> +			"Cannot change journaled "
> +			"quota options when quota turned on");
> +		return -1;
> +	}
> +	qname = match_strdup(args);
> +	if (!qname) {
> +		f2fs_msg(sb, KERN_ERR,
> +			"Not enough memory for storing quotafile name");
> +		return -1;
> +	}
> +	if (sbi->s_qf_names[qtype]) {
> +		if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
> +			ret = 1;
> +		else
> +			f2fs_msg(sb, KERN_ERR,
> +				 "%s quota file already specified",
> +				 QTYPE2NAME(qtype));
> +		goto errout;
> +	}
> +	if (strchr(qname, '/')) {
> +		f2fs_msg(sb, KERN_ERR,
> +			"quotafile must be on filesystem root");
> +		goto errout;
> +	}
> +	sbi->s_qf_names[qtype] = qname;
> +	set_opt(sbi, QUOTA);
> +	return 0;
> +errout:
> +	kfree(qname);
> +	return ret;
> +}
> +
> +static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
> +{
> +
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +
> +	if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) {
> +		f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options"
> +			" when quota turned on");
> +		return -1;
> +	}
> +	kfree(sbi->s_qf_names[qtype]);
> +	sbi->s_qf_names[qtype] = NULL;
> +	return 1;
> +}
> +
> +static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
> +{
> +	/*
> +	 * We do the test below only for project quotas. 'usrquota' and
> +	 * 'grpquota' mount options are allowed even without quota feature
> +	 * to support legacy quotas in quota files.
> +	 */
> +	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi->sb)) {
> +		f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. "
> +			 "Cannot enable project quota enforcement.");
> +		return -1;
> +	}
> +	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
> +			sbi->s_qf_names[PRJQUOTA]) {
> +		if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
> +			clear_opt(sbi, USRQUOTA);
> +
> +		if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
> +			clear_opt(sbi, GRPQUOTA);
> +
> +		if (test_opt(sbi, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
> +			clear_opt(sbi, PRJQUOTA);
> +
> +		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
> +				test_opt(sbi, PRJQUOTA)) {
> +			f2fs_msg(sbi->sb, KERN_ERR, "old and new quota "
> +					"format mixing");
> +			return -1;
> +		}
> +
> +		if (!sbi->s_jquota_fmt) {
> +			f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format "
> +					"not specified");
> +			return -1;
> +		}
> +	}
> +	return 0;
> +}
> +#endif
> +
>  static int parse_options(struct super_block *sb, char *options)
>  {
>  	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> @@ -177,6 +299,9 @@ static int parse_options(struct super_block *sb, char *options)
>  	substring_t args[MAX_OPT_ARGS];
>  	char *p, *name;
>  	int arg = 0;
> +#ifdef CONFIG_QUOTA
> +	int ret;
> +#endif
>  
>  	if (!options)
>  		return 0;
> @@ -388,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options)
>  			sb->s_flags &= ~MS_LAZYTIME;
>  			break;
>  #ifdef CONFIG_QUOTA
> +		case Opt_quota:
>  		case Opt_usrquota:
>  			set_opt(sbi, USRQUOTA);
>  			break;
> @@ -397,10 +523,66 @@ static int parse_options(struct super_block *sb, char *options)
>  		case Opt_prjquota:
>  			set_opt(sbi, PRJQUOTA);
>  			break;
> +		case Opt_usrjquota:
> +			ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_grpjquota:
> +			ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_prjjquota:
> +			ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_offusrjquota:
> +			ret = f2fs_clear_qf_name(sb, USRQUOTA);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_offgrpjquota:
> +			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_offprjjquota:
> +			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
> +			if (ret)
> +				return ret;
> +			break;
> +		case Opt_jqfmt_vfsold:
> +			sbi->s_jquota_fmt = QFMT_VFS_OLD;
> +			break;
> +		case Opt_jqfmt_vfsv0:
> +			sbi->s_jquota_fmt = QFMT_VFS_V0;
> +			break;
> +		case Opt_jqfmt_vfsv1:
> +			sbi->s_jquota_fmt = QFMT_VFS_V1;
> +			break;
> +		case Opt_noquota:
> +			clear_opt(sbi, QUOTA);
> +			clear_opt(sbi, USRQUOTA);
> +			clear_opt(sbi, GRPQUOTA);
> +			clear_opt(sbi, PRJQUOTA);
> +			break;
>  #else
> +		case Opt_quota:
>  		case Opt_usrquota:
>  		case Opt_grpquota:
>  		case Opt_prjquota:
> +		case Opt_usrjquota:
> +		case Opt_grpjquota:
> +		case Opt_prjjquota:
> +		case Opt_offusrjquota:
> +		case Opt_offgrpjquota:
> +		case Opt_offprjjquota:
> +		case Opt_jqfmt_vfsold:
> +		case Opt_jqfmt_vfsv0:
> +		case Opt_jqfmt_vfsv1:
> +		case Opt_noquota:
>  			f2fs_msg(sb, KERN_INFO,
>  					"quota operations not supported");
>  			break;
> @@ -412,6 +594,10 @@ static int parse_options(struct super_block *sb, char *options)
>  			return -EINVAL;
>  		}
>  	}
> +#ifdef CONFIG_QUOTA
> +	if (f2fs_check_quota_options(sbi))
> +		return -EINVAL;
> +#endif
>  
>  	if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
>  		f2fs_msg(sb, KERN_ERR,
> @@ -591,7 +777,6 @@ 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);
> @@ -658,6 +843,10 @@ static void f2fs_put_super(struct super_block *sb)
>  
>  	destroy_device_list(sbi);
>  	mempool_destroy(sbi->write_io_dummy);
> +#ifdef CONFIG_QUOTA
> +	for (i = 0; i < MAXQUOTAS; i++)
> +		kfree(sbi->s_qf_names[i]);
> +#endif
>  	destroy_percpu_info(sbi);
>  	for (i = 0; i < NR_PAGE_TYPE; i++)
>  		kfree(sbi->write_io[i]);
> @@ -671,6 +860,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
>  
>  	trace_f2fs_sync_fs(sb, sync);
>  
> +	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
> +		return -EAGAIN;
> +
>  	if (sync) {
>  		struct cp_control cpc;
>  
> @@ -791,6 +983,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
>  	return 0;
>  }
>  
> +static inline void f2fs_show_quota_options(struct seq_file *seq,
> +					   struct super_block *sb)
> +{
> +#ifdef CONFIG_QUOTA
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +
> +	if (sbi->s_jquota_fmt) {
> +		char *fmtname = "";
> +
> +		switch (sbi->s_jquota_fmt) {
> +		case QFMT_VFS_OLD:
> +			fmtname = "vfsold";
> +			break;
> +		case QFMT_VFS_V0:
> +			fmtname = "vfsv0";
> +			break;
> +		case QFMT_VFS_V1:
> +			fmtname = "vfsv1";
> +			break;
> +		}
> +		seq_printf(seq, ",jqfmt=%s", fmtname);
> +	}
> +
> +	if (sbi->s_qf_names[USRQUOTA])
> +		seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]);
> +
> +	if (sbi->s_qf_names[GRPQUOTA])
> +		seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]);
> +
> +	if (sbi->s_qf_names[PRJQUOTA])
> +		seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]);
> +#endif
> +}
> +
>  static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>  {
>  	struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
> @@ -864,6 +1090,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>  				sbi->fault_info.inject_rate);
>  #endif
>  #ifdef CONFIG_QUOTA
> +	if (test_opt(sbi, QUOTA))
> +		seq_puts(seq, ",quota");
>  	if (test_opt(sbi, USRQUOTA))
>  		seq_puts(seq, ",usrquota");
>  	if (test_opt(sbi, GRPQUOTA))
> @@ -871,6 +1099,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>  	if (test_opt(sbi, PRJQUOTA))
>  		seq_puts(seq, ",prjquota");
>  #endif
> +	f2fs_show_quota_options(seq, sbi->sb);
>  
>  	return 0;
>  }
> @@ -919,6 +1148,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	struct f2fs_fault_info ffi = sbi->fault_info;
>  #endif
> +#ifdef CONFIG_QUOTA
> +	int s_jquota_fmt;
> +	char *s_qf_names[MAXQUOTAS];
> +	int i, j;
> +#endif
>  
>  	/*
>  	 * Save the old mount options in case we
> @@ -928,6 +1162,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  	old_sb_flags = sb->s_flags;
>  	active_logs = sbi->active_logs;
>  
> +#ifdef CONFIG_QUOTA
> +	s_jquota_fmt = sbi->s_jquota_fmt;
> +	for (i = 0; i < MAXQUOTAS; i++) {
> +		if (sbi->s_qf_names[i]) {
> +			s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
> +							 GFP_KERNEL);
> +			if (!s_qf_names[i]) {
> +				for (j = 0; j < i; j++)
> +					kfree(s_qf_names[j]);
> +				return -ENOMEM;
> +			}
> +		} else {
> +			s_qf_names[i] = NULL;
> +		}
> +	}
> +#endif
> +
>  	/* recover superblocks we couldn't write due to previous RO mount */
>  	if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
>  		err = f2fs_commit_super(sbi, false);
> @@ -1009,6 +1260,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  			goto restore_gc;
>  	}
>  skip:
> +#ifdef CONFIG_QUOTA
> +	/* Release old quota file names */
> +	for (i = 0; i < MAXQUOTAS; i++)
> +		kfree(s_qf_names[i]);
> +#endif
>  	/* Update the POSIXACL Flag */
>  	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
>  		(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
> @@ -1023,6 +1279,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  		stop_gc_thread(sbi);
>  	}
>  restore_opts:
> +#ifdef CONFIG_QUOTA
> +	sbi->s_jquota_fmt = s_jquota_fmt;
> +	for (i = 0; i < MAXQUOTAS; i++) {
> +		kfree(sbi->s_qf_names[i]);
> +		sbi->s_qf_names[i] = s_qf_names[i];
> +	}
> +#endif
>  	sbi->mount_opt = org_mount_opt;
>  	sbi->active_logs = active_logs;
>  	sb->s_flags = old_sb_flags;
> @@ -1139,6 +1402,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
>  	return &F2FS_I(inode)->i_reserved_quota;
>  }
>  
> +static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
> +{
> +	return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type],
> +						sbi->s_jquota_fmt, type);
> +}
> +
> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi)
> +{
> +	int i, ret;
> +
> +	for (i = 0; i < MAXQUOTAS; i++) {
> +		if (sbi->s_qf_names[i]) {
> +			ret = f2fs_quota_on_mount(sbi, i);
> +			if (ret < 0)
> +				f2fs_msg(sbi->sb, KERN_ERR,
> +					"Cannot turn on journaled "
> +					"quota: error %d", ret);
> +		}
> +	}
> +}
> +
>  static int f2fs_quota_sync(struct super_block *sb, int type)
>  {
>  	struct quota_info *dqopt = sb_dqopt(sb);
> @@ -1220,7 +1504,7 @@ static int f2fs_quota_off(struct super_block *sb, int type)
>  	return err;
>  }
>  
> -static void f2fs_quota_off_umount(struct super_block *sb)
> +void f2fs_quota_off_umount(struct super_block *sb)
>  {
>  	int type;
>  
> @@ -1258,7 +1542,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = {
>  	.get_nextdqblk	= dquot_get_next_dqblk,
>  };
>  #else
> -static inline void f2fs_quota_off_umount(struct super_block *sb)
> +void f2fs_quota_off_umount(struct super_block *sb)
>  {
>  }
>  #endif
> @@ -2178,11 +2462,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	if (err)
>  		goto free_nm;
>  
> -	/* if there are nt orphan nodes free them */
> -	err = recover_orphan_inodes(sbi);
> -	if (err)
> -		goto free_node_inode;
> -
>  	/* read root inode and dentry */
>  	root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
>  	if (IS_ERR(root)) {
> @@ -2206,6 +2485,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	if (err)
>  		goto free_root_inode;
>  
> +	/* if there are nt orphan nodes free them */
> +	err = recover_orphan_inodes(sbi);
> +	if (err)
> +		goto free_sysfs;
> +
>  	/* recover fsynced data */
>  	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
>  		/*
> @@ -2215,7 +2499,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  		if (bdev_read_only(sb->s_bdev) &&
>  				!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
>  			err = -EROFS;
> -			goto free_sysfs;
> +			goto free_meta;
>  		}
>  
>  		if (need_fsck)
> @@ -2229,7 +2513,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  			need_fsck = true;
>  			f2fs_msg(sb, KERN_ERR,
>  				"Cannot recover all fsync data errno=%d", err);
> -			goto free_sysfs;
> +			goto free_meta;
>  		}
>  	} else {
>  		err = recover_fsync_data(sbi, true);
> @@ -2253,7 +2537,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  		/* After POR, we can run background GC thread.*/
>  		err = start_gc_thread(sbi);
>  		if (err)
> -			goto free_sysfs;
> +			goto free_meta;
>  	}
>  	kfree(options);
>  
> @@ -2271,8 +2555,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	f2fs_update_time(sbi, REQ_TIME);
>  	return 0;
>  
> -free_sysfs:
> +free_meta:
>  	f2fs_sync_inode_meta(sbi);
> +	/*
> +	 * Some dirty meta pages can be produced by recover_orphan_inodes()
> +	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
> +	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
> +	 * falls into an infinite loop in sync_meta_pages().
> +	 */
> +	truncate_inode_pages_final(META_MAPPING(sbi));
> +free_sysfs:
>  	f2fs_unregister_sysfs(sbi);
>  free_root_inode:
>  	dput(sb->s_root);
> @@ -2282,13 +2574,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	mutex_lock(&sbi->umount_mutex);
>  	release_ino_entry(sbi, true);
>  	f2fs_leave_shrinker(sbi);
> -	/*
> -	 * Some dirty meta pages can be produced by recover_orphan_inodes()
> -	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
> -	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
> -	 * falls into an infinite loop in sync_meta_pages().
> -	 */
> -	truncate_inode_pages_final(META_MAPPING(sbi));
>  	iput(sbi->node_inode);
>  	mutex_unlock(&sbi->umount_mutex);
>  	f2fs_destroy_stats(sbi);
> @@ -2308,6 +2593,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	for (i = 0; i < NR_PAGE_TYPE; i++)
>  		kfree(sbi->write_io[i]);
>  	destroy_percpu_info(sbi);
> +#ifdef CONFIG_QUOTA
> +	for (i = 0; i < MAXQUOTAS; i++)
> +		kfree(sbi->s_qf_names[i]);
> +#endif
>  	kfree(options);
>  free_sb_buf:
>  	kfree(raw_super);
> -- 
> 2.13.1.388.g69e6b9b4f4a9

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 RESEND] f2fs: support journalled quota
  2017-08-11  1:51 ` Jaegeuk Kim
@ 2017-08-11 10:14   ` Chao Yu
  2017-08-11 17:35     ` Jaegeuk Kim
  0 siblings, 1 reply; 5+ messages in thread
From: Chao Yu @ 2017-08-11 10:14 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-kernel, chao

Hi Jaegeuk,

On 2017/8/11 9:51, Jaegeuk Kim wrote:
> Hi Chao,
> 
> Do we have to add .get_inode_usage ?
> 
> I hit a kernel panic on __dquot_transfer() -> check_idq() saying
> "BUG: unable to handle kernel paging request at 0xfff..."

Could you provider whole kernel dump info, mount option, test method?

Thanks,

> 
> Thanks,
> 
> On 08/08, Chao Yu wrote:
>> This patch supports to enable f2fs to accept quota information through
>> mount option:
>> - {usr,grp,prj}jquota=<quota file path>
>> - jqfmt=<quota type>
>>
>> Then, in ->mount flow, we can recover quota file during log replaying,
>> by this, journelled quota can be supported.
>>
>> Signed-off-by: Chao Yu <yuchao0@huawei.com>
>> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
>> ---
>>  Documentation/filesystems/f2fs.txt |   9 +
>>  fs/f2fs/checkpoint.c               |  26 ++-
>>  fs/f2fs/f2fs.h                     |   9 +
>>  fs/f2fs/recovery.c                 |  72 +++++++-
>>  fs/f2fs/super.c                    | 327 ++++++++++++++++++++++++++++++++++---
>>  5 files changed, 413 insertions(+), 30 deletions(-)
>>
>> diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
>> index b8f495a8b67d..deafeff7795b 100644
>> --- a/Documentation/filesystems/f2fs.txt
>> +++ b/Documentation/filesystems/f2fs.txt
>> @@ -165,6 +165,15 @@ io_bits=%u             Set the bit size of write IO requests. It should be set
>>  usrquota               Enable plain user disk quota accounting.
>>  grpquota               Enable plain group disk quota accounting.
>>  prjquota               Enable plain project quota accounting.
>> +usrjquota=<file>       Appoint specified file and type during mount, so that quota
>> +grpjquota=<file>       information can be properly updated during recovery flow,
>> +prjjquota=<file>       <quota file>: must be in root directory;
>> +jqfmt=<quota type>     <quota type>: [vfsold,vfsv0,vfsv1].
>> +offusrjquota           Turn off user journelled quota.
>> +offgrpjquota           Turn off group journelled quota.
>> +offprjjquota           Turn off project journelled quota.
>> +quota                  Enable plain user disk quota accounting.
>> +noquota                Disable all plain disk quota option.
>>  
>>  ================================================================================
>>  DEBUGFS ENTRIES
>> diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
>> index da5b49183e09..04fe1df052b2 100644
>> --- a/fs/f2fs/checkpoint.c
>> +++ b/fs/f2fs/checkpoint.c
>> @@ -588,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
>>  int recover_orphan_inodes(struct f2fs_sb_info *sbi)
>>  {
>>  	block_t start_blk, orphan_blocks, i, j;
>> -	int err;
>> +	unsigned int s_flags = sbi->sb->s_flags;
>> +	int err = 0;
>>  
>>  	if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
>>  		return 0;
>>  
>> +	if (s_flags & MS_RDONLY) {
>> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
>> +		sbi->sb->s_flags &= ~MS_RDONLY;
>> +	}
>> +
>> +#ifdef CONFIG_QUOTA
>> +	/* Needed for iput() to work correctly and not trash data */
>> +	sbi->sb->s_flags |= MS_ACTIVE;
>> +	/* Turn on quotas so that they are updated correctly */
>> +	f2fs_enable_quota_files(sbi);
>> +#endif
>> +
>>  	start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
>>  	orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
>>  
>> @@ -608,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
>>  			err = recover_orphan_inode(sbi, ino);
>>  			if (err) {
>>  				f2fs_put_page(page, 1);
>> -				return err;
>> +				goto out;
>>  			}
>>  		}
>>  		f2fs_put_page(page, 1);
>>  	}
>>  	/* clear Orphan Flag */
>>  	clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG);
>> -	return 0;
>> +out:
>> +#ifdef CONFIG_QUOTA
>> +	/* Turn quotas off */
>> +	f2fs_quota_off_umount(sbi->sb);
>> +#endif
>> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
>> +
>> +	return err;
>>  }
>>  
>>  static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index 1bcaa93bfed7..cea329f75068 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX];
>>  #define F2FS_MOUNT_USRQUOTA		0x00080000
>>  #define F2FS_MOUNT_GRPQUOTA		0x00100000
>>  #define F2FS_MOUNT_PRJQUOTA		0x00200000
>> +#define F2FS_MOUNT_QUOTA		0x00400000
>>  
>>  #define clear_opt(sbi, option)	((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
>>  #define set_opt(sbi, option)	((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
>> @@ -1121,6 +1122,12 @@ struct f2fs_sb_info {
>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>  	struct f2fs_fault_info fault_info;
>>  #endif
>> +
>> +#ifdef CONFIG_QUOTA
>> +	/* Names of quota files with journalled quota */
>> +	char *s_qf_names[MAXQUOTAS];
>> +	int s_jquota_fmt;			/* Format of quota to use */
>> +#endif
>>  };
>>  
>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>> @@ -2433,6 +2440,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
>>   */
>>  int f2fs_inode_dirtied(struct inode *inode, bool sync);
>>  void f2fs_inode_synced(struct inode *inode);
>> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi);
>> +void f2fs_quota_off_umount(struct super_block *sb);
>>  int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
>>  int f2fs_sync_fs(struct super_block *sb, int sync);
>>  extern __printf(3, 4)
>> diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
>> index 2d9b8182691f..a3d02613934a 100644
>> --- a/fs/f2fs/recovery.c
>> +++ b/fs/f2fs/recovery.c
>> @@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
>>  }
>>  
>>  static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
>> -					struct list_head *head, nid_t ino)
>> +			struct list_head *head, nid_t ino, bool quota_inode)
>>  {
>>  	struct inode *inode;
>>  	struct fsync_inode_entry *entry;
>> +	int err;
>>  
>>  	inode = f2fs_iget_retry(sbi->sb, ino);
>>  	if (IS_ERR(inode))
>>  		return ERR_CAST(inode);
>>  
>> +	err = dquot_initialize(inode);
>> +	if (err)
>> +		goto err_out;
>> +
>> +	if (quota_inode) {
>> +		err = dquot_alloc_inode(inode);
>> +		if (err)
>> +			goto err_out;
>> +	}
>> +
>>  	entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
>>  	entry->inode = inode;
>>  	list_add_tail(&entry->list, head);
>>  
>>  	return entry;
>> +err_out:
>> +	iput(inode);
>> +	return ERR_PTR(err);
>>  }
>>  
>>  static void del_fsync_inode(struct fsync_inode_entry *entry)
>> @@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
>>  
>>  	entry = get_fsync_inode(dir_list, pino);
>>  	if (!entry) {
>> -		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
>> +		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
>> +							pino, false);
>>  		if (IS_ERR(entry)) {
>>  			dir = ERR_CAST(entry);
>>  			err = PTR_ERR(entry);
>> @@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
>>  				err = -EEXIST;
>>  			goto out_unmap_put;
>>  		}
>> +
>> +		err = dquot_initialize(einode);
>> +		if (err) {
>> +			iput(einode);
>> +			goto out_unmap_put;
>> +		}
>> +
>>  		err = acquire_orphan_inode(F2FS_I_SB(inode));
>>  		if (err) {
>>  			iput(einode);
>> @@ -226,18 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
>>  
>>  		entry = get_fsync_inode(head, ino_of_node(page));
>>  		if (!entry) {
>> +			bool quota_inode = false;
>> +
>>  			if (!check_only &&
>>  					IS_INODE(page) && is_dent_dnode(page)) {
>>  				err = recover_inode_page(sbi, page);
>>  				if (err)
>>  					break;
>> +				quota_inode = true;
>>  			}
>>  
>>  			/*
>>  			 * CP | dnode(F) | inode(DF)
>>  			 * For this case, we should not give up now.
>>  			 */
>> -			entry = add_fsync_inode(sbi, head, ino_of_node(page));
>> +			entry = add_fsync_inode(sbi, head, ino_of_node(page),
>> +								quota_inode);
>>  			if (IS_ERR(entry)) {
>>  				err = PTR_ERR(entry);
>>  				if (err == -ENOENT) {
>> @@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
>>  	f2fs_put_page(node_page, 1);
>>  
>>  	if (ino != dn->inode->i_ino) {
>> +		int ret;
>> +
>>  		/* Deallocate previous index in the node page */
>>  		inode = f2fs_iget_retry(sbi->sb, ino);
>>  		if (IS_ERR(inode))
>>  			return PTR_ERR(inode);
>> +
>> +		ret = dquot_initialize(inode);
>> +		if (ret) {
>> +			iput(inode);
>> +			return ret;
>> +		}
>>  	} else {
>>  		inode = dn->inode;
>>  	}
>> @@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>>  	struct list_head dir_list;
>>  	int err;
>>  	int ret = 0;
>> +	unsigned long s_flags = sbi->sb->s_flags;
>>  	bool need_writecp = false;
>>  
>> +	if (s_flags & MS_RDONLY) {
>> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
>> +		sbi->sb->s_flags &= ~MS_RDONLY;
>> +	}
>> +
>> +#ifdef CONFIG_QUOTA
>> +	/* Needed for iput() to work correctly and not trash data */
>> +	sbi->sb->s_flags |= MS_ACTIVE;
>> +	/* Turn on quotas so that they are updated correctly */
>> +	f2fs_enable_quota_files(sbi);
>> +#endif
>> +
>>  	fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
>>  			sizeof(struct fsync_inode_entry));
>> -	if (!fsync_entry_slab)
>> -		return -ENOMEM;
>> +	if (!fsync_entry_slab) {
>> +		err = -ENOMEM;
>> +		goto out;
>> +	}
>>  
>>  	INIT_LIST_HEAD(&inode_list);
>>  	INIT_LIST_HEAD(&dir_list);
>> @@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>>  	/* step #1: find fsynced inode numbers */
>>  	err = find_fsync_dnodes(sbi, &inode_list, check_only);
>>  	if (err || list_empty(&inode_list))
>> -		goto out;
>> +		goto skip;
>>  
>>  	if (check_only) {
>>  		ret = 1;
>> -		goto out;
>> +		goto skip;
>>  	}
>>  
>>  	need_writecp = true;
>> @@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>>  	err = recover_data(sbi, &inode_list, &dir_list);
>>  	if (!err)
>>  		f2fs_bug_on(sbi, !list_empty(&inode_list));
>> -out:
>> +skip:
>>  	destroy_fsync_dnodes(&inode_list);
>>  
>>  	/* truncate meta pages to be used by the recovery */
>> @@ -615,5 +664,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
>>  	}
>>  
>>  	kmem_cache_destroy(fsync_entry_slab);
>> +out:
>> +#ifdef CONFIG_QUOTA
>> +	/* Turn quotas off */
>> +	f2fs_quota_off_umount(sbi->sb);
>> +#endif
>> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
>> +
>>  	return ret ? ret: err;
>>  }
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index 54b8ff4c6fc9..4c1bdcb94133 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -25,6 +25,7 @@
>>  #include <linux/quotaops.h>
>>  #include <linux/f2fs_fs.h>
>>  #include <linux/sysfs.h>
>> +#include <linux/quota.h>
>>  
>>  #include "f2fs.h"
>>  #include "node.h"
>> @@ -107,9 +108,20 @@ enum {
>>  	Opt_fault_injection,
>>  	Opt_lazytime,
>>  	Opt_nolazytime,
>> +	Opt_quota,
>> +	Opt_noquota,
>>  	Opt_usrquota,
>>  	Opt_grpquota,
>>  	Opt_prjquota,
>> +	Opt_usrjquota,
>> +	Opt_grpjquota,
>> +	Opt_prjjquota,
>> +	Opt_offusrjquota,
>> +	Opt_offgrpjquota,
>> +	Opt_offprjjquota,
>> +	Opt_jqfmt_vfsold,
>> +	Opt_jqfmt_vfsv0,
>> +	Opt_jqfmt_vfsv1,
>>  	Opt_err,
>>  };
>>  
>> @@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = {
>>  	{Opt_fault_injection, "fault_injection=%u"},
>>  	{Opt_lazytime, "lazytime"},
>>  	{Opt_nolazytime, "nolazytime"},
>> +	{Opt_quota, "quota"},
>> +	{Opt_noquota, "noquota"},
>>  	{Opt_usrquota, "usrquota"},
>>  	{Opt_grpquota, "grpquota"},
>>  	{Opt_prjquota, "prjquota"},
>> +	{Opt_usrjquota, "usrjquota=%s"},
>> +	{Opt_grpjquota, "grpjquota=%s"},
>> +	{Opt_prjjquota, "prjjquota=%s"},
>> +	{Opt_offusrjquota, "usrjquota="},
>> +	{Opt_offgrpjquota, "grpjquota="},
>> +	{Opt_offprjjquota, "prjjquota="},
>> +	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
>> +	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
>> +	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
>>  	{Opt_err, NULL},
>>  };
>>  
>> @@ -170,6 +193,105 @@ static void init_once(void *foo)
>>  	inode_init_once(&fi->vfs_inode);
>>  }
>>  
>> +#ifdef CONFIG_QUOTA
>> +static const char * const quotatypes[] = INITQFNAMES;
>> +#define QTYPE2NAME(t) (quotatypes[t])
>> +static int f2fs_set_qf_name(struct super_block *sb, int qtype,
>> +							substring_t *args)
>> +{
>> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
>> +	char *qname;
>> +	int ret = -1;
>> +
>> +	if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) {
>> +		f2fs_msg(sb, KERN_ERR,
>> +			"Cannot change journaled "
>> +			"quota options when quota turned on");
>> +		return -1;
>> +	}
>> +	qname = match_strdup(args);
>> +	if (!qname) {
>> +		f2fs_msg(sb, KERN_ERR,
>> +			"Not enough memory for storing quotafile name");
>> +		return -1;
>> +	}
>> +	if (sbi->s_qf_names[qtype]) {
>> +		if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
>> +			ret = 1;
>> +		else
>> +			f2fs_msg(sb, KERN_ERR,
>> +				 "%s quota file already specified",
>> +				 QTYPE2NAME(qtype));
>> +		goto errout;
>> +	}
>> +	if (strchr(qname, '/')) {
>> +		f2fs_msg(sb, KERN_ERR,
>> +			"quotafile must be on filesystem root");
>> +		goto errout;
>> +	}
>> +	sbi->s_qf_names[qtype] = qname;
>> +	set_opt(sbi, QUOTA);
>> +	return 0;
>> +errout:
>> +	kfree(qname);
>> +	return ret;
>> +}
>> +
>> +static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
>> +{
>> +
>> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
>> +
>> +	if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) {
>> +		f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options"
>> +			" when quota turned on");
>> +		return -1;
>> +	}
>> +	kfree(sbi->s_qf_names[qtype]);
>> +	sbi->s_qf_names[qtype] = NULL;
>> +	return 1;
>> +}
>> +
>> +static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
>> +{
>> +	/*
>> +	 * We do the test below only for project quotas. 'usrquota' and
>> +	 * 'grpquota' mount options are allowed even without quota feature
>> +	 * to support legacy quotas in quota files.
>> +	 */
>> +	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi->sb)) {
>> +		f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. "
>> +			 "Cannot enable project quota enforcement.");
>> +		return -1;
>> +	}
>> +	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
>> +			sbi->s_qf_names[PRJQUOTA]) {
>> +		if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
>> +			clear_opt(sbi, USRQUOTA);
>> +
>> +		if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
>> +			clear_opt(sbi, GRPQUOTA);
>> +
>> +		if (test_opt(sbi, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
>> +			clear_opt(sbi, PRJQUOTA);
>> +
>> +		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
>> +				test_opt(sbi, PRJQUOTA)) {
>> +			f2fs_msg(sbi->sb, KERN_ERR, "old and new quota "
>> +					"format mixing");
>> +			return -1;
>> +		}
>> +
>> +		if (!sbi->s_jquota_fmt) {
>> +			f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format "
>> +					"not specified");
>> +			return -1;
>> +		}
>> +	}
>> +	return 0;
>> +}
>> +#endif
>> +
>>  static int parse_options(struct super_block *sb, char *options)
>>  {
>>  	struct f2fs_sb_info *sbi = F2FS_SB(sb);
>> @@ -177,6 +299,9 @@ static int parse_options(struct super_block *sb, char *options)
>>  	substring_t args[MAX_OPT_ARGS];
>>  	char *p, *name;
>>  	int arg = 0;
>> +#ifdef CONFIG_QUOTA
>> +	int ret;
>> +#endif
>>  
>>  	if (!options)
>>  		return 0;
>> @@ -388,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options)
>>  			sb->s_flags &= ~MS_LAZYTIME;
>>  			break;
>>  #ifdef CONFIG_QUOTA
>> +		case Opt_quota:
>>  		case Opt_usrquota:
>>  			set_opt(sbi, USRQUOTA);
>>  			break;
>> @@ -397,10 +523,66 @@ static int parse_options(struct super_block *sb, char *options)
>>  		case Opt_prjquota:
>>  			set_opt(sbi, PRJQUOTA);
>>  			break;
>> +		case Opt_usrjquota:
>> +			ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_grpjquota:
>> +			ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_prjjquota:
>> +			ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_offusrjquota:
>> +			ret = f2fs_clear_qf_name(sb, USRQUOTA);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_offgrpjquota:
>> +			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_offprjjquota:
>> +			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
>> +			if (ret)
>> +				return ret;
>> +			break;
>> +		case Opt_jqfmt_vfsold:
>> +			sbi->s_jquota_fmt = QFMT_VFS_OLD;
>> +			break;
>> +		case Opt_jqfmt_vfsv0:
>> +			sbi->s_jquota_fmt = QFMT_VFS_V0;
>> +			break;
>> +		case Opt_jqfmt_vfsv1:
>> +			sbi->s_jquota_fmt = QFMT_VFS_V1;
>> +			break;
>> +		case Opt_noquota:
>> +			clear_opt(sbi, QUOTA);
>> +			clear_opt(sbi, USRQUOTA);
>> +			clear_opt(sbi, GRPQUOTA);
>> +			clear_opt(sbi, PRJQUOTA);
>> +			break;
>>  #else
>> +		case Opt_quota:
>>  		case Opt_usrquota:
>>  		case Opt_grpquota:
>>  		case Opt_prjquota:
>> +		case Opt_usrjquota:
>> +		case Opt_grpjquota:
>> +		case Opt_prjjquota:
>> +		case Opt_offusrjquota:
>> +		case Opt_offgrpjquota:
>> +		case Opt_offprjjquota:
>> +		case Opt_jqfmt_vfsold:
>> +		case Opt_jqfmt_vfsv0:
>> +		case Opt_jqfmt_vfsv1:
>> +		case Opt_noquota:
>>  			f2fs_msg(sb, KERN_INFO,
>>  					"quota operations not supported");
>>  			break;
>> @@ -412,6 +594,10 @@ static int parse_options(struct super_block *sb, char *options)
>>  			return -EINVAL;
>>  		}
>>  	}
>> +#ifdef CONFIG_QUOTA
>> +	if (f2fs_check_quota_options(sbi))
>> +		return -EINVAL;
>> +#endif
>>  
>>  	if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
>>  		f2fs_msg(sb, KERN_ERR,
>> @@ -591,7 +777,6 @@ 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);
>> @@ -658,6 +843,10 @@ static void f2fs_put_super(struct super_block *sb)
>>  
>>  	destroy_device_list(sbi);
>>  	mempool_destroy(sbi->write_io_dummy);
>> +#ifdef CONFIG_QUOTA
>> +	for (i = 0; i < MAXQUOTAS; i++)
>> +		kfree(sbi->s_qf_names[i]);
>> +#endif
>>  	destroy_percpu_info(sbi);
>>  	for (i = 0; i < NR_PAGE_TYPE; i++)
>>  		kfree(sbi->write_io[i]);
>> @@ -671,6 +860,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
>>  
>>  	trace_f2fs_sync_fs(sb, sync);
>>  
>> +	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
>> +		return -EAGAIN;
>> +
>>  	if (sync) {
>>  		struct cp_control cpc;
>>  
>> @@ -791,6 +983,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
>>  	return 0;
>>  }
>>  
>> +static inline void f2fs_show_quota_options(struct seq_file *seq,
>> +					   struct super_block *sb)
>> +{
>> +#ifdef CONFIG_QUOTA
>> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
>> +
>> +	if (sbi->s_jquota_fmt) {
>> +		char *fmtname = "";
>> +
>> +		switch (sbi->s_jquota_fmt) {
>> +		case QFMT_VFS_OLD:
>> +			fmtname = "vfsold";
>> +			break;
>> +		case QFMT_VFS_V0:
>> +			fmtname = "vfsv0";
>> +			break;
>> +		case QFMT_VFS_V1:
>> +			fmtname = "vfsv1";
>> +			break;
>> +		}
>> +		seq_printf(seq, ",jqfmt=%s", fmtname);
>> +	}
>> +
>> +	if (sbi->s_qf_names[USRQUOTA])
>> +		seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]);
>> +
>> +	if (sbi->s_qf_names[GRPQUOTA])
>> +		seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]);
>> +
>> +	if (sbi->s_qf_names[PRJQUOTA])
>> +		seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]);
>> +#endif
>> +}
>> +
>>  static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>>  {
>>  	struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
>> @@ -864,6 +1090,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>>  				sbi->fault_info.inject_rate);
>>  #endif
>>  #ifdef CONFIG_QUOTA
>> +	if (test_opt(sbi, QUOTA))
>> +		seq_puts(seq, ",quota");
>>  	if (test_opt(sbi, USRQUOTA))
>>  		seq_puts(seq, ",usrquota");
>>  	if (test_opt(sbi, GRPQUOTA))
>> @@ -871,6 +1099,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>>  	if (test_opt(sbi, PRJQUOTA))
>>  		seq_puts(seq, ",prjquota");
>>  #endif
>> +	f2fs_show_quota_options(seq, sbi->sb);
>>  
>>  	return 0;
>>  }
>> @@ -919,6 +1148,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>>  	struct f2fs_fault_info ffi = sbi->fault_info;
>>  #endif
>> +#ifdef CONFIG_QUOTA
>> +	int s_jquota_fmt;
>> +	char *s_qf_names[MAXQUOTAS];
>> +	int i, j;
>> +#endif
>>  
>>  	/*
>>  	 * Save the old mount options in case we
>> @@ -928,6 +1162,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>>  	old_sb_flags = sb->s_flags;
>>  	active_logs = sbi->active_logs;
>>  
>> +#ifdef CONFIG_QUOTA
>> +	s_jquota_fmt = sbi->s_jquota_fmt;
>> +	for (i = 0; i < MAXQUOTAS; i++) {
>> +		if (sbi->s_qf_names[i]) {
>> +			s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
>> +							 GFP_KERNEL);
>> +			if (!s_qf_names[i]) {
>> +				for (j = 0; j < i; j++)
>> +					kfree(s_qf_names[j]);
>> +				return -ENOMEM;
>> +			}
>> +		} else {
>> +			s_qf_names[i] = NULL;
>> +		}
>> +	}
>> +#endif
>> +
>>  	/* recover superblocks we couldn't write due to previous RO mount */
>>  	if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
>>  		err = f2fs_commit_super(sbi, false);
>> @@ -1009,6 +1260,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>>  			goto restore_gc;
>>  	}
>>  skip:
>> +#ifdef CONFIG_QUOTA
>> +	/* Release old quota file names */
>> +	for (i = 0; i < MAXQUOTAS; i++)
>> +		kfree(s_qf_names[i]);
>> +#endif
>>  	/* Update the POSIXACL Flag */
>>  	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
>>  		(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
>> @@ -1023,6 +1279,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>>  		stop_gc_thread(sbi);
>>  	}
>>  restore_opts:
>> +#ifdef CONFIG_QUOTA
>> +	sbi->s_jquota_fmt = s_jquota_fmt;
>> +	for (i = 0; i < MAXQUOTAS; i++) {
>> +		kfree(sbi->s_qf_names[i]);
>> +		sbi->s_qf_names[i] = s_qf_names[i];
>> +	}
>> +#endif
>>  	sbi->mount_opt = org_mount_opt;
>>  	sbi->active_logs = active_logs;
>>  	sb->s_flags = old_sb_flags;
>> @@ -1139,6 +1402,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
>>  	return &F2FS_I(inode)->i_reserved_quota;
>>  }
>>  
>> +static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
>> +{
>> +	return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type],
>> +						sbi->s_jquota_fmt, type);
>> +}
>> +
>> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi)
>> +{
>> +	int i, ret;
>> +
>> +	for (i = 0; i < MAXQUOTAS; i++) {
>> +		if (sbi->s_qf_names[i]) {
>> +			ret = f2fs_quota_on_mount(sbi, i);
>> +			if (ret < 0)
>> +				f2fs_msg(sbi->sb, KERN_ERR,
>> +					"Cannot turn on journaled "
>> +					"quota: error %d", ret);
>> +		}
>> +	}
>> +}
>> +
>>  static int f2fs_quota_sync(struct super_block *sb, int type)
>>  {
>>  	struct quota_info *dqopt = sb_dqopt(sb);
>> @@ -1220,7 +1504,7 @@ static int f2fs_quota_off(struct super_block *sb, int type)
>>  	return err;
>>  }
>>  
>> -static void f2fs_quota_off_umount(struct super_block *sb)
>> +void f2fs_quota_off_umount(struct super_block *sb)
>>  {
>>  	int type;
>>  
>> @@ -1258,7 +1542,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = {
>>  	.get_nextdqblk	= dquot_get_next_dqblk,
>>  };
>>  #else
>> -static inline void f2fs_quota_off_umount(struct super_block *sb)
>> +void f2fs_quota_off_umount(struct super_block *sb)
>>  {
>>  }
>>  #endif
>> @@ -2178,11 +2462,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  	if (err)
>>  		goto free_nm;
>>  
>> -	/* if there are nt orphan nodes free them */
>> -	err = recover_orphan_inodes(sbi);
>> -	if (err)
>> -		goto free_node_inode;
>> -
>>  	/* read root inode and dentry */
>>  	root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
>>  	if (IS_ERR(root)) {
>> @@ -2206,6 +2485,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  	if (err)
>>  		goto free_root_inode;
>>  
>> +	/* if there are nt orphan nodes free them */
>> +	err = recover_orphan_inodes(sbi);
>> +	if (err)
>> +		goto free_sysfs;
>> +
>>  	/* recover fsynced data */
>>  	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
>>  		/*
>> @@ -2215,7 +2499,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  		if (bdev_read_only(sb->s_bdev) &&
>>  				!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
>>  			err = -EROFS;
>> -			goto free_sysfs;
>> +			goto free_meta;
>>  		}
>>  
>>  		if (need_fsck)
>> @@ -2229,7 +2513,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  			need_fsck = true;
>>  			f2fs_msg(sb, KERN_ERR,
>>  				"Cannot recover all fsync data errno=%d", err);
>> -			goto free_sysfs;
>> +			goto free_meta;
>>  		}
>>  	} else {
>>  		err = recover_fsync_data(sbi, true);
>> @@ -2253,7 +2537,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  		/* After POR, we can run background GC thread.*/
>>  		err = start_gc_thread(sbi);
>>  		if (err)
>> -			goto free_sysfs;
>> +			goto free_meta;
>>  	}
>>  	kfree(options);
>>  
>> @@ -2271,8 +2555,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  	f2fs_update_time(sbi, REQ_TIME);
>>  	return 0;
>>  
>> -free_sysfs:
>> +free_meta:
>>  	f2fs_sync_inode_meta(sbi);
>> +	/*
>> +	 * Some dirty meta pages can be produced by recover_orphan_inodes()
>> +	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
>> +	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
>> +	 * falls into an infinite loop in sync_meta_pages().
>> +	 */
>> +	truncate_inode_pages_final(META_MAPPING(sbi));
>> +free_sysfs:
>>  	f2fs_unregister_sysfs(sbi);
>>  free_root_inode:
>>  	dput(sb->s_root);
>> @@ -2282,13 +2574,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  	mutex_lock(&sbi->umount_mutex);
>>  	release_ino_entry(sbi, true);
>>  	f2fs_leave_shrinker(sbi);
>> -	/*
>> -	 * Some dirty meta pages can be produced by recover_orphan_inodes()
>> -	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
>> -	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
>> -	 * falls into an infinite loop in sync_meta_pages().
>> -	 */
>> -	truncate_inode_pages_final(META_MAPPING(sbi));
>>  	iput(sbi->node_inode);
>>  	mutex_unlock(&sbi->umount_mutex);
>>  	f2fs_destroy_stats(sbi);
>> @@ -2308,6 +2593,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>>  	for (i = 0; i < NR_PAGE_TYPE; i++)
>>  		kfree(sbi->write_io[i]);
>>  	destroy_percpu_info(sbi);
>> +#ifdef CONFIG_QUOTA
>> +	for (i = 0; i < MAXQUOTAS; i++)
>> +		kfree(sbi->s_qf_names[i]);
>> +#endif
>>  	kfree(options);
>>  free_sb_buf:
>>  	kfree(raw_super);
>> -- 
>> 2.13.1.388.g69e6b9b4f4a9
> 
> .
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 RESEND] f2fs: support journalled quota
  2017-08-11 10:14   ` Chao Yu
@ 2017-08-11 17:35     ` Jaegeuk Kim
  2017-08-13 11:57       ` Chao Yu
  0 siblings, 1 reply; 5+ messages in thread
From: Jaegeuk Kim @ 2017-08-11 17:35 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-f2fs-devel, linux-kernel, chao

On 08/11, Chao Yu wrote:
> Hi Jaegeuk,
> 
> On 2017/8/11 9:51, Jaegeuk Kim wrote:
> > Hi Chao,
> > 
> > Do we have to add .get_inode_usage ?
> > 
> > I hit a kernel panic on __dquot_transfer() -> check_idq() saying
> > "BUG: unable to handle kernel paging request at 0xfff..."
> 
> Could you provider whole kernel dump info, mount option, test method?

mkfs -O extra_attr -O project_quota -O inode_checksum

loop of:
 # mount -o usrquota,grpquota,prjquota,discard
 # quotacheck -u -g /dir
 # quotaon -u -g /dir
 # echo 511 > /sys/fs/f2fs/dev/inject_type
 # echo (random 3000~5000) > /sys/fs/f2fs/dev/inject_rate
 # ltp/fsstress -x "echo 3 > /proc/sys/vm/drop_caches" -X 10 -r -f fsync=0 -f sync=0 -f write=1 -f dwrite=1 -f truncate=6 -f allocsp=0 -f bulkstat=0 -f bulkstat1=0 -f freesp=0 -f zero=1 -f collapse=1 -f insert=1 -f resvsp=0 -f unresvsp=0 -S t -p 20 -n 200000 -d /dir/test &
 # sleep 10
 # src/gowodn
 # umount

[  886.628389] BUG: unable to handle kernel paging request at ffffffffd9c01f9d
[  886.655169] IP: check_idq+0x5/0x180
[  886.665391] PGD 21e0c067
[  886.665393] P4D 21e0c067
[  886.671572] PUD 21e0e067
[  886.675062] PMD 0
[  886.685069]
[  886.753302] Oops: 0000 [#1] SMP
[  886.851212] Modules linked in: quota_v2 quota_tree f2fs(O) ppdev snd_intel8x0 sb_edac snd_ac97_codec ac97_bus snd_pcm joydev input_leds snd_timer snd parport_pc parport i2c_piix4 mac_hid serio_raw soundcore ib_iser rdma_cm iw_cm ib_cm ib_core configfs iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi autofs4 btrfs raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 multipath linear hid_generic crct10dif_pclmul crc32_pclmul usbhid ghash_clmulni_intel pcbc hid aesni_intel aes_x86_64 crypto_simd glue_helper cryptd ahci libahci psmouse pata_acpi e1000 video
[  888.738972] CPU: 3 PID: 2678 Comm: fsstress Tainted: G           O    4.13.0-rc2+ #9
[  888.776776] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[  888.785881] task: ffff9597390d5a00 task.stack: ffffbb6401208000
[  888.793283] RIP: 0010:check_idq+0x5/0x180
[  888.801156] RSP: 0018:ffffbb640120bc20 EFLAGS: 00010246
[  888.878306] RAX: ffff9596fd8a3d00 RBX: ffffbb640120bd88 RCX: 0000000000000000
[  888.990955] RDX: ffffbb640120bc68 RSI: 0000000000000001 RDI: ffffffffd9c01f11
[  889.011385] RBP: ffffbb640120bd78 R08: 0000000000000001 R09: 0000000000000000
[  889.018383] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
[  889.060570] R13: 0000000000000000 R14: ffff9596fd89b4f0 R15: ffffbb640120bc68
[  889.541140] FS:  00007fe4706c4700(0000) GS:ffff95973fd80000(0000) knlGS:0000000000000000
[  890.021093] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  890.411148] CR2: ffffffffd9c01f9d CR3: 000000005d1c8000 CR4: 00000000000406e0
[  890.841158] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  891.305554] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  891.638985] Call Trace:
[  891.666669]  ? __dquot_transfer+0x1d6/0x490
[  891.945967]  ? __mutex_unlock_slowpath.isra.11+0xcb/0x130
[  892.091555]  ? mutex_unlock+0x26/0x30
[  892.112444]  ? dquot_acquire+0x74/0x120
[  892.351151]  ? dqget+0x345/0x4a0
[  892.591885]  dquot_transfer+0xce/0x130
[  892.751686]  f2fs_setattr+0x8b/0x3c0 [f2fs]
[  892.773699]  notify_change+0x2db/0x410
[  892.791540]  chown_common+0x18b/0x1e0
[  892.800299]  SyS_lchown+0x98/0xe0
[  892.933985]  entry_SYSCALL_64_fastpath+0x1e/0xa9
[  893.059419] RIP: 0033:0x7fe46fdb9327


> 
> Thanks,
> 
> > 
> > Thanks,
> > 
> > On 08/08, Chao Yu wrote:
> >> This patch supports to enable f2fs to accept quota information through
> >> mount option:
> >> - {usr,grp,prj}jquota=<quota file path>
> >> - jqfmt=<quota type>
> >>
> >> Then, in ->mount flow, we can recover quota file during log replaying,
> >> by this, journelled quota can be supported.
> >>
> >> Signed-off-by: Chao Yu <yuchao0@huawei.com>
> >> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> >> ---
> >>  Documentation/filesystems/f2fs.txt |   9 +
> >>  fs/f2fs/checkpoint.c               |  26 ++-
> >>  fs/f2fs/f2fs.h                     |   9 +
> >>  fs/f2fs/recovery.c                 |  72 +++++++-
> >>  fs/f2fs/super.c                    | 327 ++++++++++++++++++++++++++++++++++---
> >>  5 files changed, 413 insertions(+), 30 deletions(-)
> >>
> >> diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
> >> index b8f495a8b67d..deafeff7795b 100644
> >> --- a/Documentation/filesystems/f2fs.txt
> >> +++ b/Documentation/filesystems/f2fs.txt
> >> @@ -165,6 +165,15 @@ io_bits=%u             Set the bit size of write IO requests. It should be set
> >>  usrquota               Enable plain user disk quota accounting.
> >>  grpquota               Enable plain group disk quota accounting.
> >>  prjquota               Enable plain project quota accounting.
> >> +usrjquota=<file>       Appoint specified file and type during mount, so that quota
> >> +grpjquota=<file>       information can be properly updated during recovery flow,
> >> +prjjquota=<file>       <quota file>: must be in root directory;
> >> +jqfmt=<quota type>     <quota type>: [vfsold,vfsv0,vfsv1].
> >> +offusrjquota           Turn off user journelled quota.
> >> +offgrpjquota           Turn off group journelled quota.
> >> +offprjjquota           Turn off project journelled quota.
> >> +quota                  Enable plain user disk quota accounting.
> >> +noquota                Disable all plain disk quota option.
> >>  
> >>  ================================================================================
> >>  DEBUGFS ENTRIES
> >> diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
> >> index da5b49183e09..04fe1df052b2 100644
> >> --- a/fs/f2fs/checkpoint.c
> >> +++ b/fs/f2fs/checkpoint.c
> >> @@ -588,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
> >>  int recover_orphan_inodes(struct f2fs_sb_info *sbi)
> >>  {
> >>  	block_t start_blk, orphan_blocks, i, j;
> >> -	int err;
> >> +	unsigned int s_flags = sbi->sb->s_flags;
> >> +	int err = 0;
> >>  
> >>  	if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
> >>  		return 0;
> >>  
> >> +	if (s_flags & MS_RDONLY) {
> >> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
> >> +		sbi->sb->s_flags &= ~MS_RDONLY;
> >> +	}
> >> +
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Needed for iput() to work correctly and not trash data */
> >> +	sbi->sb->s_flags |= MS_ACTIVE;
> >> +	/* Turn on quotas so that they are updated correctly */
> >> +	f2fs_enable_quota_files(sbi);
> >> +#endif
> >> +
> >>  	start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
> >>  	orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
> >>  
> >> @@ -608,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
> >>  			err = recover_orphan_inode(sbi, ino);
> >>  			if (err) {
> >>  				f2fs_put_page(page, 1);
> >> -				return err;
> >> +				goto out;
> >>  			}
> >>  		}
> >>  		f2fs_put_page(page, 1);
> >>  	}
> >>  	/* clear Orphan Flag */
> >>  	clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG);
> >> -	return 0;
> >> +out:
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Turn quotas off */
> >> +	f2fs_quota_off_umount(sbi->sb);
> >> +#endif
> >> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
> >> +
> >> +	return err;
> >>  }
> >>  
> >>  static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
> >> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >> index 1bcaa93bfed7..cea329f75068 100644
> >> --- a/fs/f2fs/f2fs.h
> >> +++ b/fs/f2fs/f2fs.h
> >> @@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX];
> >>  #define F2FS_MOUNT_USRQUOTA		0x00080000
> >>  #define F2FS_MOUNT_GRPQUOTA		0x00100000
> >>  #define F2FS_MOUNT_PRJQUOTA		0x00200000
> >> +#define F2FS_MOUNT_QUOTA		0x00400000
> >>  
> >>  #define clear_opt(sbi, option)	((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
> >>  #define set_opt(sbi, option)	((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
> >> @@ -1121,6 +1122,12 @@ struct f2fs_sb_info {
> >>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>  	struct f2fs_fault_info fault_info;
> >>  #endif
> >> +
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Names of quota files with journalled quota */
> >> +	char *s_qf_names[MAXQUOTAS];
> >> +	int s_jquota_fmt;			/* Format of quota to use */
> >> +#endif
> >>  };
> >>  
> >>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >> @@ -2433,6 +2440,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
> >>   */
> >>  int f2fs_inode_dirtied(struct inode *inode, bool sync);
> >>  void f2fs_inode_synced(struct inode *inode);
> >> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi);
> >> +void f2fs_quota_off_umount(struct super_block *sb);
> >>  int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
> >>  int f2fs_sync_fs(struct super_block *sb, int sync);
> >>  extern __printf(3, 4)
> >> diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
> >> index 2d9b8182691f..a3d02613934a 100644
> >> --- a/fs/f2fs/recovery.c
> >> +++ b/fs/f2fs/recovery.c
> >> @@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
> >>  }
> >>  
> >>  static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
> >> -					struct list_head *head, nid_t ino)
> >> +			struct list_head *head, nid_t ino, bool quota_inode)
> >>  {
> >>  	struct inode *inode;
> >>  	struct fsync_inode_entry *entry;
> >> +	int err;
> >>  
> >>  	inode = f2fs_iget_retry(sbi->sb, ino);
> >>  	if (IS_ERR(inode))
> >>  		return ERR_CAST(inode);
> >>  
> >> +	err = dquot_initialize(inode);
> >> +	if (err)
> >> +		goto err_out;
> >> +
> >> +	if (quota_inode) {
> >> +		err = dquot_alloc_inode(inode);
> >> +		if (err)
> >> +			goto err_out;
> >> +	}
> >> +
> >>  	entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
> >>  	entry->inode = inode;
> >>  	list_add_tail(&entry->list, head);
> >>  
> >>  	return entry;
> >> +err_out:
> >> +	iput(inode);
> >> +	return ERR_PTR(err);
> >>  }
> >>  
> >>  static void del_fsync_inode(struct fsync_inode_entry *entry)
> >> @@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
> >>  
> >>  	entry = get_fsync_inode(dir_list, pino);
> >>  	if (!entry) {
> >> -		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
> >> +		entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
> >> +							pino, false);
> >>  		if (IS_ERR(entry)) {
> >>  			dir = ERR_CAST(entry);
> >>  			err = PTR_ERR(entry);
> >> @@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
> >>  				err = -EEXIST;
> >>  			goto out_unmap_put;
> >>  		}
> >> +
> >> +		err = dquot_initialize(einode);
> >> +		if (err) {
> >> +			iput(einode);
> >> +			goto out_unmap_put;
> >> +		}
> >> +
> >>  		err = acquire_orphan_inode(F2FS_I_SB(inode));
> >>  		if (err) {
> >>  			iput(einode);
> >> @@ -226,18 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
> >>  
> >>  		entry = get_fsync_inode(head, ino_of_node(page));
> >>  		if (!entry) {
> >> +			bool quota_inode = false;
> >> +
> >>  			if (!check_only &&
> >>  					IS_INODE(page) && is_dent_dnode(page)) {
> >>  				err = recover_inode_page(sbi, page);
> >>  				if (err)
> >>  					break;
> >> +				quota_inode = true;
> >>  			}
> >>  
> >>  			/*
> >>  			 * CP | dnode(F) | inode(DF)
> >>  			 * For this case, we should not give up now.
> >>  			 */
> >> -			entry = add_fsync_inode(sbi, head, ino_of_node(page));
> >> +			entry = add_fsync_inode(sbi, head, ino_of_node(page),
> >> +								quota_inode);
> >>  			if (IS_ERR(entry)) {
> >>  				err = PTR_ERR(entry);
> >>  				if (err == -ENOENT) {
> >> @@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
> >>  	f2fs_put_page(node_page, 1);
> >>  
> >>  	if (ino != dn->inode->i_ino) {
> >> +		int ret;
> >> +
> >>  		/* Deallocate previous index in the node page */
> >>  		inode = f2fs_iget_retry(sbi->sb, ino);
> >>  		if (IS_ERR(inode))
> >>  			return PTR_ERR(inode);
> >> +
> >> +		ret = dquot_initialize(inode);
> >> +		if (ret) {
> >> +			iput(inode);
> >> +			return ret;
> >> +		}
> >>  	} else {
> >>  		inode = dn->inode;
> >>  	}
> >> @@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
> >>  	struct list_head dir_list;
> >>  	int err;
> >>  	int ret = 0;
> >> +	unsigned long s_flags = sbi->sb->s_flags;
> >>  	bool need_writecp = false;
> >>  
> >> +	if (s_flags & MS_RDONLY) {
> >> +		f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
> >> +		sbi->sb->s_flags &= ~MS_RDONLY;
> >> +	}
> >> +
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Needed for iput() to work correctly and not trash data */
> >> +	sbi->sb->s_flags |= MS_ACTIVE;
> >> +	/* Turn on quotas so that they are updated correctly */
> >> +	f2fs_enable_quota_files(sbi);
> >> +#endif
> >> +
> >>  	fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
> >>  			sizeof(struct fsync_inode_entry));
> >> -	if (!fsync_entry_slab)
> >> -		return -ENOMEM;
> >> +	if (!fsync_entry_slab) {
> >> +		err = -ENOMEM;
> >> +		goto out;
> >> +	}
> >>  
> >>  	INIT_LIST_HEAD(&inode_list);
> >>  	INIT_LIST_HEAD(&dir_list);
> >> @@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
> >>  	/* step #1: find fsynced inode numbers */
> >>  	err = find_fsync_dnodes(sbi, &inode_list, check_only);
> >>  	if (err || list_empty(&inode_list))
> >> -		goto out;
> >> +		goto skip;
> >>  
> >>  	if (check_only) {
> >>  		ret = 1;
> >> -		goto out;
> >> +		goto skip;
> >>  	}
> >>  
> >>  	need_writecp = true;
> >> @@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
> >>  	err = recover_data(sbi, &inode_list, &dir_list);
> >>  	if (!err)
> >>  		f2fs_bug_on(sbi, !list_empty(&inode_list));
> >> -out:
> >> +skip:
> >>  	destroy_fsync_dnodes(&inode_list);
> >>  
> >>  	/* truncate meta pages to be used by the recovery */
> >> @@ -615,5 +664,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
> >>  	}
> >>  
> >>  	kmem_cache_destroy(fsync_entry_slab);
> >> +out:
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Turn quotas off */
> >> +	f2fs_quota_off_umount(sbi->sb);
> >> +#endif
> >> +	sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
> >> +
> >>  	return ret ? ret: err;
> >>  }
> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> >> index 54b8ff4c6fc9..4c1bdcb94133 100644
> >> --- a/fs/f2fs/super.c
> >> +++ b/fs/f2fs/super.c
> >> @@ -25,6 +25,7 @@
> >>  #include <linux/quotaops.h>
> >>  #include <linux/f2fs_fs.h>
> >>  #include <linux/sysfs.h>
> >> +#include <linux/quota.h>
> >>  
> >>  #include "f2fs.h"
> >>  #include "node.h"
> >> @@ -107,9 +108,20 @@ enum {
> >>  	Opt_fault_injection,
> >>  	Opt_lazytime,
> >>  	Opt_nolazytime,
> >> +	Opt_quota,
> >> +	Opt_noquota,
> >>  	Opt_usrquota,
> >>  	Opt_grpquota,
> >>  	Opt_prjquota,
> >> +	Opt_usrjquota,
> >> +	Opt_grpjquota,
> >> +	Opt_prjjquota,
> >> +	Opt_offusrjquota,
> >> +	Opt_offgrpjquota,
> >> +	Opt_offprjjquota,
> >> +	Opt_jqfmt_vfsold,
> >> +	Opt_jqfmt_vfsv0,
> >> +	Opt_jqfmt_vfsv1,
> >>  	Opt_err,
> >>  };
> >>  
> >> @@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = {
> >>  	{Opt_fault_injection, "fault_injection=%u"},
> >>  	{Opt_lazytime, "lazytime"},
> >>  	{Opt_nolazytime, "nolazytime"},
> >> +	{Opt_quota, "quota"},
> >> +	{Opt_noquota, "noquota"},
> >>  	{Opt_usrquota, "usrquota"},
> >>  	{Opt_grpquota, "grpquota"},
> >>  	{Opt_prjquota, "prjquota"},
> >> +	{Opt_usrjquota, "usrjquota=%s"},
> >> +	{Opt_grpjquota, "grpjquota=%s"},
> >> +	{Opt_prjjquota, "prjjquota=%s"},
> >> +	{Opt_offusrjquota, "usrjquota="},
> >> +	{Opt_offgrpjquota, "grpjquota="},
> >> +	{Opt_offprjjquota, "prjjquota="},
> >> +	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
> >> +	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
> >> +	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
> >>  	{Opt_err, NULL},
> >>  };
> >>  
> >> @@ -170,6 +193,105 @@ static void init_once(void *foo)
> >>  	inode_init_once(&fi->vfs_inode);
> >>  }
> >>  
> >> +#ifdef CONFIG_QUOTA
> >> +static const char * const quotatypes[] = INITQFNAMES;
> >> +#define QTYPE2NAME(t) (quotatypes[t])
> >> +static int f2fs_set_qf_name(struct super_block *sb, int qtype,
> >> +							substring_t *args)
> >> +{
> >> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> >> +	char *qname;
> >> +	int ret = -1;
> >> +
> >> +	if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) {
> >> +		f2fs_msg(sb, KERN_ERR,
> >> +			"Cannot change journaled "
> >> +			"quota options when quota turned on");
> >> +		return -1;
> >> +	}
> >> +	qname = match_strdup(args);
> >> +	if (!qname) {
> >> +		f2fs_msg(sb, KERN_ERR,
> >> +			"Not enough memory for storing quotafile name");
> >> +		return -1;
> >> +	}
> >> +	if (sbi->s_qf_names[qtype]) {
> >> +		if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
> >> +			ret = 1;
> >> +		else
> >> +			f2fs_msg(sb, KERN_ERR,
> >> +				 "%s quota file already specified",
> >> +				 QTYPE2NAME(qtype));
> >> +		goto errout;
> >> +	}
> >> +	if (strchr(qname, '/')) {
> >> +		f2fs_msg(sb, KERN_ERR,
> >> +			"quotafile must be on filesystem root");
> >> +		goto errout;
> >> +	}
> >> +	sbi->s_qf_names[qtype] = qname;
> >> +	set_opt(sbi, QUOTA);
> >> +	return 0;
> >> +errout:
> >> +	kfree(qname);
> >> +	return ret;
> >> +}
> >> +
> >> +static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
> >> +{
> >> +
> >> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> >> +
> >> +	if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) {
> >> +		f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options"
> >> +			" when quota turned on");
> >> +		return -1;
> >> +	}
> >> +	kfree(sbi->s_qf_names[qtype]);
> >> +	sbi->s_qf_names[qtype] = NULL;
> >> +	return 1;
> >> +}
> >> +
> >> +static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
> >> +{
> >> +	/*
> >> +	 * We do the test below only for project quotas. 'usrquota' and
> >> +	 * 'grpquota' mount options are allowed even without quota feature
> >> +	 * to support legacy quotas in quota files.
> >> +	 */
> >> +	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi->sb)) {
> >> +		f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. "
> >> +			 "Cannot enable project quota enforcement.");
> >> +		return -1;
> >> +	}
> >> +	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
> >> +			sbi->s_qf_names[PRJQUOTA]) {
> >> +		if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
> >> +			clear_opt(sbi, USRQUOTA);
> >> +
> >> +		if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
> >> +			clear_opt(sbi, GRPQUOTA);
> >> +
> >> +		if (test_opt(sbi, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
> >> +			clear_opt(sbi, PRJQUOTA);
> >> +
> >> +		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
> >> +				test_opt(sbi, PRJQUOTA)) {
> >> +			f2fs_msg(sbi->sb, KERN_ERR, "old and new quota "
> >> +					"format mixing");
> >> +			return -1;
> >> +		}
> >> +
> >> +		if (!sbi->s_jquota_fmt) {
> >> +			f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format "
> >> +					"not specified");
> >> +			return -1;
> >> +		}
> >> +	}
> >> +	return 0;
> >> +}
> >> +#endif
> >> +
> >>  static int parse_options(struct super_block *sb, char *options)
> >>  {
> >>  	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> >> @@ -177,6 +299,9 @@ static int parse_options(struct super_block *sb, char *options)
> >>  	substring_t args[MAX_OPT_ARGS];
> >>  	char *p, *name;
> >>  	int arg = 0;
> >> +#ifdef CONFIG_QUOTA
> >> +	int ret;
> >> +#endif
> >>  
> >>  	if (!options)
> >>  		return 0;
> >> @@ -388,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options)
> >>  			sb->s_flags &= ~MS_LAZYTIME;
> >>  			break;
> >>  #ifdef CONFIG_QUOTA
> >> +		case Opt_quota:
> >>  		case Opt_usrquota:
> >>  			set_opt(sbi, USRQUOTA);
> >>  			break;
> >> @@ -397,10 +523,66 @@ static int parse_options(struct super_block *sb, char *options)
> >>  		case Opt_prjquota:
> >>  			set_opt(sbi, PRJQUOTA);
> >>  			break;
> >> +		case Opt_usrjquota:
> >> +			ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_grpjquota:
> >> +			ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_prjjquota:
> >> +			ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_offusrjquota:
> >> +			ret = f2fs_clear_qf_name(sb, USRQUOTA);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_offgrpjquota:
> >> +			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_offprjjquota:
> >> +			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
> >> +			if (ret)
> >> +				return ret;
> >> +			break;
> >> +		case Opt_jqfmt_vfsold:
> >> +			sbi->s_jquota_fmt = QFMT_VFS_OLD;
> >> +			break;
> >> +		case Opt_jqfmt_vfsv0:
> >> +			sbi->s_jquota_fmt = QFMT_VFS_V0;
> >> +			break;
> >> +		case Opt_jqfmt_vfsv1:
> >> +			sbi->s_jquota_fmt = QFMT_VFS_V1;
> >> +			break;
> >> +		case Opt_noquota:
> >> +			clear_opt(sbi, QUOTA);
> >> +			clear_opt(sbi, USRQUOTA);
> >> +			clear_opt(sbi, GRPQUOTA);
> >> +			clear_opt(sbi, PRJQUOTA);
> >> +			break;
> >>  #else
> >> +		case Opt_quota:
> >>  		case Opt_usrquota:
> >>  		case Opt_grpquota:
> >>  		case Opt_prjquota:
> >> +		case Opt_usrjquota:
> >> +		case Opt_grpjquota:
> >> +		case Opt_prjjquota:
> >> +		case Opt_offusrjquota:
> >> +		case Opt_offgrpjquota:
> >> +		case Opt_offprjjquota:
> >> +		case Opt_jqfmt_vfsold:
> >> +		case Opt_jqfmt_vfsv0:
> >> +		case Opt_jqfmt_vfsv1:
> >> +		case Opt_noquota:
> >>  			f2fs_msg(sb, KERN_INFO,
> >>  					"quota operations not supported");
> >>  			break;
> >> @@ -412,6 +594,10 @@ static int parse_options(struct super_block *sb, char *options)
> >>  			return -EINVAL;
> >>  		}
> >>  	}
> >> +#ifdef CONFIG_QUOTA
> >> +	if (f2fs_check_quota_options(sbi))
> >> +		return -EINVAL;
> >> +#endif
> >>  
> >>  	if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
> >>  		f2fs_msg(sb, KERN_ERR,
> >> @@ -591,7 +777,6 @@ 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);
> >> @@ -658,6 +843,10 @@ static void f2fs_put_super(struct super_block *sb)
> >>  
> >>  	destroy_device_list(sbi);
> >>  	mempool_destroy(sbi->write_io_dummy);
> >> +#ifdef CONFIG_QUOTA
> >> +	for (i = 0; i < MAXQUOTAS; i++)
> >> +		kfree(sbi->s_qf_names[i]);
> >> +#endif
> >>  	destroy_percpu_info(sbi);
> >>  	for (i = 0; i < NR_PAGE_TYPE; i++)
> >>  		kfree(sbi->write_io[i]);
> >> @@ -671,6 +860,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
> >>  
> >>  	trace_f2fs_sync_fs(sb, sync);
> >>  
> >> +	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
> >> +		return -EAGAIN;
> >> +
> >>  	if (sync) {
> >>  		struct cp_control cpc;
> >>  
> >> @@ -791,6 +983,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
> >>  	return 0;
> >>  }
> >>  
> >> +static inline void f2fs_show_quota_options(struct seq_file *seq,
> >> +					   struct super_block *sb)
> >> +{
> >> +#ifdef CONFIG_QUOTA
> >> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> >> +
> >> +	if (sbi->s_jquota_fmt) {
> >> +		char *fmtname = "";
> >> +
> >> +		switch (sbi->s_jquota_fmt) {
> >> +		case QFMT_VFS_OLD:
> >> +			fmtname = "vfsold";
> >> +			break;
> >> +		case QFMT_VFS_V0:
> >> +			fmtname = "vfsv0";
> >> +			break;
> >> +		case QFMT_VFS_V1:
> >> +			fmtname = "vfsv1";
> >> +			break;
> >> +		}
> >> +		seq_printf(seq, ",jqfmt=%s", fmtname);
> >> +	}
> >> +
> >> +	if (sbi->s_qf_names[USRQUOTA])
> >> +		seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]);
> >> +
> >> +	if (sbi->s_qf_names[GRPQUOTA])
> >> +		seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]);
> >> +
> >> +	if (sbi->s_qf_names[PRJQUOTA])
> >> +		seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]);
> >> +#endif
> >> +}
> >> +
> >>  static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
> >>  {
> >>  	struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
> >> @@ -864,6 +1090,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
> >>  				sbi->fault_info.inject_rate);
> >>  #endif
> >>  #ifdef CONFIG_QUOTA
> >> +	if (test_opt(sbi, QUOTA))
> >> +		seq_puts(seq, ",quota");
> >>  	if (test_opt(sbi, USRQUOTA))
> >>  		seq_puts(seq, ",usrquota");
> >>  	if (test_opt(sbi, GRPQUOTA))
> >> @@ -871,6 +1099,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
> >>  	if (test_opt(sbi, PRJQUOTA))
> >>  		seq_puts(seq, ",prjquota");
> >>  #endif
> >> +	f2fs_show_quota_options(seq, sbi->sb);
> >>  
> >>  	return 0;
> >>  }
> >> @@ -919,6 +1148,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
> >>  #ifdef CONFIG_F2FS_FAULT_INJECTION
> >>  	struct f2fs_fault_info ffi = sbi->fault_info;
> >>  #endif
> >> +#ifdef CONFIG_QUOTA
> >> +	int s_jquota_fmt;
> >> +	char *s_qf_names[MAXQUOTAS];
> >> +	int i, j;
> >> +#endif
> >>  
> >>  	/*
> >>  	 * Save the old mount options in case we
> >> @@ -928,6 +1162,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
> >>  	old_sb_flags = sb->s_flags;
> >>  	active_logs = sbi->active_logs;
> >>  
> >> +#ifdef CONFIG_QUOTA
> >> +	s_jquota_fmt = sbi->s_jquota_fmt;
> >> +	for (i = 0; i < MAXQUOTAS; i++) {
> >> +		if (sbi->s_qf_names[i]) {
> >> +			s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
> >> +							 GFP_KERNEL);
> >> +			if (!s_qf_names[i]) {
> >> +				for (j = 0; j < i; j++)
> >> +					kfree(s_qf_names[j]);
> >> +				return -ENOMEM;
> >> +			}
> >> +		} else {
> >> +			s_qf_names[i] = NULL;
> >> +		}
> >> +	}
> >> +#endif
> >> +
> >>  	/* recover superblocks we couldn't write due to previous RO mount */
> >>  	if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
> >>  		err = f2fs_commit_super(sbi, false);
> >> @@ -1009,6 +1260,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
> >>  			goto restore_gc;
> >>  	}
> >>  skip:
> >> +#ifdef CONFIG_QUOTA
> >> +	/* Release old quota file names */
> >> +	for (i = 0; i < MAXQUOTAS; i++)
> >> +		kfree(s_qf_names[i]);
> >> +#endif
> >>  	/* Update the POSIXACL Flag */
> >>  	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
> >>  		(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
> >> @@ -1023,6 +1279,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
> >>  		stop_gc_thread(sbi);
> >>  	}
> >>  restore_opts:
> >> +#ifdef CONFIG_QUOTA
> >> +	sbi->s_jquota_fmt = s_jquota_fmt;
> >> +	for (i = 0; i < MAXQUOTAS; i++) {
> >> +		kfree(sbi->s_qf_names[i]);
> >> +		sbi->s_qf_names[i] = s_qf_names[i];
> >> +	}
> >> +#endif
> >>  	sbi->mount_opt = org_mount_opt;
> >>  	sbi->active_logs = active_logs;
> >>  	sb->s_flags = old_sb_flags;
> >> @@ -1139,6 +1402,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
> >>  	return &F2FS_I(inode)->i_reserved_quota;
> >>  }
> >>  
> >> +static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
> >> +{
> >> +	return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type],
> >> +						sbi->s_jquota_fmt, type);
> >> +}
> >> +
> >> +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi)
> >> +{
> >> +	int i, ret;
> >> +
> >> +	for (i = 0; i < MAXQUOTAS; i++) {
> >> +		if (sbi->s_qf_names[i]) {
> >> +			ret = f2fs_quota_on_mount(sbi, i);
> >> +			if (ret < 0)
> >> +				f2fs_msg(sbi->sb, KERN_ERR,
> >> +					"Cannot turn on journaled "
> >> +					"quota: error %d", ret);
> >> +		}
> >> +	}
> >> +}
> >> +
> >>  static int f2fs_quota_sync(struct super_block *sb, int type)
> >>  {
> >>  	struct quota_info *dqopt = sb_dqopt(sb);
> >> @@ -1220,7 +1504,7 @@ static int f2fs_quota_off(struct super_block *sb, int type)
> >>  	return err;
> >>  }
> >>  
> >> -static void f2fs_quota_off_umount(struct super_block *sb)
> >> +void f2fs_quota_off_umount(struct super_block *sb)
> >>  {
> >>  	int type;
> >>  
> >> @@ -1258,7 +1542,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = {
> >>  	.get_nextdqblk	= dquot_get_next_dqblk,
> >>  };
> >>  #else
> >> -static inline void f2fs_quota_off_umount(struct super_block *sb)
> >> +void f2fs_quota_off_umount(struct super_block *sb)
> >>  {
> >>  }
> >>  #endif
> >> @@ -2178,11 +2462,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  	if (err)
> >>  		goto free_nm;
> >>  
> >> -	/* if there are nt orphan nodes free them */
> >> -	err = recover_orphan_inodes(sbi);
> >> -	if (err)
> >> -		goto free_node_inode;
> >> -
> >>  	/* read root inode and dentry */
> >>  	root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
> >>  	if (IS_ERR(root)) {
> >> @@ -2206,6 +2485,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  	if (err)
> >>  		goto free_root_inode;
> >>  
> >> +	/* if there are nt orphan nodes free them */
> >> +	err = recover_orphan_inodes(sbi);
> >> +	if (err)
> >> +		goto free_sysfs;
> >> +
> >>  	/* recover fsynced data */
> >>  	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
> >>  		/*
> >> @@ -2215,7 +2499,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  		if (bdev_read_only(sb->s_bdev) &&
> >>  				!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
> >>  			err = -EROFS;
> >> -			goto free_sysfs;
> >> +			goto free_meta;
> >>  		}
> >>  
> >>  		if (need_fsck)
> >> @@ -2229,7 +2513,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  			need_fsck = true;
> >>  			f2fs_msg(sb, KERN_ERR,
> >>  				"Cannot recover all fsync data errno=%d", err);
> >> -			goto free_sysfs;
> >> +			goto free_meta;
> >>  		}
> >>  	} else {
> >>  		err = recover_fsync_data(sbi, true);
> >> @@ -2253,7 +2537,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  		/* After POR, we can run background GC thread.*/
> >>  		err = start_gc_thread(sbi);
> >>  		if (err)
> >> -			goto free_sysfs;
> >> +			goto free_meta;
> >>  	}
> >>  	kfree(options);
> >>  
> >> @@ -2271,8 +2555,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  	f2fs_update_time(sbi, REQ_TIME);
> >>  	return 0;
> >>  
> >> -free_sysfs:
> >> +free_meta:
> >>  	f2fs_sync_inode_meta(sbi);
> >> +	/*
> >> +	 * Some dirty meta pages can be produced by recover_orphan_inodes()
> >> +	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
> >> +	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
> >> +	 * falls into an infinite loop in sync_meta_pages().
> >> +	 */
> >> +	truncate_inode_pages_final(META_MAPPING(sbi));
> >> +free_sysfs:
> >>  	f2fs_unregister_sysfs(sbi);
> >>  free_root_inode:
> >>  	dput(sb->s_root);
> >> @@ -2282,13 +2574,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  	mutex_lock(&sbi->umount_mutex);
> >>  	release_ino_entry(sbi, true);
> >>  	f2fs_leave_shrinker(sbi);
> >> -	/*
> >> -	 * Some dirty meta pages can be produced by recover_orphan_inodes()
> >> -	 * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg()
> >> -	 * followed by write_checkpoint() through f2fs_write_node_pages(), which
> >> -	 * falls into an infinite loop in sync_meta_pages().
> >> -	 */
> >> -	truncate_inode_pages_final(META_MAPPING(sbi));
> >>  	iput(sbi->node_inode);
> >>  	mutex_unlock(&sbi->umount_mutex);
> >>  	f2fs_destroy_stats(sbi);
> >> @@ -2308,6 +2593,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
> >>  	for (i = 0; i < NR_PAGE_TYPE; i++)
> >>  		kfree(sbi->write_io[i]);
> >>  	destroy_percpu_info(sbi);
> >> +#ifdef CONFIG_QUOTA
> >> +	for (i = 0; i < MAXQUOTAS; i++)
> >> +		kfree(sbi->s_qf_names[i]);
> >> +#endif
> >>  	kfree(options);
> >>  free_sb_buf:
> >>  	kfree(raw_super);
> >> -- 
> >> 2.13.1.388.g69e6b9b4f4a9
> > 
> > .
> > 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 RESEND] f2fs: support journalled quota
  2017-08-11 17:35     ` Jaegeuk Kim
@ 2017-08-13 11:57       ` Chao Yu
  0 siblings, 0 replies; 5+ messages in thread
From: Chao Yu @ 2017-08-13 11:57 UTC (permalink / raw)
  To: Jaegeuk Kim, Chao Yu; +Cc: linux-f2fs-devel, linux-kernel

Hi Jaegeuk,

Thank you for providing the info.

BTW, I just notice that my personal email address has been junked from f2fs
mailing list, anyway, I will resubscribe the list. But before that, I'd like
to ask, could you please Cc to chao@kernel.org when you send the patch to
mailing list? :)

Thanks,

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-08-13 11:58 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-08  2:54 [PATCH v3 RESEND] f2fs: support journalled quota Chao Yu
2017-08-11  1:51 ` Jaegeuk Kim
2017-08-11 10:14   ` Chao Yu
2017-08-11 17:35     ` Jaegeuk Kim
2017-08-13 11:57       ` Chao Yu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).