Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0abd675e authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim
Browse files

f2fs: support plain user/group quota



This patch adds to support plain user/group quota.

Change Note by Jaegeuk Kim.

- Use f2fs page cache for quota files in order to consider garbage collection.
  so, quota files are not tolerable for sudden power-cuts, so user needs to do
  quotacheck.

- setattr() calls dquot_transfer which will transfer inode->i_blocks.
  We can't reclaim that during f2fs_evict_inode(). So, we need to count
  node blocks as well in order to match i_blocks with dquot's space.

  Note that, Chao wrote a patch to count inode->i_blocks without inode block.
  (f2fs: don't count inode block in in-memory inode.i_blocks)

- in f2fs_remount, we need to make RW in prior to dquot_resume.

- handle fault_injection case during f2fs_quota_off_umount

- TODO: Project quota

Signed-off-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent d29460e5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -162,6 +162,8 @@ mode=%s Control block allocation mode which supports "adaptive"
                       writes towards main area.
io_bits=%u             Set the bit size of write IO requests. It should be set
                       with "mode=lfs".
usrquota               Enable plain user disk quota accounting.
grpquota               Enable plain group disk quota accounting.

================================================================================
DEBUGFS ENTRIES
+6 −4
Original line number Diff line number Diff line
@@ -491,14 +491,15 @@ void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count)
{
	struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
	int err;

	if (!count)
		return 0;

	if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC)))
		return -EPERM;
	if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count)))
		return -ENOSPC;
	if (unlikely((err = inc_valid_block_count(sbi, dn->inode, &count))))
		return err;

	trace_f2fs_reserve_new_blocks(dn->inode, dn->nid,
						dn->ofs_in_node, count);
@@ -749,6 +750,7 @@ static int __allocate_data_block(struct dnode_of_data *dn)
	struct node_info ni;
	pgoff_t fofs;
	blkcnt_t count = 1;
	int err;

	if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC)))
		return -EPERM;
@@ -757,8 +759,8 @@ static int __allocate_data_block(struct dnode_of_data *dn)
	if (dn->data_blkaddr == NEW_ADDR)
		goto alloc;

	if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count)))
		return -ENOSPC;
	if (unlikely((err = inc_valid_block_count(sbi, dn->inode, &count))))
		return err;

alloc:
	get_node_info(sbi, dn->nid, &ni);
+65 −23
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/vmalloc.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/quotaops.h>
#ifdef CONFIG_F2FS_FS_ENCRYPTION
#include <linux/fscrypt_supp.h>
#else
@@ -88,6 +89,8 @@ extern char *fault_name[FAULT_MAX];
#define F2FS_MOUNT_FAULT_INJECTION	0x00010000
#define F2FS_MOUNT_ADAPTIVE		0x00020000
#define F2FS_MOUNT_LFS			0x00040000
#define F2FS_MOUNT_USRQUOTA		0x00080000
#define F2FS_MOUNT_GRPQUOTA		0x00100000

#define clear_opt(sbi, option)	((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option)	((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -521,6 +524,12 @@ struct f2fs_inode_info {
	nid_t i_xattr_nid;		/* node id that contains xattrs */
	loff_t	last_disk_size;		/* lastly written file size */

#ifdef CONFIG_QUOTA
	struct dquot *i_dquot[MAXQUOTAS];

	/* quota space reservation, managed internally by quota code */
	qsize_t i_reserved_quota;
#endif
	struct list_head dirty_list;	/* dirty list for dirs and files */
	struct list_head gdirty_list;	/* linked in global dirty list */
	struct list_head inmem_pages;	/* inmemory pages managed by f2fs */
@@ -1376,17 +1385,23 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
	return ofs == XATTR_NODE_OFFSET;
}

static inline void f2fs_i_blocks_write(struct inode *, block_t, bool);
static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi,
static inline void f2fs_i_blocks_write(struct inode *, block_t, bool, bool);
static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
				 struct inode *inode, blkcnt_t *count)
{
	blkcnt_t diff;
	blkcnt_t diff = 0, release = 0;
	block_t avail_user_block_count;
	int ret;

	ret = dquot_reserve_block(inode, *count);
	if (ret)
		return ret;

#ifdef CONFIG_F2FS_FAULT_INJECTION
	if (time_to_inject(sbi, FAULT_BLOCK)) {
		f2fs_show_injection_info(FAULT_BLOCK);
		return false;
		release = *count;
		goto enospc;
	}
#endif
	/*
@@ -1401,17 +1416,24 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi,
	if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
		diff = sbi->total_valid_block_count - avail_user_block_count;
		*count -= diff;
		release = diff;
		sbi->total_valid_block_count = avail_user_block_count;
		if (!*count) {
			spin_unlock(&sbi->stat_lock);
			percpu_counter_sub(&sbi->alloc_valid_block_count, diff);
			return false;
			goto enospc;
		}
	}
	spin_unlock(&sbi->stat_lock);

	f2fs_i_blocks_write(inode, *count, true);
	return true;
	if (release)
		dquot_release_reservation_block(inode, release);
	f2fs_i_blocks_write(inode, *count, true, true);
	return 0;

enospc:
	dquot_release_reservation_block(inode, release);
	return -ENOSPC;
}

static inline void dec_valid_block_count(struct f2fs_sb_info *sbi,
@@ -1425,7 +1447,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi,
	f2fs_bug_on(sbi, inode->i_blocks < sectors);
	sbi->total_valid_block_count -= (block_t)count;
	spin_unlock(&sbi->stat_lock);
	f2fs_i_blocks_write(inode, count, false);
	f2fs_i_blocks_write(inode, count, false, true);
}

static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type)
@@ -1554,11 +1576,18 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi)
	return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
}

static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi,
static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
					struct inode *inode, bool is_inode)
{
	block_t	valid_block_count;
	unsigned int valid_node_count;
	bool quota = inode && !is_inode;

	if (quota) {
		int ret = dquot_reserve_block(inode, 1);
		if (ret)
			return ret;
	}

	spin_lock(&sbi->stat_lock);

@@ -1566,28 +1595,33 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi,
	if (unlikely(valid_block_count + sbi->reserved_blocks >
						sbi->user_block_count)) {
		spin_unlock(&sbi->stat_lock);
		return false;
		goto enospc;
	}

	valid_node_count = sbi->total_valid_node_count + 1;
	if (unlikely(valid_node_count > sbi->total_node_count)) {
		spin_unlock(&sbi->stat_lock);
		return false;
		goto enospc;
	}

	sbi->total_valid_node_count++;
	sbi->total_valid_block_count++;
	spin_unlock(&sbi->stat_lock);

	if (inode) {
		if (is_inode)
			f2fs_mark_inode_dirty_sync(inode, true);
		else
			f2fs_i_blocks_write(inode, 1, true);
			f2fs_i_blocks_write(inode, 1, true, true);
	}

	sbi->total_valid_node_count++;
	sbi->total_valid_block_count++;
	spin_unlock(&sbi->stat_lock);

	percpu_counter_inc(&sbi->alloc_valid_block_count);
	return true;
	return 0;

enospc:
	if (quota)
		dquot_release_reservation_block(inode, 1);
	return -ENOSPC;
}

static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,
@@ -1599,12 +1633,13 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,
	f2fs_bug_on(sbi, !sbi->total_valid_node_count);
	f2fs_bug_on(sbi, !is_inode && !inode->i_blocks);

	if (!is_inode)
		f2fs_i_blocks_write(inode, 1, false);
	sbi->total_valid_node_count--;
	sbi->total_valid_block_count--;

	spin_unlock(&sbi->stat_lock);

	if (!is_inode)
		f2fs_i_blocks_write(inode, 1, false, true);
}

static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi)
@@ -1879,14 +1914,21 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc)
}

static inline void f2fs_i_blocks_write(struct inode *inode,
					block_t diff, bool add)
					block_t diff, bool add, bool claim)
{
	bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE);
	bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER);
	blkcnt_t sectors = diff << F2FS_LOG_SECTORS_PER_BLOCK;

	inode->i_blocks = add ? inode->i_blocks + sectors :
				inode->i_blocks - sectors;
	/* add = 1, claim = 1 should be dquot_reserve_block in pair */
	if (add) {
		if (claim)
			dquot_claim_block(inode, diff);
		else
			dquot_alloc_block_nofail(inode, diff);
	} else {
		dquot_free_block(inode, diff);
	}

	f2fs_mark_inode_dirty_sync(inode, true);
	if (clean || recover)
		set_inode_flag(inode, FI_AUTO_RECOVER);
+27 −7
Original line number Diff line number Diff line
@@ -442,11 +442,10 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)

static int f2fs_file_open(struct inode *inode, struct file *filp)
{
	int ret = generic_file_open(inode, filp);
	struct dentry *dir;

	if (!ret && f2fs_encrypted_inode(inode)) {
		ret = fscrypt_get_encryption_info(inode);
	if (f2fs_encrypted_inode(inode)) {
		int ret = fscrypt_get_encryption_info(inode);
		if (ret)
			return -EACCES;
		if (!fscrypt_has_encryption_key(inode))
@@ -459,7 +458,7 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
		return -EPERM;
	}
	dput(dir);
	return ret;
	return dquot_file_open(inode, filp);
}

int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
@@ -710,6 +709,20 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
	if (err)
		return err;

	if (is_quota_modification(inode, attr)) {
		err = dquot_initialize(inode);
		if (err)
			return err;
	}
	if ((attr->ia_valid & ATTR_UID &&
		!uid_eq(attr->ia_uid, inode->i_uid)) ||
		(attr->ia_valid & ATTR_GID &&
		!gid_eq(attr->ia_gid, inode->i_gid))) {
		err = dquot_transfer(inode, attr);
		if (err)
			return err;
	}

	if (attr->ia_valid & ATTR_SIZE) {
		if (f2fs_encrypted_inode(inode)) {
			err = fscrypt_get_encryption_info(inode);
@@ -996,9 +1009,9 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode,

				if (do_replace[i]) {
					f2fs_i_blocks_write(src_inode,
								1, false);
							1, false, false);
					f2fs_i_blocks_write(dst_inode,
								1, true);
							1, true, false);
					f2fs_replace_block(sbi, &dn, dn.data_blkaddr,
					blkaddr[i], ni.version, true, false);

@@ -1523,6 +1536,13 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)

	inode_lock(inode);

	/* Is it quota file? Do not allow user to mess with it */
	if (IS_NOQUOTA(inode)) {
		inode_unlock(inode);
		ret = -EPERM;
		goto unlock_out;
	}

	flags = f2fs_mask_flags(inode->i_mode, flags);

	oldflags = fi->i_flags;
@@ -1542,7 +1562,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
	inode->i_ctime = current_time(inode);
	f2fs_set_inode_flags(inode);
	f2fs_mark_inode_dirty_sync(inode, false);

unlock_out:
	inode_unlock(inode);
out:
	mnt_drop_write_file(filp);
+5 −0
Original line number Diff line number Diff line
@@ -373,6 +373,8 @@ void f2fs_evict_inode(struct inode *inode)
	if (inode->i_nlink || is_bad_inode(inode))
		goto no_delete;

	dquot_initialize(inode);

	remove_ino_entry(sbi, inode->i_ino, APPEND_INO);
	remove_ino_entry(sbi, inode->i_ino, UPDATE_INO);

@@ -405,8 +407,11 @@ void f2fs_evict_inode(struct inode *inode)

	if (err)
		update_inode_page(inode);
	dquot_free_inode(inode);
	sb_end_intwrite(inode->i_sb);
no_delete:
	dquot_drop(inode);

	stat_dec_inline_xattr(inode);
	stat_dec_inline_dir(inode);
	stat_dec_inline_inode(inode);
Loading