linux-xfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] xfsprogs: bulkstat v5
@ 2019-09-06  3:35 Darrick J. Wong
  2019-09-06  3:35 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-06  3:35 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

Hi all,

In this series, we do the heavier lifting to support the new v5 bulkstat
code.  First, we add an xfs_io command so that we can actually test the
old and new interfaces.  Then, we start converting spaceman and scrub to
use the per-AG bulkstat functions so that they can shed a lot of
duplicated geometry calculation code.  Finally, we speed up scrub phase
7 by fixing a minor thinko.

If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.

This is an extraordinary way to destroy everything.  Enjoy!
Comments and questions are, as always, welcome.

--D

xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=bulkstat-v5-factoring

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-06  3:35 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
@ 2019-09-06  3:35 ` Darrick J. Wong
  2019-09-12 23:51   ` Dave Chinner
  2019-09-06  3:35 ` [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat Darrick J. Wong
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-06  3:35 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Add a bulkstat command to xfs_io so that we can test our new xfrog code.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/Makefile        |    9 -
 io/bulkstat.c      |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c          |    1 
 io/io.h            |    1 
 libfrog/bulkstat.c |   20 ++
 libfrog/bulkstat.h |    3 
 man/man8/xfs_io.8  |   68 +++++++
 7 files changed, 631 insertions(+), 4 deletions(-)
 create mode 100644 io/bulkstat.c


diff --git a/io/Makefile b/io/Makefile
index 484e2b5a..1112605e 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
 LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
 HFILES = init.h io.h
 CFILES = init.c \
-	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
-	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
-	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
-	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
+	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
+	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
+	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
+	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
+	truncate.c utimes.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
diff --git a/io/bulkstat.c b/io/bulkstat.c
new file mode 100644
index 00000000..76ba682b
--- /dev/null
+++ b/io/bulkstat.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+#include "xfs.h"
+#include "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/bulkstat.h"
+#include "libfrog/paths.h"
+#include "io.h"
+#include "input.h"
+
+static bool debug;
+
+static void
+dump_bulkstat_time(
+	const char		*tag,
+	uint64_t		sec,
+	uint32_t		nsec)
+{
+	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
+}
+
+static void
+dump_bulkstat(
+	struct xfs_bulkstat	*bstat)
+{
+	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
+	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
+
+	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
+	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
+
+	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
+	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
+	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
+	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
+
+	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
+	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
+	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
+	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
+
+	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
+	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
+	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
+	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
+
+	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
+	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
+	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
+	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
+	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
+
+	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
+	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
+	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
+};
+
+static void
+bulkstat_help(void)
+{
+	printf(_(
+"Bulk-queries the filesystem for inode stat information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static int
+bulkstat_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat_req	*breq;
+	unsigned long long	startino = 0;
+	unsigned long long	endino = -1ULL;
+	unsigned long		batch_size = 4096;
+	unsigned long		agno = 0;
+	unsigned long		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			errno = 0;
+			agno = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			errno = 0;
+			endino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			errno = 0;
+			batch_size = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			errno = 0;
+			startino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	breq = xfrog_bulkstat_alloc_req(batch_size, startino);
+	if (!breq) {
+		perror("alloc bulkreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_bulkstat_set_ag(breq, agno);
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	while ((ret = xfrog_bulkstat(&xfd, breq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
+				(long long)breq->hdr.ino,
+				(unsigned int)breq->hdr.flags,
+				(unsigned int)breq->hdr.agno,
+				ret,
+				(unsigned int)breq->hdr.icount,
+				(unsigned int)breq->hdr.ocount);
+		if (breq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < breq->hdr.ocount; i++) {
+			if (breq->bulkstat[i].bs_ino > endino)
+				break;
+			dump_bulkstat(&breq->bulkstat[i]);
+		}
+	}
+	if (ret) {
+		errno = ret;
+		perror("xfrog_bulkstat");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(breq);
+	return 0;
+}
+
+static void
+bulkstat_single_help(void)
+{
+	printf(_(
+"Queries the filesystem for a single inode's stat information and prints it.\n"
+"\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"
+"\n"
+"Pass in inode numbers or a special inode name:\n"
+"    root    Root directory.\n"));
+}
+
+struct single_map {
+	const char		*tag;
+	uint64_t		code;
+};
+
+struct single_map tags[] = {
+	{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
+	{NULL, 0},
+};
+
+static int
+bulkstat_single_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat	bulkstat;
+	unsigned long		ver = 0;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "v:")) != -1) {
+		switch (c) {
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_single_help();
+			return 0;
+		}
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	for (i = optind; i < argc; i++) {
+		struct single_map	*sm = tags;
+		uint64_t		ino;
+		unsigned int		flags = 0;
+
+		/* Try to look up our tag... */
+		for (sm = tags; sm->tag; sm++) {
+			if (!strcmp(argv[i], sm->tag)) {
+				ino = sm->code;
+				flags |= XFS_BULK_IREQ_SPECIAL;
+				break;
+			}
+		}
+
+		/* ...or else it's an inode number. */
+		if (sm->tag == NULL) {
+			errno = 0;
+			ino = strtoull(argv[i], NULL, 10);
+			if (errno) {
+				perror(argv[i]);
+				exitcode = 1;
+				return 0;
+			}
+		}
+
+		ret = xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
+		if (ret) {
+			errno = ret;
+			perror("xfrog_bulkstat_single");
+			continue;
+		}
+
+		if (debug)
+			printf(
+_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
+				ino, flags, ret);
+
+		dump_bulkstat(&bulkstat);
+	}
+
+	return 0;
+}
+
+static void
+dump_inumbers(
+	struct xfs_inumbers	*inumbers)
+{
+	printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
+	printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
+	printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
+	printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
+}
+
+static void
+inumbers_help(void)
+{
+	printf(_(
+"Queries the filesystem for inode group information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static int
+inumbers_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_inumbers_req	*ireq;
+	unsigned long long	startino = 0;
+	unsigned long long	endino = -1ULL;
+	unsigned long		batch_size = 4096;
+	unsigned long		agno = 0;
+	unsigned long		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			errno = 0;
+			agno = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			errno = 0;
+			endino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			errno = 0;
+			batch_size = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			errno = 0;
+			startino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	ireq = xfrog_inumbers_alloc_req(batch_size, startino);
+	if (!ireq) {
+		perror("alloc inumbersreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_inumbers_set_ag(ireq, agno);
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	while ((ret = xfrog_inumbers(&xfd, ireq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
+				ireq->hdr.ino,
+				ireq->hdr.flags,
+				ireq->hdr.agno,
+				ret,
+				ireq->hdr.icount,
+				ireq->hdr.ocount);
+		if (ireq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < ireq->hdr.ocount; i++) {
+			if (ireq->inumbers[i].xi_startino > endino)
+				break;
+			dump_inumbers(&ireq->inumbers[i]);
+		}
+	}
+	if (ret) {
+		errno = ret;
+		perror("xfrog_inumbers");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(ireq);
+	return 0;
+}
+
+static cmdinfo_t	bulkstat_cmd = {
+	.name = "bulkstat",
+	.cfunc = bulkstat_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = bulkstat_help,
+};
+
+static cmdinfo_t	bulkstat_single_cmd = {
+	.name = "bulkstat_single",
+	.cfunc = bulkstat_single_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = bulkstat_single_help,
+};
+
+static cmdinfo_t	inumbers_cmd = {
+	.name = "inumbers",
+	.cfunc = inumbers_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = inumbers_help,
+};
+
+void
+bulkstat_init(void)
+{
+	bulkstat_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
+
+	bulkstat_single_cmd.args = _("inum...");
+	bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");
+
+	inumbers_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
+
+	add_command(&bulkstat_cmd);
+	add_command(&bulkstat_single_cmd);
+	add_command(&inumbers_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 7025aea5..033ed67d 100644
--- a/io/init.c
+++ b/io/init.c
@@ -46,6 +46,7 @@ init_commands(void)
 {
 	attr_init();
 	bmap_init();
+	bulkstat_init();
 	copy_range_init();
 	cowextsize_init();
 	encrypt_init();
diff --git a/io/io.h b/io/io.h
index 00dff2b7..49db902f 100644
--- a/io/io.h
+++ b/io/io.h
@@ -183,3 +183,4 @@ extern void		log_writes_init(void);
 extern void		scrub_init(void);
 extern void		repair_init(void);
 extern void		crc32cselftest_init(void);
+extern void		bulkstat_init(void);
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index 748d0f32..603a9589 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -387,6 +387,16 @@ xfrog_bulkstat_alloc_req(
 	return breq;
 }
 
+/* Set a bulkstat cursor to iterate only a particular AG. */
+void
+xfrog_bulkstat_set_ag(
+	struct xfs_bulkstat_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
+
 /* Convert an inumbers (v5) struct to a inogrp (v1) struct. */
 void
 xfrog_inumbers_to_inogrp(
@@ -514,3 +524,13 @@ xfrog_inumbers_alloc_req(
 
 	return ireq;
 }
+
+/* Set an inumbers cursor to iterate only a particular AG. */
+void
+xfrog_inumbers_set_ag(
+	struct xfs_inumbers_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index 5da7d3f5..bed4ff15 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -19,11 +19,14 @@ void xfrog_bulkstat_to_bstat(struct xfs_fd *xfd, struct xfs_bstat *bs1,
 void xfrog_bstat_to_bulkstat(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
 		const struct xfs_bstat *bs1);
 
+void xfrog_bulkstat_set_ag(struct xfs_bulkstat_req *req, uint32_t agno);
+
 struct xfs_inogrp;
 int xfrog_inumbers(struct xfs_fd *xfd, struct xfs_inumbers_req *req);
 
 struct xfs_inumbers_req *xfrog_inumbers_alloc_req(uint32_t nr,
 		uint64_t startino);
+void xfrog_inumbers_set_ag(struct xfs_inumbers_req *req, uint32_t agno);
 void xfrog_inumbers_to_inogrp(struct xfs_inogrp *ig1,
 		const struct xfs_inumbers *ig);
 void xfrog_inogrp_to_inumbers(struct xfs_inumbers *ig,
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 6e064bdd..8fd3ffbe 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -996,6 +996,45 @@ for the current memory mapping.
 
 .SH FILESYSTEM COMMANDS
 .TP
+.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Display raw stat information about a bunch of inodes in an XFS filesystem.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+Defaults to zero.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to zero.
+If this value is zero, then display starts with the allocation group
+given above.
+.RE
+.PD
+.TP
+.BI "bulkstat_single [ " inum... " | " special... " ]
+Display raw stat information about individual inodes in an XFS filesystem.
+Arguments must be inode numbers or any of the special values:
+.RS 1.0i
+.PD 0
+.TP
+.B root
+Display information about the root directory inode.
+.RE
+.PD
+.TP
 .B freeze
 Suspend all write I/O requests to the filesystem of the current file.
 Only available in expert mode and requires privileges.
@@ -1067,6 +1106,35 @@ was specified on the command line, the maximum possible inode number in
 the system will be printed along with its size.
 .PD
 .TP
+.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Prints allocation information about groups of inodes in an XFS filesystem.
+Callers can use this information to figure out which inodes are allocated.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+Defaults to zero.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to zero.
+If this value is zero, then display starts with the allocation group
+given above.
+.RE
+.PD
+.TP
 .BI "scrub " type " [ " agnumber " | " "ino" " " "gen" " ]"
 Scrub internal XFS filesystem metadata.  The
 .BI type


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat
  2019-09-06  3:35 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
  2019-09-06  3:35 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
@ 2019-09-06  3:35 ` Darrick J. Wong
  2019-09-12 23:52   ` Dave Chinner
  2019-09-06  3:36 ` [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations Darrick J. Wong
  2019-09-06  3:36 ` [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation Darrick J. Wong
  3 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-06  3:35 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Now that xfrog_bulkstat supports per-AG bulkstat, we can get rid of the
open coded one in spaceman.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 spaceman/health.c |   16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)


diff --git a/spaceman/health.c b/spaceman/health.c
index b6e1fcd9..e70fd6fd 100644
--- a/spaceman/health.c
+++ b/spaceman/health.c
@@ -268,28 +268,22 @@ report_bulkstat_health(
 {
 	struct xfs_bulkstat_req	*breq;
 	char			descr[256];
-	uint64_t		startino = 0;
-	uint64_t		lastino = -1ULL;
 	uint32_t		i;
 	int			error;
 
-	if (agno != NULLAGNUMBER) {
-		startino = cvt_agino_to_ino(&file->xfd, agno, 0);
-		lastino = cvt_agino_to_ino(&file->xfd, agno + 1, 0) - 1;
-	}
-
-	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, startino);
+	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, 0);
 	if (!breq) {
 		perror("bulk alloc req");
 		exitcode = 1;
 		return 1;
 	}
 
+	if (agno != NULLAGNUMBER)
+		xfrog_bulkstat_set_ag(breq, agno);
+
 	while ((error = xfrog_bulkstat(&file->xfd, breq) == 0) &&
 			breq->hdr.ocount > 0) {
 		for (i = 0; i < breq->hdr.ocount; i++) {
-			if (breq->bulkstat[i].bs_ino > lastino)
-				goto out;
 			snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
 					breq->bulkstat[i].bs_ino);
 			report_sick(descr, inode_flags,
@@ -301,7 +295,7 @@ report_bulkstat_health(
 		errno = error;
 		perror("bulkstat");
 	}
-out:
+
 	free(breq);
 	return error;
 }


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations
  2019-09-06  3:35 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
  2019-09-06  3:35 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
  2019-09-06  3:35 ` [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat Darrick J. Wong
@ 2019-09-06  3:36 ` Darrick J. Wong
  2019-09-12 23:55   ` Dave Chinner
  2019-09-06  3:36 ` [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation Darrick J. Wong
  3 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-06  3:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Now that we're done reworking the xfrog bulkstat wrapper functions, we
can adapt xfs_scrub to use the per-ag iteration functionality.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 scrub/fscounters.c |   22 ++++++----------------
 scrub/inodes.c     |   20 ++++++--------------
 2 files changed, 12 insertions(+), 30 deletions(-)


diff --git a/scrub/fscounters.c b/scrub/fscounters.c
index 2fdf658a..a2cf8171 100644
--- a/scrub/fscounters.c
+++ b/scrub/fscounters.c
@@ -35,29 +35,25 @@ struct xfs_count_inodes {
  * exist in the filesystem, assuming we've already scrubbed that.
  */
 static bool
-xfs_count_inodes_range(
+xfs_count_inodes_ag(
 	struct scrub_ctx	*ctx,
 	const char		*descr,
-	uint64_t		first_ino,
-	uint64_t		last_ino,
+	uint32_t		agno,
 	uint64_t		*count)
 {
 	struct xfs_inumbers_req	*ireq;
 	uint64_t		nr = 0;
 	int			error;
 
-	ASSERT(!(first_ino & (XFS_INODES_PER_CHUNK - 1)));
-	ASSERT((last_ino & (XFS_INODES_PER_CHUNK - 1)));
-
-	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	ireq = xfrog_inumbers_alloc_req(1, 0);
 	if (!ireq) {
 		str_info(ctx, descr, _("Insufficient memory; giving up."));
 		return false;
 	}
+	xfrog_inumbers_set_ag(ireq, agno);
 
 	while (!(error = xfrog_inumbers(&ctx->mnt, ireq))) {
-		if (ireq->hdr.ocount == 0 ||
-		    ireq->inumbers[0].xi_startino >= last_ino)
+		if (ireq->hdr.ocount == 0)
 			break;
 		nr += ireq->inumbers[0].xi_alloccount;
 	}
@@ -83,8 +79,6 @@ xfs_count_ag_inodes(
 	struct xfs_count_inodes	*ci = arg;
 	struct scrub_ctx	*ctx = (struct scrub_ctx *)wq->wq_ctx;
 	char			descr[DESCR_BUFSZ];
-	uint64_t		ag_ino;
-	uint64_t		next_ag_ino;
 	bool			moveon;
 
 	snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"),
@@ -92,11 +86,7 @@ xfs_count_ag_inodes(
 				minor(ctx->fsinfo.fs_datadev),
 				agno);
 
-	ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0);
-	next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0);
-
-	moveon = xfs_count_inodes_range(ctx, descr, ag_ino, next_ag_ino - 1,
-			&ci->counters[agno]);
+	moveon = xfs_count_inodes_ag(ctx, descr, agno, &ci->counters[agno]);
 	if (!moveon)
 		ci->moveon = false;
 }
diff --git a/scrub/inodes.c b/scrub/inodes.c
index 65c404ab..c7aadae7 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -82,12 +82,11 @@ xfs_iterate_inodes_range_check(
  * but we also can detect iget failures.
  */
 static bool
-xfs_iterate_inodes_range(
+xfs_iterate_inodes_ag(
 	struct scrub_ctx	*ctx,
 	const char		*descr,
 	void			*fshandle,
-	uint64_t		first_ino,
-	uint64_t		last_ino,
+	uint32_t		agno,
 	xfs_inode_iter_fn	fn,
 	void			*arg)
 {
@@ -113,13 +112,14 @@ xfs_iterate_inodes_range(
 		return false;
 	}
 
-	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	ireq = xfrog_inumbers_alloc_req(1, 0);
 	if (!ireq) {
 		str_info(ctx, descr, _("Insufficient memory; giving up."));
 		free(breq);
 		return false;
 	}
 	inogrp = &ireq->inumbers[0];
+	xfrog_inumbers_set_ag(ireq, agno);
 
 	/* Find the inode chunk & alloc mask */
 	error = xfrog_inumbers(&ctx->mnt, ireq);
@@ -147,9 +147,6 @@ xfs_iterate_inodes_range(
 		for (i = 0, bs = breq->bulkstat;
 		     i < inogrp->xi_alloccount;
 		     i++, bs++) {
-			if (bs->bs_ino > last_ino)
-				goto out;
-
 			handle.ha_fid.fid_ino = bs->bs_ino;
 			handle.ha_fid.fid_gen = bs->bs_gen;
 			error = fn(ctx, &handle, bs, arg);
@@ -214,8 +211,6 @@ xfs_scan_ag_inodes(
 	struct xfs_scan_inodes	*si = arg;
 	struct scrub_ctx	*ctx = (struct scrub_ctx *)wq->wq_ctx;
 	char			descr[DESCR_BUFSZ];
-	uint64_t		ag_ino;
-	uint64_t		next_ag_ino;
 	bool			moveon;
 
 	snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"),
@@ -223,11 +218,8 @@ xfs_scan_ag_inodes(
 				minor(ctx->fsinfo.fs_datadev),
 				agno);
 
-	ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0);
-	next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0);
-
-	moveon = xfs_iterate_inodes_range(ctx, descr, ctx->fshandle, ag_ino,
-			next_ag_ino - 1, si->fn, si->arg);
+	moveon = xfs_iterate_inodes_ag(ctx, descr, ctx->fshandle, agno,
+			si->fn, si->arg);
 	if (!moveon)
 		si->moveon = false;
 }


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation
  2019-09-06  3:35 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
                   ` (2 preceding siblings ...)
  2019-09-06  3:36 ` [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations Darrick J. Wong
@ 2019-09-06  3:36 ` Darrick J. Wong
  2019-09-12 23:56   ` Dave Chinner
  3 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-06  3:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Improve the efficiency of the phase 7 inode counts by batching requests,
now that we have per-AG inumbers wrappers.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 scrub/fscounters.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)


diff --git a/scrub/fscounters.c b/scrub/fscounters.c
index a2cf8171..ad467e0c 100644
--- a/scrub/fscounters.c
+++ b/scrub/fscounters.c
@@ -43,9 +43,10 @@ xfs_count_inodes_ag(
 {
 	struct xfs_inumbers_req	*ireq;
 	uint64_t		nr = 0;
+	unsigned int		i;
 	int			error;
 
-	ireq = xfrog_inumbers_alloc_req(1, 0);
+	ireq = xfrog_inumbers_alloc_req(64, 0);
 	if (!ireq) {
 		str_info(ctx, descr, _("Insufficient memory; giving up."));
 		return false;
@@ -55,7 +56,8 @@ xfs_count_inodes_ag(
 	while (!(error = xfrog_inumbers(&ctx->mnt, ireq))) {
 		if (ireq->hdr.ocount == 0)
 			break;
-		nr += ireq->inumbers[0].xi_alloccount;
+		for (i = 0; i < ireq->hdr.ocount; i++)
+			nr += ireq->inumbers[i].xi_alloccount;
 	}
 
 	free(ireq);


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-06  3:35 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
@ 2019-09-12 23:51   ` Dave Chinner
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2019-09-12 23:51 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: sandeen, linux-xfs

On Thu, Sep 05, 2019 at 08:35:44PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a bulkstat command to xfs_io so that we can test our new xfrog code.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  io/Makefile        |    9 -
>  io/bulkstat.c      |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  io/init.c          |    1 
>  io/io.h            |    1 
>  libfrog/bulkstat.c |   20 ++
>  libfrog/bulkstat.h |    3 
>  man/man8/xfs_io.8  |   68 +++++++
>  7 files changed, 631 insertions(+), 4 deletions(-)
>  create mode 100644 io/bulkstat.c
> 
> 
> diff --git a/io/Makefile b/io/Makefile
> index 484e2b5a..1112605e 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
>  LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
>  HFILES = init.h io.h
>  CFILES = init.c \
> -	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
> -	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
> -	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
> -	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
> +	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
> +	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
> +	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
> +	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
> +	truncate.c utimes.c
>  
>  LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
>  LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
> diff --git a/io/bulkstat.c b/io/bulkstat.c
> new file mode 100644
> index 00000000..76ba682b
> --- /dev/null
> +++ b/io/bulkstat.c
> @@ -0,0 +1,533 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 Oracle.  All Rights Reserved.
> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> + */
> +#include "xfs.h"
> +#include "platform_defs.h"
> +#include "command.h"
> +#include "init.h"
> +#include "libfrog/fsgeom.h"
> +#include "libfrog/bulkstat.h"
> +#include "libfrog/paths.h"
> +#include "io.h"
> +#include "input.h"
> +
> +static bool debug;
> +
> +static void
> +dump_bulkstat_time(
> +	const char		*tag,
> +	uint64_t		sec,
> +	uint32_t		nsec)
> +{
> +	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
> +}
> +
> +static void
> +dump_bulkstat(
> +	struct xfs_bulkstat	*bstat)
> +{
> +	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
> +	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
> +
> +	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
> +	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
> +
> +	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
> +	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
> +	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
> +	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
> +
> +	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
> +	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
> +	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
> +	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
> +
> +	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
> +	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
> +	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
> +	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
> +
> +	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
> +	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
> +	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
> +	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
> +	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
> +
> +	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
> +	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
> +	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
> +};
> +
> +static void
> +bulkstat_help(void)
> +{
> +	printf(_(
> +"Bulk-queries the filesystem for inode stat information and prints it.\n"
> +"\n"
> +"   -a   Only iterate this AG.\n"
> +"   -d   Print debugging output.\n"
> +"   -e   Stop after this inode.\n"
> +"   -n   Ask for this many results at once.\n"
> +"   -s   Inode to start with.\n"
> +"   -v   Use this version of the ioctl (1 or 5).\n"));
> +}
> +
> +static int
> +bulkstat_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> +	struct xfs_bulkstat_req	*breq;
> +	unsigned long long	startino = 0;
> +	unsigned long long	endino = -1ULL;
> +	unsigned long		batch_size = 4096;
> +	unsigned long		agno = 0;
> +	unsigned long		ver = 0;
> +	bool			has_agno = false;
> +	unsigned int		i;
> +	int			c;
> +	int			ret;
> +
> +	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {

options c and q are not documented above, and not handled in the
case statement below.

> +		switch (c) {
> +		case 'a':
> +			errno = 0;
> +			agno = strtoul(optarg, NULL, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			has_agno = true;
> +			break;

Why not use cvt_u32() and friends for these so they are directly
converted to the correct type and overflow checked at the same time?

[...]

> +static void
> +inumbers_help(void)
> +{
> +	printf(_(
> +"Queries the filesystem for inode group information and prints it.\n"
> +"\n"
> +"   -a   Only iterate this AG.\n"
> +"   -d   Print debugging output.\n"
> +"   -e   Stop after this inode.\n"
> +"   -n   Ask for this many results at once.\n"
> +"   -s   Inode to start with.\n"
> +"   -v   Use this version of the ioctl (1 or 5).\n"));
> +}
> +
> +static int
> +inumbers_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> +	struct xfs_inumbers_req	*ireq;
> +	unsigned long long	startino = 0;
> +	unsigned long long	endino = -1ULL;
> +	unsigned long		batch_size = 4096;
> +	unsigned long		agno = 0;
> +	unsigned long		ver = 0;
> +	bool			has_agno = false;
> +	unsigned int		i;
> +	int			c;
> +	int			ret;
> +
> +	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {

Again, c and q not defined. Same comments about cvt_type() as
well...

> +	if (has_agno)
> +		xfrog_inumbers_set_ag(ireq, agno);
> +
> +	switch (ver) {
> +	case 1:
> +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> +		break;
> +	case 5:
> +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
> +		break;
> +	default:
> +		break;
> +	}

Common helper?

> +static cmdinfo_t	bulkstat_cmd = {
> +	.name = "bulkstat",
> +	.cfunc = bulkstat_f,
> +	.argmin = 0,
> +	.argmax = -1,
> +	.flags = CMD_NOMAP_OK,
> +	.help = bulkstat_help,
> +};

Theres are all oneshot commands, right?

> +
> +static cmdinfo_t	bulkstat_single_cmd = {
> +	.name = "bulkstat_single",
> +	.cfunc = bulkstat_single_f,
> +	.argmin = 0,
> +	.argmax = -1,
> +	.flags = CMD_NOMAP_OK,
> +	.help = bulkstat_single_help,
> +};

Doesn't this require at least one parameter?

>  
>  .SH FILESYSTEM COMMANDS
>  .TP
> +.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> +Display raw stat information about a bunch of inodes in an XFS filesystem.
> +Options are as follows:
> +.RS 1.0i
> +.PD 0
> +.TP
> +.BI \-a " agno"
> +Display only results from the given allocation group.
> +Defaults to zero.

Need to say "If not specified, will all AGs in the fielsystem"

> @@ -1067,6 +1106,35 @@ was specified on the command line, the maximum possible inode number in
>  the system will be printed along with its size.
>  .PD
>  .TP
> +.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> +Prints allocation information about groups of inodes in an XFS filesystem.
> +Callers can use this information to figure out which inodes are allocated.
> +Options are as follows:
> +.RS 1.0i
> +.PD 0
> +.TP
> +.BI \-a " agno"
> +Display only results from the given allocation group.
> +Defaults to zero.

Same again.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat
  2019-09-06  3:35 ` [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat Darrick J. Wong
@ 2019-09-12 23:52   ` Dave Chinner
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2019-09-12 23:52 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: sandeen, linux-xfs

On Thu, Sep 05, 2019 at 08:35:54PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Now that xfrog_bulkstat supports per-AG bulkstat, we can get rid of the
> open coded one in spaceman.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations
  2019-09-06  3:36 ` [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations Darrick J. Wong
@ 2019-09-12 23:55   ` Dave Chinner
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2019-09-12 23:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: sandeen, linux-xfs

On Thu, Sep 05, 2019 at 08:36:03PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Now that we're done reworking the xfrog bulkstat wrapper functions, we
> can adapt xfs_scrub to use the per-ag iteration functionality.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  scrub/fscounters.c |   22 ++++++----------------
>  scrub/inodes.c     |   20 ++++++--------------
>  2 files changed, 12 insertions(+), 30 deletions(-)

Nice simplification.

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation
  2019-09-06  3:36 ` [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation Darrick J. Wong
@ 2019-09-12 23:56   ` Dave Chinner
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2019-09-12 23:56 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: sandeen, linux-xfs

On Thu, Sep 05, 2019 at 08:36:11PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Improve the efficiency of the phase 7 inode counts by batching requests,
> now that we have per-AG inumbers wrappers.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Makes sense.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-30 20:15         ` Darrick J. Wong
@ 2019-10-07 19:13           ` Eric Sandeen
  0 siblings, 0 replies; 17+ messages in thread
From: Eric Sandeen @ 2019-10-07 19:13 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 9/30/19 3:15 PM, Darrick J. Wong wrote:
> On Mon, Sep 30, 2019 at 03:02:27PM -0500, Eric Sandeen wrote:
>> On 9/26/19 11:18 PM, Darrick J. Wong wrote:
>>>>> +
>>>>> +	inumbers_cmd.args =
>>>>> +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
>>>> <missing the -v option>
>>>>
>>>>> +	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
>>>> I'm confused, why aren't all these ^^^ just in the structure definitions?
>>> All of these ... what?  I'm confused, sorry.
>>>
>>
>> I'm wondering why these 2 fields get set up in bulkstat_init(), vs at
>> cmdinfo_t structure definition time, i.e.
>>
>> static cmdinfo_t        inumbers_cmd = {
>>         .name = "inumbers",
>>         .cfunc = inumbers_f,
>>         .argmin = 0,
>>         .argmax = -1,
>>         .flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
>>         .args =
>> _("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino] [-v version]");
>>         .oneline = _("Query inode groups in a filesystem");
>>         .help = inumbers_help,
>> };
>>
>> like ~every other command does?
> 
> [repeating irc conversation]
> 
> _() is a function, but static initializers require constant rvalues.

Sorry, my bad for missing that.

Thanks,
-Eric

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-30 20:02       ` Eric Sandeen
@ 2019-09-30 20:15         ` Darrick J. Wong
  2019-10-07 19:13           ` Eric Sandeen
  0 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-30 20:15 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs

On Mon, Sep 30, 2019 at 03:02:27PM -0500, Eric Sandeen wrote:
> On 9/26/19 11:18 PM, Darrick J. Wong wrote:
> >>> +
> >>> +	inumbers_cmd.args =
> >>> +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
> >> <missing the -v option>
> >>
> >>> +	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
> >> I'm confused, why aren't all these ^^^ just in the structure definitions?
> > All of these ... what?  I'm confused, sorry.
> > 
> 
> I'm wondering why these 2 fields get set up in bulkstat_init(), vs at
> cmdinfo_t structure definition time, i.e.
> 
> static cmdinfo_t        inumbers_cmd = {
>         .name = "inumbers",
>         .cfunc = inumbers_f,
>         .argmin = 0,
>         .argmax = -1,
>         .flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
>         .args =
> _("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino] [-v version]");
>         .oneline = _("Query inode groups in a filesystem");
>         .help = inumbers_help,
> };
> 
> like ~every other command does?

[repeating irc conversation]

_() is a function, but static initializers require constant rvalues.

--D

> 
> 
> -Eric

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-27  4:18     ` Darrick J. Wong
@ 2019-09-30 20:02       ` Eric Sandeen
  2019-09-30 20:15         ` Darrick J. Wong
  0 siblings, 1 reply; 17+ messages in thread
From: Eric Sandeen @ 2019-09-30 20:02 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/26/19 11:18 PM, Darrick J. Wong wrote:
>>> +
>>> +	inumbers_cmd.args =
>>> +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
>> <missing the -v option>
>>
>>> +	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
>> I'm confused, why aren't all these ^^^ just in the structure definitions?
> All of these ... what?  I'm confused, sorry.
> 

I'm wondering why these 2 fields get set up in bulkstat_init(), vs at
cmdinfo_t structure definition time, i.e.

static cmdinfo_t        inumbers_cmd = {
        .name = "inumbers",
        .cfunc = inumbers_f,
        .argmin = 0,
        .argmax = -1,
        .flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
        .args =
_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino] [-v version]");
        .oneline = _("Query inode groups in a filesystem");
        .help = inumbers_help,
};

like ~every other command does?


-Eric

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-26 22:40   ` Eric Sandeen
  2019-09-26 22:46     ` Eric Sandeen
@ 2019-09-27  4:18     ` Darrick J. Wong
  2019-09-30 20:02       ` Eric Sandeen
  1 sibling, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-27  4:18 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs

On Thu, Sep 26, 2019 at 05:40:11PM -0500, Eric Sandeen wrote:
> On 9/25/19 4:33 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Add a bulkstat command to xfs_io so that we can test our new xfrog code.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  io/Makefile        |    9 -
> >  io/bulkstat.c      |  522 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  io/init.c          |    1 
> >  io/io.h            |    1 
> >  libfrog/bulkstat.c |   20 ++
> >  libfrog/bulkstat.h |    3 
> >  man/man8/xfs_io.8  |   66 +++++++
> >  7 files changed, 618 insertions(+), 4 deletions(-)
> >  create mode 100644 io/bulkstat.c
> > 
> > 
> > diff --git a/io/Makefile b/io/Makefile
> > index 484e2b5a..1112605e 100644
> > --- a/io/Makefile
> > +++ b/io/Makefile
> > @@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
> >  LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
> >  HFILES = init.h io.h
> >  CFILES = init.c \
> > -	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
> > -	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
> > -	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
> > -	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
> > +	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
> > +	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
> > +	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
> > +	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
> > +	truncate.c utimes.c
> >  
> >  LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
> >  LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
> > diff --git a/io/bulkstat.c b/io/bulkstat.c
> > new file mode 100644
> > index 00000000..625f0abe
> > --- /dev/null
> > +++ b/io/bulkstat.c
> > @@ -0,0 +1,522 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2019 Oracle.  All Rights Reserved.
> > + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> > + */
> > +#include "xfs.h"
> > +#include "platform_defs.h"
> > +#include "command.h"
> > +#include "init.h"
> > +#include "libfrog/fsgeom.h"
> > +#include "libfrog/bulkstat.h"
> > +#include "libfrog/paths.h"
> > +#include "io.h"
> > +#include "input.h"
> > +
> > +static bool debug;
> 
> since I'm old and my brain is weakening... do you need to turn this
> global debug back off in each foo_f function unless the -d debug
> option was specified?

Yes.  Fixed.

> > +
> > +static void
> > +dump_bulkstat_time(
> > +	const char		*tag,
> > +	uint64_t		sec,
> > +	uint32_t		nsec)
> > +{
> > +	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
> > +}
> > +
> > +static void
> > +dump_bulkstat(
> > +	struct xfs_bulkstat	*bstat)
> > +{
> > +	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
> > +	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
> > +
> > +	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
> > +	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
> > +
> > +	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
> > +	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
> > +	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
> > +	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
> > +
> > +	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
> > +	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
> > +	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
> > +	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
> > +
> > +	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
> > +	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
> > +	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
> > +	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
> > +
> > +	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
> > +	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
> > +	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
> > +	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
> > +	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
> > +
> > +	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
> > +	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
> > +	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
> > +};
> > +
> > +static void
> > +bulkstat_help(void)
> > +{
> > +	printf(_(
> > +"Bulk-queries the filesystem for inode stat information and prints it.\n"
> > +"\n"
> > +"   -a   Only iterate this AG.\n"
> > +"   -d   Print debugging output.\n"
> > +"   -e   Stop after this inode.\n"
> > +"   -n   Ask for this many results at once.\n"
> > +"   -s   Inode to start with.\n"
> > +"   -v   Use this version of the ioctl (1 or 5).\n"));
> 
> +"   -a <agno>  Only iterate this AG.\n"
> +"   -d         Print debugging output.\n"
> +"   -e <ino>   Stop after this inode.\n"
> +"   -n <nr>    Ask for this many results at once.\n"
> +"   -s <ino>   Inode to start with.\n"
> +"   -v <ver>   Use this version of the ioctl (1 or 5).\n"));

Fixed.

> > +}
> > +
> > +static void
> > +set_xfd_flags(
> > +	struct xfs_fd	*xfd,
> > +	uint32_t	ver)
> > +{
> > +	switch (ver) {
> > +	case 1:
> > +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> > +		break;
> > +	case 5:
> > +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static int
> > +bulkstat_f(
> > +	int			argc,
> > +	char			**argv)
> > +{
> > +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> > +	struct xfs_bulkstat_req	*breq;
> > +	uint64_t		startino = 0;
> > +	uint64_t		endino = -1ULL;
> > +	uint32_t		batch_size = 4096;
> > +	uint32_t		agno = 0;
> > +	uint32_t		ver = 0;
> > +	bool			has_agno = false;
> > +	unsigned int		i;
> > +	int			c;
> > +	int			ret;
> > +
> > +	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
> > +		switch (c) {
> > +		case 'a':
> > +			agno = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			has_agno = true;
> > +			break;
> > +		case 'd':
> > +			debug = true;
> > +			break;
> > +		case 'e':
> > +			endino = cvt_u64(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 'n':
> > +			batch_size = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 's':
> > +			startino = cvt_u64(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 'v':
> > +			ver = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			if (ver != 1 && ver != 5) {
> > +				fprintf(stderr, "version must be 1 or 5.\n");
> > +				return 1;
> > +			}
> > +			break;
> > +		default:
> > +			bulkstat_help();
> > +			return 0;
> > +		}
> > +	}
> > +	if (optind != argc) {
> > +		bulkstat_help();
> > +		return 0;
> > +	}
> > +
> > +	ret = xfd_prepare_geometry(&xfd);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("xfd_prepare_geometry");
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	breq = xfrog_bulkstat_alloc_req(batch_size, startino);
> > +	if (!breq) {
> > +		perror("alloc bulkreq");
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	if (has_agno)
> > +		xfrog_bulkstat_set_ag(breq, agno);
> > +
> > +	set_xfd_flags(&xfd, ver);
> > +
> > +	while ((ret = xfrog_bulkstat(&xfd, breq)) == 0) {
> > +		if (debug)
> > +			printf(
> > +_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
> > +				(long long)breq->hdr.ino,
> > +				(unsigned int)breq->hdr.flags,
> > +				(unsigned int)breq->hdr.agno,
> > +				ret,
> > +				(unsigned int)breq->hdr.icount,
> > +				(unsigned int)breq->hdr.ocount);
> > +		if (breq->hdr.ocount == 0)
> > +			break;
> > +
> > +		for (i = 0; i < breq->hdr.ocount; i++) {
> > +			if (breq->bulkstat[i].bs_ino > endino)
> > +				break;
> > +			dump_bulkstat(&breq->bulkstat[i]);
> > +		}
> > +	}
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("xfrog_bulkstat");
> > +		exitcode = 1;
> 
> free(breq) (or just drop the return & fall through ...)

Good catch, thank you. :)

> 
> > +		return 0;
> > +	}
> > +
> > +	free(breq);
> > +	return 0;
> > +}
> > +
> > +static void
> > +bulkstat_single_help(void)
> > +{
> > +	printf(_(
> > +"Queries the filesystem for a single inode's stat information and prints it.\n"
> > +"\n"
> > +"   -v   Use this version of the ioctl (1 or 5).\n"
> 
> +"   -v <ver>   Use this version of the ioctl (1 or 5).\n"));
> 
> (I'm realizing all our long help is preeeeettttty free form but still worth
> noting that it requires an optarg)

Er... more than bulkstat_single_cmd.args?

Ok, I suppose I could change it to "[-d] [-v] inum..." to imply that we
take optarg arguments.

> since it can take more than one, I wonder if the man page's text
> ("individual inodes") is better than "a single inode's"
> 
> The other interesting thing is that it takes a start ino but gives you the
> first allocated inode >= that number, right:
> 
> xfs_io> bulkstat_single 128 129 130
> bs_ino = 160
> ...
> bs_ino = 160
> ...
> bs_ino = 160
> ...
> 
> and while that's how the interface works, I wonder if the help and manpage
> should be (sigh) more clear about it somehow.

Yeah, I'll make a note of that.

> also, probably wants a -d debug option

yep.

> 
> > +"\n"
> > +"Pass in inode numbers or a special inode name:\n"
> > +"    root    Root directory.\n"));
> > +}
> > +
> > +struct single_map {
> > +	const char		*tag;
> > +	uint64_t		code;
> > +};
> > +
> > +struct single_map tags[] = {
> > +	{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
> > +	{NULL, 0},
> > +};
> > +
> > +static int
> > +bulkstat_single_f(
> > +	int			argc,
> > +	char			**argv)
> > +{
> > +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> > +	struct xfs_bulkstat	bulkstat;
> > +	unsigned long		ver = 0;
> > +	unsigned int		i;
> > +	int			c;
> > +	int			ret;
> > +
> > +	while ((c = getopt(argc, argv, "v:")) != -1) {
> > +		switch (c) {
> > +		case 'v':
> > +			errno = 0;
> > +			ver = strtoull(optarg, NULL, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			if (ver != 1 && ver != 5) {
> > +				fprintf(stderr, "version must be 1 or 5.\n");
> > +				return 1;
> > +			}
> > +			break;
> > +		default:
> > +			bulkstat_single_help();
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	ret = xfd_prepare_geometry(&xfd);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("xfd_prepare_geometry");
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	switch (ver) {
> 
> set_xfd_flags() ?

I thought I fixed that! :/

> > +	case 1:
> > +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> > +		break;
> > +	case 5:
> > +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	for (i = optind; i < argc; i++) {
> > +		struct single_map	*sm = tags;
> > +		uint64_t		ino;
> > +		unsigned int		flags = 0;
> > +
> > +		/* Try to look up our tag... */
> > +		for (sm = tags; sm->tag; sm++) {
> > +			if (!strcmp(argv[i], sm->tag)) {
> > +				ino = sm->code;
> > +				flags |= XFS_BULK_IREQ_SPECIAL;
> > +				break;
> > +			}
> > +		}
> > +
> > +		/* ...or else it's an inode number. */
> > +		if (sm->tag == NULL) {
> > +			errno = 0;
> > +			ino = strtoull(argv[i], NULL, 10);
> > +			if (errno) {
> > +				perror(argv[i]);
> > +				exitcode = 1;
> > +				return 0;
> > +			}
> > +		}
> > +
> > +		ret = xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
> > +		if (ret) {
> > +			errno = ret;
> > +			perror("xfrog_bulkstat_single");
> > +			continue;
> > +		}
> > +
> > +		if (debug)
> 
> I guess there should be a -d option for this cmd as well?

Yes (we're still on bulkstat_single iirc).

> > +			printf(
> > +_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
> > +				ino, flags, ret);
> > +
> > +		dump_bulkstat(&bulkstat);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void
> > +dump_inumbers(
> > +	struct xfs_inumbers	*inumbers)
> > +{
> > +	printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
> > +	printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
> > +	printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
> > +	printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
> > +}
> > +
> > +static void
> > +inumbers_help(void)
> > +{
> > +	printf(_(
> > +"Queries the filesystem for inode group information and prints it.\n"
> > +"\n"
> > +"   -a   Only iterate this AG.\n"
> 
> -a <agno> ....

Fixed.

> > +"   -d   Print debugging output.\n"
> > +"   -e   Stop after this inode.\n"
> > +"   -n   Ask for this many results at once.\n"
> > +"   -s   Inode to start with.\n"
> > +"   -v   Use this version of the ioctl (1 or 5).\n"));
> > +}
> > +
> > +static int
> > +inumbers_f(
> > +	int			argc,
> > +	char			**argv)
> > +{
> > +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> > +	struct xfs_inumbers_req	*ireq;
> > +	uint64_t		startino = 0;
> > +	uint64_t		endino = -1ULL;
> > +	uint32_t		batch_size = 4096;
> > +	uint32_t		agno = 0;
> > +	uint32_t		ver = 0;
> > +	bool			has_agno = false;
> > +	unsigned int		i;
> > +	int			c;
> > +	int			ret;
> > +
> > +	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
> > +		switch (c) {
> > +		case 'a':
> > +			agno = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			has_agno = true;
> > +			break;
> > +		case 'd':
> > +			debug = true;
> > +			break;
> > +		case 'e':
> > +			endino = cvt_u64(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 'n':
> > +			batch_size = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 's':
> > +			startino = cvt_u64(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			break;
> > +		case 'v':
> > +			ver = cvt_u32(optarg, 10);
> > +			if (errno) {
> > +				perror(optarg);
> > +				return 1;
> > +			}
> > +			if (ver != 1 && ver != 5) {
> > +				fprintf(stderr, "version must be 1 or 5.\n");
> > +				return 1;
> > +			}
> > +			break;
> > +		default:
> > +			bulkstat_help();
> > +			return 0;
> > +		}
> > +	}
> > +	if (optind != argc) {
> > +		bulkstat_help();
> > +		return 0;
> > +	}
> > +
> > +	ret = xfd_prepare_geometry(&xfd);
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("xfd_prepare_geometry");
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	ireq = xfrog_inumbers_alloc_req(batch_size, startino);
> > +	if (!ireq) {
> > +		perror("alloc inumbersreq");
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	if (has_agno)
> > +		xfrog_inumbers_set_ag(ireq, agno);
> > +
> > +	set_xfd_flags(&xfd, ver);
> > +
> > +	while ((ret = xfrog_inumbers(&xfd, ireq)) == 0) {
> > +		if (debug)
> > +			printf(
> > +_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
> > +				ireq->hdr.ino,
> > +				ireq->hdr.flags,
> > +				ireq->hdr.agno,
> > +				ret,
> > +				ireq->hdr.icount,
> > +				ireq->hdr.ocount);
> > +		if (ireq->hdr.ocount == 0)
> > +			break;
> > +
> > +		for (i = 0; i < ireq->hdr.ocount; i++) {
> > +			if (ireq->inumbers[i].xi_startino > endino)
> > +				break;
> > +			dump_inumbers(&ireq->inumbers[i]);
> > +		}
> > +	}
> > +	if (ret) {
> > +		errno = ret;
> > +		perror("xfrog_inumbers");
> > +		exitcode = 1;
> 
> ireq leak here, just drop return to fall through?

Fixed.

> > +		return 0;
> > +	}
> > +
> > +	free(ireq);
> > +	return 0;
> > +}
> > +
> > +static cmdinfo_t	bulkstat_cmd = {
> > +	.name = "bulkstat",
> > +	.cfunc = bulkstat_f,
> > +	.argmin = 0,
> > +	.argmax = -1,
> > +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> > +	.help = bulkstat_help,
> > +};
> > +
> > +static cmdinfo_t	bulkstat_single_cmd = {
> > +	.name = "bulkstat_single",
> > +	.cfunc = bulkstat_single_f,
> > +	.argmin = 1,
> > +	.argmax = -1,
> > +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> > +	.help = bulkstat_single_help,
> > +};
> > +
> > +static cmdinfo_t	inumbers_cmd = {
> > +	.name = "inumbers",
> > +	.cfunc = inumbers_f,
> > +	.argmin = 0,
> > +	.argmax = -1,
> > +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> > +	.help = inumbers_help,
> > +};
> > +
> > +void
> > +bulkstat_init(void)
> > +{
> > +	bulkstat_cmd.args =
> > +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
> 
> <missing the -v option>
> 
> > +	bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
> > +
> > +	bulkstat_single_cmd.args = _("inum...");
> 
> <-d if you add it>
> 
> > +	bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");
> 
> "Stat individual inodes in a filesystem (or ones that come later etc etc etc)"

I'll add that to the long format help.

> > +
> > +	inumbers_cmd.args =
> > +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
> 
> <missing the -v option>
> 
> > +	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
> 
> I'm confused, why aren't all these ^^^ just in the structure definitions?

All of these ... what?  I'm confused, sorry.

> > +	add_command(&bulkstat_cmd);
> > +	add_command(&bulkstat_single_cmd);
> > +	add_command(&inumbers_cmd);
> > +}
> > diff --git a/io/init.c b/io/init.c
> > index 7025aea5..033ed67d 100644
> > --- a/io/init.c
> > +++ b/io/init.c
> > @@ -46,6 +46,7 @@ init_commands(void)
> >  {
> >  	attr_init();
> >  	bmap_init();
> > +	bulkstat_init();
> >  	copy_range_init();
> >  	cowextsize_init();
> >  	encrypt_init();
> > diff --git a/io/io.h b/io/io.h
> > index 00dff2b7..49db902f 100644
> > --- a/io/io.h
> > +++ b/io/io.h
> > @@ -183,3 +183,4 @@ extern void		log_writes_init(void);
> >  extern void		scrub_init(void);
> >  extern void		repair_init(void);
> >  extern void		crc32cselftest_init(void);
> > +extern void		bulkstat_init(void);
> > diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
> > index 85594e5e..538b5197 100644
> > --- a/libfrog/bulkstat.c
> > +++ b/libfrog/bulkstat.c
> > @@ -435,6 +435,16 @@ xfrog_bulkstat_alloc_req(
> >  	return breq;
> >  }
> >  
> > +/* Set a bulkstat cursor to iterate only a particular AG. */
> > +void
> > +xfrog_bulkstat_set_ag(
> > +	struct xfs_bulkstat_req	*req,
> > +	uint32_t		agno)
> > +{
> > +	req->hdr.agno = agno;
> > +	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
> > +}
> > +
> >  /* Convert a inumbers data from v5 format to v1 format. */
> >  void
> >  xfrog_inumbers_v5_to_v1(
> > @@ -562,3 +572,13 @@ xfrog_inumbers_alloc_req(
> >  
> >  	return ireq;
> >  }
> > +
> > +/* Set an inumbers cursor to iterate only a particular AG. */
> > +void
> > +xfrog_inumbers_set_ag(
> > +	struct xfs_inumbers_req	*req,
> > +	uint32_t		agno)
> > +{
> > +	req->hdr.agno = agno;
> > +	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
> > +}
> > diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
> > index a085da3d..133a99b8 100644
> > --- a/libfrog/bulkstat.h
> > +++ b/libfrog/bulkstat.h
> > @@ -19,11 +19,14 @@ int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
> >  void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
> >  		const struct xfs_bstat *bs1);
> >  
> > +void xfrog_bulkstat_set_ag(struct xfs_bulkstat_req *req, uint32_t agno);
> > +
> >  struct xfs_inogrp;
> >  int xfrog_inumbers(struct xfs_fd *xfd, struct xfs_inumbers_req *req);
> >  
> >  struct xfs_inumbers_req *xfrog_inumbers_alloc_req(uint32_t nr,
> >  		uint64_t startino);
> > +void xfrog_inumbers_set_ag(struct xfs_inumbers_req *req, uint32_t agno);
> >  void xfrog_inumbers_v5_to_v1(struct xfs_inogrp *ig1,
> >  		const struct xfs_inumbers *ig);
> >  void xfrog_inumbers_v1_to_v5(struct xfs_inumbers *ig,
> > diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
> > index 6e064bdd..1e09b9e4 100644
> > --- a/man/man8/xfs_io.8
> > +++ b/man/man8/xfs_io.8
> > @@ -996,6 +996,44 @@ for the current memory mapping.
> >  
> >  .SH FILESYSTEM COMMANDS
> >  .TP
> > +.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> > +Display raw stat information about a bunch of inodes in an XFS filesystem.
> > +Options are as follows:
> > +.RS 1.0i
> > +.PD 0
> > +.TP
> > +.BI \-a " agno"
> > +Display only results from the given allocation group.
> > +If not specified, all results returned will be displayed.
> > +.TP
> > +.BI \-d
> > +Print debugging information about call results.
> > +.TP
> > +.BI \-e " endino"
> > +Stop displaying records when this inode number is reached.
> > +Defaults to stopping when the system call stops returning results.
> > +.TP
> > +.BI \-n " batchsize"
> > +Retrieve at most this many records per call.
> > +Defaults to 4,096.
> > +.TP
> > +.BI \-s " startino"
> > +Display inode allocation records starting with this inode.
> > +Defaults to the first inode in the filesystem.
> 
> -v option not documented
> 
> > +.RE
> > +.PD
> > +.TP
> > +.BI "bulkstat_single [ " inum... " | " special... " ]
> > +Display raw stat information about individual inodes in an XFS filesystem.
> > +Arguments must be inode numbers or any of the special values:
> 
> add -d ?

Ok to both.

> > +.RS 1.0i
> > +.PD 0
> > +.TP
> > +.B root
> > +Display information about the root directory inode.
> > +.RE
> > +.PD
> > +.TP
> >  .B freeze
> >  Suspend all write I/O requests to the filesystem of the current file.
> >  Only available in expert mode and requires privileges.
> > @@ -1067,6 +1105,34 @@ was specified on the command line, the maximum possible inode number in
> >  the system will be printed along with its size.
> >  .PD
> >  .TP
> > +.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> > +Prints allocation information about groups of inodes in an XFS filesystem.
> > +Callers can use this information to figure out which inodes are allocated.
> > +Options are as follows:
> > +.RS 1.0i
> > +.PD 0
> > +.TP
> > +.BI \-a " agno"
> > +Display only results from the given allocation group.
> > +If not specified, all results returned will be displayed.
> > +.TP
> > +.BI \-d
> > +Print debugging information about call results.
> > +.TP
> > +.BI \-e " endino"
> > +Stop displaying records when this inode number is reached.
> > +Defaults to stopping when the system call stops returning results.
> > +.TP
> > +.BI \-n " batchsize"
> > +Retrieve at most this many records per call.
> > +Defaults to 4,096.
> > +.TP
> > +.BI \-s " startino"
> > +Display inode allocation records starting with this inode.
> > +Defaults to the first inode in the filesystem.
> 
> -v option not documented

Fixed.

--D

> > +.RE
> > +.PD
> > +.TP
> >  .BI "scrub " type " [ " agnumber " | " "ino" " " "gen" " ]"
> >  Scrub internal XFS filesystem metadata.  The
> >  .BI type
> > 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-26 22:40   ` Eric Sandeen
@ 2019-09-26 22:46     ` Eric Sandeen
  2019-09-27  4:18     ` Darrick J. Wong
  1 sibling, 0 replies; 17+ messages in thread
From: Eric Sandeen @ 2019-09-26 22:46 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/26/19 5:40 PM, Eric Sandeen wrote:

>> +static bool debug;
> since I'm old and my brain is weakening... do you need to turn this global
> debug back off in each foo_f function unless the -d debug option was specified?
> 

Actually it doesn't seem to be used in any sub-functions so it should
probably be local to each foo_f()

-Eric

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-25 21:33 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
@ 2019-09-26 22:40   ` Eric Sandeen
  2019-09-26 22:46     ` Eric Sandeen
  2019-09-27  4:18     ` Darrick J. Wong
  0 siblings, 2 replies; 17+ messages in thread
From: Eric Sandeen @ 2019-09-26 22:40 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/25/19 4:33 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a bulkstat command to xfs_io so that we can test our new xfrog code.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  io/Makefile        |    9 -
>  io/bulkstat.c      |  522 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  io/init.c          |    1 
>  io/io.h            |    1 
>  libfrog/bulkstat.c |   20 ++
>  libfrog/bulkstat.h |    3 
>  man/man8/xfs_io.8  |   66 +++++++
>  7 files changed, 618 insertions(+), 4 deletions(-)
>  create mode 100644 io/bulkstat.c
> 
> 
> diff --git a/io/Makefile b/io/Makefile
> index 484e2b5a..1112605e 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
>  LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
>  HFILES = init.h io.h
>  CFILES = init.c \
> -	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
> -	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
> -	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
> -	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
> +	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
> +	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
> +	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
> +	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
> +	truncate.c utimes.c
>  
>  LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
>  LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
> diff --git a/io/bulkstat.c b/io/bulkstat.c
> new file mode 100644
> index 00000000..625f0abe
> --- /dev/null
> +++ b/io/bulkstat.c
> @@ -0,0 +1,522 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 Oracle.  All Rights Reserved.
> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> + */
> +#include "xfs.h"
> +#include "platform_defs.h"
> +#include "command.h"
> +#include "init.h"
> +#include "libfrog/fsgeom.h"
> +#include "libfrog/bulkstat.h"
> +#include "libfrog/paths.h"
> +#include "io.h"
> +#include "input.h"
> +
> +static bool debug;

since I'm old and my brain is weakening... do you need to turn this global
debug back off in each foo_f function unless the -d debug option was specified?

> +
> +static void
> +dump_bulkstat_time(
> +	const char		*tag,
> +	uint64_t		sec,
> +	uint32_t		nsec)
> +{
> +	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
> +}
> +
> +static void
> +dump_bulkstat(
> +	struct xfs_bulkstat	*bstat)
> +{
> +	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
> +	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
> +
> +	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
> +	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
> +
> +	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
> +	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
> +	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
> +	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
> +
> +	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
> +	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
> +	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
> +	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
> +
> +	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
> +	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
> +	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
> +	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
> +
> +	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
> +	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
> +	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
> +	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
> +	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
> +
> +	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
> +	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
> +	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
> +};
> +
> +static void
> +bulkstat_help(void)
> +{
> +	printf(_(
> +"Bulk-queries the filesystem for inode stat information and prints it.\n"
> +"\n"
> +"   -a   Only iterate this AG.\n"
> +"   -d   Print debugging output.\n"
> +"   -e   Stop after this inode.\n"
> +"   -n   Ask for this many results at once.\n"
> +"   -s   Inode to start with.\n"
> +"   -v   Use this version of the ioctl (1 or 5).\n"));

+"   -a <agno>  Only iterate this AG.\n"
+"   -d         Print debugging output.\n"
+"   -e <ino>   Stop after this inode.\n"
+"   -n <nr>    Ask for this many results at once.\n"
+"   -s <ino>   Inode to start with.\n"
+"   -v <ver>   Use this version of the ioctl (1 or 5).\n"));

> +}
> +
> +static void
> +set_xfd_flags(
> +	struct xfs_fd	*xfd,
> +	uint32_t	ver)
> +{
> +	switch (ver) {
> +	case 1:
> +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> +		break;
> +	case 5:
> +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static int
> +bulkstat_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> +	struct xfs_bulkstat_req	*breq;
> +	uint64_t		startino = 0;
> +	uint64_t		endino = -1ULL;
> +	uint32_t		batch_size = 4096;
> +	uint32_t		agno = 0;
> +	uint32_t		ver = 0;
> +	bool			has_agno = false;
> +	unsigned int		i;
> +	int			c;
> +	int			ret;
> +
> +	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
> +		switch (c) {
> +		case 'a':
> +			agno = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			has_agno = true;
> +			break;
> +		case 'd':
> +			debug = true;
> +			break;
> +		case 'e':
> +			endino = cvt_u64(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 'n':
> +			batch_size = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 's':
> +			startino = cvt_u64(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 'v':
> +			ver = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			if (ver != 1 && ver != 5) {
> +				fprintf(stderr, "version must be 1 or 5.\n");
> +				return 1;
> +			}
> +			break;
> +		default:
> +			bulkstat_help();
> +			return 0;
> +		}
> +	}
> +	if (optind != argc) {
> +		bulkstat_help();
> +		return 0;
> +	}
> +
> +	ret = xfd_prepare_geometry(&xfd);
> +	if (ret) {
> +		errno = ret;
> +		perror("xfd_prepare_geometry");
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	breq = xfrog_bulkstat_alloc_req(batch_size, startino);
> +	if (!breq) {
> +		perror("alloc bulkreq");
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	if (has_agno)
> +		xfrog_bulkstat_set_ag(breq, agno);
> +
> +	set_xfd_flags(&xfd, ver);
> +
> +	while ((ret = xfrog_bulkstat(&xfd, breq)) == 0) {
> +		if (debug)
> +			printf(
> +_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
> +				(long long)breq->hdr.ino,
> +				(unsigned int)breq->hdr.flags,
> +				(unsigned int)breq->hdr.agno,
> +				ret,
> +				(unsigned int)breq->hdr.icount,
> +				(unsigned int)breq->hdr.ocount);
> +		if (breq->hdr.ocount == 0)
> +			break;
> +
> +		for (i = 0; i < breq->hdr.ocount; i++) {
> +			if (breq->bulkstat[i].bs_ino > endino)
> +				break;
> +			dump_bulkstat(&breq->bulkstat[i]);
> +		}
> +	}
> +	if (ret) {
> +		errno = ret;
> +		perror("xfrog_bulkstat");
> +		exitcode = 1;

free(breq) (or just drop the return & fall through ...)

> +		return 0;
> +	}
> +
> +	free(breq);
> +	return 0;
> +}
> +
> +static void
> +bulkstat_single_help(void)
> +{
> +	printf(_(
> +"Queries the filesystem for a single inode's stat information and prints it.\n"
> +"\n"
> +"   -v   Use this version of the ioctl (1 or 5).\n"

+"   -v <ver>   Use this version of the ioctl (1 or 5).\n"));

(I'm realizing all our long help is preeeeettttty free form but still worth
noting that it requires an optarg)

since it can take more than one, I wonder if the man page's text
("individual inodes") is better than "a single inode's"

The other interesting thing is that it takes a start ino but gives you the
first allocated inode >= that number, right:

xfs_io> bulkstat_single 128 129 130
bs_ino = 160
...
bs_ino = 160
...
bs_ino = 160
...

and while that's how the interface works, I wonder if the help and manpage
should be (sigh) more clear about it somehow.

also, probably wants a -d debug option


> +"\n"
> +"Pass in inode numbers or a special inode name:\n"
> +"    root    Root directory.\n"));
> +}
> +
> +struct single_map {
> +	const char		*tag;
> +	uint64_t		code;
> +};
> +
> +struct single_map tags[] = {
> +	{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
> +	{NULL, 0},
> +};
> +
> +static int
> +bulkstat_single_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> +	struct xfs_bulkstat	bulkstat;
> +	unsigned long		ver = 0;
> +	unsigned int		i;
> +	int			c;
> +	int			ret;
> +
> +	while ((c = getopt(argc, argv, "v:")) != -1) {
> +		switch (c) {
> +		case 'v':
> +			errno = 0;
> +			ver = strtoull(optarg, NULL, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			if (ver != 1 && ver != 5) {
> +				fprintf(stderr, "version must be 1 or 5.\n");
> +				return 1;
> +			}
> +			break;
> +		default:
> +			bulkstat_single_help();
> +			return 0;
> +		}
> +	}
> +
> +	ret = xfd_prepare_geometry(&xfd);
> +	if (ret) {
> +		errno = ret;
> +		perror("xfd_prepare_geometry");
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	switch (ver) {

set_xfd_flags() ?

> +	case 1:
> +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> +		break;
> +	case 5:
> +		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	for (i = optind; i < argc; i++) {
> +		struct single_map	*sm = tags;
> +		uint64_t		ino;
> +		unsigned int		flags = 0;
> +
> +		/* Try to look up our tag... */
> +		for (sm = tags; sm->tag; sm++) {
> +			if (!strcmp(argv[i], sm->tag)) {
> +				ino = sm->code;
> +				flags |= XFS_BULK_IREQ_SPECIAL;
> +				break;
> +			}
> +		}
> +
> +		/* ...or else it's an inode number. */
> +		if (sm->tag == NULL) {
> +			errno = 0;
> +			ino = strtoull(argv[i], NULL, 10);
> +			if (errno) {
> +				perror(argv[i]);
> +				exitcode = 1;
> +				return 0;
> +			}
> +		}
> +
> +		ret = xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
> +		if (ret) {
> +			errno = ret;
> +			perror("xfrog_bulkstat_single");
> +			continue;
> +		}
> +
> +		if (debug)

I guess there should be a -d option for this cmd as well?

> +			printf(
> +_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
> +				ino, flags, ret);
> +
> +		dump_bulkstat(&bulkstat);
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +dump_inumbers(
> +	struct xfs_inumbers	*inumbers)
> +{
> +	printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
> +	printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
> +	printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
> +	printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
> +}
> +
> +static void
> +inumbers_help(void)
> +{
> +	printf(_(
> +"Queries the filesystem for inode group information and prints it.\n"
> +"\n"
> +"   -a   Only iterate this AG.\n"

-a <agno> ....

> +"   -d   Print debugging output.\n"
> +"   -e   Stop after this inode.\n"
> +"   -n   Ask for this many results at once.\n"
> +"   -s   Inode to start with.\n"
> +"   -v   Use this version of the ioctl (1 or 5).\n"));
> +}
> +
> +static int
> +inumbers_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> +	struct xfs_inumbers_req	*ireq;
> +	uint64_t		startino = 0;
> +	uint64_t		endino = -1ULL;
> +	uint32_t		batch_size = 4096;
> +	uint32_t		agno = 0;
> +	uint32_t		ver = 0;
> +	bool			has_agno = false;
> +	unsigned int		i;
> +	int			c;
> +	int			ret;
> +
> +	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
> +		switch (c) {
> +		case 'a':
> +			agno = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			has_agno = true;
> +			break;
> +		case 'd':
> +			debug = true;
> +			break;
> +		case 'e':
> +			endino = cvt_u64(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 'n':
> +			batch_size = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 's':
> +			startino = cvt_u64(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			break;
> +		case 'v':
> +			ver = cvt_u32(optarg, 10);
> +			if (errno) {
> +				perror(optarg);
> +				return 1;
> +			}
> +			if (ver != 1 && ver != 5) {
> +				fprintf(stderr, "version must be 1 or 5.\n");
> +				return 1;
> +			}
> +			break;
> +		default:
> +			bulkstat_help();
> +			return 0;
> +		}
> +	}
> +	if (optind != argc) {
> +		bulkstat_help();
> +		return 0;
> +	}
> +
> +	ret = xfd_prepare_geometry(&xfd);
> +	if (ret) {
> +		errno = ret;
> +		perror("xfd_prepare_geometry");
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	ireq = xfrog_inumbers_alloc_req(batch_size, startino);
> +	if (!ireq) {
> +		perror("alloc inumbersreq");
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	if (has_agno)
> +		xfrog_inumbers_set_ag(ireq, agno);
> +
> +	set_xfd_flags(&xfd, ver);
> +
> +	while ((ret = xfrog_inumbers(&xfd, ireq)) == 0) {
> +		if (debug)
> +			printf(
> +_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
> +				ireq->hdr.ino,
> +				ireq->hdr.flags,
> +				ireq->hdr.agno,
> +				ret,
> +				ireq->hdr.icount,
> +				ireq->hdr.ocount);
> +		if (ireq->hdr.ocount == 0)
> +			break;
> +
> +		for (i = 0; i < ireq->hdr.ocount; i++) {
> +			if (ireq->inumbers[i].xi_startino > endino)
> +				break;
> +			dump_inumbers(&ireq->inumbers[i]);
> +		}
> +	}
> +	if (ret) {
> +		errno = ret;
> +		perror("xfrog_inumbers");
> +		exitcode = 1;

ireq leak here, just drop return to fall through?

> +		return 0;
> +	}
> +
> +	free(ireq);
> +	return 0;
> +}
> +
> +static cmdinfo_t	bulkstat_cmd = {
> +	.name = "bulkstat",
> +	.cfunc = bulkstat_f,
> +	.argmin = 0,
> +	.argmax = -1,
> +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> +	.help = bulkstat_help,
> +};
> +
> +static cmdinfo_t	bulkstat_single_cmd = {
> +	.name = "bulkstat_single",
> +	.cfunc = bulkstat_single_f,
> +	.argmin = 1,
> +	.argmax = -1,
> +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> +	.help = bulkstat_single_help,
> +};
> +
> +static cmdinfo_t	inumbers_cmd = {
> +	.name = "inumbers",
> +	.cfunc = inumbers_f,
> +	.argmin = 0,
> +	.argmax = -1,
> +	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
> +	.help = inumbers_help,
> +};
> +
> +void
> +bulkstat_init(void)
> +{
> +	bulkstat_cmd.args =
> +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");

<missing the -v option>

> +	bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
> +
> +	bulkstat_single_cmd.args = _("inum...");

<-d if you add it>

> +	bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");

"Stat individual inodes in a filesystem (or ones that come later etc etc etc)"

> +
> +	inumbers_cmd.args =
> +		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");

<missing the -v option>

> +	inumbers_cmd.oneline = _("Query inode groups in a filesystem");

I'm confused, why aren't all these ^^^ just in the structure definitions?

> +	add_command(&bulkstat_cmd);
> +	add_command(&bulkstat_single_cmd);
> +	add_command(&inumbers_cmd);
> +}
> diff --git a/io/init.c b/io/init.c
> index 7025aea5..033ed67d 100644
> --- a/io/init.c
> +++ b/io/init.c
> @@ -46,6 +46,7 @@ init_commands(void)
>  {
>  	attr_init();
>  	bmap_init();
> +	bulkstat_init();
>  	copy_range_init();
>  	cowextsize_init();
>  	encrypt_init();
> diff --git a/io/io.h b/io/io.h
> index 00dff2b7..49db902f 100644
> --- a/io/io.h
> +++ b/io/io.h
> @@ -183,3 +183,4 @@ extern void		log_writes_init(void);
>  extern void		scrub_init(void);
>  extern void		repair_init(void);
>  extern void		crc32cselftest_init(void);
> +extern void		bulkstat_init(void);
> diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
> index 85594e5e..538b5197 100644
> --- a/libfrog/bulkstat.c
> +++ b/libfrog/bulkstat.c
> @@ -435,6 +435,16 @@ xfrog_bulkstat_alloc_req(
>  	return breq;
>  }
>  
> +/* Set a bulkstat cursor to iterate only a particular AG. */
> +void
> +xfrog_bulkstat_set_ag(
> +	struct xfs_bulkstat_req	*req,
> +	uint32_t		agno)
> +{
> +	req->hdr.agno = agno;
> +	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
> +}
> +
>  /* Convert a inumbers data from v5 format to v1 format. */
>  void
>  xfrog_inumbers_v5_to_v1(
> @@ -562,3 +572,13 @@ xfrog_inumbers_alloc_req(
>  
>  	return ireq;
>  }
> +
> +/* Set an inumbers cursor to iterate only a particular AG. */
> +void
> +xfrog_inumbers_set_ag(
> +	struct xfs_inumbers_req	*req,
> +	uint32_t		agno)
> +{
> +	req->hdr.agno = agno;
> +	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
> +}
> diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
> index a085da3d..133a99b8 100644
> --- a/libfrog/bulkstat.h
> +++ b/libfrog/bulkstat.h
> @@ -19,11 +19,14 @@ int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
>  void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
>  		const struct xfs_bstat *bs1);
>  
> +void xfrog_bulkstat_set_ag(struct xfs_bulkstat_req *req, uint32_t agno);
> +
>  struct xfs_inogrp;
>  int xfrog_inumbers(struct xfs_fd *xfd, struct xfs_inumbers_req *req);
>  
>  struct xfs_inumbers_req *xfrog_inumbers_alloc_req(uint32_t nr,
>  		uint64_t startino);
> +void xfrog_inumbers_set_ag(struct xfs_inumbers_req *req, uint32_t agno);
>  void xfrog_inumbers_v5_to_v1(struct xfs_inogrp *ig1,
>  		const struct xfs_inumbers *ig);
>  void xfrog_inumbers_v1_to_v5(struct xfs_inumbers *ig,
> diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
> index 6e064bdd..1e09b9e4 100644
> --- a/man/man8/xfs_io.8
> +++ b/man/man8/xfs_io.8
> @@ -996,6 +996,44 @@ for the current memory mapping.
>  
>  .SH FILESYSTEM COMMANDS
>  .TP
> +.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> +Display raw stat information about a bunch of inodes in an XFS filesystem.
> +Options are as follows:
> +.RS 1.0i
> +.PD 0
> +.TP
> +.BI \-a " agno"
> +Display only results from the given allocation group.
> +If not specified, all results returned will be displayed.
> +.TP
> +.BI \-d
> +Print debugging information about call results.
> +.TP
> +.BI \-e " endino"
> +Stop displaying records when this inode number is reached.
> +Defaults to stopping when the system call stops returning results.
> +.TP
> +.BI \-n " batchsize"
> +Retrieve at most this many records per call.
> +Defaults to 4,096.
> +.TP
> +.BI \-s " startino"
> +Display inode allocation records starting with this inode.
> +Defaults to the first inode in the filesystem.

-v option not documented

> +.RE
> +.PD
> +.TP
> +.BI "bulkstat_single [ " inum... " | " special... " ]
> +Display raw stat information about individual inodes in an XFS filesystem.
> +Arguments must be inode numbers or any of the special values:

add -d ?

> +.RS 1.0i
> +.PD 0
> +.TP
> +.B root
> +Display information about the root directory inode.
> +.RE
> +.PD
> +.TP
>  .B freeze
>  Suspend all write I/O requests to the filesystem of the current file.
>  Only available in expert mode and requires privileges.
> @@ -1067,6 +1105,34 @@ was specified on the command line, the maximum possible inode number in
>  the system will be printed along with its size.
>  .PD
>  .TP
> +.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
> +Prints allocation information about groups of inodes in an XFS filesystem.
> +Callers can use this information to figure out which inodes are allocated.
> +Options are as follows:
> +.RS 1.0i
> +.PD 0
> +.TP
> +.BI \-a " agno"
> +Display only results from the given allocation group.
> +If not specified, all results returned will be displayed.
> +.TP
> +.BI \-d
> +Print debugging information about call results.
> +.TP
> +.BI \-e " endino"
> +Stop displaying records when this inode number is reached.
> +Defaults to stopping when the system call stops returning results.
> +.TP
> +.BI \-n " batchsize"
> +Retrieve at most this many records per call.
> +Defaults to 4,096.
> +.TP
> +.BI \-s " startino"
> +Display inode allocation records starting with this inode.
> +Defaults to the first inode in the filesystem.

-v option not documented

> +.RE
> +.PD
> +.TP
>  .BI "scrub " type " [ " agnumber " | " "ino" " " "gen" " ]"
>  Scrub internal XFS filesystem metadata.  The
>  .BI type
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/4] xfs_io: add a bulkstat command
  2019-09-25 21:32 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
@ 2019-09-25 21:33 ` Darrick J. Wong
  2019-09-26 22:40   ` Eric Sandeen
  0 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:33 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Add a bulkstat command to xfs_io so that we can test our new xfrog code.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/Makefile        |    9 -
 io/bulkstat.c      |  522 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c          |    1 
 io/io.h            |    1 
 libfrog/bulkstat.c |   20 ++
 libfrog/bulkstat.h |    3 
 man/man8/xfs_io.8  |   66 +++++++
 7 files changed, 618 insertions(+), 4 deletions(-)
 create mode 100644 io/bulkstat.c


diff --git a/io/Makefile b/io/Makefile
index 484e2b5a..1112605e 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
 LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
 HFILES = init.h io.h
 CFILES = init.c \
-	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
-	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
-	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
-	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
+	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
+	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
+	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
+	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
+	truncate.c utimes.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
diff --git a/io/bulkstat.c b/io/bulkstat.c
new file mode 100644
index 00000000..625f0abe
--- /dev/null
+++ b/io/bulkstat.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+#include "xfs.h"
+#include "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/bulkstat.h"
+#include "libfrog/paths.h"
+#include "io.h"
+#include "input.h"
+
+static bool debug;
+
+static void
+dump_bulkstat_time(
+	const char		*tag,
+	uint64_t		sec,
+	uint32_t		nsec)
+{
+	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
+}
+
+static void
+dump_bulkstat(
+	struct xfs_bulkstat	*bstat)
+{
+	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
+	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
+
+	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
+	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
+
+	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
+	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
+	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
+	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
+
+	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
+	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
+	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
+	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
+
+	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
+	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
+	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
+	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
+
+	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
+	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
+	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
+	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
+	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
+
+	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
+	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
+	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
+};
+
+static void
+bulkstat_help(void)
+{
+	printf(_(
+"Bulk-queries the filesystem for inode stat information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static void
+set_xfd_flags(
+	struct xfs_fd	*xfd,
+	uint32_t	ver)
+{
+	switch (ver) {
+	case 1:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+}
+
+static int
+bulkstat_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat_req	*breq;
+	uint64_t		startino = 0;
+	uint64_t		endino = -1ULL;
+	uint32_t		batch_size = 4096;
+	uint32_t		agno = 0;
+	uint32_t		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			agno = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			endino = cvt_u64(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			batch_size = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			startino = cvt_u64(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			ver = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	breq = xfrog_bulkstat_alloc_req(batch_size, startino);
+	if (!breq) {
+		perror("alloc bulkreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_bulkstat_set_ag(breq, agno);
+
+	set_xfd_flags(&xfd, ver);
+
+	while ((ret = xfrog_bulkstat(&xfd, breq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
+				(long long)breq->hdr.ino,
+				(unsigned int)breq->hdr.flags,
+				(unsigned int)breq->hdr.agno,
+				ret,
+				(unsigned int)breq->hdr.icount,
+				(unsigned int)breq->hdr.ocount);
+		if (breq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < breq->hdr.ocount; i++) {
+			if (breq->bulkstat[i].bs_ino > endino)
+				break;
+			dump_bulkstat(&breq->bulkstat[i]);
+		}
+	}
+	if (ret) {
+		errno = ret;
+		perror("xfrog_bulkstat");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(breq);
+	return 0;
+}
+
+static void
+bulkstat_single_help(void)
+{
+	printf(_(
+"Queries the filesystem for a single inode's stat information and prints it.\n"
+"\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"
+"\n"
+"Pass in inode numbers or a special inode name:\n"
+"    root    Root directory.\n"));
+}
+
+struct single_map {
+	const char		*tag;
+	uint64_t		code;
+};
+
+struct single_map tags[] = {
+	{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
+	{NULL, 0},
+};
+
+static int
+bulkstat_single_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat	bulkstat;
+	unsigned long		ver = 0;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "v:")) != -1) {
+		switch (c) {
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_single_help();
+			return 0;
+		}
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	for (i = optind; i < argc; i++) {
+		struct single_map	*sm = tags;
+		uint64_t		ino;
+		unsigned int		flags = 0;
+
+		/* Try to look up our tag... */
+		for (sm = tags; sm->tag; sm++) {
+			if (!strcmp(argv[i], sm->tag)) {
+				ino = sm->code;
+				flags |= XFS_BULK_IREQ_SPECIAL;
+				break;
+			}
+		}
+
+		/* ...or else it's an inode number. */
+		if (sm->tag == NULL) {
+			errno = 0;
+			ino = strtoull(argv[i], NULL, 10);
+			if (errno) {
+				perror(argv[i]);
+				exitcode = 1;
+				return 0;
+			}
+		}
+
+		ret = xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
+		if (ret) {
+			errno = ret;
+			perror("xfrog_bulkstat_single");
+			continue;
+		}
+
+		if (debug)
+			printf(
+_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
+				ino, flags, ret);
+
+		dump_bulkstat(&bulkstat);
+	}
+
+	return 0;
+}
+
+static void
+dump_inumbers(
+	struct xfs_inumbers	*inumbers)
+{
+	printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
+	printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
+	printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
+	printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
+}
+
+static void
+inumbers_help(void)
+{
+	printf(_(
+"Queries the filesystem for inode group information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static int
+inumbers_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_inumbers_req	*ireq;
+	uint64_t		startino = 0;
+	uint64_t		endino = -1ULL;
+	uint32_t		batch_size = 4096;
+	uint32_t		agno = 0;
+	uint32_t		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			ret;
+
+	while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			agno = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			endino = cvt_u64(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			batch_size = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			startino = cvt_u64(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			ver = cvt_u32(optarg, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	ret = xfd_prepare_geometry(&xfd);
+	if (ret) {
+		errno = ret;
+		perror("xfd_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	ireq = xfrog_inumbers_alloc_req(batch_size, startino);
+	if (!ireq) {
+		perror("alloc inumbersreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_inumbers_set_ag(ireq, agno);
+
+	set_xfd_flags(&xfd, ver);
+
+	while ((ret = xfrog_inumbers(&xfd, ireq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
+				ireq->hdr.ino,
+				ireq->hdr.flags,
+				ireq->hdr.agno,
+				ret,
+				ireq->hdr.icount,
+				ireq->hdr.ocount);
+		if (ireq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < ireq->hdr.ocount; i++) {
+			if (ireq->inumbers[i].xi_startino > endino)
+				break;
+			dump_inumbers(&ireq->inumbers[i]);
+		}
+	}
+	if (ret) {
+		errno = ret;
+		perror("xfrog_inumbers");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(ireq);
+	return 0;
+}
+
+static cmdinfo_t	bulkstat_cmd = {
+	.name = "bulkstat",
+	.cfunc = bulkstat_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
+	.help = bulkstat_help,
+};
+
+static cmdinfo_t	bulkstat_single_cmd = {
+	.name = "bulkstat_single",
+	.cfunc = bulkstat_single_f,
+	.argmin = 1,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
+	.help = bulkstat_single_help,
+};
+
+static cmdinfo_t	inumbers_cmd = {
+	.name = "inumbers",
+	.cfunc = inumbers_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
+	.help = inumbers_help,
+};
+
+void
+bulkstat_init(void)
+{
+	bulkstat_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
+
+	bulkstat_single_cmd.args = _("inum...");
+	bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");
+
+	inumbers_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
+
+	add_command(&bulkstat_cmd);
+	add_command(&bulkstat_single_cmd);
+	add_command(&inumbers_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 7025aea5..033ed67d 100644
--- a/io/init.c
+++ b/io/init.c
@@ -46,6 +46,7 @@ init_commands(void)
 {
 	attr_init();
 	bmap_init();
+	bulkstat_init();
 	copy_range_init();
 	cowextsize_init();
 	encrypt_init();
diff --git a/io/io.h b/io/io.h
index 00dff2b7..49db902f 100644
--- a/io/io.h
+++ b/io/io.h
@@ -183,3 +183,4 @@ extern void		log_writes_init(void);
 extern void		scrub_init(void);
 extern void		repair_init(void);
 extern void		crc32cselftest_init(void);
+extern void		bulkstat_init(void);
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index 85594e5e..538b5197 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -435,6 +435,16 @@ xfrog_bulkstat_alloc_req(
 	return breq;
 }
 
+/* Set a bulkstat cursor to iterate only a particular AG. */
+void
+xfrog_bulkstat_set_ag(
+	struct xfs_bulkstat_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
+
 /* Convert a inumbers data from v5 format to v1 format. */
 void
 xfrog_inumbers_v5_to_v1(
@@ -562,3 +572,13 @@ xfrog_inumbers_alloc_req(
 
 	return ireq;
 }
+
+/* Set an inumbers cursor to iterate only a particular AG. */
+void
+xfrog_inumbers_set_ag(
+	struct xfs_inumbers_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index a085da3d..133a99b8 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -19,11 +19,14 @@ int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
 void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
 		const struct xfs_bstat *bs1);
 
+void xfrog_bulkstat_set_ag(struct xfs_bulkstat_req *req, uint32_t agno);
+
 struct xfs_inogrp;
 int xfrog_inumbers(struct xfs_fd *xfd, struct xfs_inumbers_req *req);
 
 struct xfs_inumbers_req *xfrog_inumbers_alloc_req(uint32_t nr,
 		uint64_t startino);
+void xfrog_inumbers_set_ag(struct xfs_inumbers_req *req, uint32_t agno);
 void xfrog_inumbers_v5_to_v1(struct xfs_inogrp *ig1,
 		const struct xfs_inumbers *ig);
 void xfrog_inumbers_v1_to_v5(struct xfs_inumbers *ig,
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 6e064bdd..1e09b9e4 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -996,6 +996,44 @@ for the current memory mapping.
 
 .SH FILESYSTEM COMMANDS
 .TP
+.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Display raw stat information about a bunch of inodes in an XFS filesystem.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+If not specified, all results returned will be displayed.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+Defaults to 4,096.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to the first inode in the filesystem.
+.RE
+.PD
+.TP
+.BI "bulkstat_single [ " inum... " | " special... " ]
+Display raw stat information about individual inodes in an XFS filesystem.
+Arguments must be inode numbers or any of the special values:
+.RS 1.0i
+.PD 0
+.TP
+.B root
+Display information about the root directory inode.
+.RE
+.PD
+.TP
 .B freeze
 Suspend all write I/O requests to the filesystem of the current file.
 Only available in expert mode and requires privileges.
@@ -1067,6 +1105,34 @@ was specified on the command line, the maximum possible inode number in
 the system will be printed along with its size.
 .PD
 .TP
+.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Prints allocation information about groups of inodes in an XFS filesystem.
+Callers can use this information to figure out which inodes are allocated.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+If not specified, all results returned will be displayed.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+Defaults to 4,096.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to the first inode in the filesystem.
+.RE
+.PD
+.TP
 .BI "scrub " type " [ " agnumber " | " "ino" " " "gen" " ]"
 Scrub internal XFS filesystem metadata.  The
 .BI type


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 1/4] xfs_io: add a bulkstat command
  2019-08-26 21:27 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
@ 2019-08-26 21:27 ` Darrick J. Wong
  0 siblings, 0 replies; 17+ messages in thread
From: Darrick J. Wong @ 2019-08-26 21:27 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Add a bulkstat command to xfs_io so that we can test our new xfrog code.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/xfrog.h    |    3 
 io/Makefile        |    9 -
 io/bulkstat.c      |  525 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c          |    1 
 io/io.h            |    1 
 libfrog/bulkstat.c |   20 ++
 man/man8/xfs_io.8  |   68 +++++++
 7 files changed, 623 insertions(+), 4 deletions(-)
 create mode 100644 io/bulkstat.c


diff --git a/include/xfrog.h b/include/xfrog.h
index 0dcee510..df7cfdf6 100644
--- a/include/xfrog.h
+++ b/include/xfrog.h
@@ -188,11 +188,14 @@ void xfrog_bulkstat_to_bstat(struct xfs_fd *xfd, struct xfs_bstat *bs1,
 void xfrog_bstat_to_bulkstat(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
 		const struct xfs_bstat *bs1);
 
+void xfrog_bulkstat_set_ag(struct xfs_bulkstat_req *req, uint32_t agno);
+
 struct xfs_inogrp;
 int xfrog_inumbers(struct xfs_fd *xfd, struct xfs_inumbers_req *req);
 
 struct xfs_inumbers_req *xfrog_inumbers_alloc_req(uint32_t nr,
 		uint64_t startino);
+void xfrog_inumbers_set_ag(struct xfs_inumbers_req *req, uint32_t agno);
 void xfrog_inumbers_to_inogrp(struct xfs_inogrp *ig1,
 		const struct xfs_inumbers *ig);
 void xfrog_inogrp_to_inumbers(struct xfs_inumbers *ig,
diff --git a/io/Makefile b/io/Makefile
index 484e2b5a..1112605e 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -9,10 +9,11 @@ LTCOMMAND = xfs_io
 LSRCFILES = xfs_bmap.sh xfs_freeze.sh xfs_mkfile.sh
 HFILES = init.h io.h
 CFILES = init.c \
-	attr.c bmap.c crc32cselftest.c cowextsize.c encrypt.c file.c freeze.c \
-	fsync.c getrusage.c imap.c inject.c label.c link.c mmap.c open.c \
-	parent.c pread.c prealloc.c pwrite.c reflink.c resblks.c scrub.c \
-	seek.c shutdown.c stat.c swapext.c sync.c truncate.c utimes.c
+	attr.c bmap.c bulkstat.c crc32cselftest.c cowextsize.c encrypt.c \
+	file.c freeze.c fsync.c getrusage.c imap.c inject.c label.c link.c \
+	mmap.c open.c parent.c pread.c prealloc.c pwrite.c reflink.c \
+	resblks.c scrub.c seek.c shutdown.c stat.c swapext.c sync.c \
+	truncate.c utimes.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG) $(LIBPTHREAD)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE) $(LIBFROG)
diff --git a/io/bulkstat.c b/io/bulkstat.c
new file mode 100644
index 00000000..7944a0fa
--- /dev/null
+++ b/io/bulkstat.c
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+#include "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "path.h"
+#include "io.h"
+#include "input.h"
+#include "xfrog.h"
+
+static bool debug;
+
+static void
+dump_bulkstat_time(
+	const char		*tag,
+	uint64_t		sec,
+	uint32_t		nsec)
+{
+	printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
+}
+
+static void
+dump_bulkstat(
+	struct xfs_bulkstat	*bstat)
+{
+	printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
+	printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
+
+	printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
+	printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
+
+	dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
+	dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
+	dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
+	dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
+
+	printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
+	printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
+	printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
+	printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
+
+	printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
+	printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
+	printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
+	printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
+
+	printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
+	printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
+	printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
+	printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
+	printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
+
+	printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
+	printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
+	printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
+};
+
+static void
+bulkstat_help(void)
+{
+	printf(_(
+"Bulk-queries the filesystem for inode stat information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static int
+bulkstat_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat_req	*breq;
+	unsigned long long	startino = 0;
+	unsigned long long	endino = -1ULL;
+	unsigned long		batch_size = 4096;
+	unsigned long		agno = 0;
+	unsigned long		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			error;
+
+	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			errno = 0;
+			agno = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			errno = 0;
+			endino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			errno = 0;
+			batch_size = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			errno = 0;
+			startino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	error = xfrog_prepare_geometry(&xfd);
+	if (error) {
+		perror("xfrog_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	breq = xfrog_bulkstat_alloc_req(batch_size, startino);
+	if (!breq) {
+		perror("alloc bulkreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_bulkstat_set_ag(breq, agno);
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	while ((error = xfrog_bulkstat(&xfd, breq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
+				(long long)breq->hdr.ino,
+				(unsigned int)breq->hdr.flags,
+				(unsigned int)breq->hdr.agno,
+				error,
+				(unsigned int)breq->hdr.icount,
+				(unsigned int)breq->hdr.ocount);
+		if (breq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < breq->hdr.ocount; i++) {
+			if (breq->bulkstat[i].bs_ino > endino)
+				break;
+			dump_bulkstat(&breq->bulkstat[i]);
+		}
+	}
+	if (error) {
+		perror("xfrog_bulkstat");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(breq);
+	return 0;
+}
+
+static void
+bulkstat_single_help(void)
+{
+	printf(_(
+"Queries the filesystem for a single inode's stat information and prints it.\n"
+"\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"
+"\n"
+"Pass in inode numbers or a special inode name:\n"
+"    root    Root directory.\n"));
+}
+
+struct single_map {
+	const char		*tag;
+	uint64_t		code;
+};
+
+struct single_map tags[] = {
+	{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
+	{NULL, 0},
+};
+
+static int
+bulkstat_single_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat	bulkstat;
+	unsigned long		ver = 0;
+	unsigned int		i;
+	int			c;
+	int			error;
+
+	while ((c = getopt(argc, argv, "v:")) != -1) {
+		switch (c) {
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_single_help();
+			return 0;
+		}
+	}
+
+	error = xfrog_prepare_geometry(&xfd);
+	if (error) {
+		perror("xfrog_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	for (i = optind; i < argc; i++) {
+		struct single_map	*sm = tags;
+		uint64_t		ino;
+		unsigned int		flags = 0;
+
+		/* Try to look up our tag... */
+		for (sm = tags; sm->tag; sm++) {
+			if (!strcmp(argv[i], sm->tag)) {
+				ino = sm->code;
+				flags |= XFS_BULK_IREQ_SPECIAL;
+				break;
+			}
+		}
+
+		/* ...or else it's an inode number. */
+		if (sm->tag == NULL) {
+			errno = 0;
+			ino = strtoull(argv[i], NULL, 10);
+			if (errno) {
+				perror(argv[i]);
+				exitcode = 1;
+				return 0;
+			}
+		}
+
+		error = xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
+		if (error) {
+			perror("xfrog_bulkstat_single");
+			continue;
+		}
+
+		if (debug)
+			printf(
+_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
+				ino, flags, error);
+
+		dump_bulkstat(&bulkstat);
+	}
+
+	return 0;
+}
+
+static void
+dump_inumbers(
+	struct xfs_inumbers	*inumbers)
+{
+	printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
+	printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
+	printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
+	printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
+}
+
+static void
+inumbers_help(void)
+{
+	printf(_(
+"Queries the filesystem for inode group information and prints it.\n"
+"\n"
+"   -a   Only iterate this AG.\n"
+"   -d   Print debugging output.\n"
+"   -e   Stop after this inode.\n"
+"   -n   Ask for this many results at once.\n"
+"   -s   Inode to start with.\n"
+"   -v   Use this version of the ioctl (1 or 5).\n"));
+}
+
+static int
+inumbers_f(
+	int			argc,
+	char			**argv)
+{
+	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
+	struct xfs_inumbers_req	*ireq;
+	unsigned long long	startino = 0;
+	unsigned long long	endino = -1ULL;
+	unsigned long		batch_size = 4096;
+	unsigned long		agno = 0;
+	unsigned long		ver = 0;
+	bool			has_agno = false;
+	unsigned int		i;
+	int			c;
+	int			error;
+
+	while ((c = getopt(argc, argv, "a:cde:n:qs:v:")) != -1) {
+		switch (c) {
+		case 'a':
+			errno = 0;
+			agno = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			has_agno = true;
+			break;
+		case 'd':
+			debug = true;
+			break;
+		case 'e':
+			errno = 0;
+			endino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'n':
+			errno = 0;
+			batch_size = strtoul(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 's':
+			errno = 0;
+			startino = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			break;
+		case 'v':
+			errno = 0;
+			ver = strtoull(optarg, NULL, 10);
+			if (errno) {
+				perror(optarg);
+				return 1;
+			}
+			if (ver != 1 && ver != 5) {
+				fprintf(stderr, "version must be 1 or 5.\n");
+				return 1;
+			}
+			break;
+		default:
+			bulkstat_help();
+			return 0;
+		}
+	}
+	if (optind != argc) {
+		bulkstat_help();
+		return 0;
+	}
+
+	error = xfrog_prepare_geometry(&xfd);
+	if (error) {
+		perror("xfrog_prepare_geometry");
+		exitcode = 1;
+		return 0;
+	}
+
+	ireq = xfrog_inumbers_alloc_req(batch_size, startino);
+	if (!ireq) {
+		perror("alloc inumbersreq");
+		exitcode = 1;
+		return 0;
+	}
+
+	if (has_agno)
+		xfrog_inumbers_set_ag(ireq, agno);
+
+	switch (ver) {
+	case 1:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	case 5:
+		xfd.flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
+		break;
+	default:
+		break;
+	}
+
+	while ((error = xfrog_inumbers(&xfd, ireq)) == 0) {
+		if (debug)
+			printf(
+_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
+				ireq->hdr.ino,
+				ireq->hdr.flags,
+				ireq->hdr.agno,
+				error,
+				ireq->hdr.icount,
+				ireq->hdr.ocount);
+		if (ireq->hdr.ocount == 0)
+			break;
+
+		for (i = 0; i < ireq->hdr.ocount; i++) {
+			if (ireq->inumbers[i].xi_startino > endino)
+				break;
+			dump_inumbers(&ireq->inumbers[i]);
+		}
+	}
+	if (error) {
+		perror("xfrog_inumbers");
+		exitcode = 1;
+		return 0;
+	}
+
+	free(ireq);
+	return 0;
+}
+
+static cmdinfo_t	bulkstat_cmd = {
+	.name = "bulkstat",
+	.cfunc = bulkstat_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = bulkstat_help,
+};
+
+static cmdinfo_t	bulkstat_single_cmd = {
+	.name = "bulkstat_single",
+	.cfunc = bulkstat_single_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = bulkstat_single_help,
+};
+
+static cmdinfo_t	inumbers_cmd = {
+	.name = "inumbers",
+	.cfunc = inumbers_f,
+	.argmin = 0,
+	.argmax = -1,
+	.flags = CMD_NOMAP_OK,
+	.help = inumbers_help,
+};
+
+void
+bulkstat_init(void)
+{
+	bulkstat_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
+
+	bulkstat_single_cmd.args = _("inum...");
+	bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");
+
+	inumbers_cmd.args =
+		_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino]");
+	inumbers_cmd.oneline = _("Query inode groups in a filesystem");
+
+	add_command(&bulkstat_cmd);
+	add_command(&bulkstat_single_cmd);
+	add_command(&inumbers_cmd);
+}
diff --git a/io/init.c b/io/init.c
index 7025aea5..033ed67d 100644
--- a/io/init.c
+++ b/io/init.c
@@ -46,6 +46,7 @@ init_commands(void)
 {
 	attr_init();
 	bmap_init();
+	bulkstat_init();
 	copy_range_init();
 	cowextsize_init();
 	encrypt_init();
diff --git a/io/io.h b/io/io.h
index 0848ab98..5b21b0ca 100644
--- a/io/io.h
+++ b/io/io.h
@@ -183,3 +183,4 @@ extern void		log_writes_init(void);
 extern void		scrub_init(void);
 extern void		repair_init(void);
 extern void		crc32cselftest_init(void);
+extern void		bulkstat_init(void);
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index 26db9af2..00100491 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -384,6 +384,16 @@ xfrog_bulkstat_alloc_req(
 	return breq;
 }
 
+/* Set a bulkstat cursor to iterate only a particular AG. */
+void
+xfrog_bulkstat_set_ag(
+	struct xfs_bulkstat_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
+
 /* Convert an inumbers (v5) struct to a inogrp (v1) struct. */
 void
 xfrog_inumbers_to_inogrp(
@@ -501,3 +511,13 @@ xfrog_inumbers_alloc_req(
 
 	return ireq;
 }
+
+/* Set an inumbers cursor to iterate only a particular AG. */
+void
+xfrog_inumbers_set_ag(
+	struct xfs_inumbers_req	*req,
+	uint32_t		agno)
+{
+	req->hdr.agno = agno;
+	req->hdr.flags |= XFS_BULK_IREQ_AGNO;
+}
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 6e064bdd..8fd3ffbe 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -996,6 +996,45 @@ for the current memory mapping.
 
 .SH FILESYSTEM COMMANDS
 .TP
+.BI "bulkstat [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Display raw stat information about a bunch of inodes in an XFS filesystem.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+Defaults to zero.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to zero.
+If this value is zero, then display starts with the allocation group
+given above.
+.RE
+.PD
+.TP
+.BI "bulkstat_single [ " inum... " | " special... " ]
+Display raw stat information about individual inodes in an XFS filesystem.
+Arguments must be inode numbers or any of the special values:
+.RS 1.0i
+.PD 0
+.TP
+.B root
+Display information about the root directory inode.
+.RE
+.PD
+.TP
 .B freeze
 Suspend all write I/O requests to the filesystem of the current file.
 Only available in expert mode and requires privileges.
@@ -1067,6 +1106,35 @@ was specified on the command line, the maximum possible inode number in
 the system will be printed along with its size.
 .PD
 .TP
+.BI "inumbers [ \-a " agno " ] [ \-d ] [ \-e " endino " ] [ \-n " batchsize " ] [ \-s " startino " ]
+Prints allocation information about groups of inodes in an XFS filesystem.
+Callers can use this information to figure out which inodes are allocated.
+Options are as follows:
+.RS 1.0i
+.PD 0
+.TP
+.BI \-a " agno"
+Display only results from the given allocation group.
+Defaults to zero.
+.TP
+.BI \-d
+Print debugging information about call results.
+.TP
+.BI \-e " endino"
+Stop displaying records when this inode number is reached.
+Defaults to stopping when the system call stops returning results.
+.TP
+.BI \-n " batchsize"
+Retrieve at most this many records per call.
+.TP
+.BI \-s " startino"
+Display inode allocation records starting with this inode.
+Defaults to zero.
+If this value is zero, then display starts with the allocation group
+given above.
+.RE
+.PD
+.TP
 .BI "scrub " type " [ " agnumber " | " "ino" " " "gen" " ]"
 Scrub internal XFS filesystem metadata.  The
 .BI type


^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2019-10-07 19:13 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-06  3:35 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
2019-09-06  3:35 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
2019-09-12 23:51   ` Dave Chinner
2019-09-06  3:35 ` [PATCH 2/4] xfs_spaceman: remove open-coded per-ag bulkstat Darrick J. Wong
2019-09-12 23:52   ` Dave Chinner
2019-09-06  3:36 ` [PATCH 3/4] xfs_scrub: convert to per-ag inode bulkstat operations Darrick J. Wong
2019-09-12 23:55   ` Dave Chinner
2019-09-06  3:36 ` [PATCH 4/4] xfs_scrub: batch inumbers calls during fscounters calculation Darrick J. Wong
2019-09-12 23:56   ` Dave Chinner
  -- strict thread matches above, loose matches on Subject: below --
2019-09-25 21:32 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
2019-09-25 21:33 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong
2019-09-26 22:40   ` Eric Sandeen
2019-09-26 22:46     ` Eric Sandeen
2019-09-27  4:18     ` Darrick J. Wong
2019-09-30 20:02       ` Eric Sandeen
2019-09-30 20:15         ` Darrick J. Wong
2019-10-07 19:13           ` Eric Sandeen
2019-08-26 21:27 [PATCH 0/4] xfsprogs: bulkstat v5 Darrick J. Wong
2019-08-26 21:27 ` [PATCH 1/4] xfs_io: add a bulkstat command Darrick J. Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).