1 From: Aditya Kali <adityakali@google.com>
3 This patch is an attempt towards supporting quotas as first class
4 feature in ext4. It is based on the proposal at:
5 https://ext4.wiki.kernel.org/index.php/Design_For_1st_Class_Quota_in_Ext4
6 This patch introduces a new feature - EXT4_FEATURE_RO_COMPAT_QUOTA which, when
7 turned on, enables quota accounting at mount time iteself. Also, the
8 quota inodes are stored in two additional superblock fields.
9 Some changes introduced by this patch that should be pointed out are:
10 1) Two new ext4-superblock fields - s_usr_quota_inum and s_grp_quota_inum
11 for storing the quota inodes in use.
12 2) If the QUOTA feature and corresponding quota inodes are set in superblock,
13 Quotas are turned on at mount time irrespective of the quota mount options.
14 Thus the mount options 'quota', 'usrquota' and 'grpquota' are completely
15 ignored with the new QUOTA feature flag.
16 3) Default quota inodes are: inode#3 for tracking userquota and inode#4 for
17 tracking group quota. The superblock fields can be set to use other inodes
19 4) mke2fs or tune2fs will initialize these inodes when quota feature is
20 being set. The default reserved inodes will not be visible to user as
22 5) Once quotas are turned on, they cannot be turned off while the FS is
23 mounted. This is because we do not want to let the quota get inconsistent.
24 6) With the QUOTA feature set, since the quota inodes are hidden, some of the
25 utilities from quota-tools will no longer work correctly. Instead, e2fsprogs
26 will include support for fixing the quota files.
27 7) Support is only for the new V2 quota file format.
29 Signed-off-by: Aditya Kali <adityakali@google.com>
31 Index: linux-stage/fs/ext4/ext4.h
32 ===================================================================
33 --- linux-stage.orig/fs/ext4/ext4.h 2012-06-26 11:26:23.345235745 +0200
34 +++ linux-stage/fs/ext4/ext4.h 2012-06-26 11:37:38.250355000 +0200
35 @@ -162,6 +162,8 @@ typedef struct ext4_io_end {
37 #define EXT4_BAD_INO 1 /* Bad blocks inode */
38 #define EXT4_ROOT_INO 2 /* Root inode */
39 +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */
40 +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */
41 #define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */
42 #define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
43 #define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
44 @@ -1016,7 +1018,9 @@ struct ext4_super_block {
45 __u8 s_last_error_func[32]; /* function where the error happened */
46 #define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts)
47 __u8 s_mount_opts[64];
48 - __le32 s_reserved[112]; /* Padding to the end of the block */
49 + __le32 s_usr_quota_inum; /* inode for tracking user quota */
50 + __le32 s_grp_quota_inum; /* inode for tracking group quota */
51 + __le32 s_reserved[110]; /* Padding to the end of the block */
55 @@ -1090,6 +1094,7 @@ struct ext4_sb_info {
57 char *s_qf_names[MAXQUOTAS]; /* Names of quota files with journalled quota */
58 int s_jquota_fmt; /* Format of quota to use */
59 + unsigned long s_qf_inums[MAXQUOTAS]; /* Quota file inodes */
61 unsigned int s_want_extra_isize; /* New inodes should reserve # bytes */
62 struct rb_root system_blks;
63 @@ -1189,6 +1194,8 @@ static inline struct timespec ext4_curre
64 static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
66 return ino == EXT4_ROOT_INO ||
67 + ino == EXT4_USR_QUOTA_INO ||
68 + ino == EXT4_GRP_QUOTA_INO ||
69 ino == EXT4_JOURNAL_INO ||
70 ino == EXT4_RESIZE_INO ||
71 (ino >= EXT4_FIRST_INO(sb) &&
72 @@ -1293,6 +1300,7 @@ EXT4_INODE_BIT_FNS(state, state_flags)
73 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
74 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
75 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
76 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
78 #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
79 #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
80 @@ -1325,7 +1333,8 @@ EXT4_INODE_BIT_FNS(state, state_flags)
81 EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \
82 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
83 EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
84 - EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
85 + EXT4_FEATURE_RO_COMPAT_HUGE_FILE| \
86 + EXT4_FEATURE_RO_COMPAT_QUOTA)
89 * Default values for user and/or group using reserved blocks
90 Index: linux-stage/fs/ext4/ext4_jbd2.h
91 ===================================================================
92 --- linux-stage.orig/fs/ext4/ext4_jbd2.h 2012-06-26 11:35:31.025105000 +0200
93 +++ linux-stage/fs/ext4/ext4_jbd2.h 2012-06-26 11:37:38.250631000 +0200
96 /* Amount of blocks needed for quota update - we know that the structure was
97 * allocated so we need to update only data block */
98 -#define EXT4_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 1 : 0)
99 +#define EXT4_QUOTA_TRANS_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
100 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
102 /* Amount of blocks needed for quota insert/delete - we do some block writes
103 * but inode, sb and group updates are done only once */
104 -#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\
105 - (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0)
106 +#define EXT4_QUOTA_INIT_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
107 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
108 + (DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
109 + +3+DQUOT_INIT_REWRITE) : 0)
111 -#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\
112 - (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
113 +#define EXT4_QUOTA_DEL_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
114 + EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
115 + (DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
116 + +3+DQUOT_DEL_REWRITE) : 0)
118 #define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
119 #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
120 Index: linux-stage/fs/ext4/super.c
121 ===================================================================
122 --- linux-stage.orig/fs/ext4/super.c 2012-06-26 11:37:30.905374000 +0200
123 +++ linux-stage/fs/ext4/super.c 2012-06-26 11:38:30.997488000 +0200
124 @@ -86,6 +86,11 @@ wait_queue_head_t aio_wq[WQ_HASH_SZ];
126 static int bigendian_extents;
129 +static int ext4_acct_on(struct super_block *sb);
130 +static int ext4_acct_off(struct super_block *sb);
133 ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
134 struct ext4_group_desc *bg)
136 @@ -670,6 +675,12 @@ static void ext4_put_super(struct super_
138 ext4_unregister_li_request(sb);
141 + /* disable usage tracking which was enabled at mount time */
142 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
146 flush_workqueue(sbi->dio_unwritten_wq);
147 destroy_workqueue(sbi->dio_unwritten_wq);
149 @@ -2142,14 +2153,22 @@ static void ext4_orphan_cleanup(struct s
151 /* Needed for iput() to work correctly and not trash data */
152 sb->s_flags |= MS_ACTIVE;
153 - /* Turn on quotas so that they are updated correctly */
154 - for (i = 0; i < MAXQUOTAS; i++) {
155 - if (EXT4_SB(sb)->s_qf_names[i]) {
156 - int ret = ext4_quota_on_mount(sb, i);
158 - ext4_msg(sb, KERN_ERR,
159 - "Cannot turn on journaled "
160 - "quota: error %d", ret);
161 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
163 + ret = ext4_acct_on(sb);
165 + ext4_msg(sb, KERN_ERR, "Failed to turn on usage "
166 + "tracking for quota: error %d", ret);
168 + /* Turn on quotas so that they are updated correctly */
169 + for (i = 0; i < MAXQUOTAS; i++) {
170 + if (EXT4_SB(sb)->s_qf_names[i]) {
171 + int ret = ext4_quota_on_mount(sb, i);
173 + ext4_msg(sb, KERN_ERR,
174 + "Cannot turn on journaled "
175 + "quota: error %d", ret);
180 @@ -2193,10 +2212,14 @@ static void ext4_orphan_cleanup(struct s
181 ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
182 PLURAL(nr_truncates));
184 - /* Turn quotas off */
185 - for (i = 0; i < MAXQUOTAS; i++) {
186 - if (sb_dqopt(sb)->files[i])
187 - vfs_quota_off(sb, i, 0);
188 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
191 + /* Turn quotas off */
192 + for (i = 0; i < MAXQUOTAS; i++) {
193 + if (sb_dqopt(sb)->files[i])
194 + vfs_quota_off(sb, i, 0);
198 sb->s_flags = s_flags; /* Restore MS_RDONLY status */
199 @@ -3395,6 +3418,15 @@ static int ext4_fill_super(struct super_
201 sb->s_qcop = &ext4_qctl_operations;
202 sb->dq_op = &ext4_quota_operations;
204 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
205 + /* Use new qctl operations with quota on function that does not
206 + * require user specified quota file path. */
207 + sb->s_qcop = &ext4_qctl_operations;
209 + sbi->s_qf_inums[USRQUOTA] = es->s_usr_quota_inum;
210 + sbi->s_qf_inums[GRPQUOTA] = es->s_grp_quota_inum;
213 INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
214 mutex_init(&sbi->s_orphan_lock);
215 @@ -3622,8 +3654,31 @@ no_journal:
217 descr = "out journal";
219 - ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
220 - "Opts: %s%s", descr, sbi->s_es->s_mount_opts,
222 + /* Enable space tracking during mount, enforcement can be enabled/disable
223 + * later with quota_on/off */
224 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
225 + !(sb->s_flags & MS_RDONLY)) {
226 + ret = ext4_acct_on(sb);
228 + ext4_msg(sb, KERN_ERR, "Can't enable usage tracking on "
229 + "a filesystem with the QUOTA feature set");
230 + goto failed_mount4;
234 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
235 + !(sb->s_flags & MS_RDONLY))
236 + ext4_msg(sb, KERN_WARNING, "Mounting a filesystem with the "
237 + "QUOTA feature set whereas the kernel does not "
238 + "support quota, e2fsck will be required to fix usage "
241 +#endif /* CONFIG_QUOTA */
243 + ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. quota=%s. "
244 + "Opts: %s%s", descr, sb_any_quota_loaded(sb) ? "on" : "off",
245 + sbi->s_es->s_mount_opts,
246 *sbi->s_es->s_mount_opts ? "; " : "");
249 @@ -3981,6 +4036,12 @@ static int ext4_commit_super(struct supe
250 &EXT4_SB(sb)->s_freeblocks_counter));
251 es->s_free_inodes_count = cpu_to_le32(percpu_counter_sum_positive(
252 &EXT4_SB(sb)->s_freeinodes_counter));
254 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
255 + es->s_usr_quota_inum = EXT4_SB(sb)->s_qf_inums[USRQUOTA];
256 + es->s_grp_quota_inum = EXT4_SB(sb)->s_qf_inums[GRPQUOTA];
260 BUFFER_TRACE(sbh, "marking dirty");
261 mark_buffer_dirty(sbh);
262 @@ -4531,6 +4592,22 @@ static int ext4_quota_on(struct super_bl
266 + /* When QUOTA feature is set, quota on enables enforcement, accounting
267 + * being already enabled at mount time */
268 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
269 + struct inode *qf_inode;
271 + if (!EXT4_SB(sb)->s_qf_inums[type])
273 + qf_inode = ext4_iget(sb, EXT4_SB(sb)->s_qf_inums[type]);
274 + if (IS_ERR(qf_inode))
275 + return PTR_ERR(qf_inode);
276 + err = vfs_quota_enable(qf_inode, type, QFMT_VFS_V1,
277 + DQUOT_LIMITS_ENABLED);
282 if (!test_opt(sb, QUOTA))
284 /* When remounting, no checks are needed and in fact, name is NULL */
285 @@ -4630,9 +4707,114 @@ static int ext4_quota_off(struct super_b
289 + /* When QUOTA feature is set, quota off just disables enforcement but
290 + * leaves accounting on */
291 + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
292 + return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED);
294 return vfs_quota_off(sb, type, remount);
298 + * New quota_on function that is used to turn accounting on when QUOTA
301 +static int ext4_acct_on(struct super_block *sb)
303 + struct inode *qf_inode[MAXQUOTAS];
306 + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) ||
307 + !EXT4_SB(sb)->s_qf_inums[USRQUOTA] ||
308 + !EXT4_SB(sb)->s_qf_inums[GRPQUOTA])
311 + qf_inode[USRQUOTA] = ext4_iget(sb, EXT4_SB(sb)->s_qf_inums[USRQUOTA]);
312 + if (IS_ERR(qf_inode[USRQUOTA])) {
313 + EXT4_SB(sb)->s_qf_inums[USRQUOTA] = 0;
314 + return PTR_ERR(qf_inode[USRQUOTA]);
316 + qf_inode[GRPQUOTA] = ext4_iget(sb, EXT4_SB(sb)->s_qf_inums[GRPQUOTA]);
317 + if (IS_ERR(qf_inode[GRPQUOTA])) {
318 + iput(qf_inode[USRQUOTA]);
319 + EXT4_SB(sb)->s_qf_inums[GRPQUOTA] = 0;
320 + return PTR_ERR(qf_inode[GRPQUOTA]);
324 + * When we journal data on quota file, we have to flush journal to see
325 + * all updates to the file when we bypass pagecache...
327 + if (EXT4_SB(sb)->s_journal) {
329 + * We don't need to lock updates but journal_flush() could
330 + * otherwise be livelocked...
332 + jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
333 + rc = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
334 + jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
336 + iput(qf_inode[USRQUOTA]);
337 + iput(qf_inode[GRPQUOTA]);
342 + /* only enable quota accounting by default */
343 + rc = vfs_quota_enable(qf_inode[USRQUOTA], USRQUOTA, QFMT_VFS_V1,
344 + DQUOT_USAGE_ENABLED);
345 + iput(qf_inode[USRQUOTA]);
347 + iput(qf_inode[GRPQUOTA]);
350 + rc = vfs_quota_enable(qf_inode[GRPQUOTA], GRPQUOTA, QFMT_VFS_V1,
351 + DQUOT_USAGE_ENABLED);
352 + iput(qf_inode[GRPQUOTA]);
357 + * New quota_on function that is used to turn off accounting when QUOTA feature
360 +static int ext4_acct_off(struct super_block *sb)
364 + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
367 + for (type = 0; type < MAXQUOTAS; type++) {
368 + struct inode *inode = sb_dqopt(sb)->files[type];
373 + /* Update modification times of quota files when userspace can
374 + * start looking at them */
375 + handle = ext4_journal_start(inode, 1);
376 + if (IS_ERR(handle))
379 + inode->i_mtime = inode->i_ctime = CURRENT_TIME;
380 + ext4_mark_inode_dirty(handle, inode);
381 + ext4_journal_stop(handle);
385 + for (type = 0; type < MAXQUOTAS; type++) {
387 + ret = vfs_quota_disable(sb, type,
388 + DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
397 /* Read data from quotafile - avoid pagecache and such because we cannot afford
398 * acquiring the locks... As quota files are never truncated and quota code
399 * itself serializes the operations (and noone else should touch the files)