[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20120112122618.GB8778@quack.suse.cz>
Date: Thu, 12 Jan 2012 13:26:18 +0100
From: Jan Kara <jack@...e.cz>
To: Aditya Kali <adityakali@...gle.com>
Cc: adilger@...ger.ca, tytso@....edu, dmonakhov@...nvz.org,
jack@...e.cz, johann@...mcloud.com, linux-ext4@...r.kernel.org
Subject: Re: [PATCH v5] ext4: make quota as first class supported feature
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@...gle.com>
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@...e.cz>
Reviewed-by: Jan Kara <jack@...e.cz>
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@...e.cz>
SUSE Labs, CR
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists