+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <libxfs.h>
+#include "libxfs.h"
+#include "threads.h"
+#include "prefetch.h"
#include "avl.h"
#include "globals.h"
#include "agheader.h"
#include "protos.h"
#include "err_protos.h"
#include "dinode.h"
-#include "threads.h"
#include "progress.h"
-#include "prefetch.h"
-
-/*
- * walks an unlinked list, returns 1 on an error (bogus pointer) or
- * I/O error
- */
-int
-walk_unlinked_list(xfs_mount_t *mp, xfs_agnumber_t agno, xfs_agino_t start_ino)
-{
- xfs_buf_t *bp;
- xfs_dinode_t *dip;
- xfs_agino_t current_ino = start_ino;
- xfs_agblock_t agbno;
- int state;
-
- while (current_ino != NULLAGINO) {
- if (verify_aginum(mp, agno, current_ino))
- return(1);
- if ((bp = get_agino_buf(mp, agno, current_ino, &dip)) == NULL)
- return(1);
- /*
- * if this looks like a decent inode, then continue
- * following the unlinked pointers. If not, bail.
- */
- if (verify_dinode(mp, dip, agno, current_ino) == 0) {
- /*
- * check if the unlinked list points to an unknown
- * inode. if so, put it on the uncertain inode list
- * and set block map appropriately.
- */
- if (find_inode_rec(agno, current_ino) == NULL) {
- add_aginode_uncertain(agno, current_ino, 1);
- agbno = XFS_AGINO_TO_AGBNO(mp, current_ino);
-
- pthread_mutex_lock(&ag_locks[agno]);
- state = get_bmap(agno, agbno);
- switch (state) {
- case XR_E_BAD_STATE:
- do_error(_(
- "bad state in block map %d\n"),
- state);
- break;
- default:
- /*
- * the block looks like inodes
- * so be conservative and try
- * to scavenge what's in there.
- * if what's there is completely
- * bogus, it'll show up later
- * and the inode will be trashed
- * anyway, hopefully without
- * losing too much other data
- */
- set_bmap(agno, agbno, XR_E_INO);
- break;
- }
- pthread_mutex_unlock(&ag_locks[agno]);
- }
- current_ino = be32_to_cpu(dip->di_next_unlinked);
- } else {
- current_ino = NULLAGINO;;
- }
- libxfs_putbuf(bp);
- }
-
- return(0);
-}
+#include "bmap.h"
+#include "threads.h"
-void
-process_agi_unlinked(xfs_mount_t *mp, xfs_agnumber_t agno)
+static void
+process_agi_unlinked(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno)
{
- xfs_agnumber_t i;
- xfs_buf_t *bp;
- xfs_agi_t *agip;
- int err = 0;
- int agi_dirty = 0;
+ struct xfs_buf *bp;
+ struct xfs_agi *agip;
+ xfs_agnumber_t i;
+ int agi_dirty = 0;
bp = libxfs_readbuf(mp->m_dev,
XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
- mp->m_sb.sb_sectsize/BBSIZE, 0);
+ mp->m_sb.sb_sectsize/BBSIZE, 0, &xfs_agi_buf_ops);
if (!bp)
- do_error(_("cannot read agi block %lld for ag %u\n"),
+ do_error(_("cannot read agi block %" PRId64 " for ag %u\n"),
XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), agno);
agip = XFS_BUF_TO_AGI(bp);
- ASSERT(no_modify || be32_to_cpu(agip->agi_seqno) == agno);
+ ASSERT(be32_to_cpu(agip->agi_seqno) == agno);
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
- if (be32_to_cpu(agip->agi_unlinked[i]) != NULLAGINO) {
- err += walk_unlinked_list(mp, agno,
- be32_to_cpu(agip->agi_unlinked[i]));
- /*
- * clear the list
- */
- if (!no_modify) {
- agip->agi_unlinked[i] = cpu_to_be32(NULLAGINO);
- agi_dirty = 1;
- }
+ if (agip->agi_unlinked[i] != cpu_to_be32(NULLAGINO)) {
+ agip->agi_unlinked[i] = cpu_to_be32(NULLAGINO);
+ agi_dirty = 1;
}
}
- if (err)
- do_warn(_("error following ag %d unlinked list\n"), agno);
-
- ASSERT(agi_dirty == 0 || (agi_dirty && !no_modify));
-
- if (agi_dirty && !no_modify)
+ if (agi_dirty)
libxfs_writebuf(bp, 0);
else
libxfs_putbuf(bp);
static void
process_ag_func(
- work_queue_t *wq,
+ struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
*/
wait_for_inode_prefetch(arg);
do_log(_(" - agno = %d\n"), agno);
- process_aginodes(wq->mp, arg, agno, 1, 0, 1);
+ process_aginodes(wq->wq_ctx, arg, agno, 1, 0, 1);
+ blkmap_free_final();
cleanup_inode_prefetch(arg);
}
process_ags(
xfs_mount_t *mp)
{
- int i, j;
- xfs_agnumber_t agno;
- work_queue_t *queues;
- prefetch_args_t *pf_args[2];
+ do_inode_prefetch(mp, ag_stride, process_ag_func, false, false);
+}
- queues = malloc(thread_count * sizeof(work_queue_t));
+static void
+do_uncertain_aginodes(
+ struct workqueue *wq,
+ xfs_agnumber_t agno,
+ void *arg)
+{
+ int *count = arg;
- if (ag_stride) {
- /*
- * create one worker thread for each segment of the volume
- */
- for (i = 0, agno = 0; i < thread_count; i++) {
- create_work_queue(&queues[i], mp, 1);
- pf_args[0] = NULL;
- for (j = 0; j < ag_stride && agno < mp->m_sb.sb_agcount;
- j++, agno++) {
- pf_args[0] = start_inode_prefetch(agno, 0, pf_args[0]);
- queue_work(&queues[i], process_ag_func, agno, pf_args[0]);
- }
- }
- /*
- * wait for workers to complete
- */
- for (i = 0; i < thread_count; i++)
- destroy_work_queue(&queues[i]);
- } else {
- queues[0].mp = mp;
- pf_args[0] = start_inode_prefetch(0, 0, NULL);
- for (i = 0; i < mp->m_sb.sb_agcount; i++) {
- pf_args[(~i) & 1] = start_inode_prefetch(i + 1, 0,
- pf_args[i & 1]);
- process_ag_func(&queues[0], i, pf_args[i & 1]);
- }
- }
- free(queues);
+ *count = process_uncertain_aginodes(wq->wq_ctx, agno);
+
+#ifdef XR_INODE_TRACE
+ fprintf(stderr,
+ "\t\t phase 3 - ag %d process_uncertain_inodes returns %d\n",
+ *count, j);
+#endif
+
+ PROG_RPT_INC(prog_rpt_done[agno], 1);
}
void
-phase3(xfs_mount_t *mp)
+phase3(
+ struct xfs_mount *mp,
+ int scan_threads)
{
- int i, j;
+ int i, j;
+ int *counts;
+ struct workqueue wq;
do_log(_("Phase 3 - for each AG...\n"));
if (!no_modify)
else
do_log(_(" - scan (but don't clear) agi unlinked lists...\n"));
- set_progress_msg(PROG_FMT_AGI_UNLINKED, (__uint64_t) glob_agcount);
+ set_progress_msg(PROG_FMT_AGI_UNLINKED, (uint64_t) glob_agcount);
- /*
- * first, let's look at the possibly bogus inodes
- */
+ /* first clear the agi unlinked AGI list */
+ if (!no_modify) {
+ for (i = 0; i < mp->m_sb.sb_agcount; i++)
+ process_agi_unlinked(mp, i);
+ }
+
+ /* now look at possibly bogus inodes */
for (i = 0; i < mp->m_sb.sb_agcount; i++) {
- /*
- * walk unlinked list to add more potential inodes to list
- */
- process_agi_unlinked(mp, i);
check_uncertain_aginodes(mp, i);
PROG_RPT_INC(prog_rpt_done[i], 1);
}
do_log(_(
" - process known inodes and perform inode discovery...\n"));
- set_progress_msg(PROG_FMT_PROCESS_INO, (__uint64_t) mp->m_sb.sb_icount);
+ set_progress_msg(PROG_FMT_PROCESS_INO, (uint64_t) mp->m_sb.sb_icount);
process_ags(mp);
* process newly discovered inode chunks
*/
do_log(_(" - process newly discovered inodes...\n"));
- set_progress_msg(PROG_FMT_NEW_INODES, (__uint64_t) glob_agcount);
+ set_progress_msg(PROG_FMT_NEW_INODES, (uint64_t) glob_agcount);
+
+ counts = calloc(sizeof(*counts), mp->m_sb.sb_agcount);
+ if (!counts) {
+ do_abort(_("no memory for uncertain inode counts\n"));
+ return;
+ }
+
do {
/*
* have to loop until no ag has any uncertain
* inodes
*/
j = 0;
- for (i = 0; i < mp->m_sb.sb_agcount; i++) {
- j += process_uncertain_aginodes(mp, i);
-#ifdef XR_INODE_TRACE
- fprintf(stderr,
- "\t\t phase 3 - process_uncertain_inodes returns %d\n", j);
-#endif
- PROG_RPT_INC(prog_rpt_done[i], 1);
- }
+ memset(counts, 0, mp->m_sb.sb_agcount * sizeof(*counts));
+
+ create_work_queue(&wq, mp, scan_threads);
+
+ for (i = 0; i < mp->m_sb.sb_agcount; i++)
+ queue_work(&wq, do_uncertain_aginodes, i, &counts[i]);
+
+ destroy_work_queue(&wq);
+
+ /* tally up the counts */
+ for (i = 0; i < mp->m_sb.sb_agcount; i++)
+ j += counts[i];
+
} while (j != 0);
+
+ free(counts);
+
print_final_rpt();
}