Commit 4b2414d0 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim

f2fs: support journalled quota

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: default avatarChao Yu <yuchao0@huawei.com>
[Jaegeuk Kim: Fix wrong return values.]
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent b8c502b8
...@@ -165,6 +165,15 @@ io_bits=%u Set the bit size of write IO requests. It should be set ...@@ -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. usrquota Enable plain user disk quota accounting.
grpquota Enable plain group disk quota accounting. grpquota Enable plain group disk quota accounting.
prjquota Enable plain project 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 DEBUGFS ENTRIES
......
...@@ -588,11 +588,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) ...@@ -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) int recover_orphan_inodes(struct f2fs_sb_info *sbi)
{ {
block_t start_blk, orphan_blocks, i, j; 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)) if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
return 0; 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); start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
orphan_blocks = __start_sum_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) ...@@ -608,14 +621,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
err = recover_orphan_inode(sbi, ino); err = recover_orphan_inode(sbi, ino);
if (err) { if (err) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
return err; goto out;
} }
} }
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
/* clear Orphan Flag */ /* clear Orphan Flag */
clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_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) static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
......
...@@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX]; ...@@ -92,6 +92,7 @@ extern char *fault_name[FAULT_MAX];
#define F2FS_MOUNT_USRQUOTA 0x00080000 #define F2FS_MOUNT_USRQUOTA 0x00080000
#define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_GRPQUOTA 0x00100000
#define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_PRJQUOTA 0x00200000
#define F2FS_MOUNT_QUOTA 0x00400000
#define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
...@@ -1117,6 +1118,12 @@ struct f2fs_sb_info { ...@@ -1117,6 +1118,12 @@ struct f2fs_sb_info {
#ifdef CONFIG_F2FS_FAULT_INJECTION #ifdef CONFIG_F2FS_FAULT_INJECTION
struct f2fs_fault_info fault_info; struct f2fs_fault_info fault_info;
#endif #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 #ifdef CONFIG_F2FS_FAULT_INJECTION
...@@ -2429,6 +2436,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) ...@@ -2429,6 +2436,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
*/ */
int f2fs_inode_dirtied(struct inode *inode, bool sync); int f2fs_inode_dirtied(struct inode *inode, bool sync);
void f2fs_inode_synced(struct inode *inode); 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_commit_super(struct f2fs_sb_info *sbi, bool recover);
int f2fs_sync_fs(struct super_block *sb, int sync); int f2fs_sync_fs(struct super_block *sb, int sync);
extern __printf(3, 4) extern __printf(3, 4)
......
...@@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, ...@@ -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, 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 inode *inode;
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
int err;
inode = f2fs_iget_retry(sbi->sb, ino); inode = f2fs_iget_retry(sbi->sb, ino);
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(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 = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
entry->inode = inode; entry->inode = inode;
list_add_tail(&entry->list, head); list_add_tail(&entry->list, head);
return entry; return entry;
err_out:
iput(inode);
return ERR_PTR(err);
} }
static void del_fsync_inode(struct fsync_inode_entry *entry) static void del_fsync_inode(struct fsync_inode_entry *entry)
...@@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage, ...@@ -107,7 +121,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
entry = get_fsync_inode(dir_list, pino); entry = get_fsync_inode(dir_list, pino);
if (!entry) { 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)) { if (IS_ERR(entry)) {
dir = ERR_CAST(entry); dir = ERR_CAST(entry);
err = PTR_ERR(entry); err = PTR_ERR(entry);
...@@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage, ...@@ -140,6 +155,13 @@ static int recover_dentry(struct inode *inode, struct page *ipage,
err = -EEXIST; err = -EEXIST;
goto out_unmap_put; goto out_unmap_put;
} }
err = dquot_initialize(einode);
if (err) {
iput(einode);
goto out_unmap_put;
}
err = acquire_orphan_inode(F2FS_I_SB(inode)); err = acquire_orphan_inode(F2FS_I_SB(inode));
if (err) { if (err) {
iput(einode); iput(einode);
...@@ -226,18 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, ...@@ -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)); entry = get_fsync_inode(head, ino_of_node(page));
if (!entry) { if (!entry) {
bool quota_inode = false;
if (!check_only && if (!check_only &&
IS_INODE(page) && is_dent_dnode(page)) { IS_INODE(page) && is_dent_dnode(page)) {
err = recover_inode_page(sbi, page); err = recover_inode_page(sbi, page);
if (err) if (err)
break; break;
quota_inode = true;
} }
/* /*
* CP | dnode(F) | inode(DF) * CP | dnode(F) | inode(DF)
* For this case, we should not give up now. * 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)) { if (IS_ERR(entry)) {
err = PTR_ERR(entry); err = PTR_ERR(entry);
if (err == -ENOENT) { if (err == -ENOENT) {
...@@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, ...@@ -328,10 +354,18 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
f2fs_put_page(node_page, 1); f2fs_put_page(node_page, 1);
if (ino != dn->inode->i_ino) { if (ino != dn->inode->i_ino) {
int ret;
/* Deallocate previous index in the node page */ /* Deallocate previous index in the node page */
inode = f2fs_iget_retry(sbi->sb, ino); inode = f2fs_iget_retry(sbi->sb, ino);
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
ret = dquot_initialize(inode);
if (ret) {
iput(inode);
return ret;
}
} else { } else {
inode = dn->inode; inode = dn->inode;
} }
...@@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) ...@@ -558,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
struct list_head dir_list; struct list_head dir_list;
int err; int err;
int ret = 0; int ret = 0;
unsigned long s_flags = sbi->sb->s_flags;
bool need_writecp = false; 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", fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
sizeof(struct fsync_inode_entry)); sizeof(struct fsync_inode_entry));
if (!fsync_entry_slab) if (!fsync_entry_slab) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&inode_list); INIT_LIST_HEAD(&inode_list);
INIT_LIST_HEAD(&dir_list); INIT_LIST_HEAD(&dir_list);
...@@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) ...@@ -574,11 +623,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
/* step #1: find fsynced inode numbers */ /* step #1: find fsynced inode numbers */
err = find_fsync_dnodes(sbi, &inode_list, check_only); err = find_fsync_dnodes(sbi, &inode_list, check_only);
if (err || list_empty(&inode_list)) if (err || list_empty(&inode_list))
goto out; goto skip;
if (check_only) { if (check_only) {
ret = 1; ret = 1;
goto out; goto skip;
} }
need_writecp = true; need_writecp = true;
...@@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) ...@@ -587,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
err = recover_data(sbi, &inode_list, &dir_list); err = recover_data(sbi, &inode_list, &dir_list);
if (!err) if (!err)
f2fs_bug_on(sbi, !list_empty(&inode_list)); f2fs_bug_on(sbi, !list_empty(&inode_list));
out: skip:
destroy_fsync_dnodes(&inode_list); destroy_fsync_dnodes(&inode_list);
/* truncate meta pages to be used by the recovery */ /* 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) ...@@ -615,5 +664,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
} }
kmem_cache_destroy(fsync_entry_slab); 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; return ret ? ret: err;
} }
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/quotaops.h> #include <linux/quotaops.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/quota.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
...@@ -107,9 +108,20 @@ enum { ...@@ -107,9 +108,20 @@ enum {
Opt_fault_injection, Opt_fault_injection,
Opt_lazytime, Opt_lazytime,
Opt_nolazytime, Opt_nolazytime,
Opt_quota,
Opt_noquota,
Opt_usrquota, Opt_usrquota,
Opt_grpquota, Opt_grpquota,
Opt_prjquota, 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, Opt_err,
}; };
...@@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = { ...@@ -145,9 +157,20 @@ static match_table_t f2fs_tokens = {
{Opt_fault_injection, "fault_injection=%u"}, {Opt_fault_injection, "fault_injection=%u"},
{Opt_lazytime, "lazytime"}, {Opt_lazytime, "lazytime"},
{Opt_nolazytime, "nolazytime"}, {Opt_nolazytime, "nolazytime"},
{Opt_quota, "quota"},
{Opt_noquota, "noquota"},
{Opt_usrquota, "usrquota"}, {Opt_usrquota, "usrquota"},
{Opt_grpquota, "grpquota"}, {Opt_grpquota, "grpquota"},
{Opt_prjquota, "prjquota"}, {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}, {Opt_err, NULL},
}; };
...@@ -170,6 +193,104 @@ static void init_once(void *foo) ...@@ -170,6 +193,104 @@ static void init_once(void *foo)
inode_init_once(&fi->vfs_inode); 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 = -EINVAL;
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 -EINVAL;
}
qname = match_strdup(args);
if (!qname) {
f2fs_msg(sb, KERN_ERR,
"Not enough memory for storing quotafile name");
return -EINVAL;
}
if (sbi->s_qf_names[qtype]) {
if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
ret = 0;
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 -EINVAL;
}
kfree(sbi->s_qf_names[qtype]);
sbi->s_qf_names[qtype] = NULL;
return 0;
}
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) static int parse_options(struct super_block *sb, char *options)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
...@@ -177,6 +298,9 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -177,6 +298,9 @@ static int parse_options(struct super_block *sb, char *options)
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
char *p, *name; char *p, *name;
int arg = 0; int arg = 0;
#ifdef CONFIG_QUOTA
int ret;
#endif
if (!options) if (!options)
return 0; return 0;
...@@ -388,6 +512,7 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -388,6 +512,7 @@ static int parse_options(struct super_block *sb, char *options)
sb->s_flags &= ~MS_LAZYTIME; sb->s_flags &= ~MS_LAZYTIME;
break; break;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
case Opt_quota:
case Opt_usrquota: case Opt_usrquota:
set_opt(sbi, USRQUOTA); set_opt(sbi, USRQUOTA);
break; break;
...@@ -397,10 +522,66 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -397,10 +522,66 @@ static int parse_options(struct super_block *sb, char *options)
case Opt_prjquota: case Opt_prjquota:
set_opt(sbi, PRJQUOTA); set_opt(sbi, PRJQUOTA);
break; 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 #else
case Opt_quota:
case Opt_usrquota: case Opt_usrquota:
case Opt_grpquota: case Opt_grpquota:
case Opt_prjquota: 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, f2fs_msg(sb, KERN_INFO,
"quota operations not supported"); "quota operations not supported");
break; break;
...@@ -412,6 +593,10 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -412,6 +593,10 @@ static int parse_options(struct super_block *sb, char *options)
return -EINVAL; return -EINVAL;
} }
} }
#ifdef CONFIG_QUOTA
if (f2fs_check_quota_options(sbi))
return -EINVAL;
#endif
if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) { if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
f2fs_msg(sb, KERN_ERR, f2fs_msg(sb, KERN_ERR,
...@@ -591,7 +776,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) ...@@ -591,7 +776,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi)
kfree(sbi->devs); kfree(sbi->devs);
} }
static void f2fs_quota_off_umount(struct super_block *sb);
static void f2fs_put_super(struct super_block *sb) static void f2fs_put_super(struct super_block *sb)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
...@@ -658,6 +842,10 @@ static void f2fs_put_super(struct super_block *sb) ...@@ -658,6 +842,10 @@ static void f2fs_put_super(struct super_block *sb)
destroy_device_list(sbi); destroy_device_list(sbi);
mempool_destroy(sbi->write_io_dummy); 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); destroy_percpu_info(sbi);
for (i = 0; i < NR_PAGE_TYPE; i++) for (i = 0; i < NR_PAGE_TYPE; i++)
kfree(sbi->write_io[i]); kfree(sbi->write_io[i]);
...@@ -671,6 +859,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) ...@@ -671,6 +859,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
trace_f2fs_sync_fs(sb, sync); trace_f2fs_sync_fs(sb, sync);
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
return -EAGAIN;
if (sync) { if (sync) {
struct cp_control cpc; struct cp_control cpc;
...@@ -791,6 +982,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -791,6 +982,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; 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) static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
...@@ -864,6 +1089,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -864,6 +1089,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
sbi->fault_info.inject_rate); sbi->fault_info.inject_rate);
#endif #endif
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
if (test_opt(sbi, QUOTA))
seq_puts(seq, ",quota");
if (test_opt(sbi, USRQUOTA)) if (test_opt(sbi, USRQUOTA))
seq_puts(seq, ",usrquota"); seq_puts(seq, ",usrquota");
if (test_opt(sbi, GRPQUOTA)) if (test_opt(sbi, GRPQUOTA))
...@@ -871,6 +1098,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -871,6 +1098,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
if (test_opt(sbi, PRJQUOTA)) if (test_opt(sbi, PRJQUOTA))
seq_puts(seq, ",prjquota"); seq_puts(seq, ",prjquota");
#endif #endif
f2fs_show_quota_options(seq, sbi->sb);
return 0; return 0;
} }
...@@ -919,6 +1147,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) ...@@ -919,6 +1147,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
#ifdef CONFIG_F2FS_FAULT_INJECTION #ifdef CONFIG_F2FS_FAULT_INJECTION
struct f2fs_fault_info ffi = sbi->fault_info; struct f2fs_fault_info ffi = sbi->fault_info;
#endif #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 * Save the old mount options in case we
...@@ -928,6 +1161,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) ...@@ -928,6 +1161,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
old_sb_flags = sb->s_flags; old_sb_flags = sb->s_flags;
active_logs = sbi->active_logs; 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 */ /* recover superblocks we couldn't write due to previous RO mount */
if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) { if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
err = f2fs_commit_super(sbi, false); err = f2fs_commit_super(sbi, false);
...@@ -1009,6 +1259,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) ...@@ -1009,6 +1259,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
goto restore_gc; goto restore_gc;
} }
skip: 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 */ /* Update the POSIXACL Flag */
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
...@@ -1023,6 +1278,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) ...@@ -1023,6 +1278,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
stop_gc_thread(sbi); stop_gc_thread(sbi);
} }
restore_opts: 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->mount_opt = org_mount_opt;
sbi->active_logs = active_logs; sbi->active_logs = active_logs;
sb->s_flags = old_sb_flags; sb->s_flags = old_sb_flags;
...@@ -1139,6 +1401,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode) ...@@ -1139,6 +1401,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
return &F2FS_I(inode)->i_reserved_quota; 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) static int f2fs_quota_sync(struct super_block *sb, int type)
{ {
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
...@@ -1220,7 +1503,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) ...@@ -1220,7 +1503,7 @@ static int f2fs_quota_off(struct super_block *sb, int type)
return err; return err;
} }
static void f2fs_quota_off_umount(struct super_block *sb) void f2fs_quota_off_umount(struct super_block *sb)
{ {
int type; int type;
...@@ -1258,7 +1541,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = { ...@@ -1258,7 +1541,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = {
.get_nextdqblk = dquot_get_next_dqblk, .get_nextdqblk = dquot_get_next_dqblk,
}; };
#else #else
static inline void f2fs_quota_off_umount(struct super_block *sb) void f2fs_quota_off_umount(struct super_block *sb)
{ {
} }
#endif #endif
...@@ -2178,11 +2461,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2178,11 +2461,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err) if (err)
goto free_nm; 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 */ /* read root inode and dentry */
root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
if (IS_ERR(root)) { if (IS_ERR(root)) {
...@@ -2206,6 +2484,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2206,6 +2484,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err) if (err)
goto free_root_inode; 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 */ /* recover fsynced data */
if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
/* /*
...@@ -2215,7 +2498,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2215,7 +2498,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (bdev_read_only(sb->s_bdev) && if (bdev_read_only(sb->s_bdev) &&
!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
err = -EROFS; err = -EROFS;
goto free_sysfs; goto free_meta;
} }
if (need_fsck) if (need_fsck)
...@@ -2229,7 +2512,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2229,7 +2512,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
need_fsck = true; need_fsck = true;
f2fs_msg(sb, KERN_ERR, f2fs_msg(sb, KERN_ERR,
"Cannot recover all fsync data errno=%d", err); "Cannot recover all fsync data errno=%d", err);
goto free_sysfs; goto free_meta;
} }
} else { } else {
err = recover_fsync_data(sbi, true); err = recover_fsync_data(sbi, true);
...@@ -2253,7 +2536,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2253,7 +2536,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
/* After POR, we can run background GC thread.*/ /* After POR, we can run background GC thread.*/
err = start_gc_thread(sbi); err = start_gc_thread(sbi);
if (err) if (err)
goto free_sysfs; goto free_meta;
} }
kfree(options); kfree(options);
...@@ -2271,8 +2554,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2271,8 +2554,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
f2fs_update_time(sbi, REQ_TIME); f2fs_update_time(sbi, REQ_TIME);
return 0; return 0;
free_sysfs: free_meta:
f2fs_sync_inode_meta(sbi); 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); f2fs_unregister_sysfs(sbi);
free_root_inode: free_root_inode:
dput(sb->s_root); dput(sb->s_root);
...@@ -2282,13 +2573,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2282,13 +2573,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
mutex_lock(&sbi->umount_mutex); mutex_lock(&sbi->umount_mutex);
release_ino_entry(sbi, true); release_ino_entry(sbi, true);
f2fs_leave_shrinker(sbi); 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); iput(sbi->node_inode);
mutex_unlock(&sbi->umount_mutex); mutex_unlock(&sbi->umount_mutex);
f2fs_destroy_stats(sbi); f2fs_destroy_stats(sbi);
...@@ -2308,6 +2592,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2308,6 +2592,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
for (i = 0; i < NR_PAGE_TYPE; i++) for (i = 0; i < NR_PAGE_TYPE; i++)
kfree(sbi->write_io[i]); kfree(sbi->write_io[i]);
destroy_percpu_info(sbi); destroy_percpu_info(sbi);
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
kfree(sbi->s_qf_names[i]);
#endif
kfree(options); kfree(options);
free_sb_buf: free_sb_buf:
kfree(raw_super); kfree(raw_super);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment