Skip to content

Commit 93c0f79

Browse files
committed
Merge tag 'metadir-quotas-6.13_2024-11-05' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into staging-merge
xfs: persist quota options with metadir [v5.5 07/10] Store the quota files in the metadata directory tree instead of the superblock. Since we're introducing a new incompat feature flag, let's also make the mount process bring up quotas in whatever state they were when the filesystem was last unmounted, instead of requiring sysadmins to remember that themselves. With a bit of luck, this should all go splendidly. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2 parents b939bcd + d5d9dd5 commit 93c0f79

File tree

11 files changed

+589
-64
lines changed

11 files changed

+589
-64
lines changed

fs/xfs/libxfs/xfs_dquot_buf.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include "xfs_trans.h"
1717
#include "xfs_qm.h"
1818
#include "xfs_error.h"
19+
#include "xfs_health.h"
20+
#include "xfs_metadir.h"
21+
#include "xfs_metafile.h"
1922

2023
int
2124
xfs_calc_dquots_per_chunk(
@@ -323,3 +326,190 @@ xfs_dquot_to_disk_ts(
323326

324327
return cpu_to_be32(t);
325328
}
329+
330+
inline unsigned int
331+
xfs_dqinode_sick_mask(xfs_dqtype_t type)
332+
{
333+
switch (type) {
334+
case XFS_DQTYPE_USER:
335+
return XFS_SICK_FS_UQUOTA;
336+
case XFS_DQTYPE_GROUP:
337+
return XFS_SICK_FS_GQUOTA;
338+
case XFS_DQTYPE_PROJ:
339+
return XFS_SICK_FS_PQUOTA;
340+
}
341+
342+
ASSERT(0);
343+
return 0;
344+
}
345+
346+
/*
347+
* Load the inode for a given type of quota, assuming that the sb fields have
348+
* been sorted out. This is not true when switching quota types on a V4
349+
* filesystem, so do not use this function for that. If metadir is enabled,
350+
* @dp must be the /quota metadir.
351+
*
352+
* Returns -ENOENT if the quota inode field is NULLFSINO; 0 and an inode on
353+
* success; or a negative errno.
354+
*/
355+
int
356+
xfs_dqinode_load(
357+
struct xfs_trans *tp,
358+
struct xfs_inode *dp,
359+
xfs_dqtype_t type,
360+
struct xfs_inode **ipp)
361+
{
362+
struct xfs_mount *mp = tp->t_mountp;
363+
struct xfs_inode *ip;
364+
enum xfs_metafile_type metafile_type = xfs_dqinode_metafile_type(type);
365+
int error;
366+
367+
if (!xfs_has_metadir(mp)) {
368+
xfs_ino_t ino;
369+
370+
switch (type) {
371+
case XFS_DQTYPE_USER:
372+
ino = mp->m_sb.sb_uquotino;
373+
break;
374+
case XFS_DQTYPE_GROUP:
375+
ino = mp->m_sb.sb_gquotino;
376+
break;
377+
case XFS_DQTYPE_PROJ:
378+
ino = mp->m_sb.sb_pquotino;
379+
break;
380+
default:
381+
ASSERT(0);
382+
return -EFSCORRUPTED;
383+
}
384+
385+
/* Should have set 0 to NULLFSINO when loading superblock */
386+
if (ino == NULLFSINO)
387+
return -ENOENT;
388+
389+
error = xfs_trans_metafile_iget(tp, ino, metafile_type, &ip);
390+
} else {
391+
error = xfs_metadir_load(tp, dp, xfs_dqinode_path(type),
392+
metafile_type, &ip);
393+
if (error == -ENOENT)
394+
return error;
395+
}
396+
if (error) {
397+
if (xfs_metadata_is_sick(error))
398+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
399+
return error;
400+
}
401+
402+
if (XFS_IS_CORRUPT(mp, ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS &&
403+
ip->i_df.if_format != XFS_DINODE_FMT_BTREE)) {
404+
xfs_irele(ip);
405+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
406+
return -EFSCORRUPTED;
407+
}
408+
409+
if (XFS_IS_CORRUPT(mp, ip->i_projid != 0)) {
410+
xfs_irele(ip);
411+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
412+
return -EFSCORRUPTED;
413+
}
414+
415+
*ipp = ip;
416+
return 0;
417+
}
418+
419+
/* Create a metadata directory quota inode. */
420+
int
421+
xfs_dqinode_metadir_create(
422+
struct xfs_inode *dp,
423+
xfs_dqtype_t type,
424+
struct xfs_inode **ipp)
425+
{
426+
struct xfs_metadir_update upd = {
427+
.dp = dp,
428+
.metafile_type = xfs_dqinode_metafile_type(type),
429+
.path = xfs_dqinode_path(type),
430+
};
431+
int error;
432+
433+
error = xfs_metadir_start_create(&upd);
434+
if (error)
435+
return error;
436+
437+
error = xfs_metadir_create(&upd, S_IFREG);
438+
if (error)
439+
return error;
440+
441+
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
442+
443+
error = xfs_metadir_commit(&upd);
444+
if (error)
445+
return error;
446+
447+
xfs_finish_inode_setup(upd.ip);
448+
*ipp = upd.ip;
449+
return 0;
450+
}
451+
452+
#ifndef __KERNEL__
453+
/* Link a metadata directory quota inode. */
454+
int
455+
xfs_dqinode_metadir_link(
456+
struct xfs_inode *dp,
457+
xfs_dqtype_t type,
458+
struct xfs_inode *ip)
459+
{
460+
struct xfs_metadir_update upd = {
461+
.dp = dp,
462+
.metafile_type = xfs_dqinode_metafile_type(type),
463+
.path = xfs_dqinode_path(type),
464+
.ip = ip,
465+
};
466+
int error;
467+
468+
error = xfs_metadir_start_link(&upd);
469+
if (error)
470+
return error;
471+
472+
error = xfs_metadir_link(&upd);
473+
if (error)
474+
return error;
475+
476+
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
477+
478+
return xfs_metadir_commit(&upd);
479+
}
480+
#endif /* __KERNEL__ */
481+
482+
/* Create the parent directory for all quota inodes and load it. */
483+
int
484+
xfs_dqinode_mkdir_parent(
485+
struct xfs_mount *mp,
486+
struct xfs_inode **dpp)
487+
{
488+
if (!mp->m_metadirip) {
489+
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
490+
return -EFSCORRUPTED;
491+
}
492+
493+
return xfs_metadir_mkdir(mp->m_metadirip, "quota", dpp);
494+
}
495+
496+
/*
497+
* Load the parent directory of all quota inodes. Pass the inode to the caller
498+
* because quota functions (e.g. QUOTARM) can be called on the quota files even
499+
* if quotas are not enabled.
500+
*/
501+
int
502+
xfs_dqinode_load_parent(
503+
struct xfs_trans *tp,
504+
struct xfs_inode **dpp)
505+
{
506+
struct xfs_mount *mp = tp->t_mountp;
507+
508+
if (!mp->m_metadirip) {
509+
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
510+
return -EFSCORRUPTED;
511+
}
512+
513+
return xfs_metadir_load(tp, mp->m_metadirip, "quota", XFS_METAFILE_DIR,
514+
dpp);
515+
}

fs/xfs/libxfs/xfs_fs.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,13 @@ struct xfs_scrub_vec_head {
825825
#define XFS_SCRUB_METAPATH_RTDIR (1) /* rtrgroups metadir */
826826
#define XFS_SCRUB_METAPATH_RTBITMAP (2) /* per-rtg bitmap */
827827
#define XFS_SCRUB_METAPATH_RTSUMMARY (3) /* per-rtg summary */
828+
#define XFS_SCRUB_METAPATH_QUOTADIR (4) /* quota metadir */
829+
#define XFS_SCRUB_METAPATH_USRQUOTA (5) /* user quota */
830+
#define XFS_SCRUB_METAPATH_GRPQUOTA (6) /* group quota */
831+
#define XFS_SCRUB_METAPATH_PRJQUOTA (7) /* project quota */
828832

829833
/* Number of metapath sm_ino values */
830-
#define XFS_SCRUB_METAPATH_NR (4)
834+
#define XFS_SCRUB_METAPATH_NR (8)
831835

832836
/*
833837
* ioctl limits

fs/xfs/libxfs/xfs_quota_defs.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,47 @@ time64_t xfs_dquot_from_disk_ts(struct xfs_disk_dquot *ddq,
143143
__be32 dtimer);
144144
__be32 xfs_dquot_to_disk_ts(struct xfs_dquot *ddq, time64_t timer);
145145

146+
static inline const char *
147+
xfs_dqinode_path(xfs_dqtype_t type)
148+
{
149+
switch (type) {
150+
case XFS_DQTYPE_USER:
151+
return "user";
152+
case XFS_DQTYPE_GROUP:
153+
return "group";
154+
case XFS_DQTYPE_PROJ:
155+
return "project";
156+
}
157+
158+
ASSERT(0);
159+
return NULL;
160+
}
161+
162+
static inline enum xfs_metafile_type
163+
xfs_dqinode_metafile_type(xfs_dqtype_t type)
164+
{
165+
switch (type) {
166+
case XFS_DQTYPE_USER:
167+
return XFS_METAFILE_USRQUOTA;
168+
case XFS_DQTYPE_GROUP:
169+
return XFS_METAFILE_GRPQUOTA;
170+
case XFS_DQTYPE_PROJ:
171+
return XFS_METAFILE_PRJQUOTA;
172+
}
173+
174+
ASSERT(0);
175+
return XFS_METAFILE_UNKNOWN;
176+
}
177+
178+
unsigned int xfs_dqinode_sick_mask(xfs_dqtype_t type);
179+
180+
int xfs_dqinode_load(struct xfs_trans *tp, struct xfs_inode *dp,
181+
xfs_dqtype_t type, struct xfs_inode **ipp);
182+
int xfs_dqinode_metadir_create(struct xfs_inode *dp, xfs_dqtype_t type,
183+
struct xfs_inode **ipp);
184+
int xfs_dqinode_metadir_link(struct xfs_inode *dp, xfs_dqtype_t type,
185+
struct xfs_inode *ip);
186+
int xfs_dqinode_mkdir_parent(struct xfs_mount *mp, struct xfs_inode **dpp);
187+
int xfs_dqinode_load_parent(struct xfs_trans *tp, struct xfs_inode **dpp);
188+
146189
#endif /* __XFS_QUOTA_H__ */

fs/xfs/libxfs/xfs_sb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ xfs_sb_quota_to_disk(
858858

859859
if (xfs_sb_is_v5(from) &&
860860
(from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)) {
861+
to->sb_qflags = cpu_to_be16(from->sb_qflags);
861862
to->sb_uquotino = cpu_to_be64(0);
862863
to->sb_gquotino = cpu_to_be64(0);
863864
to->sb_pquotino = cpu_to_be64(0);

fs/xfs/scrub/metapath.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,74 @@ xchk_setup_metapath_rtginode(
165165
# define xchk_setup_metapath_rtginode(...) (-ENOENT)
166166
#endif /* CONFIG_XFS_RT */
167167

168+
#ifdef CONFIG_XFS_QUOTA
169+
/* Scan the /quota directory itself. */
170+
static int
171+
xchk_setup_metapath_quotadir(
172+
struct xfs_scrub *sc)
173+
{
174+
struct xfs_trans *tp;
175+
struct xfs_inode *dp = NULL;
176+
int error;
177+
178+
error = xfs_trans_alloc_empty(sc->mp, &tp);
179+
if (error)
180+
return error;
181+
182+
error = xfs_dqinode_load_parent(tp, &dp);
183+
xfs_trans_cancel(tp);
184+
if (error)
185+
return error;
186+
187+
error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
188+
kasprintf(GFP_KERNEL, "quota"), dp);
189+
xfs_irele(dp);
190+
return error;
191+
}
192+
193+
/* Scan a quota inode under the /quota directory. */
194+
static int
195+
xchk_setup_metapath_dqinode(
196+
struct xfs_scrub *sc,
197+
xfs_dqtype_t type)
198+
{
199+
struct xfs_trans *tp = NULL;
200+
struct xfs_inode *dp = NULL;
201+
struct xfs_inode *ip = NULL;
202+
const char *path;
203+
int error;
204+
205+
error = xfs_trans_alloc_empty(sc->mp, &tp);
206+
if (error)
207+
return error;
208+
209+
error = xfs_dqinode_load_parent(tp, &dp);
210+
if (error)
211+
goto out_cancel;
212+
213+
error = xfs_dqinode_load(tp, dp, type, &ip);
214+
if (error)
215+
goto out_dp;
216+
217+
xfs_trans_cancel(tp);
218+
tp = NULL;
219+
220+
path = kasprintf(GFP_KERNEL, "%s", xfs_dqinode_path(type));
221+
error = xchk_setup_metapath_scan(sc, dp, path, ip);
222+
223+
xfs_irele(ip);
224+
out_dp:
225+
xfs_irele(dp);
226+
out_cancel:
227+
if (tp)
228+
xfs_trans_cancel(tp);
229+
return error;
230+
}
231+
#else
232+
# define xchk_setup_metapath_quotadir(...) (-ENOENT)
233+
# define xchk_setup_metapath_dqinode(...) (-ENOENT)
234+
#endif /* CONFIG_XFS_QUOTA */
235+
168236
int
169237
xchk_setup_metapath(
170238
struct xfs_scrub *sc)
@@ -186,6 +254,14 @@ xchk_setup_metapath(
186254
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
187255
case XFS_SCRUB_METAPATH_RTSUMMARY:
188256
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
257+
case XFS_SCRUB_METAPATH_QUOTADIR:
258+
return xchk_setup_metapath_quotadir(sc);
259+
case XFS_SCRUB_METAPATH_USRQUOTA:
260+
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
261+
case XFS_SCRUB_METAPATH_GRPQUOTA:
262+
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
263+
case XFS_SCRUB_METAPATH_PRJQUOTA:
264+
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
189265
default:
190266
return -ENOENT;
191267
}

fs/xfs/xfs_mount.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,13 @@ xfs_mountfs(
852852
if (error)
853853
goto out_fail_wait;
854854

855+
/*
856+
* If we're resuming quota status, pick up the preliminary qflags from
857+
* the ondisk superblock so that we know if we should recover dquots.
858+
*/
859+
if (xfs_is_resuming_quotaon(mp))
860+
xfs_qm_resume_quotaon(mp);
861+
855862
/*
856863
* Log's mount-time initialization. The first part of recovery can place
857864
* some items on the AIL, to be handled when recovery is finished or
@@ -865,6 +872,14 @@ xfs_mountfs(
865872
goto out_inodegc_shrinker;
866873
}
867874

875+
/*
876+
* If we're resuming quota status and recovered the log, re-sample the
877+
* qflags from the ondisk superblock now that we've recovered it, just
878+
* in case someone shut down enforcement just before a crash.
879+
*/
880+
if (xfs_clear_resuming_quotaon(mp) && xlog_recovery_needed(mp->m_log))
881+
xfs_qm_resume_quotaon(mp);
882+
868883
/*
869884
* If logged xattrs are still enabled after log recovery finishes, then
870885
* they'll be available until unmount. Otherwise, turn them off.

0 commit comments

Comments
 (0)