// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <[email protected]> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_inode.h" #include "xfs_quota.h" #include "xfs_qm.h" #include "xfs_icache.h" #include "xfs_bmap_util.h" #include "xfs_ialloc.h" #include "xfs_ag.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/repair.h" #include "scrub/xfile.h" #include "scrub/xfarray.h" #include "scrub/iscan.h" #include "scrub/quota.h" #include "scrub/quotacheck.h" #include "scrub/trace.h" /* * Live Quotacheck * =============== * * Quota counters are "summary" metadata, in the sense that they are computed * as the summation of the block usage counts for every file on the filesystem. * Therefore, we compute the correct icount, bcount, and rtbcount values by * creating a shadow quota counter structure and walking every inode. */ /* Track the quota deltas for a dquot in a transaction. */ struct xqcheck_dqtrx { … }; #define XQCHECK_MAX_NR_DQTRXS … /* * Track the quota deltas for all dquots attached to a transaction if the * quota deltas are being applied to an inode that we already scanned. */ struct xqcheck_dqacct { … }; /* Free a shadow dquot accounting structure. */ static void xqcheck_dqacct_free( void *ptr, void *arg) { … } /* Set us up to scrub quota counters. */ int xchk_setup_quotacheck( struct xfs_scrub *sc) { … } /* * Part 1: Collecting dquot resource usage counts. For each xfs_dquot attached * to each inode, we create a shadow dquot, and compute the inode count and add * the data/rt block usage from what we see. * * To avoid false corruption reports in part 2, any failure in this part must * set the INCOMPLETE flag even when a negative errno is returned. This care * must be taken with certain errno values (i.e. EFSBADCRC, EFSCORRUPTED, * ECANCELED) that are absorbed into a scrub state flag update by * xchk_*_process_error. Scrub and repair share the same incore data * structures, so the INCOMPLETE flag is critical to prevent a repair based on * insufficient information. * * Because we are scanning a live filesystem, it's possible that another thread * will try to update the quota counters for an inode that we've already * scanned. This will cause our counts to be incorrect. Therefore, we hook * the live transaction code in two places: (1) when the callers update the * per-transaction dqtrx structure to log quota counter updates; and (2) when * transaction commit actually logs those updates to the incore dquot. By * shadowing transaction updates in this manner, live quotacheck can ensure * by locking the dquot and the shadow structure that its own copies are not * out of date. Because the hook code runs in a different process context from * the scrub code and the scrub state flags are not accessed atomically, * failures in the hook code must abort the iscan and the scrubber must notice * the aborted scan and set the incomplete flag. * * Note that we use srcu notifier hooks to minimize the overhead when live * quotacheck is /not/ running. */ /* Update an incore dquot counter information from a live update. */ static int xqcheck_update_incore_counts( struct xqcheck *xqc, struct xfarray *counts, xfs_dqid_t id, int64_t inodes, int64_t nblks, int64_t rtblks) { … } /* Decide if this is the shadow dquot accounting structure for a transaction. */ static int xqcheck_dqacct_obj_cmpfn( struct rhashtable_compare_arg *arg, const void *obj) { … } static const struct rhashtable_params xqcheck_dqacct_hash_params = …; /* Find a shadow dqtrx slot for the given dquot. */ STATIC struct xqcheck_dqtrx * xqcheck_get_dqtrx( struct xqcheck_dqacct *dqa, xfs_dqtype_t q_type, xfs_dqid_t q_id) { … } /* * Create and fill out a quota delta tracking structure to shadow the updates * going on in the regular quota code. */ static int xqcheck_mod_live_ino_dqtrx( struct notifier_block *nb, unsigned long action, void *data) { … } /* * Apply the transaction quota deltas to our shadow quota accounting info when * the regular quota code are doing the same. */ static int xqcheck_apply_live_dqtrx( struct notifier_block *nb, unsigned long action, void *data) { … } /* Record this inode's quota usage in our shadow quota counter data. */ STATIC int xqcheck_collect_inode( struct xqcheck *xqc, struct xfs_inode *ip) { … } /* Walk all the allocated inodes and run a quota scan on them. */ STATIC int xqcheck_collect_counts( struct xqcheck *xqc) { … } /* * Part 2: Comparing dquot resource counters. Walk each xfs_dquot, comparing * the resource usage counters against our shadow dquots; and then walk each * shadow dquot (that wasn't covered in the first part), comparing it against * the xfs_dquot. */ /* * Check the dquot data against what we observed. Caller must hold the dquot * lock. */ STATIC int xqcheck_compare_dquot( struct xqcheck *xqc, xfs_dqtype_t dqtype, struct xfs_dquot *dq) { … } /* * Walk all the observed dquots, and make sure there's a matching incore * dquot and that its counts match ours. */ STATIC int xqcheck_walk_observations( struct xqcheck *xqc, xfs_dqtype_t dqtype) { … } /* Compare the quota counters we observed against the live dquots. */ STATIC int xqcheck_compare_dqtype( struct xqcheck *xqc, xfs_dqtype_t dqtype) { … } /* Tear down everything associated with a quotacheck. */ static void xqcheck_teardown_scan( void *priv) { … } /* * Scan all inodes in the entire filesystem to generate quota counter data. * If the scan is successful, the quota data will be left alive for a repair. * If any error occurs, we'll tear everything down. */ STATIC int xqcheck_setup_scan( struct xfs_scrub *sc, struct xqcheck *xqc) { … } /* Scrub all counters for a given quota type. */ int xchk_quotacheck( struct xfs_scrub *sc) { … }