Whamcloud - gitweb
LU-1182 ldiskfs-osd: space accounting support
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / ext4-quota-first-class.patch
1 From: Aditya Kali <adityakali@google.com>
2
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
18    as well.
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
21    regular files.
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.
28
29 Signed-off-by: Aditya Kali <adityakali@google.com>
30 ---
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 {
36   */
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 */
52  };
53  
54  #ifdef __KERNEL__
55 @@ -1090,6 +1094,7 @@ struct ext4_sb_info {
56  #ifdef CONFIG_QUOTA
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 */
60  #endif
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)
65  {
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
77  
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)
87  
88  /*
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
94 @@ -89,14 +89,20 @@
95  #ifdef CONFIG_QUOTA
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)) ?\
101 +               1 : 0)
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)
110  
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)
117  #else
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];
125  
126  static int bigendian_extents;
127  
128 +#ifdef CONFIG_QUOTA
129 +static int ext4_acct_on(struct super_block *sb);
130 +static int ext4_acct_off(struct super_block *sb);
131 +#endif
132 +
133  ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
134                                struct ext4_group_desc *bg)
135  {
136 @@ -670,6 +675,12 @@ static void ext4_put_super(struct super_
137  
138         ext4_unregister_li_request(sb);
139  
140 +#ifdef CONFIG_QUOTA
141 +       /* disable usage tracking which was enabled at mount time */
142 +       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
143 +               ext4_acct_off(sb);
144 +#endif
145 +
146         flush_workqueue(sbi->dio_unwritten_wq);
147         destroy_workqueue(sbi->dio_unwritten_wq);
148  
149 @@ -2142,14 +2153,22 @@ static void ext4_orphan_cleanup(struct s
150  #ifdef CONFIG_QUOTA
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);
157 -                       if (ret < 0)
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)) {
162 +               int ret;
163 +               ret = ext4_acct_on(sb);
164 +               if (ret)
165 +                       ext4_msg(sb, KERN_ERR, "Failed to turn on usage "
166 +                                "tracking for quota: error %d", ret);
167 +       } else {
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);
172 +                               if (ret < 0)
173 +                                       ext4_msg(sb, KERN_ERR,
174 +                                               "Cannot turn on journaled "
175 +                                               "quota: error %d", ret);
176 +                       }
177                 }
178         }
179  #endif
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));
183  #ifdef CONFIG_QUOTA
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)) {
189 +               ext4_acct_off(sb);
190 +       } else {
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);
195 +               }
196         }
197  #endif
198         sb->s_flags = s_flags; /* Restore MS_RDONLY status */
199 @@ -3395,6 +3418,15 @@ static int ext4_fill_super(struct super_
200  #ifdef CONFIG_QUOTA
201         sb->s_qcop = &ext4_qctl_operations;
202         sb->dq_op = &ext4_quota_operations;
203 +
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;
208 +
209 +               sbi->s_qf_inums[USRQUOTA] = es->s_usr_quota_inum;
210 +               sbi->s_qf_inums[GRPQUOTA] = es->s_grp_quota_inum;
211 +       }
212  #endif
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:
216         } else
217                 descr = "out journal";
218  
219 -       ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
220 -                "Opts: %s%s", descr, sbi->s_es->s_mount_opts,
221 +#ifdef CONFIG_QUOTA
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);
227 +               if (ret) {
228 +                       ext4_msg(sb, KERN_ERR, "Can't enable usage tracking on "
229 +                                "a filesystem with the QUOTA feature set");
230 +                       goto failed_mount4;
231 +               }
232 +       }
233 +#else
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 "
239 +                        "information");
240 +
241 +#endif  /* CONFIG_QUOTA */
242 +
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 ? "; " : "");
247  
248         lock_kernel();
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));
253 +#ifdef CONFIG_QUOTA
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];
257 +       }
258 +#endif
259         sb->s_dirt = 0;
260         BUFFER_TRACE(sbh, "marking dirty");
261         mark_buffer_dirty(sbh);
262 @@ -4531,6 +4592,22 @@ static int ext4_quota_on(struct super_bl
263         int err;
264         struct path path;
265  
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;
270 +
271 +               if (!EXT4_SB(sb)->s_qf_inums[type])
272 +                       return -EINVAL;
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);
278 +               iput(qf_inode);
279 +               return err;
280 +       }
281 +
282         if (!test_opt(sb, QUOTA))
283                 return -EINVAL;
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
286                 iput(inode);
287         }
288  
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);
293 +
294         return vfs_quota_off(sb, type, remount);
295  }
296  
297 +/*
298 + * New quota_on function that is used to turn accounting on when QUOTA
299 + * feature is set.
300 + */
301 +static int ext4_acct_on(struct super_block *sb)
302 +{
303 +       struct inode *qf_inode[MAXQUOTAS];
304 +       int           rc;
305 +
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])
309 +               return -EINVAL;
310 +
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]);
315 +       }
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]);
321 +       }
322 +
323 +       /*
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...
326 +        */
327 +       if (EXT4_SB(sb)->s_journal) {
328 +               /*
329 +                * We don't need to lock updates but journal_flush() could
330 +                * otherwise be livelocked...
331 +                */
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);
335 +               if (rc) {
336 +                       iput(qf_inode[USRQUOTA]);
337 +                       iput(qf_inode[GRPQUOTA]);
338 +                       return rc;
339 +               }
340 +       }
341 +
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]);
346 +       if (rc) {
347 +               iput(qf_inode[GRPQUOTA]);
348 +               return rc;
349 +       }
350 +       rc = vfs_quota_enable(qf_inode[GRPQUOTA], GRPQUOTA, QFMT_VFS_V1,
351 +                             DQUOT_USAGE_ENABLED);
352 +       iput(qf_inode[GRPQUOTA]);
353 +       return rc;
354 +}
355 +
356 +/*
357 + * New quota_on function that is used to turn off accounting when QUOTA feature
358 + * is set.
359 + */
360 +static int ext4_acct_off(struct super_block *sb)
361 +{
362 +       int type, rc = 0;
363 +
364 +       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
365 +               return -EINVAL;
366 +
367 +       for (type = 0; type < MAXQUOTAS; type++) {
368 +               struct inode *inode = sb_dqopt(sb)->files[type];
369 +               handle_t     *handle;
370 +
371 +               if (!inode)
372 +                       continue;
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))
377 +                       goto out;
378 +
379 +               inode->i_mtime = inode->i_ctime = CURRENT_TIME;
380 +               ext4_mark_inode_dirty(handle, inode);
381 +               ext4_journal_stop(handle);
382 +       }
383 +
384 +out:
385 +       for (type = 0; type < MAXQUOTAS; type++) {
386 +               int ret;
387 +               ret = vfs_quota_disable(sb, type,
388 +                                   DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
389 +               if (!rc && ret)
390 +                       rc = ret;
391 +       }
392 +       return rc;
393 +}
394 +
395 +
396 +
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)