On Wed, Aug 30, 2023 at 04:25:09PM -0700, Darrick J. Wong wrote: > From: Darrick J. Wong <djwong@xxxxxxxxxx> > > Create a new command to dump the resource usage of files in the unlinked > buckets. > > Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> Reviewed-by: Bill O'Donnell <bodonnel@xxxxxxxxxx> > --- > db/Makefile | 2 > db/command.c | 1 > db/command.h | 1 > db/iunlink.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++ > libxfs/libxfs_api_defs.h | 1 > man/man8/xfs_db.8 | 19 ++++ > 6 files changed, 227 insertions(+), 1 deletion(-) > create mode 100644 db/iunlink.c > > diff --git a/db/Makefile b/db/Makefile > index 2f95f670..d00801ab 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 02f778b9..b4021c86 100644 > --- a/db/command.c > +++ b/db/command.c > @@ -127,6 +127,7 @@ init_commands(void) > info_init(); > inode_init(); > input_init(); > + iunlink_init(); > logres_init(); > logformat_init(); > io_init(); > diff --git a/db/command.h b/db/command.h > index 498983ff..a89e7150 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 00000000..303b5daf > --- /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@xxxxxxxxxx> > + */ > +#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 026aa510..ddba5c7c 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 60dcdc52..2d6d0da4 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 >