]> git.apps.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_db: dump unlinked buckets
authorDarrick J. Wong <djwong@kernel.org>
Mon, 25 Sep 2023 21:59:45 +0000 (14:59 -0700)
committerCarlos Maiolino <cem@kernel.org>
Thu, 5 Oct 2023 12:56:23 +0000 (14:56 +0200)
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>
db/Makefile
db/command.c
db/command.h
db/iunlink.c [new file with mode: 0644]
libxfs/libxfs_api_defs.h
man/man8/xfs_db.8

index 2f95f67075df0d084843e2846216e65a2b971df7..d00801ab4739c694430b0950c784cee17c4f2475 100644 (file)
@@ -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
 
index 02f778b9316b86fa2e3a9ce59c8a779cd61ac2aa..2bbd7b0b24f9568dac1ca0f2925f7b700019ea34 100644 (file)
@@ -141,4 +141,5 @@ init_commands(void)
        dquot_init();
        fuzz_init();
        timelimit_init();
+       iunlink_init();
 }
index 498983ff92fa3d4eac13a6f7b17c4c69763cec19..a89e71504f9c7c1a7043befec8be1fc4aa7b3669 100644 (file)
@@ -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 (file)
index 0000000..303b5da
--- /dev/null
@@ -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);
+}
index 026aa510ca181e914f590cb70941249c348b20db..ddba5c7c71dbde61a2f8f08ce138aa4ed8dfd451 100644 (file)
 #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
index 60dcdc52cbaba57eb953db034e08d99c2b0120af..2d6d0da4d7bee9e97603c0a2322c1559a5c4c5db 100644 (file)
@@ -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