aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2023-09-25 14:59:45 -0700
committerCarlos Maiolino <cem@kernel.org>2023-10-05 14:56:23 +0200
commit69b07d331612421f7e9b3723cc1afa527769f5c0 (patch)
tree442647d760213ded2182c208a678dbb11ac0d06e
parente17ccef351b424b11ddccc9d228d02a025f9639a (diff)
downloadxfsprogs-dev-69b07d331612421f7e9b3723cc1afa527769f5c0.tar.gz
xfs_db: dump unlinked buckets
Create a new command to dump the resource usage of files in the unlinked buckets. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Bill O'Donnell <bodonnel@redhat.com> Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com> Signed-off-by: Carlos Maiolino <cem@kernel.org>
-rw-r--r--db/Makefile2
-rw-r--r--db/command.c1
-rw-r--r--db/command.h1
-rw-r--r--db/iunlink.c204
-rw-r--r--libxfs/libxfs_api_defs.h1
-rw-r--r--man/man8/xfs_db.819
6 files changed, 227 insertions, 1 deletions
diff --git a/db/Makefile b/db/Makefile
index 2f95f67075..d00801ab47 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -14,7 +14,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \
fuzz.h obfuscate.h
-CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c namei.c \
+CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c iunlink.c namei.c \
timelimit.c
LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
diff --git a/db/command.c b/db/command.c
index 02f778b931..2bbd7b0b24 100644
--- a/db/command.c
+++ b/db/command.c
@@ -141,4 +141,5 @@ init_commands(void)
dquot_init();
fuzz_init();
timelimit_init();
+ iunlink_init();
}
diff --git a/db/command.h b/db/command.h
index 498983ff92..a89e71504f 100644
--- a/db/command.h
+++ b/db/command.h
@@ -34,3 +34,4 @@ extern void info_init(void);
extern void btheight_init(void);
extern void timelimit_init(void);
extern void namei_init(void);
+extern void iunlink_init(void);
diff --git a/db/iunlink.c b/db/iunlink.c
new file mode 100644
index 0000000000..303b5dafb7
--- /dev/null
+++ b/db/iunlink.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "command.h"
+#include "output.h"
+#include "init.h"
+
+static xfs_filblks_t
+count_rtblocks(
+ struct xfs_inode *ip)
+{
+ struct xfs_iext_cursor icur;
+ struct xfs_bmbt_irec got;
+ xfs_filblks_t count = 0;
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
+ int error;
+
+ error = -libxfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+ if (error) {
+ dbprintf(
+_("could not read AG %u agino %u extents, err=%d\n"),
+ XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
+ XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
+ error);
+ return 0;
+ }
+
+ for_each_xfs_iext(ifp, &icur, &got)
+ if (!isnullstartblock(got.br_startblock))
+ count += got.br_blockcount;
+ return count;
+}
+
+static xfs_agino_t
+get_next_unlinked(
+ xfs_agnumber_t agno,
+ xfs_agino_t agino,
+ bool verbose)
+{
+ struct xfs_buf *ino_bp;
+ struct xfs_dinode *dip;
+ struct xfs_inode *ip;
+ xfs_ino_t ino;
+ xfs_agino_t ret;
+ int error;
+
+ ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ error = -libxfs_iget(mp, NULL, ino, 0, &ip);
+ if (error)
+ goto bad;
+
+ if (verbose) {
+ xfs_filblks_t blocks, rtblks = 0;
+
+ if (XFS_IS_REALTIME_INODE(ip))
+ rtblks = count_rtblocks(ip);
+ blocks = ip->i_nblocks - rtblks;
+
+ dbprintf(_(" blocks %llu rtblocks %llu\n"),
+ blocks, rtblks);
+ } else {
+ dbprintf("\n");
+ }
+
+ error = -libxfs_imap_to_bp(mp, NULL, &ip->i_imap, &ino_bp);
+ if (error)
+ goto bad;
+
+ dip = xfs_buf_offset(ino_bp, ip->i_imap.im_boffset);
+ ret = be32_to_cpu(dip->di_next_unlinked);
+ libxfs_buf_relse(ino_bp);
+
+ return ret;
+bad:
+ dbprintf(_("AG %u agino %u: %s\n"), agno, agino, strerror(error));
+ return NULLAGINO;
+}
+
+static void
+dump_unlinked_bucket(
+ xfs_agnumber_t agno,
+ struct xfs_buf *agi_bp,
+ unsigned int bucket,
+ bool quiet,
+ bool verbose)
+{
+ struct xfs_agi *agi = agi_bp->b_addr;
+ xfs_agino_t agino;
+ unsigned int i = 0;
+
+ agino = be32_to_cpu(agi->agi_unlinked[bucket]);
+ if (agino != NULLAGINO)
+ dbprintf(_("AG %u bucket %u agino %u"), agno, bucket, agino);
+ else if (!quiet && agino == NULLAGINO)
+ dbprintf(_("AG %u bucket %u agino NULL\n"), agno, bucket);
+
+ while (agino != NULLAGINO) {
+ agino = get_next_unlinked(agno, agino, verbose);
+ if (agino != NULLAGINO)
+ dbprintf(_(" [%u] agino %u"), i++, agino);
+ else if (!quiet && agino == NULLAGINO)
+ dbprintf(_(" [%u] agino NULL\n"), i++);
+ }
+}
+
+static void
+dump_unlinked(
+ struct xfs_perag *pag,
+ unsigned int bucket,
+ bool quiet,
+ bool verbose)
+{
+ struct xfs_buf *agi_bp;
+ xfs_agnumber_t agno = pag->pag_agno;
+ int error;
+
+ error = -libxfs_ialloc_read_agi(pag, NULL, &agi_bp);
+ if (error) {
+ dbprintf(_("AGI %u: %s\n"), agno, strerror(errno));
+ return;
+ }
+
+ if (bucket != -1U) {
+ dump_unlinked_bucket(agno, agi_bp, bucket, quiet, verbose);
+ goto relse;
+ }
+
+ for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) {
+ dump_unlinked_bucket(agno, agi_bp, bucket, quiet, verbose);
+ }
+
+relse:
+ libxfs_buf_relse(agi_bp);
+}
+
+static int
+dump_iunlinked_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno = NULLAGNUMBER;
+ unsigned int bucket = -1U;
+ bool quiet = false;
+ bool verbose = false;
+ int c;
+
+ while ((c = getopt(argc, argv, "a:b:qv")) != EOF) {
+ switch (c) {
+ case 'a':
+ agno = atoi(optarg);
+ if (agno >= mp->m_sb.sb_agcount) {
+ dbprintf(_("Unknown AG %u, agcount is %u.\n"),
+ agno, mp->m_sb.sb_agcount);
+ return 0;
+ }
+ break;
+ case 'b':
+ bucket = atoi(optarg);
+ if (bucket >= XFS_AGI_UNLINKED_BUCKETS) {
+ dbprintf(_("Unknown bucket %u, max is 63.\n"),
+ bucket);
+ return 0;
+ }
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ dbprintf(_("Bad option for dump_iunlinked command.\n"));
+ return 0;
+ }
+ }
+
+ if (agno != NULLAGNUMBER) {
+ struct xfs_perag *pag = libxfs_perag_get(mp, agno);
+
+ dump_unlinked(pag, bucket, quiet, verbose);
+ libxfs_perag_put(pag);
+ return 0;
+ }
+
+ for_each_perag(mp, agno, pag)
+ dump_unlinked(pag, bucket, quiet, verbose);
+
+ return 0;
+}
+
+static const cmdinfo_t dump_iunlinked_cmd =
+ { "dump_iunlinked", NULL, dump_iunlinked_f, 0, -1, 0,
+ N_("[-a agno] [-b bucket] [-q] [-v]"),
+ N_("dump chain of unlinked inode buckets"), NULL };
+
+void
+iunlink_init(void)
+{
+ add_command(&dump_iunlinked_cmd);
+}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 026aa510ca..ddba5c7c71 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -125,6 +125,7 @@
#define xfs_idestroy_fork libxfs_idestroy_fork
#define xfs_iext_lookup_extent libxfs_iext_lookup_extent
#define xfs_ifork_zap_attr libxfs_ifork_zap_attr
+#define xfs_imap_to_bp libxfs_imap_to_bp
#define xfs_initialize_perag libxfs_initialize_perag
#define xfs_initialize_perag_data libxfs_initialize_perag_data
#define xfs_init_local_fork libxfs_init_local_fork
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 60dcdc52cb..2d6d0da4d7 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -579,6 +579,25 @@ print the current debug option bits. These are for the use of the implementor.
.BI "dquot [" \-g | \-p | \-u ] " id"
Set current address to a group, project or user quota block for the given ID. Defaults to user quota.
.TP
+.BI "dump_iunlinked [-a " agno " ] [-b " bucket " ] [-q] [-v]"
+Dump the contents of unlinked buckets.
+
+Options include:
+.RS 1.0i
+.TP 0.4i
+.B \-a
+Print only this AG's unlinked buckets.
+.TP 0.4i
+.B \-b
+Print only this bucket within each AGI.
+.TP 0.4i
+.B \-q
+Only print the essentials.
+.TP 0.4i
+.B \-v
+Print resource usage of each file on the unlinked lists.
+.RE
+.TP
.BI "echo [" arg "] ..."
Echo the arguments to the output.
.TP