162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2019 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_btree.h" 1562306a36Sopenharmony_ci#include "xfs_ialloc.h" 1662306a36Sopenharmony_ci#include "xfs_ialloc_btree.h" 1762306a36Sopenharmony_ci#include "xfs_iwalk.h" 1862306a36Sopenharmony_ci#include "xfs_error.h" 1962306a36Sopenharmony_ci#include "xfs_trace.h" 2062306a36Sopenharmony_ci#include "xfs_icache.h" 2162306a36Sopenharmony_ci#include "xfs_health.h" 2262306a36Sopenharmony_ci#include "xfs_trans.h" 2362306a36Sopenharmony_ci#include "xfs_pwork.h" 2462306a36Sopenharmony_ci#include "xfs_ag.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Walking Inodes in the Filesystem 2862306a36Sopenharmony_ci * ================================ 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * This iterator function walks a subset of filesystem inodes in increasing 3162306a36Sopenharmony_ci * order from @startino until there are no more inodes. For each allocated 3262306a36Sopenharmony_ci * inode it finds, it calls a walk function with the relevant inode number and 3362306a36Sopenharmony_ci * a pointer to caller-provided data. The walk function can return the usual 3462306a36Sopenharmony_ci * negative error code to stop the iteration; 0 to continue the iteration; or 3562306a36Sopenharmony_ci * -ECANCELED to stop the iteration. This return value is returned to the 3662306a36Sopenharmony_ci * caller. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Internally, we allow the walk function to do anything, which means that we 3962306a36Sopenharmony_ci * cannot maintain the inobt cursor or our lock on the AGI buffer. We 4062306a36Sopenharmony_ci * therefore cache the inobt records in kernel memory and only call the walk 4162306a36Sopenharmony_ci * function when our memory buffer is full. @nr_recs is the number of records 4262306a36Sopenharmony_ci * that we've cached, and @sz_recs is the size of our cache. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * It is the responsibility of the walk function to ensure it accesses 4562306a36Sopenharmony_ci * allocated inodes, as the inobt records may be stale by the time they are 4662306a36Sopenharmony_ci * acted upon. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct xfs_iwalk_ag { 5062306a36Sopenharmony_ci /* parallel work control data; will be null if single threaded */ 5162306a36Sopenharmony_ci struct xfs_pwork pwork; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct xfs_mount *mp; 5462306a36Sopenharmony_ci struct xfs_trans *tp; 5562306a36Sopenharmony_ci struct xfs_perag *pag; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Where do we start the traversal? */ 5862306a36Sopenharmony_ci xfs_ino_t startino; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* What was the last inode number we saw when iterating the inobt? */ 6162306a36Sopenharmony_ci xfs_ino_t lastino; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Array of inobt records we cache. */ 6462306a36Sopenharmony_ci struct xfs_inobt_rec_incore *recs; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Number of entries allocated for the @recs array. */ 6762306a36Sopenharmony_ci unsigned int sz_recs; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Number of entries in the @recs array that are in use. */ 7062306a36Sopenharmony_ci unsigned int nr_recs; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Inode walk function and data pointer. */ 7362306a36Sopenharmony_ci xfs_iwalk_fn iwalk_fn; 7462306a36Sopenharmony_ci xfs_inobt_walk_fn inobt_walk_fn; 7562306a36Sopenharmony_ci void *data; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Make it look like the inodes up to startino are free so that 7962306a36Sopenharmony_ci * bulkstat can start its inode iteration at the correct place without 8062306a36Sopenharmony_ci * needing to special case everywhere. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci unsigned int trim_start:1; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Skip empty inobt records? */ 8562306a36Sopenharmony_ci unsigned int skip_empty:1; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Drop the (hopefully empty) transaction when calling iwalk_fn. */ 8862306a36Sopenharmony_ci unsigned int drop_trans:1; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Loop over all clusters in a chunk for a given incore inode allocation btree 9362306a36Sopenharmony_ci * record. Do a readahead if there are any allocated inodes in that cluster. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ciSTATIC void 9662306a36Sopenharmony_cixfs_iwalk_ichunk_ra( 9762306a36Sopenharmony_ci struct xfs_mount *mp, 9862306a36Sopenharmony_ci struct xfs_perag *pag, 9962306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct xfs_ino_geometry *igeo = M_IGEO(mp); 10262306a36Sopenharmony_ci xfs_agblock_t agbno; 10362306a36Sopenharmony_ci struct blk_plug plug; 10462306a36Sopenharmony_ci int i; /* inode chunk index */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci agbno = XFS_AGINO_TO_AGBNO(mp, irec->ir_startino); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci blk_start_plug(&plug); 10962306a36Sopenharmony_ci for (i = 0; i < XFS_INODES_PER_CHUNK; i += igeo->inodes_per_cluster) { 11062306a36Sopenharmony_ci xfs_inofree_t imask; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci imask = xfs_inobt_maskn(i, igeo->inodes_per_cluster); 11362306a36Sopenharmony_ci if (imask & ~irec->ir_free) { 11462306a36Sopenharmony_ci xfs_btree_reada_bufs(mp, pag->pag_agno, agbno, 11562306a36Sopenharmony_ci igeo->blocks_per_cluster, 11662306a36Sopenharmony_ci &xfs_inode_buf_ops); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci agbno += igeo->blocks_per_cluster; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci blk_finish_plug(&plug); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Set the bits in @irec's free mask that correspond to the inodes before 12562306a36Sopenharmony_ci * @agino so that we skip them. This is how we restart an inode walk that was 12662306a36Sopenharmony_ci * interrupted in the middle of an inode record. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ciSTATIC void 12962306a36Sopenharmony_cixfs_iwalk_adjust_start( 13062306a36Sopenharmony_ci xfs_agino_t agino, /* starting inode of chunk */ 13162306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec) /* btree record */ 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int idx; /* index into inode chunk */ 13462306a36Sopenharmony_ci int i; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci idx = agino - irec->ir_startino; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* 13962306a36Sopenharmony_ci * We got a right chunk with some left inodes allocated at it. Grab 14062306a36Sopenharmony_ci * the chunk record. Mark all the uninteresting inodes free because 14162306a36Sopenharmony_ci * they're before our start point. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci for (i = 0; i < idx; i++) { 14462306a36Sopenharmony_ci if (XFS_INOBT_MASK(i) & ~irec->ir_free) 14562306a36Sopenharmony_ci irec->ir_freecount++; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci irec->ir_free |= xfs_inobt_maskn(0, idx); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Allocate memory for a walk. */ 15262306a36Sopenharmony_ciSTATIC int 15362306a36Sopenharmony_cixfs_iwalk_alloc( 15462306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci size_t size; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ASSERT(iwag->recs == NULL); 15962306a36Sopenharmony_ci iwag->nr_recs = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Allocate a prefetch buffer for inobt records. */ 16262306a36Sopenharmony_ci size = iwag->sz_recs * sizeof(struct xfs_inobt_rec_incore); 16362306a36Sopenharmony_ci iwag->recs = kmem_alloc(size, KM_MAYFAIL); 16462306a36Sopenharmony_ci if (iwag->recs == NULL) 16562306a36Sopenharmony_ci return -ENOMEM; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* Free memory we allocated for a walk. */ 17162306a36Sopenharmony_ciSTATIC void 17262306a36Sopenharmony_cixfs_iwalk_free( 17362306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci kmem_free(iwag->recs); 17662306a36Sopenharmony_ci iwag->recs = NULL; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* For each inuse inode in each cached inobt record, call our function. */ 18062306a36Sopenharmony_ciSTATIC int 18162306a36Sopenharmony_cixfs_iwalk_ag_recs( 18262306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct xfs_mount *mp = iwag->mp; 18562306a36Sopenharmony_ci struct xfs_trans *tp = iwag->tp; 18662306a36Sopenharmony_ci struct xfs_perag *pag = iwag->pag; 18762306a36Sopenharmony_ci xfs_ino_t ino; 18862306a36Sopenharmony_ci unsigned int i, j; 18962306a36Sopenharmony_ci int error; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for (i = 0; i < iwag->nr_recs; i++) { 19262306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec = &iwag->recs[i]; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci trace_xfs_iwalk_ag_rec(mp, pag->pag_agno, irec); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (xfs_pwork_want_abort(&iwag->pwork)) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (iwag->inobt_walk_fn) { 20062306a36Sopenharmony_ci error = iwag->inobt_walk_fn(mp, tp, pag->pag_agno, irec, 20162306a36Sopenharmony_ci iwag->data); 20262306a36Sopenharmony_ci if (error) 20362306a36Sopenharmony_ci return error; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!iwag->iwalk_fn) 20762306a36Sopenharmony_ci continue; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (j = 0; j < XFS_INODES_PER_CHUNK; j++) { 21062306a36Sopenharmony_ci if (xfs_pwork_want_abort(&iwag->pwork)) 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Skip if this inode is free */ 21462306a36Sopenharmony_ci if (XFS_INOBT_MASK(j) & irec->ir_free) 21562306a36Sopenharmony_ci continue; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Otherwise call our function. */ 21862306a36Sopenharmony_ci ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, 21962306a36Sopenharmony_ci irec->ir_startino + j); 22062306a36Sopenharmony_ci error = iwag->iwalk_fn(mp, tp, ino, iwag->data); 22162306a36Sopenharmony_ci if (error) 22262306a36Sopenharmony_ci return error; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* Delete cursor and let go of AGI. */ 23062306a36Sopenharmony_cistatic inline void 23162306a36Sopenharmony_cixfs_iwalk_del_inobt( 23262306a36Sopenharmony_ci struct xfs_trans *tp, 23362306a36Sopenharmony_ci struct xfs_btree_cur **curpp, 23462306a36Sopenharmony_ci struct xfs_buf **agi_bpp, 23562306a36Sopenharmony_ci int error) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci if (*curpp) { 23862306a36Sopenharmony_ci xfs_btree_del_cursor(*curpp, error); 23962306a36Sopenharmony_ci *curpp = NULL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci if (*agi_bpp) { 24262306a36Sopenharmony_ci xfs_trans_brelse(tp, *agi_bpp); 24362306a36Sopenharmony_ci *agi_bpp = NULL; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Set ourselves up for walking inobt records starting from a given point in 24962306a36Sopenharmony_ci * the filesystem. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * If caller passed in a nonzero start inode number, load the record from the 25262306a36Sopenharmony_ci * inobt and make the record look like all the inodes before agino are free so 25362306a36Sopenharmony_ci * that we skip them, and then move the cursor to the next inobt record. This 25462306a36Sopenharmony_ci * is how we support starting an iwalk in the middle of an inode chunk. 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * If the caller passed in a start number of zero, move the cursor to the first 25762306a36Sopenharmony_ci * inobt record. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * The caller is responsible for cleaning up the cursor and buffer pointer 26062306a36Sopenharmony_ci * regardless of the error status. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ciSTATIC int 26362306a36Sopenharmony_cixfs_iwalk_ag_start( 26462306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag, 26562306a36Sopenharmony_ci xfs_agino_t agino, 26662306a36Sopenharmony_ci struct xfs_btree_cur **curpp, 26762306a36Sopenharmony_ci struct xfs_buf **agi_bpp, 26862306a36Sopenharmony_ci int *has_more) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct xfs_mount *mp = iwag->mp; 27162306a36Sopenharmony_ci struct xfs_trans *tp = iwag->tp; 27262306a36Sopenharmony_ci struct xfs_perag *pag = iwag->pag; 27362306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec; 27462306a36Sopenharmony_ci int error; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Set up a fresh cursor and empty the inobt cache. */ 27762306a36Sopenharmony_ci iwag->nr_recs = 0; 27862306a36Sopenharmony_ci error = xfs_inobt_cur(pag, tp, XFS_BTNUM_INO, curpp, agi_bpp); 27962306a36Sopenharmony_ci if (error) 28062306a36Sopenharmony_ci return error; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Starting at the beginning of the AG? That's easy! */ 28362306a36Sopenharmony_ci if (agino == 0) 28462306a36Sopenharmony_ci return xfs_inobt_lookup(*curpp, 0, XFS_LOOKUP_GE, has_more); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * Otherwise, we have to grab the inobt record where we left off, stuff 28862306a36Sopenharmony_ci * the record into our cache, and then see if there are more records. 28962306a36Sopenharmony_ci * We require a lookup cache of at least two elements so that the 29062306a36Sopenharmony_ci * caller doesn't have to deal with tearing down the cursor to walk the 29162306a36Sopenharmony_ci * records. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci error = xfs_inobt_lookup(*curpp, agino, XFS_LOOKUP_LE, has_more); 29462306a36Sopenharmony_ci if (error) 29562306a36Sopenharmony_ci return error; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * If the LE lookup at @agino yields no records, jump ahead to the 29962306a36Sopenharmony_ci * inobt cursor increment to see if there are more records to process. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci if (!*has_more) 30262306a36Sopenharmony_ci goto out_advance; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Get the record, should always work */ 30562306a36Sopenharmony_ci irec = &iwag->recs[iwag->nr_recs]; 30662306a36Sopenharmony_ci error = xfs_inobt_get_rec(*curpp, irec, has_more); 30762306a36Sopenharmony_ci if (error) 30862306a36Sopenharmony_ci return error; 30962306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, *has_more != 1)) 31062306a36Sopenharmony_ci return -EFSCORRUPTED; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci iwag->lastino = XFS_AGINO_TO_INO(mp, pag->pag_agno, 31362306a36Sopenharmony_ci irec->ir_startino + XFS_INODES_PER_CHUNK - 1); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * If the LE lookup yielded an inobt record before the cursor position, 31762306a36Sopenharmony_ci * skip it and see if there's another one after it. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino) 32062306a36Sopenharmony_ci goto out_advance; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * If agino fell in the middle of the inode record, make it look like 32462306a36Sopenharmony_ci * the inodes up to agino are free so that we don't return them again. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci if (iwag->trim_start) 32762306a36Sopenharmony_ci xfs_iwalk_adjust_start(agino, irec); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * The prefetch calculation is supposed to give us a large enough inobt 33162306a36Sopenharmony_ci * record cache that grab_ichunk can stage a partial first record and 33262306a36Sopenharmony_ci * the loop body can cache a record without having to check for cache 33362306a36Sopenharmony_ci * space until after it reads an inobt record. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci iwag->nr_recs++; 33662306a36Sopenharmony_ci ASSERT(iwag->nr_recs < iwag->sz_recs); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ciout_advance: 33962306a36Sopenharmony_ci return xfs_btree_increment(*curpp, 0, has_more); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/* 34362306a36Sopenharmony_ci * The inobt record cache is full, so preserve the inobt cursor state and 34462306a36Sopenharmony_ci * run callbacks on the cached inobt records. When we're done, restore the 34562306a36Sopenharmony_ci * cursor state to wherever the cursor would have been had the cache not been 34662306a36Sopenharmony_ci * full (and therefore we could've just incremented the cursor) if *@has_more 34762306a36Sopenharmony_ci * is true. On exit, *@has_more will indicate whether or not the caller should 34862306a36Sopenharmony_ci * try for more inode records. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ciSTATIC int 35162306a36Sopenharmony_cixfs_iwalk_run_callbacks( 35262306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag, 35362306a36Sopenharmony_ci struct xfs_btree_cur **curpp, 35462306a36Sopenharmony_ci struct xfs_buf **agi_bpp, 35562306a36Sopenharmony_ci int *has_more) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct xfs_mount *mp = iwag->mp; 35862306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec; 35962306a36Sopenharmony_ci xfs_agino_t next_agino; 36062306a36Sopenharmony_ci int error; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci next_agino = XFS_INO_TO_AGINO(mp, iwag->lastino) + 1; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ASSERT(iwag->nr_recs > 0); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Delete cursor but remember the last record we cached... */ 36762306a36Sopenharmony_ci xfs_iwalk_del_inobt(iwag->tp, curpp, agi_bpp, 0); 36862306a36Sopenharmony_ci irec = &iwag->recs[iwag->nr_recs - 1]; 36962306a36Sopenharmony_ci ASSERT(next_agino >= irec->ir_startino + XFS_INODES_PER_CHUNK); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (iwag->drop_trans) { 37262306a36Sopenharmony_ci xfs_trans_cancel(iwag->tp); 37362306a36Sopenharmony_ci iwag->tp = NULL; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci error = xfs_iwalk_ag_recs(iwag); 37762306a36Sopenharmony_ci if (error) 37862306a36Sopenharmony_ci return error; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* ...empty the cache... */ 38162306a36Sopenharmony_ci iwag->nr_recs = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!has_more) 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (iwag->drop_trans) { 38762306a36Sopenharmony_ci error = xfs_trans_alloc_empty(mp, &iwag->tp); 38862306a36Sopenharmony_ci if (error) 38962306a36Sopenharmony_ci return error; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* ...and recreate the cursor just past where we left off. */ 39362306a36Sopenharmony_ci error = xfs_inobt_cur(iwag->pag, iwag->tp, XFS_BTNUM_INO, curpp, 39462306a36Sopenharmony_ci agi_bpp); 39562306a36Sopenharmony_ci if (error) 39662306a36Sopenharmony_ci return error; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return xfs_inobt_lookup(*curpp, next_agino, XFS_LOOKUP_GE, has_more); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* Walk all inodes in a single AG, from @iwag->startino to the end of the AG. */ 40262306a36Sopenharmony_ciSTATIC int 40362306a36Sopenharmony_cixfs_iwalk_ag( 40462306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct xfs_mount *mp = iwag->mp; 40762306a36Sopenharmony_ci struct xfs_perag *pag = iwag->pag; 40862306a36Sopenharmony_ci struct xfs_buf *agi_bp = NULL; 40962306a36Sopenharmony_ci struct xfs_btree_cur *cur = NULL; 41062306a36Sopenharmony_ci xfs_agino_t agino; 41162306a36Sopenharmony_ci int has_more; 41262306a36Sopenharmony_ci int error = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Set up our cursor at the right place in the inode btree. */ 41562306a36Sopenharmony_ci ASSERT(pag->pag_agno == XFS_INO_TO_AGNO(mp, iwag->startino)); 41662306a36Sopenharmony_ci agino = XFS_INO_TO_AGINO(mp, iwag->startino); 41762306a36Sopenharmony_ci error = xfs_iwalk_ag_start(iwag, agino, &cur, &agi_bp, &has_more); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci while (!error && has_more) { 42062306a36Sopenharmony_ci struct xfs_inobt_rec_incore *irec; 42162306a36Sopenharmony_ci xfs_ino_t rec_fsino; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci cond_resched(); 42462306a36Sopenharmony_ci if (xfs_pwork_want_abort(&iwag->pwork)) 42562306a36Sopenharmony_ci goto out; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Fetch the inobt record. */ 42862306a36Sopenharmony_ci irec = &iwag->recs[iwag->nr_recs]; 42962306a36Sopenharmony_ci error = xfs_inobt_get_rec(cur, irec, &has_more); 43062306a36Sopenharmony_ci if (error || !has_more) 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Make sure that we always move forward. */ 43462306a36Sopenharmony_ci rec_fsino = XFS_AGINO_TO_INO(mp, pag->pag_agno, irec->ir_startino); 43562306a36Sopenharmony_ci if (iwag->lastino != NULLFSINO && 43662306a36Sopenharmony_ci XFS_IS_CORRUPT(mp, iwag->lastino >= rec_fsino)) { 43762306a36Sopenharmony_ci error = -EFSCORRUPTED; 43862306a36Sopenharmony_ci goto out; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci iwag->lastino = rec_fsino + XFS_INODES_PER_CHUNK - 1; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* No allocated inodes in this chunk; skip it. */ 44362306a36Sopenharmony_ci if (iwag->skip_empty && irec->ir_freecount == irec->ir_count) { 44462306a36Sopenharmony_ci error = xfs_btree_increment(cur, 0, &has_more); 44562306a36Sopenharmony_ci if (error) 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci continue; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Start readahead for this inode chunk in anticipation of 45262306a36Sopenharmony_ci * walking the inodes. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (iwag->iwalk_fn) 45562306a36Sopenharmony_ci xfs_iwalk_ichunk_ra(mp, pag, irec); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * If there's space in the buffer for more records, increment 45962306a36Sopenharmony_ci * the btree cursor and grab more. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (++iwag->nr_recs < iwag->sz_recs) { 46262306a36Sopenharmony_ci error = xfs_btree_increment(cur, 0, &has_more); 46362306a36Sopenharmony_ci if (error || !has_more) 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci continue; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Otherwise, we need to save cursor state and run the callback 47062306a36Sopenharmony_ci * function on the cached records. The run_callbacks function 47162306a36Sopenharmony_ci * is supposed to return a cursor pointing to the record where 47262306a36Sopenharmony_ci * we would be if we had been able to increment like above. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci ASSERT(has_more); 47562306a36Sopenharmony_ci error = xfs_iwalk_run_callbacks(iwag, &cur, &agi_bp, &has_more); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (iwag->nr_recs == 0 || error) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Walk the unprocessed records in the cache. */ 48262306a36Sopenharmony_ci error = xfs_iwalk_run_callbacks(iwag, &cur, &agi_bp, &has_more); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciout: 48562306a36Sopenharmony_ci xfs_iwalk_del_inobt(iwag->tp, &cur, &agi_bp, error); 48662306a36Sopenharmony_ci return error; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci/* 49062306a36Sopenharmony_ci * We experimentally determined that the reduction in ioctl call overhead 49162306a36Sopenharmony_ci * diminishes when userspace asks for more than 2048 inodes, so we'll cap 49262306a36Sopenharmony_ci * prefetch at this point. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci#define IWALK_MAX_INODE_PREFETCH (2048U) 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* 49762306a36Sopenharmony_ci * Given the number of inodes to prefetch, set the number of inobt records that 49862306a36Sopenharmony_ci * we cache in memory, which controls the number of inodes we try to read 49962306a36Sopenharmony_ci * ahead. Set the maximum if @inodes == 0. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_cistatic inline unsigned int 50262306a36Sopenharmony_cixfs_iwalk_prefetch( 50362306a36Sopenharmony_ci unsigned int inodes) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci unsigned int inobt_records; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * If the caller didn't tell us the number of inodes they wanted, 50962306a36Sopenharmony_ci * assume the maximum prefetch possible for best performance. 51062306a36Sopenharmony_ci * Otherwise, cap prefetch at that maximum so that we don't start an 51162306a36Sopenharmony_ci * absurd amount of prefetch. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (inodes == 0) 51462306a36Sopenharmony_ci inodes = IWALK_MAX_INODE_PREFETCH; 51562306a36Sopenharmony_ci inodes = min(inodes, IWALK_MAX_INODE_PREFETCH); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Round the inode count up to a full chunk. */ 51862306a36Sopenharmony_ci inodes = round_up(inodes, XFS_INODES_PER_CHUNK); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * In order to convert the number of inodes to prefetch into an 52262306a36Sopenharmony_ci * estimate of the number of inobt records to cache, we require a 52362306a36Sopenharmony_ci * conversion factor that reflects our expectations of the average 52462306a36Sopenharmony_ci * loading factor of an inode chunk. Based on data gathered, most 52562306a36Sopenharmony_ci * (but not all) filesystems manage to keep the inode chunks totally 52662306a36Sopenharmony_ci * full, so we'll underestimate slightly so that our readahead will 52762306a36Sopenharmony_ci * still deliver the performance we want on aging filesystems: 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * inobt = inodes / (INODES_PER_CHUNK * (4 / 5)); 53062306a36Sopenharmony_ci * 53162306a36Sopenharmony_ci * The funny math is to avoid integer division. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci inobt_records = (inodes * 5) / (4 * XFS_INODES_PER_CHUNK); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * Allocate enough space to prefetch at least two inobt records so that 53762306a36Sopenharmony_ci * we can cache both the record where the iwalk started and the next 53862306a36Sopenharmony_ci * record. This simplifies the AG inode walk loop setup code. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci return max(inobt_records, 2U); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* 54462306a36Sopenharmony_ci * Walk all inodes in the filesystem starting from @startino. The @iwalk_fn 54562306a36Sopenharmony_ci * will be called for each allocated inode, being passed the inode's number and 54662306a36Sopenharmony_ci * @data. @max_prefetch controls how many inobt records' worth of inodes we 54762306a36Sopenharmony_ci * try to readahead. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ciint 55062306a36Sopenharmony_cixfs_iwalk( 55162306a36Sopenharmony_ci struct xfs_mount *mp, 55262306a36Sopenharmony_ci struct xfs_trans *tp, 55362306a36Sopenharmony_ci xfs_ino_t startino, 55462306a36Sopenharmony_ci unsigned int flags, 55562306a36Sopenharmony_ci xfs_iwalk_fn iwalk_fn, 55662306a36Sopenharmony_ci unsigned int inode_records, 55762306a36Sopenharmony_ci void *data) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct xfs_iwalk_ag iwag = { 56062306a36Sopenharmony_ci .mp = mp, 56162306a36Sopenharmony_ci .tp = tp, 56262306a36Sopenharmony_ci .iwalk_fn = iwalk_fn, 56362306a36Sopenharmony_ci .data = data, 56462306a36Sopenharmony_ci .startino = startino, 56562306a36Sopenharmony_ci .sz_recs = xfs_iwalk_prefetch(inode_records), 56662306a36Sopenharmony_ci .trim_start = 1, 56762306a36Sopenharmony_ci .skip_empty = 1, 56862306a36Sopenharmony_ci .pwork = XFS_PWORK_SINGLE_THREADED, 56962306a36Sopenharmony_ci .lastino = NULLFSINO, 57062306a36Sopenharmony_ci }; 57162306a36Sopenharmony_ci struct xfs_perag *pag; 57262306a36Sopenharmony_ci xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino); 57362306a36Sopenharmony_ci int error; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ASSERT(agno < mp->m_sb.sb_agcount); 57662306a36Sopenharmony_ci ASSERT(!(flags & ~XFS_IWALK_FLAGS_ALL)); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci error = xfs_iwalk_alloc(&iwag); 57962306a36Sopenharmony_ci if (error) 58062306a36Sopenharmony_ci return error; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for_each_perag_from(mp, agno, pag) { 58362306a36Sopenharmony_ci iwag.pag = pag; 58462306a36Sopenharmony_ci error = xfs_iwalk_ag(&iwag); 58562306a36Sopenharmony_ci if (error) 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci iwag.startino = XFS_AGINO_TO_INO(mp, agno + 1, 0); 58862306a36Sopenharmony_ci if (flags & XFS_INOBT_WALK_SAME_AG) 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci iwag.pag = NULL; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (iwag.pag) 59462306a36Sopenharmony_ci xfs_perag_rele(pag); 59562306a36Sopenharmony_ci xfs_iwalk_free(&iwag); 59662306a36Sopenharmony_ci return error; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* Run per-thread iwalk work. */ 60062306a36Sopenharmony_cistatic int 60162306a36Sopenharmony_cixfs_iwalk_ag_work( 60262306a36Sopenharmony_ci struct xfs_mount *mp, 60362306a36Sopenharmony_ci struct xfs_pwork *pwork) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag; 60662306a36Sopenharmony_ci int error = 0; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci iwag = container_of(pwork, struct xfs_iwalk_ag, pwork); 60962306a36Sopenharmony_ci if (xfs_pwork_want_abort(pwork)) 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci error = xfs_iwalk_alloc(iwag); 61362306a36Sopenharmony_ci if (error) 61462306a36Sopenharmony_ci goto out; 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * Grab an empty transaction so that we can use its recursive buffer 61762306a36Sopenharmony_ci * locking abilities to detect cycles in the inobt without deadlocking. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci error = xfs_trans_alloc_empty(mp, &iwag->tp); 62062306a36Sopenharmony_ci if (error) 62162306a36Sopenharmony_ci goto out; 62262306a36Sopenharmony_ci iwag->drop_trans = 1; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci error = xfs_iwalk_ag(iwag); 62562306a36Sopenharmony_ci if (iwag->tp) 62662306a36Sopenharmony_ci xfs_trans_cancel(iwag->tp); 62762306a36Sopenharmony_ci xfs_iwalk_free(iwag); 62862306a36Sopenharmony_ciout: 62962306a36Sopenharmony_ci xfs_perag_put(iwag->pag); 63062306a36Sopenharmony_ci kmem_free(iwag); 63162306a36Sopenharmony_ci return error; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* 63562306a36Sopenharmony_ci * Walk all the inodes in the filesystem using multiple threads to process each 63662306a36Sopenharmony_ci * AG. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ciint 63962306a36Sopenharmony_cixfs_iwalk_threaded( 64062306a36Sopenharmony_ci struct xfs_mount *mp, 64162306a36Sopenharmony_ci xfs_ino_t startino, 64262306a36Sopenharmony_ci unsigned int flags, 64362306a36Sopenharmony_ci xfs_iwalk_fn iwalk_fn, 64462306a36Sopenharmony_ci unsigned int inode_records, 64562306a36Sopenharmony_ci bool polled, 64662306a36Sopenharmony_ci void *data) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct xfs_pwork_ctl pctl; 64962306a36Sopenharmony_ci struct xfs_perag *pag; 65062306a36Sopenharmony_ci xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino); 65162306a36Sopenharmony_ci int error; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ASSERT(agno < mp->m_sb.sb_agcount); 65462306a36Sopenharmony_ci ASSERT(!(flags & ~XFS_IWALK_FLAGS_ALL)); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci error = xfs_pwork_init(mp, &pctl, xfs_iwalk_ag_work, "xfs_iwalk"); 65762306a36Sopenharmony_ci if (error) 65862306a36Sopenharmony_ci return error; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci for_each_perag_from(mp, agno, pag) { 66162306a36Sopenharmony_ci struct xfs_iwalk_ag *iwag; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (xfs_pwork_ctl_want_abort(&pctl)) 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci iwag = kmem_zalloc(sizeof(struct xfs_iwalk_ag), 0); 66762306a36Sopenharmony_ci iwag->mp = mp; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * perag is being handed off to async work, so take a passive 67162306a36Sopenharmony_ci * reference for the async work to release. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci iwag->pag = xfs_perag_hold(pag); 67462306a36Sopenharmony_ci iwag->iwalk_fn = iwalk_fn; 67562306a36Sopenharmony_ci iwag->data = data; 67662306a36Sopenharmony_ci iwag->startino = startino; 67762306a36Sopenharmony_ci iwag->sz_recs = xfs_iwalk_prefetch(inode_records); 67862306a36Sopenharmony_ci iwag->lastino = NULLFSINO; 67962306a36Sopenharmony_ci xfs_pwork_queue(&pctl, &iwag->pwork); 68062306a36Sopenharmony_ci startino = XFS_AGINO_TO_INO(mp, pag->pag_agno + 1, 0); 68162306a36Sopenharmony_ci if (flags & XFS_INOBT_WALK_SAME_AG) 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci if (pag) 68562306a36Sopenharmony_ci xfs_perag_rele(pag); 68662306a36Sopenharmony_ci if (polled) 68762306a36Sopenharmony_ci xfs_pwork_poll(&pctl); 68862306a36Sopenharmony_ci return xfs_pwork_destroy(&pctl); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/* 69262306a36Sopenharmony_ci * Allow callers to cache up to a page's worth of inobt records. This reflects 69362306a36Sopenharmony_ci * the existing inumbers prefetching behavior. Since the inobt walk does not 69462306a36Sopenharmony_ci * itself do anything with the inobt records, we can set a fairly high limit 69562306a36Sopenharmony_ci * here. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci#define MAX_INOBT_WALK_PREFETCH \ 69862306a36Sopenharmony_ci (PAGE_SIZE / sizeof(struct xfs_inobt_rec_incore)) 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * Given the number of records that the user wanted, set the number of inobt 70262306a36Sopenharmony_ci * records that we buffer in memory. Set the maximum if @inobt_records == 0. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_cistatic inline unsigned int 70562306a36Sopenharmony_cixfs_inobt_walk_prefetch( 70662306a36Sopenharmony_ci unsigned int inobt_records) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * If the caller didn't tell us the number of inobt records they 71062306a36Sopenharmony_ci * wanted, assume the maximum prefetch possible for best performance. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci if (inobt_records == 0) 71362306a36Sopenharmony_ci inobt_records = MAX_INOBT_WALK_PREFETCH; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * Allocate enough space to prefetch at least two inobt records so that 71762306a36Sopenharmony_ci * we can cache both the record where the iwalk started and the next 71862306a36Sopenharmony_ci * record. This simplifies the AG inode walk loop setup code. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci inobt_records = max(inobt_records, 2U); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* 72362306a36Sopenharmony_ci * Cap prefetch at that maximum so that we don't use an absurd amount 72462306a36Sopenharmony_ci * of memory. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci return min_t(unsigned int, inobt_records, MAX_INOBT_WALK_PREFETCH); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci/* 73062306a36Sopenharmony_ci * Walk all inode btree records in the filesystem starting from @startino. The 73162306a36Sopenharmony_ci * @inobt_walk_fn will be called for each btree record, being passed the incore 73262306a36Sopenharmony_ci * record and @data. @max_prefetch controls how many inobt records we try to 73362306a36Sopenharmony_ci * cache ahead of time. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ciint 73662306a36Sopenharmony_cixfs_inobt_walk( 73762306a36Sopenharmony_ci struct xfs_mount *mp, 73862306a36Sopenharmony_ci struct xfs_trans *tp, 73962306a36Sopenharmony_ci xfs_ino_t startino, 74062306a36Sopenharmony_ci unsigned int flags, 74162306a36Sopenharmony_ci xfs_inobt_walk_fn inobt_walk_fn, 74262306a36Sopenharmony_ci unsigned int inobt_records, 74362306a36Sopenharmony_ci void *data) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct xfs_iwalk_ag iwag = { 74662306a36Sopenharmony_ci .mp = mp, 74762306a36Sopenharmony_ci .tp = tp, 74862306a36Sopenharmony_ci .inobt_walk_fn = inobt_walk_fn, 74962306a36Sopenharmony_ci .data = data, 75062306a36Sopenharmony_ci .startino = startino, 75162306a36Sopenharmony_ci .sz_recs = xfs_inobt_walk_prefetch(inobt_records), 75262306a36Sopenharmony_ci .pwork = XFS_PWORK_SINGLE_THREADED, 75362306a36Sopenharmony_ci .lastino = NULLFSINO, 75462306a36Sopenharmony_ci }; 75562306a36Sopenharmony_ci struct xfs_perag *pag; 75662306a36Sopenharmony_ci xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino); 75762306a36Sopenharmony_ci int error; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ASSERT(agno < mp->m_sb.sb_agcount); 76062306a36Sopenharmony_ci ASSERT(!(flags & ~XFS_INOBT_WALK_FLAGS_ALL)); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci error = xfs_iwalk_alloc(&iwag); 76362306a36Sopenharmony_ci if (error) 76462306a36Sopenharmony_ci return error; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci for_each_perag_from(mp, agno, pag) { 76762306a36Sopenharmony_ci iwag.pag = pag; 76862306a36Sopenharmony_ci error = xfs_iwalk_ag(&iwag); 76962306a36Sopenharmony_ci if (error) 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci iwag.startino = XFS_AGINO_TO_INO(mp, pag->pag_agno + 1, 0); 77262306a36Sopenharmony_ci if (flags & XFS_INOBT_WALK_SAME_AG) 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci iwag.pag = NULL; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (iwag.pag) 77862306a36Sopenharmony_ci xfs_perag_rele(pag); 77962306a36Sopenharmony_ci xfs_iwalk_free(&iwag); 78062306a36Sopenharmony_ci return error; 78162306a36Sopenharmony_ci} 782