Re: [PATCH v5] ext4: make quota as first class supported feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed 11-01-12 11:11:56, Aditya Kali wrote:
> This patch is an attempt towards supporting quotas as first class
> feature in ext4. It is based on the proposal at:
> https://ext4.wiki.kernel.org/index.php/Design_For_1st_Class_Quota_in_Ext4
> This patch introduces a new feature - EXT4_FEATURE_RO_COMPAT_QUOTA which, when
> turned on, enables quota accounting at mount time iteself. Also, the
> quota inodes are stored in two additional superblock fields.
> Some changes introduced by this patch that should be pointed out are:
> 1) Two new ext4-superblock fields - s_usr_quota_inum and s_grp_quota_inum
>    for storing the quota inodes in use.
> 2) Default quota inodes are: inode#3 for tracking userquota and inode#4 for
>    tracking group quota. The superblock fields can be set to use other inodes
>    as well.
> 3) If the QUOTA feature and corresponding quota inodes are set in superblock,
>    the quota usage tracking is turned on at mount time. On 'quotaon' ioctl, the
>    quota limits enforcement is turned on. 'quotaoff' ioctl turns off only the
>    limits enforcement in this case.
> 4) When QUOTA feature is in use, the quota mount options 'quota', 'usrquota',
>    'grpquota' are ignored by the kernel.
> 5) mke2fs or tune2fs can be used to set the QUOTA feature and initialize quota
>    inodes. The default reserved inodes will not be visible to user as
>    regular files.
> 6) The quota-tools will need to be modified to support hidden quota files on
>    ext4. E2fsprogs will also include support for creating and fixing quota
>    files.
> 7) Support is only for the new V2 quota file format.
> 
> Signed-off-by: Aditya Kali <adityakali@xxxxxxxxxx>
  Thanks. The patch looks good now. I also tested it with latest
quota-tools & e2fsprogs + one change to quota code and quota works as
expected. You can add:
  Tested-by: Jan Kara <jack@xxxxxxx>
  Reviewed-by: Jan Kara <jack@xxxxxxx>

								Honza

> ---
>  fs/ext4/ext4.h      |    5 ++-
>  fs/ext4/ext4_jbd2.h |   18 +++++--
>  fs/ext4/super.c     |  135 ++++++++++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 149 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 513004f..9345150 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1279,6 +1279,8 @@ static inline struct timespec ext4_current_time(struct inode *inode)
>  static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
>  {
>  	return ino == EXT4_ROOT_INO ||
> +		ino == EXT4_USR_QUOTA_INO ||
> +		ino == EXT4_GRP_QUOTA_INO ||
>  		ino == EXT4_JOURNAL_INO ||
>  		ino == EXT4_RESIZE_INO ||
>  		(ino >= EXT4_FIRST_INO(sb) &&
> @@ -1453,7 +1455,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
>  					 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
>  					 EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
>  					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
> -					 EXT4_FEATURE_RO_COMPAT_BIGALLOC)
> +					 EXT4_FEATURE_RO_COMPAT_BIGALLOC | \
> +					 EXT4_FEATURE_RO_COMPAT_QUOTA)
>  
>  /*
>   * Default values for user and/or group using reserved blocks
> diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
> index 5802fa1..45de190 100644
> --- a/fs/ext4/ext4_jbd2.h
> +++ b/fs/ext4/ext4_jbd2.h
> @@ -87,14 +87,20 @@
>  #ifdef CONFIG_QUOTA
>  /* Amount of blocks needed for quota update - we know that the structure was
>   * allocated so we need to update only data block */
> -#define EXT4_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 1 : 0)
> +#define EXT4_QUOTA_TRANS_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
> +		EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
> +		1 : 0)
>  /* Amount of blocks needed for quota insert/delete - we do some block writes
>   * but inode, sb and group updates are done only once */
> -#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\
> -		(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0)
> -
> -#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\
> -		(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
> +#define EXT4_QUOTA_INIT_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
> +		EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
> +		(DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
> +		 +3+DQUOT_INIT_REWRITE) : 0)
> +
> +#define EXT4_QUOTA_DEL_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
> +		EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
> +		(DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
> +		 +3+DQUOT_DEL_REWRITE) : 0)
>  #else
>  #define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
>  #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 502c61f..14cc694 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -1238,12 +1238,18 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot);
>  static int ext4_write_info(struct super_block *sb, int type);
>  static int ext4_quota_on(struct super_block *sb, int type, int format_id,
>  			 struct path *path);
> +static int ext4_quota_on_sysfile(struct super_block *sb, int type,
> +				 int format_id);
>  static int ext4_quota_off(struct super_block *sb, int type);
> +static int ext4_quota_off_sysfile(struct super_block *sb, int type);
>  static int ext4_quota_on_mount(struct super_block *sb, int type);
>  static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
>  			       size_t len, loff_t off);
>  static ssize_t ext4_quota_write(struct super_block *sb, int type,
>  				const char *data, size_t len, loff_t off);
> +static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
> +			     unsigned int flags);
> +static int ext4_enable_quotas(struct super_block *sb);
>  
>  static const struct dquot_operations ext4_quota_operations = {
>  	.get_reserved_space = ext4_get_reserved_space,
> @@ -1265,6 +1271,16 @@ static const struct quotactl_ops ext4_qctl_operations = {
>  	.get_dqblk	= dquot_get_dqblk,
>  	.set_dqblk	= dquot_set_dqblk
>  };
> +
> +static const struct quotactl_ops ext4_qctl_sysfile_operations = {
> +	.quota_on_meta	= ext4_quota_on_sysfile,
> +	.quota_off	= ext4_quota_off_sysfile,
> +	.quota_sync	= dquot_quota_sync,
> +	.get_info	= dquot_get_dqinfo,
> +	.set_info	= dquot_set_dqinfo,
> +	.get_dqblk	= dquot_get_dqblk,
> +	.set_dqblk	= dquot_set_dqblk
> +};
>  #endif
>  
>  static const struct super_operations ext4_sops = {
> @@ -2710,6 +2726,16 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
>  			 "extents feature\n");
>  		return 0;
>  	}
> +
> +#ifndef CONFIG_QUOTA
> +	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
> +	    !readonly) {
> +		ext4_msg(sb, KERN_ERR,
> +			 "Filesystem with quota feature cannot be mounted RDWR "
> +			 "without CONFIG_QUOTA");
> +		return 0;
> +	}
> +#endif  /* CONFIG_QUOTA */
>  	return 1;
>  }
>  
> @@ -3606,6 +3632,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  #ifdef CONFIG_QUOTA
>  	sb->s_qcop = &ext4_qctl_operations;
>  	sb->dq_op = &ext4_quota_operations;
> +
> +	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> +		/* Use qctl operations for hidden quota files. */
> +		sb->s_qcop = &ext4_qctl_sysfile_operations;
> +	}
>  #endif
>  	memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
>  
> @@ -3813,6 +3844,16 @@ no_journal:
>  	} else
>  		descr = "out journal";
>  
> +#ifdef CONFIG_QUOTA
> +	/* Enable quota usage during mount. */
> +	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
> +	    !(sb->s_flags & MS_RDONLY)) {
> +		ret = ext4_enable_quotas(sb);
> +		if (ret)
> +			goto failed_mount7;
> +	}
> +#endif  /* CONFIG_QUOTA */
> +
>  	ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
>  		 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
>  		 *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
> @@ -4553,8 +4594,18 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  			kfree(old_opts.s_qf_names[i]);
>  #endif
>  	unlock_super(sb);
> -	if (enable_quota)
> -		dquot_resume(sb, -1);
> +	if (enable_quota) {
> +		if (sb_any_quota_suspended(sb))
> +			dquot_resume(sb, -1);
> +		else if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
> +					EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> +			err = ext4_enable_quotas(sb);
> +			if (err) {
> +				lock_super(sb);
> +				goto restore_opts;
> +			}
> +		}
> +	}
>  
>  	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
>  	kfree(orig_data);
> @@ -4813,6 +4864,74 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
>  	return dquot_quota_on(sb, type, format_id, path);
>  }
>  
> +static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
> +			     unsigned int flags)
> +{
> +	int err;
> +	struct inode *qf_inode;
> +	unsigned long qf_inums[MAXQUOTAS] = {
> +		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
> +		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
> +	};
> +
> +	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
> +
> +	if (!qf_inums[type])
> +		return -EPERM;
> +
> +	qf_inode = ext4_iget(sb, qf_inums[type]);
> +	if (IS_ERR(qf_inode)) {
> +		ext4_error(sb, "Bad quota inode # %lu", qf_inums[type]);
> +		return PTR_ERR(qf_inode);
> +	}
> +
> +	err = dquot_enable(qf_inode, type, format_id, flags);
> +	iput(qf_inode);
> +
> +	return err;
> +}
> +
> +/* Enable usage tracking for all quota types. */
> +static int ext4_enable_quotas(struct super_block *sb)
> +{
> +	int type, err = 0;
> +	unsigned long qf_inums[MAXQUOTAS] = {
> +		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
> +		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
> +	};
> +
> +	sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
> +	for (type = 0; type < MAXQUOTAS; type++) {
> +		if (qf_inums[type]) {
> +			err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
> +						DQUOT_USAGE_ENABLED);
> +			if (err) {
> +				ext4_warning(sb,
> +					"Failed to enable quota (type=%d) "
> +					"tracking. Please run e2fsck to fix.",
> +					type);
> +				return err;
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * quota_on function that is used when QUOTA feature is set.
> + */
> +static int ext4_quota_on_sysfile(struct super_block *sb, int type,
> +				 int format_id)
> +{
> +	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
> +		return -EINVAL;
> +
> +	/*
> +	 * USAGE was enabled at mount time. Only need to enable LIMITS now.
> +	 */
> +	return ext4_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED);
> +}
> +
>  static int ext4_quota_off(struct super_block *sb, int type)
>  {
>  	struct inode *inode = sb_dqopt(sb)->files[type];
> @@ -4839,6 +4958,18 @@ out:
>  	return dquot_quota_off(sb, type);
>  }
>  
> +/*
> + * quota_off function that is used when QUOTA feature is set.
> + */
> +static int ext4_quota_off_sysfile(struct super_block *sb, int type)
> +{
> +	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
> +		return -EINVAL;
> +
> +	/* Disable only the limits. */
> +	return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
> +}
> +
>  /* Read data from quotafile - avoid pagecache and such because we cannot afford
>   * acquiring the locks... As quota files are never truncated and quota code
>   * itself serializes the operations (and no one else should touch the files)
> -- 
> 1.7.7.3
> 
-- 
Jan Kara <jack@xxxxxxx>
SUSE Labs, CR
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux