aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorAditya Kali <adityakali@google.com>2012-07-22 20:21:31 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-07-22 20:21:31 -0400
commit7c319d328505b7781b65238ae9f53293b5ee0ca8 (patch)
tree3a0e0c521eee02faa5a87738ac750f51d199371f /fs/ext4
parent4bd809dbbf177ad0c450d702466b1da63e1b4b7e (diff)
ext4: make quota as first class supported feature
This patch adds support for quotas as a first class feature in ext4; which is to say, the quota files are stored in hidden inodes as file system metadata, instead of as separate files visible in the file system directory hierarchy. 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. Tested-by: Jan Kara <jack@suse.cz> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Johann Lombardi <johann@whamcloud.com> Signed-off-by: Aditya Kali <adityakali@google.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/ext4.h5
-rw-r--r--fs/ext4/ext4_jbd2.h18
-rw-r--r--fs/ext4/super.c137
3 files changed, 150 insertions, 10 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 4a49f8225d0..1610e808ebe 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1315,6 +1315,8 @@ static inline struct timespec ext4_current_time(struct inode *inode)
1315static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) 1315static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
1316{ 1316{
1317 return ino == EXT4_ROOT_INO || 1317 return ino == EXT4_ROOT_INO ||
1318 ino == EXT4_USR_QUOTA_INO ||
1319 ino == EXT4_GRP_QUOTA_INO ||
1318 ino == EXT4_JOURNAL_INO || 1320 ino == EXT4_JOURNAL_INO ||
1319 ino == EXT4_RESIZE_INO || 1321 ino == EXT4_RESIZE_INO ||
1320 (ino >= EXT4_FIRST_INO(sb) && 1322 (ino >= EXT4_FIRST_INO(sb) &&
@@ -1497,7 +1499,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
1497 EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\ 1499 EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
1498 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\ 1500 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
1499 EXT4_FEATURE_RO_COMPAT_BIGALLOC |\ 1501 EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
1500 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) 1502 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
1503 EXT4_FEATURE_RO_COMPAT_QUOTA)
1501 1504
1502/* 1505/*
1503 * Default values for user and/or group using reserved blocks 1506 * 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 f440e8f1841..1393c830411 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -87,14 +87,20 @@
87#ifdef CONFIG_QUOTA 87#ifdef CONFIG_QUOTA
88/* Amount of blocks needed for quota update - we know that the structure was 88/* Amount of blocks needed for quota update - we know that the structure was
89 * allocated so we need to update only data block */ 89 * allocated so we need to update only data block */
90#define EXT4_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 1 : 0) 90#define EXT4_QUOTA_TRANS_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
91 EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
92 1 : 0)
91/* Amount of blocks needed for quota insert/delete - we do some block writes 93/* Amount of blocks needed for quota insert/delete - we do some block writes
92 * but inode, sb and group updates are done only once */ 94 * but inode, sb and group updates are done only once */
93#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\ 95#define EXT4_QUOTA_INIT_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
94 (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0) 96 EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
95 97 (DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
96#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\ 98 +3+DQUOT_INIT_REWRITE) : 0)
97 (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0) 99
100#define EXT4_QUOTA_DEL_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
101 EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
102 (DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
103 +3+DQUOT_DEL_REWRITE) : 0)
98#else 104#else
99#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0 105#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
100#define EXT4_QUOTA_INIT_BLOCKS(sb) 0 106#define EXT4_QUOTA_INIT_BLOCKS(sb) 0
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 78b7ede2efa..bebf8e5bf08 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1137,12 +1137,18 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot);
1137static int ext4_write_info(struct super_block *sb, int type); 1137static int ext4_write_info(struct super_block *sb, int type);
1138static int ext4_quota_on(struct super_block *sb, int type, int format_id, 1138static int ext4_quota_on(struct super_block *sb, int type, int format_id,
1139 struct path *path); 1139 struct path *path);
1140static int ext4_quota_on_sysfile(struct super_block *sb, int type,
1141 int format_id);
1140static int ext4_quota_off(struct super_block *sb, int type); 1142static int ext4_quota_off(struct super_block *sb, int type);
1143static int ext4_quota_off_sysfile(struct super_block *sb, int type);
1141static int ext4_quota_on_mount(struct super_block *sb, int type); 1144static int ext4_quota_on_mount(struct super_block *sb, int type);
1142static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data, 1145static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
1143 size_t len, loff_t off); 1146 size_t len, loff_t off);
1144static ssize_t ext4_quota_write(struct super_block *sb, int type, 1147static ssize_t ext4_quota_write(struct super_block *sb, int type,
1145 const char *data, size_t len, loff_t off); 1148 const char *data, size_t len, loff_t off);
1149static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
1150 unsigned int flags);
1151static int ext4_enable_quotas(struct super_block *sb);
1146 1152
1147static const struct dquot_operations ext4_quota_operations = { 1153static const struct dquot_operations ext4_quota_operations = {
1148 .get_reserved_space = ext4_get_reserved_space, 1154 .get_reserved_space = ext4_get_reserved_space,
@@ -1164,6 +1170,16 @@ static const struct quotactl_ops ext4_qctl_operations = {
1164 .get_dqblk = dquot_get_dqblk, 1170 .get_dqblk = dquot_get_dqblk,
1165 .set_dqblk = dquot_set_dqblk 1171 .set_dqblk = dquot_set_dqblk
1166}; 1172};
1173
1174static const struct quotactl_ops ext4_qctl_sysfile_operations = {
1175 .quota_on_meta = ext4_quota_on_sysfile,
1176 .quota_off = ext4_quota_off_sysfile,
1177 .quota_sync = dquot_quota_sync,
1178 .get_info = dquot_get_dqinfo,
1179 .set_info = dquot_set_dqinfo,
1180 .get_dqblk = dquot_get_dqblk,
1181 .set_dqblk = dquot_set_dqblk
1182};
1167#endif 1183#endif
1168 1184
1169static const struct super_operations ext4_sops = { 1185static const struct super_operations ext4_sops = {
@@ -2661,6 +2677,16 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
2661 "extents feature\n"); 2677 "extents feature\n");
2662 return 0; 2678 return 0;
2663 } 2679 }
2680
2681#ifndef CONFIG_QUOTA
2682 if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
2683 !readonly) {
2684 ext4_msg(sb, KERN_ERR,
2685 "Filesystem with quota feature cannot be mounted RDWR "
2686 "without CONFIG_QUOTA");
2687 return 0;
2688 }
2689#endif /* CONFIG_QUOTA */
2664 return 1; 2690 return 1;
2665} 2691}
2666 2692
@@ -3748,6 +3774,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
3748#ifdef CONFIG_QUOTA 3774#ifdef CONFIG_QUOTA
3749 sb->s_qcop = &ext4_qctl_operations; 3775 sb->s_qcop = &ext4_qctl_operations;
3750 sb->dq_op = &ext4_quota_operations; 3776 sb->dq_op = &ext4_quota_operations;
3777
3778 if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
3779 /* Use qctl operations for hidden quota files. */
3780 sb->s_qcop = &ext4_qctl_sysfile_operations;
3781 }
3751#endif 3782#endif
3752 memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid)); 3783 memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
3753 3784
@@ -3960,6 +3991,16 @@ no_journal:
3960 } else 3991 } else
3961 descr = "out journal"; 3992 descr = "out journal";
3962 3993
3994#ifdef CONFIG_QUOTA
3995 /* Enable quota usage during mount. */
3996 if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
3997 !(sb->s_flags & MS_RDONLY)) {
3998 ret = ext4_enable_quotas(sb);
3999 if (ret)
4000 goto failed_mount7;
4001 }
4002#endif /* CONFIG_QUOTA */
4003
3963 ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " 4004 ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
3964 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts, 4005 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
3965 *sbi->s_es->s_mount_opts ? "; " : "", orig_data); 4006 *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
@@ -4682,16 +4723,26 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
4682 if (sbi->s_journal == NULL) 4723 if (sbi->s_journal == NULL)
4683 ext4_commit_super(sb, 1); 4724 ext4_commit_super(sb, 1);
4684 4725
4726 unlock_super(sb);
4685#ifdef CONFIG_QUOTA 4727#ifdef CONFIG_QUOTA
4686 /* Release old quota file names */ 4728 /* Release old quota file names */
4687 for (i = 0; i < MAXQUOTAS; i++) 4729 for (i = 0; i < MAXQUOTAS; i++)
4688 if (old_opts.s_qf_names[i] && 4730 if (old_opts.s_qf_names[i] &&
4689 old_opts.s_qf_names[i] != sbi->s_qf_names[i]) 4731 old_opts.s_qf_names[i] != sbi->s_qf_names[i])
4690 kfree(old_opts.s_qf_names[i]); 4732 kfree(old_opts.s_qf_names[i]);
4733 if (enable_quota) {
4734 if (sb_any_quota_suspended(sb))
4735 dquot_resume(sb, -1);
4736 else if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
4737 EXT4_FEATURE_RO_COMPAT_QUOTA)) {
4738 err = ext4_enable_quotas(sb);
4739 if (err) {
4740 lock_super(sb);
4741 goto restore_opts;
4742 }
4743 }
4744 }
4691#endif 4745#endif
4692 unlock_super(sb);
4693 if (enable_quota)
4694 dquot_resume(sb, -1);
4695 4746
4696 ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data); 4747 ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
4697 kfree(orig_data); 4748 kfree(orig_data);
@@ -4904,6 +4955,74 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
4904 return dquot_quota_on(sb, type, format_id, path); 4955 return dquot_quota_on(sb, type, format_id, path);
4905} 4956}
4906 4957
4958static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
4959 unsigned int flags)
4960{
4961 int err;
4962 struct inode *qf_inode;
4963 unsigned long qf_inums[MAXQUOTAS] = {
4964 le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
4965 le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
4966 };
4967
4968 BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
4969
4970 if (!qf_inums[type])
4971 return -EPERM;
4972
4973 qf_inode = ext4_iget(sb, qf_inums[type]);
4974 if (IS_ERR(qf_inode)) {
4975 ext4_error(sb, "Bad quota inode # %lu", qf_inums[type]);
4976 return PTR_ERR(qf_inode);
4977 }
4978
4979 err = dquot_enable(qf_inode, type, format_id, flags);
4980 iput(qf_inode);
4981
4982 return err;
4983}
4984
4985/* Enable usage tracking for all quota types. */
4986static int ext4_enable_quotas(struct super_block *sb)
4987{
4988 int type, err = 0;
4989 unsigned long qf_inums[MAXQUOTAS] = {
4990 le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
4991 le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
4992 };
4993
4994 sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
4995 for (type = 0; type < MAXQUOTAS; type++) {
4996 if (qf_inums[type]) {
4997 err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
4998 DQUOT_USAGE_ENABLED);
4999 if (err) {
5000 ext4_warning(sb,
5001 "Failed to enable quota (type=%d) "
5002 "tracking. Please run e2fsck to fix.",
5003 type);
5004 return err;
5005 }
5006 }
5007 }
5008 return 0;
5009}
5010
5011/*
5012 * quota_on function that is used when QUOTA feature is set.
5013 */
5014static int ext4_quota_on_sysfile(struct super_block *sb, int type,
5015 int format_id)
5016{
5017 if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
5018 return -EINVAL;
5019
5020 /*
5021 * USAGE was enabled at mount time. Only need to enable LIMITS now.
5022 */
5023 return ext4_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED);
5024}
5025
4907static int ext4_quota_off(struct super_block *sb, int type) 5026static int ext4_quota_off(struct super_block *sb, int type)
4908{ 5027{
4909 struct inode *inode = sb_dqopt(sb)->files[type]; 5028 struct inode *inode = sb_dqopt(sb)->files[type];
@@ -4930,6 +5049,18 @@ out:
4930 return dquot_quota_off(sb, type); 5049 return dquot_quota_off(sb, type);
4931} 5050}
4932 5051
5052/*
5053 * quota_off function that is used when QUOTA feature is set.
5054 */
5055static int ext4_quota_off_sysfile(struct super_block *sb, int type)
5056{
5057 if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
5058 return -EINVAL;
5059
5060 /* Disable only the limits. */
5061 return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
5062}
5063
4933/* Read data from quotafile - avoid pagecache and such because we cannot afford 5064/* Read data from quotafile - avoid pagecache and such because we cannot afford
4934 * acquiring the locks... As quota files are never truncated and quota code 5065 * acquiring the locks... As quota files are never truncated and quota code
4935 * itself serializes the operations (and no one else should touch the files) 5066 * itself serializes the operations (and no one else should touch the files)