1 From 7c319d328505b7781b65238ae9f53293b5ee0ca8 Mon Sep 17 00:00:00 2001
2 From: Aditya Kali <adityakali@google.com>
3 Date: Sun, 22 Jul 2012 20:21:31 -0400
4 Subject: ext4: make quota as first class supported feature
5 Git-commit: 7c319d32, 281b5995
6 Patch-mainline: v3.6-rc1
8 This patch adds support for quotas as a first class feature in ext4;
9 which is to say, the quota files are stored in hidden inodes as file
10 system metadata, instead of as separate files visible in the file system
13 It is based on the proposal at:
14 https://ext4.wiki.kernel.org/index.php/Design_For_1st_Class_Quota_in_Ext4
16 This patch introduces a new feature - EXT4_FEATURE_RO_COMPAT_QUOTA
17 which, when turned on, enables quota accounting at mount time
18 iteself. Also, the quota inodes are stored in two additional superblock
19 fields. Some changes introduced by this patch that should be pointed
22 1) Two new ext4-superblock fields - s_usr_quota_inum and
23 s_grp_quota_inum for storing the quota inodes in use.
24 2) Default quota inodes are: inode#3 for tracking userquota and inode#4
25 for tracking group quota. The superblock fields can be set to use
27 3) If the QUOTA feature and corresponding quota inodes are set in
28 superblock, the quota usage tracking is turned on at mount time. On
29 'quotaon' ioctl, the quota limits enforcement is turned
30 on. 'quotaoff' ioctl turns off only the limits enforcement in this
32 4) When QUOTA feature is in use, the quota mount options 'quota',
33 'usrquota', 'grpquota' are ignored by the kernel.
34 5) mke2fs or tune2fs can be used to set the QUOTA feature and initialize
35 quota inodes. The default reserved inodes will not be visible to user
37 6) The quota-tools will need to be modified to support hidden quota
38 files on ext4. E2fsprogs will also include support for creating and
40 7) Support is only for the new V2 quota file format.
42 Tested-by: Jan Kara <jack@suse.cz>
43 Reviewed-by: Jan Kara <jack@suse.cz>
44 Reviewed-by: Johann Lombardi <johann@whamcloud.com>
45 Signed-off-by: Aditya Kali <adityakali@google.com>
46 Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
47 Acked-by: Jeff Mahoney <jeffm@suse.com>
49 fs/ext4/ext4.h | 10 +++
50 fs/ext4/ext4_jbd2.h | 16 ++++--
51 fs/ext4/super.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++--
52 3 files changed, 153 insertions(+), 10 deletions(-)
56 @@ -1063,7 +1063,10 @@ struct ext4_super_block {
57 __u8 s_last_error_func[32]; /* function where the error happened */
58 #define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts)
59 __u8 s_mount_opts[64];
60 - __le32 s_reserved[112]; /* Padding to the end of the block */
61 + __le32 s_usr_quota_inum; /* inode for tracking user quota */
62 + __le32 s_grp_quota_inum; /* inode for tracking group quota */
63 + __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
64 + __le32 s_reserved[109]; /* Padding to the end of the block */
67 #define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START)
68 @@ -1238,6 +1241,8 @@ static inline struct timespec ext4_curre
69 static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
71 return ino == EXT4_ROOT_INO ||
72 + ino == EXT4_USR_QUOTA_INO ||
73 + ino == EXT4_GRP_QUOTA_INO ||
74 ino == EXT4_JOURNAL_INO ||
75 ino == EXT4_RESIZE_INO ||
76 (ino >= EXT4_FIRST_INO(sb) &&
77 @@ -1398,7 +1403,8 @@ static inline void ext4_clear_state_flag
78 EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \
79 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
80 EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
81 - EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
82 + EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
83 + EXT4_FEATURE_RO_COMPAT_QUOTA)
86 * Default values for user and/or group using reserved blocks
87 --- a/fs/ext4/ext4_jbd2.h
88 +++ b/fs/ext4/ext4_jbd2.h
91 /* Amount of blocks needed for quota update - we know that the structure was
92 * allocated so we need to update only data block */
93 -#define EXT4_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 1 : 0)
94 +#define EXT4_QUOTA_TRANS_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
95 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
97 /* Amount of blocks needed for quota insert/delete - we do some block writes
98 * but inode, sb and group updates are done only once */
99 -#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\
100 - (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0)
101 +#define EXT4_QUOTA_INIT_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
102 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
103 + (DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
104 + +3+DQUOT_INIT_REWRITE) : 0)
106 -#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\
107 - (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
108 +#define EXT4_QUOTA_DEL_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
109 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
110 + (DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
111 + +3+DQUOT_DEL_REWRITE) : 0)
113 #define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
114 #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
115 --- a/fs/ext4/super.c
116 +++ b/fs/ext4/super.c
117 @@ -1241,12 +1241,18 @@ static int ext4_mark_dquot_dirty(struct
118 static int ext4_write_info(struct super_block *sb, int type);
119 static int ext4_quota_on(struct super_block *sb, int type, int format_id,
121 +static int ext4_quota_on_sysfile(struct super_block *sb, int type,
123 static int ext4_quota_off(struct super_block *sb, int type);
124 +static int ext4_quota_off_sysfile(struct super_block *sb, int type);
125 static int ext4_quota_on_mount(struct super_block *sb, int type);
126 static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
127 size_t len, loff_t off);
128 static ssize_t ext4_quota_write(struct super_block *sb, int type,
129 const char *data, size_t len, loff_t off);
130 +static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
131 + unsigned int flags);
132 +static int ext4_enable_quotas(struct super_block *sb);
134 static const struct dquot_operations ext4_quota_operations = {
135 .get_reserved_space = ext4_get_reserved_space,
136 @@ -1268,6 +1274,16 @@ static const struct quotactl_ops ext4_qc
137 .get_dqblk = dquot_get_dqblk,
138 .set_dqblk = dquot_set_dqblk
141 +static const struct quotactl_ops ext4_qctl_sysfile_operations = {
142 + .quota_on_meta = ext4_quota_on_sysfile,
143 + .quota_off = ext4_quota_off_sysfile,
144 + .quota_sync = dquot_quota_sync,
145 + .get_info = dquot_get_dqinfo,
146 + .set_info = dquot_set_dqinfo,
147 + .get_dqblk = dquot_get_dqblk,
148 + .set_dqblk = dquot_set_dqblk
152 static const struct super_operations ext4_sops = {
153 @@ -2689,6 +2705,16 @@ static int ext4_feature_set_ok(struct su
158 +#ifndef CONFIG_QUOTA
159 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
161 + ext4_msg(sb, KERN_ERR,
162 + "Filesystem with quota feature cannot be mounted RDWR "
163 + "without CONFIG_QUOTA");
166 +#endif /* CONFIG_QUOTA */
170 @@ -3528,6 +3554,11 @@ static int ext4_fill_super(struct super_
172 sb->s_qcop = &ext4_qctl_operations;
173 sb->dq_op = &ext4_quota_operations;
175 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
176 + /* Use qctl operations for hidden quota files. */
177 + sb->s_qcop = &ext4_qctl_sysfile_operations;
180 memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
182 @@ -3755,6 +3786,16 @@ no_journal:
184 descr = "out journal";
187 + /* Enable quota usage during mount. */
188 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
189 + !(sb->s_flags & MS_RDONLY)) {
190 + ret = ext4_enable_quotas(sb);
192 + goto failed_mount7;
194 +#endif /* CONFIG_QUOTA */
196 ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
197 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
198 *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
199 @@ -4493,16 +4534,26 @@ static int ext4_remount(struct super_blo
200 if (sbi->s_journal == NULL)
201 ext4_commit_super(sb, 1);
205 /* Release old quota file names */
206 for (i = 0; i < MAXQUOTAS; i++)
207 if (old_opts.s_qf_names[i] &&
208 old_opts.s_qf_names[i] != sbi->s_qf_names[i])
209 kfree(old_opts.s_qf_names[i]);
210 + if (enable_quota) {
211 + if (sb_any_quota_suspended(sb))
212 + dquot_resume(sb, -1);
213 + else if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
214 + EXT4_FEATURE_RO_COMPAT_QUOTA)) {
215 + err = ext4_enable_quotas(sb);
225 - dquot_resume(sb, -1);
227 ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
229 @@ -4750,6 +4801,74 @@ static int ext4_quota_on(struct super_bl
230 return dquot_quota_on(sb, type, format_id, path);
233 +static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
234 + unsigned int flags)
237 + struct inode *qf_inode;
238 + unsigned long qf_inums[MAXQUOTAS] = {
239 + le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
240 + le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
243 + BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
245 + if (!qf_inums[type])
248 + qf_inode = ext4_iget(sb, qf_inums[type]);
249 + if (IS_ERR(qf_inode)) {
250 + ext4_error(sb, "Bad quota inode # %lu", qf_inums[type]);
251 + return PTR_ERR(qf_inode);
254 + err = dquot_enable(qf_inode, type, format_id, flags);
260 +/* Enable usage tracking for all quota types. */
261 +static int ext4_enable_quotas(struct super_block *sb)
264 + unsigned long qf_inums[MAXQUOTAS] = {
265 + le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
266 + le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
269 + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
270 + for (type = 0; type < MAXQUOTAS; type++) {
271 + if (qf_inums[type]) {
272 + err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
273 + DQUOT_USAGE_ENABLED);
276 + "Failed to enable quota (type=%d) "
277 + "tracking. Please run e2fsck to fix.",
287 + * quota_on function that is used when QUOTA feature is set.
289 +static int ext4_quota_on_sysfile(struct super_block *sb, int type,
292 + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
296 + * USAGE was enabled at mount time. Only need to enable LIMITS now.
298 + return ext4_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED);
301 static int ext4_quota_off(struct super_block *sb, int type)
303 struct inode *inode = sb_dqopt(sb)->files[type];
304 @@ -4776,6 +4895,18 @@ out:
305 return dquot_quota_off(sb, type);
309 + * quota_off function that is used when QUOTA feature is set.
311 +static int ext4_quota_off_sysfile(struct super_block *sb, int type)
313 + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
316 + /* Disable only the limits. */
317 + return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
320 /* Read data from quotafile - avoid pagecache and such because we cannot afford
321 * acquiring the locks... As quota files are never truncated and quota code
322 * itself serializes the operations (and no one else should touch the files)