212 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2023 Oracle. All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_bit.h"10#include "xfs_format.h"11#include "xfs_trans_resv.h"12#include "xfs_mount.h"13#include "xfs_log_format.h"14#include "xfs_trans.h"15#include "xfs_inode.h"16#include "xfs_quota.h"17#include "xfs_qm.h"18#include "xfs_bmap.h"19#include "scrub/scrub.h"20#include "scrub/common.h"21#include "scrub/quota.h"22#include "scrub/trace.h"23 24/* Initialize a dquot iteration cursor. */25void26xchk_dqiter_init(27 struct xchk_dqiter *cursor,28 struct xfs_scrub *sc,29 xfs_dqtype_t dqtype)30{31 cursor->sc = sc;32 cursor->bmap.br_startoff = NULLFILEOFF;33 cursor->dqtype = dqtype & XFS_DQTYPE_REC_MASK;34 cursor->quota_ip = xfs_quota_inode(sc->mp, cursor->dqtype);35 cursor->id = 0;36}37 38/*39 * Ensure that the cached data fork mapping for the dqiter cursor is fresh and40 * covers the dquot pointed to by the scan cursor.41 */42STATIC int43xchk_dquot_iter_revalidate_bmap(44 struct xchk_dqiter *cursor)45{46 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;47 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,48 XFS_DATA_FORK);49 xfs_fileoff_t fileoff;50 xfs_dqid_t this_id = cursor->id;51 int nmaps = 1;52 int error;53 54 fileoff = this_id / qi->qi_dqperchunk;55 56 /*57 * If we have a mapping for cursor->id and it's still fresh, there's58 * no need to reread the bmbt.59 */60 if (cursor->bmap.br_startoff != NULLFILEOFF &&61 cursor->if_seq == ifp->if_seq &&62 cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff)63 return 0;64 65 /* Look up the data fork mapping for the dquot id of interest. */66 error = xfs_bmapi_read(cursor->quota_ip, fileoff,67 XFS_MAX_FILEOFF - fileoff, &cursor->bmap, &nmaps, 0);68 if (error)69 return error;70 if (!nmaps) {71 ASSERT(nmaps > 0);72 return -EFSCORRUPTED;73 }74 if (cursor->bmap.br_startoff > fileoff) {75 ASSERT(cursor->bmap.br_startoff == fileoff);76 return -EFSCORRUPTED;77 }78 79 cursor->if_seq = ifp->if_seq;80 trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id);81 return 0;82}83 84/* Advance the dqiter cursor to the next non-sparse region of the quota file. */85STATIC int86xchk_dquot_iter_advance_bmap(87 struct xchk_dqiter *cursor,88 uint64_t *next_ondisk_id)89{90 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;91 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,92 XFS_DATA_FORK);93 xfs_fileoff_t fileoff;94 uint64_t next_id;95 int nmaps = 1;96 int error;97 98 /* Find the dquot id for the next non-hole mapping. */99 do {100 fileoff = cursor->bmap.br_startoff + cursor->bmap.br_blockcount;101 if (fileoff > XFS_DQ_ID_MAX / qi->qi_dqperchunk) {102 /* The hole goes beyond the max dquot id, we're done */103 *next_ondisk_id = -1ULL;104 return 0;105 }106 107 error = xfs_bmapi_read(cursor->quota_ip, fileoff,108 XFS_MAX_FILEOFF - fileoff, &cursor->bmap,109 &nmaps, 0);110 if (error)111 return error;112 if (!nmaps) {113 /* Must have reached the end of the mappings. */114 *next_ondisk_id = -1ULL;115 return 0;116 }117 if (cursor->bmap.br_startoff > fileoff) {118 ASSERT(cursor->bmap.br_startoff == fileoff);119 return -EFSCORRUPTED;120 }121 } while (!xfs_bmap_is_real_extent(&cursor->bmap));122 123 next_id = cursor->bmap.br_startoff * qi->qi_dqperchunk;124 if (next_id > XFS_DQ_ID_MAX) {125 /* The hole goes beyond the max dquot id, we're done */126 *next_ondisk_id = -1ULL;127 return 0;128 }129 130 /* Propose jumping forward to the dquot in the next allocated block. */131 *next_ondisk_id = next_id;132 cursor->if_seq = ifp->if_seq;133 trace_xchk_dquot_iter_advance_bmap(cursor, *next_ondisk_id);134 return 0;135}136 137/*138 * Find the id of the next highest incore dquot. Normally this will correspond139 * exactly with the quota file block mappings, but repair might have erased a140 * mapping because it was crosslinked; in that case, we need to re-allocate the141 * space so that we can reset q_blkno.142 */143STATIC void144xchk_dquot_iter_advance_incore(145 struct xchk_dqiter *cursor,146 uint64_t *next_incore_id)147{148 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;149 struct radix_tree_root *tree = xfs_dquot_tree(qi, cursor->dqtype);150 struct xfs_dquot *dq;151 unsigned int nr_found;152 153 *next_incore_id = -1ULL;154 155 mutex_lock(&qi->qi_tree_lock);156 nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1);157 if (nr_found)158 *next_incore_id = dq->q_id;159 mutex_unlock(&qi->qi_tree_lock);160 161 trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id);162}163 164/*165 * Walk all incore dquots of this filesystem. Caller must set *@cursorp to166 * zero before the first call, and must not hold the quota file ILOCK.167 * Returns 1 and a valid *@dqpp; 0 and *@dqpp == NULL when there are no more168 * dquots to iterate; or a negative errno.169 */170int171xchk_dquot_iter(172 struct xchk_dqiter *cursor,173 struct xfs_dquot **dqpp)174{175 struct xfs_mount *mp = cursor->sc->mp;176 struct xfs_dquot *dq = NULL;177 uint64_t next_ondisk, next_incore = -1ULL;178 unsigned int lock_mode;179 int error = 0;180 181 if (cursor->id > XFS_DQ_ID_MAX)182 return 0;183 next_ondisk = cursor->id;184 185 /* Revalidate and/or advance the cursor. */186 lock_mode = xfs_ilock_data_map_shared(cursor->quota_ip);187 error = xchk_dquot_iter_revalidate_bmap(cursor);188 if (!error && !xfs_bmap_is_real_extent(&cursor->bmap))189 error = xchk_dquot_iter_advance_bmap(cursor, &next_ondisk);190 xfs_iunlock(cursor->quota_ip, lock_mode);191 if (error)192 return error;193 194 if (next_ondisk > cursor->id)195 xchk_dquot_iter_advance_incore(cursor, &next_incore);196 197 /* Pick the next dquot in the sequence and return it. */198 cursor->id = min(next_ondisk, next_incore);199 if (cursor->id > XFS_DQ_ID_MAX)200 return 0;201 202 trace_xchk_dquot_iter(cursor, cursor->id);203 204 error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq);205 if (error)206 return error;207 208 cursor->id = dq->q_id + 1;209 *dqpp = dq;210 return 1;211}212