All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] xfsprogs: port utilities to bulkstat v5
@ 2019-09-25 21:32 Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
                   ` (3 more replies)
  0 siblings, 4 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:32 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

Hi all,

Port the libfrog bulkstat/inumbers wrapper functions to use the new v5
interfaces and to enable falling back to the v1 interfaces if need be.
This series ports only the more lightweight users of these interfaces.

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-porting

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

* [PATCH 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-25 21:32 [PATCH 0/4] xfsprogs: port utilities to bulkstat v5 Darrick J. Wong
@ 2019-09-25 21:32 ` Darrick J. Wong
  2019-09-26 18:18   ` Eric Sandeen
                     ` (2 more replies)
  2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
                   ` (2 subsequent siblings)
  3 siblings, 3 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:32 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_bulkstat.2   |  330 +++++++++++++++++++++++++++++++++++++++
 man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
 2 files changed, 336 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_bulkstat.2


diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
new file mode 100644
index 00000000..f687cfe8
--- /dev/null
+++ b/man/man2/ioctl_xfs_bulkstat.2
@@ -0,0 +1,330 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
+.SH DESCRIPTION
+Query stat information for a group of XFS inodes.
+This ioctl uses
+.B struct xfs_bulkstat_req
+to set up a bulk transfer with the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_bulkstat     bulkstat[];
+};
+
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr.ino
+should be set to the number of the first inode for which the caller wants
+information, or zero to start with the first inode in the filesystem.
+Note that this is a different semantic than the
+.B lastip
+in the old
+.B FSBULKSTAT
+ioctl.
+After the call, this value will be set to the number of the next inode for
+which information could supplied.
+This sets up the next call for an iteration loop.
+.PP
+If the
+.B XFS_BULK_REQ_SPECIAL
+flag is set, this field is interpreted as follows:
+.RS 0.4i
+.TP
+.B XFS_BULK_IREQ_SPECIAL_ROOT
+Return stat information for the root directory inode.
+.RE
+.PP
+.PP
+.I hdr.flags
+is a bit set of operational flags:
+.RS 0.4i
+.TP
+.B XFS_BULK_REQ_AGNO
+If this is set, the call will only return results for the allocation group (AG)
+set in
+.BR hdr.agno .
+If
+.B hdr.ino
+is set to zero, results will be returned starting with the first inode in the
+AG.
+This flag may not be set at the same time as the
+.B XFS_BULK_REQ_SPECIAL
+flag.
+.TP
+.B XFS_BULK_REQ_SPECIAL
+If this is set, results will be returned for only the special inode
+specified in the
+.B hdr.ino
+field.
+This flag may not be set at the same time as the
+.B XFS_BULK_REQ_AGNO
+flag.
+.RE
+.PP
+.I hdr.icount
+is the number of inodes to examine.
+.PP
+.I hdr.ocount
+will be set to the number of records returned.
+.PP
+.I hdr.agno
+is the number of the allocation group (AG) for which we want results.
+If the
+.B XFS_BULK_REQ_AGNO
+flag is not set, this field is ignored.
+.PP
+.I hdr.reserved
+must be set to zero.
+
+.PP
+.I bulkstat
+is an array of
+.B struct xfs_bulkstat
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat {
+	uint64_t                bs_ino;
+	uint64_t                bs_size;
+
+	uint64_t                bs_blocks;
+	uint64_t                bs_xflags;
+
+	uint64_t                bs_atime;
+	uint64_t                bs_mtime;
+
+	uint64_t                bs_ctime;
+	uint64_t                bs_btime;
+
+	uint32_t                bs_gen;
+	uint32_t                bs_uid;
+	uint32_t                bs_gid;
+	uint32_t                bs_projectid;
+
+	uint32_t                bs_atime_nsec;
+	uint32_t                bs_mtime_nsec;
+	uint32_t                bs_ctime_nsec;
+	uint32_t                bs_btime_nsec;
+
+	uint32_t                bs_blksize;
+	uint32_t                bs_rdev;
+	uint32_t                bs_cowextsize_blks;
+	uint32_t                bs_extsize_blks;
+
+	uint32_t                bs_nlink;
+	uint32_t                bs_extents;
+	uint32_t                bs_aextents;
+	uint16_t                bs_version;
+	uint16_t                bs_forkoff;
+
+	uint16_t                bs_sick;
+	uint16_t                bs_checked;
+	uint16_t                bs_mode;
+	uint16_t                bs_pad2;
+
+	uint64_t                bs_pad[7];
+};
+.fi
+.in
+.PP
+.I bs_ino
+is the inode number of this record.
+.PP
+.I bs_size
+is the size of the file, in bytes.
+.PP
+.I bs_blocks
+is the number of filesystem blocks allocated to this file, including metadata.
+.PP
+.I bs_xflags
+tell us what extended flags are set this inode.
+These flags are the same values as those defined in the
+.B XFS INODE FLAGS
+section of the
+.BR ioctl_xfs_fsgetxattr (2)
+manpage.
+.PP
+.I bs_atime
+is the last time this file was accessed, in seconds.
+.PP
+.I bs_mtime
+is the last time the contents of this file were modified, in seconds.
+.PP
+.I bs_ctime
+is the last time this inode record was modified, in seconds.
+.PP
+.I bs_btime
+is the time this inode record was created, in seconds.
+.PP
+.I bs_gen
+is the generation number of the inode record.
+.PP
+.I bs_uid
+is the user id.
+.PP
+.I bs_gid
+is the group id.
+.PP
+.I bs_projectid
+is the the project id.
+.PP
+.I bs_atime_nsec
+is the nanoseconds component of the last time this file was accessed.
+.PP
+.I bs_mtime_nsec
+is the nanoseconds component of the last time the contents of this file were
+modified.
+.PP
+.I bs_ctime_nsec
+is the nanoseconds component of the last time this inode record was modified.
+.PP
+.I bs_btime_nsec
+is the nanoseconds component of the time this inode record was created.
+.PP
+.I bs_blksize
+is the size of a data block for this file, in units of bytes.
+.PP
+.I bs_rdev
+is the encoded device id if this is a special file.
+.PP
+.I bs_cowextsize_blks
+is the Copy on Write extent size hint for this file, in units of data blocks.
+.PP
+.I bs_extsize_blks
+is the extent size hint for this file, in units of data blocks.
+.PP
+.I bs_nlink
+is the number of hard links to this inode.
+.PP
+.I bs_extents
+is the number of storage mappings associated with this file's data.
+.PP
+.I bs_aextents
+is the number of storage mappings associated with this file's extended
+attributes.
+.PP
+.I bs_version
+is the version of this data structure.
+Currently, only 1 or 5 are supported.
+.PP
+.I bs_forkoff
+is the offset of the attribute fork in the inode record, in bytes.
+.PP
+The fields
+.IR bs_sick " and " bs_checked
+indicate the relative health of various allocation group metadata.
+Please see the section
+.B XFS INODE METADATA HEALTH REPORTING
+for more information.
+.PP
+.I bs_mode
+is the file type and mode.
+.PP
+.I bs_pad[7]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH XFS INODE METADATA HEALTH REPORTING
+.PP
+The online filesystem checking utility scans inode metadata and records what it
+finds in the kernel incore state.
+The following scheme is used for userspace to read the incore health status of
+an inode:
+.IP \[bu] 2
+If a given sick flag is set in
+.IR bs_sick ,
+then that piece of metadata has been observed to be damaged.
+The same bit should be set in
+.IR bs_checked .
+.IP \[bu]
+If a given sick flag is set in
+.I bs_checked
+but is not set in
+.IR bs_sick ,
+then that piece of metadata has been checked and is not faulty.
+.IP \[bu]
+If a given sick flag is not set in
+.IR bs_checked ,
+then no conclusion can be made.
+.PP
+The following flags apply to these fields:
+.RS 0.4i
+.TP
+.B XFS_BS_SICK_INODE
+The inode's record itself.
+.TP
+.B XFS_BS_SICK_BMBTD
+File data extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTA
+Extended attribute extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTC
+Copy on Write staging extent mappings.
+.TP
+.B XFS_BS_SICK_DIR
+Directory information.
+.TP
+.B XFS_BS_SICK_XATTR
+Extended attribute data.
+.TP
+.B XFS_BS_SICK_SYMLINK
+Symbolic link target.
+.TP
+.B XFS_BS_SICK_PARENT
+Parent pointers.
+.RE
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_fsgetxattr (2)
diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
index 8f880c5a..3f059942 100644
--- a/man/man2/ioctl_xfs_fsbulkstat.2
+++ b/man/man2/ioctl_xfs_fsbulkstat.2
@@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
 .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
 .SH DESCRIPTION
 Query stat information for a group of XFS inodes.
+.PP
+NOTE: This ioctl has been superseded.
+Please see the
+.BR ioctl_xfs_bulkstat (2)
+manpage for information about its replacement.
+.PP
 These ioctls use
 .B struct xfs_fsop_bulkreq
 to set up a bulk transfer with the kernel:


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

* [PATCH 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-25 21:32 [PATCH 0/4] xfsprogs: port utilities to bulkstat v5 Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
@ 2019-09-25 21:32 ` Darrick J. Wong
  2019-09-26 18:36   ` Eric Sandeen
                     ` (2 more replies)
  2019-09-25 21:32 ` [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS Darrick J. Wong
  3 siblings, 3 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:32 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Add a manpage describing the new v5 XFS_IOC_INUMBERS ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_inumbers.2 |  118 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_inumbers.2


diff --git a/man/man2/ioctl_xfs_inumbers.2 b/man/man2/ioctl_xfs_inumbers.2
new file mode 100644
index 00000000..b1e854d3
--- /dev/null
+++ b/man/man2/ioctl_xfs_inumbers.2
@@ -0,0 +1,118 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-INUMBERS 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_inumbers \- query allocation information for groups of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_INUMBERS, struct xfs_inumbers_req *" arg );
+.SH DESCRIPTION
+Query inode allocation information for groups of XFS inodes.
+This ioctl uses
+.B struct xfs_inumbers_req
+to set up a bulk transfer with the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_inumbers_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_inumbers     inumbers[];
+};
+
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr
+describes the information to query.
+The layout and behavior are documented in the
+.BR ioctl_xfs_bulkstat (2)
+manpage and will not be discussed further here.
+
+.PP
+.I inumbers
+is an array of
+.B struct xfs_inumbers
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_inumbers {
+	uint64_t                xi_startino;
+	uint64_t                xi_allocmask;
+	uint8_t                 xi_alloccount;
+	uint8_t                 xi_version;
+	uint8_t                 xi_padding[6];
+};
+.fi
+.in
+.PP
+This structure describes inode usage information for a group of 64 consecutive
+inode numbers.
+.PP
+.I xi_startino
+is the first inode number of this group.
+.PP
+.I xi_allocmask
+is a bitmask telling which inodes in this group are allocated.
+To clarify, bit
+.B N
+is set if inode
+.BR xi_startino + N
+is allocated.
+.PP
+.I xi_alloccount
+is the number of inodes in this group that are allocated.
+This should be equal to popcnt(xi_allocmask).
+.PP
+.I xi_version
+is the version of this data structure.
+Currently, only 1 or 5 are supported.
+.PP
+.I xi_padding[6]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_bulkstat (2).


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

* [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics
  2019-09-25 21:32 [PATCH 0/4] xfsprogs: port utilities to bulkstat v5 Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
@ 2019-09-25 21:32 ` Darrick J. Wong
  2019-09-26 21:01   ` Eric Sandeen
  2019-09-27 20:14   ` [PATCH v2 " Darrick J. Wong
  2019-09-25 21:32 ` [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS Darrick J. Wong
  3 siblings, 2 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:32 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Convert xfrog_bulkstat() and xfrog_bulkstat_single() to take arguments
using v5 bulkstat semantics and return bulkstat information in v5
structures.  If the v5 ioctl is not available, the xfrog wrapper should
use the v1 ioctl to emulate v5 behaviors.  Add flags to the xfs_fd
structure to constrain emulation for debugging purposes.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fsr/xfs_fsr.c      |   64 ++++++--
 io/open.c          |   27 ++-
 io/swapext.c       |    9 +
 libfrog/bulkstat.c |  431 +++++++++++++++++++++++++++++++++++++++++++++++++---
 libfrog/bulkstat.h |   14 +-
 libfrog/fsgeom.h   |    9 +
 quota/quot.c       |   29 ++-
 scrub/inodes.c     |   39 +++--
 scrub/inodes.h     |    2 
 scrub/phase3.c     |    6 -
 scrub/phase5.c     |    8 -
 scrub/phase6.c     |    2 
 scrub/unicrash.c   |    6 -
 scrub/unicrash.h   |    4 
 spaceman/health.c  |   33 ++--
 15 files changed, 572 insertions(+), 111 deletions(-)


diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
index a53eb924..af5d6169 100644
--- a/fsr/xfs_fsr.c
+++ b/fsr/xfs_fsr.c
@@ -466,6 +466,17 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
 				ptr = strchr(ptr, ' ');
 				if (ptr) {
 					startino = strtoull(++ptr, NULL, 10);
+					/*
+					 * NOTE: The inode number read in from
+					 * the leftoff file is the last inode
+					 * to have been fsr'd.  Since the v5
+					 * xfrog_bulkstat function wants to be
+					 * passed the first inode that we want
+					 * to examine, increment the value that
+					 * we read in.  The debug message below
+					 * prints the lastoff value.
+					 */
+					startino++;
 				}
 			}
 			if (startpass < 0)
@@ -484,7 +495,7 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
 
 	if (vflag) {
 		fsrprintf(_("START: pass=%d ino=%llu %s %s\n"),
-			  fs->npass, (unsigned long long)startino,
+			  fs->npass, (unsigned long long)startino - 1,
 			  fs->dev, fs->mnt);
 	}
 
@@ -576,12 +587,10 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 	int	fd;
 	int	count = 0;
 	int	ret;
-	uint32_t buflenout;
-	struct xfs_bstat buf[GRABSZ];
 	char	fname[64];
 	char	*tname;
 	jdm_fshandle_t	*fshandlep;
-	xfs_ino_t	lastino = startino;
+	struct xfs_bulkstat_req	*breq;
 
 	fsrprintf(_("%s start inode=%llu\n"), mntdir,
 		(unsigned long long)startino);
@@ -604,10 +613,21 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 
 	tmp_init(mntdir);
 
-	while ((ret = xfrog_bulkstat(&fsxfd, &lastino, GRABSZ, &buf[0],
-				&buflenout)) == 0) {
-		struct xfs_bstat *p;
-		struct xfs_bstat *endp;
+	breq = xfrog_bulkstat_alloc_req(GRABSZ, startino);
+	if (!breq) {
+		fsrprintf(_("Skipping %s: not enough memory\n"),
+			  mntdir);
+		xfd_close(&fsxfd);
+		free(fshandlep);
+		return -1;
+	}
+
+	while ((ret = xfrog_bulkstat(&fsxfd, breq) == 0)) {
+		struct xfs_bstat	bs1;
+		struct xfs_bulkstat	*buf = breq->bulkstat;
+		struct xfs_bulkstat	*p;
+		struct xfs_bulkstat	*endp;
+		uint32_t		buflenout = breq->hdr.ocount;
 
 		if (buflenout == 0)
 			goto out0;
@@ -615,7 +635,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 		/* Each loop through, defrag targetrange percent of the files */
 		count = (buflenout * targetrange) / 100;
 
-		qsort((char *)buf, buflenout, sizeof(struct xfs_bstat), cmp);
+		qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
 
 		for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
 			/* Do some obvious checks now */
@@ -623,7 +643,14 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			     (p->bs_extents < 2))
 				continue;
 
-			fd = jdm_open(fshandlep, p, O_RDWR|O_DIRECT);
+			ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
+			if (ret) {
+				fsrprintf(_("bstat conversion error: %s\n"),
+						strerror(ret));
+				continue;
+			}
+
+			fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
 			if (fd < 0) {
 				/* This probably means the file was
 				 * removed while in progress of handling
@@ -641,7 +668,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			/* Get a tmp file name */
 			tname = tmp_next(mntdir);
 
-			ret = fsrfile_common(fname, tname, mntdir, fd, p);
+			ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
 
 			leftoffino = p->bs_ino;
 
@@ -653,6 +680,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			}
 		}
 		if (endtime && endtime < time(NULL)) {
+			free(breq);
 			tmp_close(mntdir);
 			xfd_close(&fsxfd);
 			fsrall_cleanup(1);
@@ -662,6 +690,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 	if (ret)
 		fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
 out0:
+	free(breq);
 	tmp_close(mntdir);
 	xfd_close(&fsxfd);
 	free(fshandlep);
@@ -701,6 +730,7 @@ fsrfile(
 	xfs_ino_t		ino)
 {
 	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
+	struct xfs_bulkstat	bulkstat;
 	struct xfs_bstat	statbuf;
 	jdm_fshandle_t		*fshandlep;
 	int			fd = -1;
@@ -725,12 +755,18 @@ fsrfile(
 		goto out;
 	}
 
-	error = xfrog_bulkstat_single(&fsxfd, ino, &statbuf);
+	error = xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
 	if (error) {
 		fsrprintf(_("unable to get bstat on %s: %s\n"),
 			fname, strerror(error));
 		goto out;
 	}
+	error = xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
+	if (error) {
+		fsrprintf(_("bstat conversion error on %s: %s\n"),
+			fname, strerror(error));
+		goto out;
+	}
 
 	fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
 	if (fd < 0) {
@@ -951,7 +987,7 @@ fsr_setup_attr_fork(
 
 	i = 0;
 	do {
-		struct xfs_bstat tbstat;
+		struct xfs_bulkstat	tbstat;
 		char		name[64];
 		int		ret;
 
@@ -960,7 +996,7 @@ fsr_setup_attr_fork(
 		 * this to compare against the target and determine what we
 		 * need to do.
 		 */
-		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, &tbstat);
+		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0, &tbstat);
 		if (ret) {
 			fsrprintf(_("unable to get bstat on temp file: %s\n"),
 						strerror(ret));
diff --git a/io/open.c b/io/open.c
index 99ca0dd3..e0e7fb3e 100644
--- a/io/open.c
+++ b/io/open.c
@@ -723,8 +723,7 @@ inode_f(
 	int			argc,
 	char			**argv)
 {
-	struct xfs_bstat	bstat;
-	uint32_t		count = 0;
+	struct xfs_bulkstat	bulkstat;
 	uint64_t		result_ino = 0;
 	uint64_t		userino = NULLFSINO;
 	char			*p;
@@ -775,26 +774,40 @@ inode_f(
 		}
 	} else if (ret_next) {
 		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
+		struct xfs_bulkstat_req	*breq;
+
+		/*
+		 * The -n option means that the caller wants to know the number
+		 * of the next allocated inode, so we need to increment here.
+		 */
+		breq = xfrog_bulkstat_alloc_req(1, userino + 1);
+		if (!breq) {
+			perror("alloc bulkstat");
+			exitcode = 1;
+			return 0;
+		}
 
 		/* get next inode */
-		ret = xfrog_bulkstat(&xfd, &userino, 1, &bstat, &count);
+		ret = xfrog_bulkstat(&xfd, breq);
 		if (ret) {
 			errno = ret;
 			perror("bulkstat");
+			free(breq);
 			exitcode = 1;
 			return 0;
 		}
 
 		/* The next inode in use, or 0 if none */
-		if (count)
-			result_ino = bstat.bs_ino;
+		if (breq->hdr.ocount)
+			result_ino = breq->bulkstat[0].bs_ino;
 		else
 			result_ino = 0;
+		free(breq);
 	} else {
 		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
 
 		/* get this inode */
-		ret = xfrog_bulkstat_single(&xfd, userino, &bstat);
+		ret = xfrog_bulkstat_single(&xfd, userino, 0, &bulkstat);
 		if (ret == EINVAL) {
 			/* Not in use */
 			result_ino = 0;
@@ -804,7 +817,7 @@ inode_f(
 			exitcode = 1;
 			return 0;
 		} else {
-			result_ino = bstat.bs_ino;
+			result_ino = bulkstat.bs_ino;
 		}
 	}
 
diff --git a/io/swapext.c b/io/swapext.c
index 2b4918f8..1139cf21 100644
--- a/io/swapext.c
+++ b/io/swapext.c
@@ -28,6 +28,7 @@ swapext_f(
 	char			**argv)
 {
 	struct xfs_fd		fxfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat	bulkstat;
 	int			fd;
 	int			error;
 	struct xfs_swapext	sx;
@@ -48,12 +49,18 @@ swapext_f(
 		goto out;
 	}
 
-	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, &sx.sx_stat);
+	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, 0, &bulkstat);
 	if (error) {
 		errno = error;
 		perror("bulkstat");
 		goto out;
 	}
+	error = xfrog_bulkstat_v5_to_v1(&fxfd, &sx.sx_stat, &bulkstat);
+	if (error) {
+		errno = error;
+		perror("bulkstat conversion");
+		goto out;
+	}
 	sx.sx_version = XFS_SX_VERSION;
 	sx.sx_fdtarget = file->fd;
 	sx.sx_fdtmp = fd;
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index fa10f298..300963f1 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -3,55 +3,438 @@
  * Copyright (C) 2019 Oracle.  All Rights Reserved.
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
  */
+#include <string.h>
+#include <strings.h>
 #include "xfs.h"
 #include "fsgeom.h"
 #include "bulkstat.h"
 
+/*
+ * Wrapper functions for BULKSTAT and INUMBERS
+ * ===========================================
+ *
+ * The functions in this file are thin wrappers around the most recent version
+ * of the BULKSTAT and INUMBERS ioctls.  BULKSTAT is used to query XFS-specific
+ * stat information about a group of inodes.  INUMBERS is used to query
+ * allocation information about batches of XFS inodes.
+ *
+ * At the moment, the public xfrog_* functions provide all functionality of the
+ * V5 interface.  If the V5 interface is not available on the running kernel,
+ * the functions will emulate them as best they can with previous versions of
+ * the interface (currently V1).  If emulation is not possible, EINVAL will be
+ * returned.
+ *
+ * The XFROG_FLAG_BULKSTAT_FORCE_V[15] flags can be used to force use of a
+ * particular version of the kernel interface for testing.
+ */
+
+/*
+ * Grab the fs geometry information that is needed to needed to emulate v5 with
+ * v1 interfaces.
+ */
+static inline int
+xfrog_bulkstat_prep_v1_emulation(
+	struct xfs_fd		*xfd)
+{
+	if (xfd->fsgeom.blocksize > 0)
+		return 0;
+
+	return xfd_prepare_geometry(xfd);
+}
+
+/* Bulkstat a single inode using v5 ioctl. */
+static int
+xfrog_bulkstat_single5(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	struct xfs_bulkstat_req		*req;
+	int				ret;
+
+	if (flags & ~(XFS_BULK_IREQ_SPECIAL))
+		return EINVAL;
+
+	req = xfrog_bulkstat_alloc_req(1, ino);
+	if (!req)
+		return ENOMEM;
+
+	req->hdr.flags = flags;
+	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
+	if (ret) {
+		ret = errno;
+		goto free;
+	}
+
+	if (req->hdr.ocount == 0) {
+		ret = ENOENT;
+		goto free;
+	}
+
+	memcpy(bulkstat, req->bulkstat, sizeof(struct xfs_bulkstat));
+free:
+	free(req);
+	return ret;
+}
+
+/* Bulkstat a single inode using v1 ioctl. */
+static int
+xfrog_bulkstat_single1(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	struct xfs_bstat		bstat;
+	struct xfs_fsop_bulkreq		bulkreq = { 0 };
+	int				error;
+
+	if (flags)
+		return EINVAL;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	bulkreq.lastip = (__u64 *)&ino;
+	bulkreq.icount = 1;
+	bulkreq.ubuffer = &bstat;
+	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+	if (error)
+		return errno;
+
+	xfrog_bulkstat_v1_to_v5(xfd, bulkstat, &bstat);
+	return 0;
+}
+
 /* Bulkstat a single inode.  Returns zero or a positive error code. */
 int
 xfrog_bulkstat_single(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	int				error;
+
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_bulkstat_single5(xfd, ino, flags, bulkstat);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_bulkstat_single1(xfd, ino, flags, bulkstat);
+}
+
+/*
+ * Set up the necessary control structures to emulate a V5 bulk request ioctl
+ * by calling a V1 bulk request ioctl.  This enables callers to run on older
+ * kernels.
+ *
+ * Returns 0 if the emulation should proceed; ECANCELED if there are no
+ * records; or a positive error code.
+ */
+static int
+xfrog_bulk_req_v1_setup(
 	struct xfs_fd		*xfd,
-	uint64_t		ino,
-	struct xfs_bstat	*ubuffer)
+	struct xfs_bulk_ireq	*hdr,
+	struct xfs_fsop_bulkreq	*bulkreq,
+	size_t			rec_size)
+{
+	void			*buf;
+
+	if (hdr->flags & XFS_BULK_IREQ_AGNO) {
+		uint32_t	agno = cvt_ino_to_agno(xfd, hdr->ino);
+
+		if (hdr->ino == 0)
+			hdr->ino = cvt_agino_to_ino(xfd, hdr->agno, 0);
+		else if (agno < hdr->agno)
+			return EINVAL;
+		else if (agno > hdr->agno)
+			goto no_results;
+	}
+
+	if (cvt_ino_to_agno(xfd, hdr->ino) > xfd->fsgeom.agcount)
+		goto no_results;
+
+	buf = malloc(hdr->icount * rec_size);
+	if (!buf)
+		return errno;
+
+	if (hdr->ino)
+		hdr->ino--;
+	bulkreq->lastip = (__u64 *)&hdr->ino,
+	bulkreq->icount = hdr->icount,
+	bulkreq->ocount = (__s32 *)&hdr->ocount,
+	bulkreq->ubuffer = buf;
+	return 0;
+
+no_results:
+	hdr->ocount = 0;
+	return ECANCELED;
+}
+
+/*
+ * Clean up after using a V1 bulk request to emulate a V5 bulk request call.
+ *
+ * If the ioctl was successful, we need to convert the returned V1-format bulk
+ * request data into the V5-format bulk request data and copy it into the
+ * caller's buffer.  We also need to free all resources allocated during the
+ * setup setup.
+ */
+static int
+xfrog_bulk_req_v1_cleanup(
+	struct xfs_fd		*xfd,
+	struct xfs_bulk_ireq	*hdr,
+	struct xfs_fsop_bulkreq	*bulkreq,
+	size_t			v1_rec_size,
+	uint64_t		(*v1_ino)(void *v1_rec),
+	void			*v5_records,
+	size_t			v5_rec_size,
+	void			(*cvt)(struct xfs_fd *xfd, void *v5, void *v1),
+	unsigned int		startino_adj,
+	int			error)
+{
+	void			*v1_rec = bulkreq->ubuffer;
+	void			*v5_rec = v5_records;
+	unsigned int		i;
+
+	if (error == ECANCELED) {
+		error = 0;
+		goto free;
+	}
+	if (error)
+		goto free;
+
+	/*
+	 * Convert each record from v1 to v5 format, keeping the startino
+	 * value up to date and (if desired) stopping at the end of the
+	 * AG.
+	 */
+	for (i = 0;
+	     i < hdr->ocount;
+	     i++, v1_rec += v1_rec_size, v5_rec += v5_rec_size) {
+		uint64_t	ino = v1_ino(v1_rec);
+
+		/* Stop if we hit a different AG. */
+		if ((hdr->flags & XFS_BULK_IREQ_AGNO) &&
+		    cvt_ino_to_agno(xfd, ino) != hdr->agno) {
+			hdr->ocount = i;
+			break;
+		}
+		cvt(xfd, v5_rec, v1_rec);
+		hdr->ino = ino + startino_adj;
+	}
+
+free:
+	free(bulkreq->ubuffer);
+	return error;
+}
+
+static uint64_t xfrog_bstat_ino(void *v1_rec)
+{
+	return ((struct xfs_bstat *)v1_rec)->bs_ino;
+}
+
+static void xfrog_bstat_cvt(struct xfs_fd *xfd, void *v5, void *v1)
+{
+	xfrog_bulkstat_v1_to_v5(xfd, v5, v1);
+}
+
+/* Bulkstat a bunch of inodes using the v5 interface. */
+static int
+xfrog_bulkstat5(
+	struct xfs_fd		*xfd,
+	struct xfs_bulkstat_req	*req)
 {
-	__u64			i = ino;
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= &i,
-		.icount		= 1,
-		.ubuffer	= ubuffer,
-		.ocount		= NULL,
-	};
 	int			ret;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
 	if (ret)
 		return errno;
 	return 0;
 }
 
+/* Bulkstat a bunch of inodes using the v1 interface. */
+static int
+xfrog_bulkstat1(
+	struct xfs_fd		*xfd,
+	struct xfs_bulkstat_req	*req)
+{
+	struct xfs_fsop_bulkreq	bulkreq = { 0 };
+	int			error;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	error = xfrog_bulk_req_v1_setup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_bstat));
+	if (error == ECANCELED)
+		goto out_teardown;
+	if (error)
+		return error;
+
+	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
+	if (error)
+		error = errno;
+
+out_teardown:
+	return xfrog_bulk_req_v1_cleanup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_bstat), xfrog_bstat_ino,
+			&req->bulkstat, sizeof(struct xfs_bulkstat),
+			xfrog_bstat_cvt, 1, error);
+}
+
 /* Bulkstat a bunch of inodes.  Returns zero or a positive error code. */
 int
 xfrog_bulkstat(
 	struct xfs_fd		*xfd,
-	uint64_t		*lastino,
-	uint32_t		icount,
-	struct xfs_bstat	*ubuffer,
-	uint32_t		*ocount)
+	struct xfs_bulkstat_req	*req)
 {
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= (__u64 *)lastino,
-		.icount		= icount,
-		.ubuffer	= ubuffer,
-		.ocount		= (__s32 *)ocount,
-	};
-	int			ret;
+	int			error;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
-	if (ret)
-		return errno;
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_bulkstat5(xfd, req);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_bulkstat1(xfd, req);
+}
+
+static bool
+time_too_big(
+	uint64_t	time)
+{
+	time_t		TIME_MAX;
+
+	memset(&TIME_MAX, 0xFF, sizeof(TIME_MAX));
+	return time > TIME_MAX;
+}
+
+/* Convert bulkstat data from v5 format to v1 format. */
+int
+xfrog_bulkstat_v5_to_v1(
+	struct xfs_fd			*xfd,
+	struct xfs_bstat		*bs1,
+	const struct xfs_bulkstat	*bs5)
+{
+	if (bs5->bs_aextents > UINT16_MAX ||
+	    cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks) > UINT32_MAX ||
+	    cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks) > UINT32_MAX ||
+	    time_too_big(bs5->bs_atime) ||
+	    time_too_big(bs5->bs_ctime) ||
+	    time_too_big(bs5->bs_mtime))
+		return ERANGE;
+
+	bs1->bs_ino = bs5->bs_ino;
+	bs1->bs_mode = bs5->bs_mode;
+	bs1->bs_nlink = bs5->bs_nlink;
+	bs1->bs_uid = bs5->bs_uid;
+	bs1->bs_gid = bs5->bs_gid;
+	bs1->bs_rdev = bs5->bs_rdev;
+	bs1->bs_blksize = bs5->bs_blksize;
+	bs1->bs_size = bs5->bs_size;
+	bs1->bs_atime.tv_sec = bs5->bs_atime;
+	bs1->bs_mtime.tv_sec = bs5->bs_mtime;
+	bs1->bs_ctime.tv_sec = bs5->bs_ctime;
+	bs1->bs_atime.tv_nsec = bs5->bs_atime_nsec;
+	bs1->bs_mtime.tv_nsec = bs5->bs_mtime_nsec;
+	bs1->bs_ctime.tv_nsec = bs5->bs_ctime_nsec;
+	bs1->bs_blocks = bs5->bs_blocks;
+	bs1->bs_xflags = bs5->bs_xflags;
+	bs1->bs_extsize = cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks);
+	bs1->bs_extents = bs5->bs_extents;
+	bs1->bs_gen = bs5->bs_gen;
+	bs1->bs_projid_lo = bs5->bs_projectid & 0xFFFF;
+	bs1->bs_forkoff = bs5->bs_forkoff;
+	bs1->bs_projid_hi = bs5->bs_projectid >> 16;
+	bs1->bs_sick = bs5->bs_sick;
+	bs1->bs_checked = bs5->bs_checked;
+	bs1->bs_cowextsize = cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks);
+	bs1->bs_dmevmask = 0;
+	bs1->bs_dmstate = 0;
+	bs1->bs_aextents = bs5->bs_aextents;
 	return 0;
 }
 
+/* Convert bulkstat data from v1 format to v5 format. */
+void
+xfrog_bulkstat_v1_to_v5(
+	struct xfs_fd			*xfd,
+	struct xfs_bulkstat		*bs5,
+	const struct xfs_bstat		*bs1)
+{
+	memset(bs5, 0, sizeof(*bs5));
+	bs5->bs_version = XFS_BULKSTAT_VERSION_V1;
+
+	bs5->bs_ino = bs1->bs_ino;
+	bs5->bs_mode = bs1->bs_mode;
+	bs5->bs_nlink = bs1->bs_nlink;
+	bs5->bs_uid = bs1->bs_uid;
+	bs5->bs_gid = bs1->bs_gid;
+	bs5->bs_rdev = bs1->bs_rdev;
+	bs5->bs_blksize = bs1->bs_blksize;
+	bs5->bs_size = bs1->bs_size;
+	bs5->bs_atime = bs1->bs_atime.tv_sec;
+	bs5->bs_mtime = bs1->bs_mtime.tv_sec;
+	bs5->bs_ctime = bs1->bs_ctime.tv_sec;
+	bs5->bs_atime_nsec = bs1->bs_atime.tv_nsec;
+	bs5->bs_mtime_nsec = bs1->bs_mtime.tv_nsec;
+	bs5->bs_ctime_nsec = bs1->bs_ctime.tv_nsec;
+	bs5->bs_blocks = bs1->bs_blocks;
+	bs5->bs_xflags = bs1->bs_xflags;
+	bs5->bs_extsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_extsize);
+	bs5->bs_extents = bs1->bs_extents;
+	bs5->bs_gen = bs1->bs_gen;
+	bs5->bs_projectid = bstat_get_projid(bs1);
+	bs5->bs_forkoff = bs1->bs_forkoff;
+	bs5->bs_sick = bs1->bs_sick;
+	bs5->bs_checked = bs1->bs_checked;
+	bs5->bs_cowextsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_cowextsize);
+	bs5->bs_aextents = bs1->bs_aextents;
+}
+
+/* Allocate a bulkstat request.  On error returns NULL and sets errno. */
+struct xfs_bulkstat_req *
+xfrog_bulkstat_alloc_req(
+	uint32_t		nr,
+	uint64_t		startino)
+{
+	struct xfs_bulkstat_req	*breq;
+
+	breq = calloc(1, XFS_BULKSTAT_REQ_SIZE(nr));
+	if (!breq)
+		return NULL;
+
+	breq->hdr.icount = nr;
+	breq->hdr.ino = startino;
+
+	return breq;
+}
+
 /*
  * Query inode allocation bitmask information.  Returns zero or a positive
  * error code.
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index 83ac0e37..bbbc69a2 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -8,10 +8,16 @@
 
 /* Bulkstat wrappers */
 struct xfs_bstat;
-int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino,
-		struct xfs_bstat *ubuffer);
-int xfrog_bulkstat(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
-		struct xfs_bstat *ubuffer, uint32_t *ocount);
+int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino, unsigned int flags,
+		struct xfs_bulkstat *bulkstat);
+int xfrog_bulkstat(struct xfs_fd *xfd, struct xfs_bulkstat_req *req);
+
+struct xfs_bulkstat_req *xfrog_bulkstat_alloc_req(uint32_t nr,
+		uint64_t startino);
+int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
+		const struct xfs_bulkstat *bstat);
+void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
+		const struct xfs_bstat *bs1);
 
 struct xfs_inogrp;
 int xfrog_inumbers(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index 55b14c2b..ca38324e 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -39,8 +39,17 @@ struct xfs_fd {
 
 	/* log2 of sb_blocksize / sb_sectsize */
 	unsigned int		blkbb_log;
+
+	/* XFROG_FLAG_* state flags */
+	unsigned int		flags;
 };
 
+/* Only use v1 bulkstat/inumbers ioctls. */
+#define XFROG_FLAG_BULKSTAT_FORCE_V1	(1 << 0)
+
+/* Only use v5 bulkstat/inumbers ioctls. */
+#define XFROG_FLAG_BULKSTAT_FORCE_V5	(1 << 1)
+
 /* Static initializers */
 #define XFS_FD_INIT(_fd)	{ .fd = (_fd), }
 #define XFS_FD_INIT_EMPTY	XFS_FD_INIT(-1)
diff --git a/quota/quot.c b/quota/quot.c
index 686b2726..7edfad16 100644
--- a/quota/quot.c
+++ b/quota/quot.c
@@ -69,7 +69,7 @@ quot_help(void)
 
 static void
 quot_bulkstat_add(
-	struct xfs_bstat *p,
+	struct xfs_bulkstat	*p,
 	uint		flags)
 {
 	du_t		*dp;
@@ -93,7 +93,7 @@ quot_bulkstat_add(
 	}
 	for (i = 0; i < 3; i++) {
 		id = (i == 0) ? p->bs_uid : ((i == 1) ?
-			p->bs_gid : bstat_get_projid(p));
+			p->bs_gid : p->bs_projectid);
 		hp = &duhash[i][id % DUHASH];
 		for (dp = *hp; dp; dp = dp->next)
 			if (dp->id == id)
@@ -113,11 +113,11 @@ quot_bulkstat_add(
 		}
 		dp->blocks += size;
 
-		if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
+		if (now - p->bs_atime > 30 * (60*60*24))
 			dp->blocks30 += size;
-		if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
+		if (now - p->bs_atime > 60 * (60*60*24))
 			dp->blocks60 += size;
-		if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
+		if (now - p->bs_atime > 90 * (60*60*24))
 			dp->blocks90 += size;
 		dp->nfiles++;
 	}
@@ -129,9 +129,7 @@ quot_bulkstat_mount(
 	unsigned int		flags)
 {
 	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
-	struct xfs_bstat	*buf;
-	uint64_t		last = 0;
-	uint32_t		count;
+	struct xfs_bulkstat_req	*breq;
 	int			i, sts, ret;
 	du_t			**dp;
 
@@ -154,25 +152,24 @@ quot_bulkstat_mount(
 		return;
 	}
 
-	buf = (struct xfs_bstat *)calloc(NBSTAT, sizeof(struct xfs_bstat));
-	if (!buf) {
+	breq = xfrog_bulkstat_alloc_req(NBSTAT, 0);
+	if (!breq) {
 		perror("calloc");
 		xfd_close(&fsxfd);
 		return;
 	}
 
-	while ((sts = xfrog_bulkstat(&fsxfd, &last, NBSTAT, buf,
-				&count)) == 0) {
-		if (count == 0)
+	while ((sts = xfrog_bulkstat(&fsxfd, breq)) == 0) {
+		if (breq->hdr.ocount == 0)
 			break;
-		for (i = 0; i < count; i++)
-			quot_bulkstat_add(&buf[i], flags);
+		for (i = 0; i < breq->hdr.ocount; i++)
+			quot_bulkstat_add(&breq->bulkstat[i], flags);
 	}
 	if (sts < 0) {
 		errno = sts;
 		perror("XFS_IOC_FSBULKSTAT");
 	}
-	free(buf);
+	free(breq);
 	xfd_close(&fsxfd);
 }
 
diff --git a/scrub/inodes.c b/scrub/inodes.c
index 580a845e..2112c9d1 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -50,9 +50,9 @@ static void
 xfs_iterate_inodes_range_check(
 	struct scrub_ctx	*ctx,
 	struct xfs_inogrp	*inogrp,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
-	struct xfs_bstat	*bs;
+	struct xfs_bulkstat	*bs;
 	int			i;
 	int			error;
 
@@ -66,9 +66,9 @@ xfs_iterate_inodes_range_check(
 
 		/* Load the one inode. */
 		error = xfrog_bulkstat_single(&ctx->mnt,
-				inogrp->xi_startino + i, bs);
+				inogrp->xi_startino + i, 0, bs);
 		if (error || bs->bs_ino != inogrp->xi_startino + i) {
-			memset(bs, 0, sizeof(struct xfs_bstat));
+			memset(bs, 0, sizeof(struct xfs_bulkstat));
 			bs->bs_ino = inogrp->xi_startino + i;
 			bs->bs_blksize = ctx->mnt_sv.f_frsize;
 		}
@@ -93,41 +93,41 @@ xfs_iterate_inodes_range(
 {
 	struct xfs_handle	handle;
 	struct xfs_inogrp	inogrp;
-	struct xfs_bstat	bstat[XFS_INODES_PER_CHUNK];
+	struct xfs_bulkstat_req	*breq;
 	char			idescr[DESCR_BUFSZ];
-	struct xfs_bstat	*bs;
+	struct xfs_bulkstat	*bs;
 	uint64_t		igrp_ino;
-	uint64_t		ino;
-	uint32_t		bulklen = 0;
 	uint32_t		igrplen = 0;
 	bool			moveon = true;
 	int			i;
 	int			error;
 	int			stale_count = 0;
 
-
-	memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat));
-
 	memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid));
 	handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
 			sizeof(handle.ha_fid.fid_len);
 	handle.ha_fid.fid_pad = 0;
 
+	breq = xfrog_bulkstat_alloc_req(XFS_INODES_PER_CHUNK, 0);
+	if (!breq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		return false;
+	}
+
 	/* Find the inode chunk & alloc mask */
 	igrp_ino = first_ino;
 	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
 	while (!error && igrplen) {
-		/* Load the inodes. */
-		ino = inogrp.xi_startino - 1;
-
 		/*
 		 * We can have totally empty inode chunks on filesystems where
 		 * there are more than 64 inodes per block.  Skip these.
 		 */
 		if (inogrp.xi_alloccount == 0)
 			goto igrp_retry;
-		error = xfrog_bulkstat(&ctx->mnt, &ino, inogrp.xi_alloccount,
-				bstat, &bulklen);
+
+		breq->hdr.ino = inogrp.xi_startino;
+		breq->hdr.icount = inogrp.xi_alloccount;
+		error = xfrog_bulkstat(&ctx->mnt, breq);
 		if (error) {
 			char	errbuf[DESCR_BUFSZ];
 
@@ -135,10 +135,12 @@ xfs_iterate_inodes_range(
 						errbuf, DESCR_BUFSZ));
 		}
 
-		xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
+		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
 
 		/* Iterate all the inodes. */
-		for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
+		for (i = 0, bs = breq->bulkstat;
+		     i < inogrp.xi_alloccount;
+		     i++, bs++) {
 			if (bs->bs_ino > last_ino)
 				goto out;
 
@@ -184,6 +186,7 @@ _("Changed too many times during scan; giving up."));
 		str_liberror(ctx, error, descr);
 		moveon = false;
 	}
+	free(breq);
 out:
 	return moveon;
 }
diff --git a/scrub/inodes.h b/scrub/inodes.h
index 631848c3..3341c6d9 100644
--- a/scrub/inodes.h
+++ b/scrub/inodes.h
@@ -7,7 +7,7 @@
 #define XFS_SCRUB_INODES_H_
 
 typedef int (*xfs_inode_iter_fn)(struct scrub_ctx *ctx,
-		struct xfs_handle *handle, struct xfs_bstat *bs, void *arg);
+		struct xfs_handle *handle, struct xfs_bulkstat *bs, void *arg);
 
 #define XFS_ITERATE_INODES_ABORT	(-1)
 bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 81c64cd1..a32d1ced 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -30,7 +30,7 @@ xfs_scrub_fd(
 	struct scrub_ctx	*ctx,
 	bool			(*fn)(struct scrub_ctx *ctx, uint64_t ino,
 				      uint32_t gen, struct xfs_action_list *a),
-	struct xfs_bstat	*bs,
+	struct xfs_bulkstat	*bs,
 	struct xfs_action_list	*alist)
 {
 	return fn(ctx, bs->bs_ino, bs->bs_gen, alist);
@@ -45,7 +45,7 @@ struct scrub_inode_ctx {
 static void
 xfs_scrub_inode_vfs_error(
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	char			descr[DESCR_BUFSZ];
 	xfs_agnumber_t		agno;
@@ -65,7 +65,7 @@ static int
 xfs_scrub_inode(
 	struct scrub_ctx	*ctx,
 	struct xfs_handle	*handle,
-	struct xfs_bstat	*bstat,
+	struct xfs_bulkstat	*bstat,
 	void			*arg)
 {
 	struct xfs_action_list	alist;
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 3ff34251..99cd51b2 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -80,7 +80,7 @@ xfs_scrub_scan_dirents(
 	struct scrub_ctx	*ctx,
 	const char		*descr,
 	int			*fd,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	struct unicrash		*uc = NULL;
 	DIR			*dir;
@@ -140,7 +140,7 @@ xfs_scrub_scan_fhandle_namespace_xattrs(
 	struct scrub_ctx		*ctx,
 	const char			*descr,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat,
+	struct xfs_bulkstat		*bstat,
 	const struct attrns_decode	*attr_ns)
 {
 	struct attrlist_cursor		cur;
@@ -200,7 +200,7 @@ xfs_scrub_scan_fhandle_xattrs(
 	struct scrub_ctx		*ctx,
 	const char			*descr,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat)
+	struct xfs_bulkstat		*bstat)
 {
 	const struct attrns_decode	*ns;
 	bool				moveon = true;
@@ -228,7 +228,7 @@ static int
 xfs_scrub_connections(
 	struct scrub_ctx	*ctx,
 	struct xfs_handle	*handle,
-	struct xfs_bstat	*bstat,
+	struct xfs_bulkstat	*bstat,
 	void			*arg)
 {
 	bool			*pmoveon = arg;
diff --git a/scrub/phase6.c b/scrub/phase6.c
index 506e75d2..b41f90e0 100644
--- a/scrub/phase6.c
+++ b/scrub/phase6.c
@@ -172,7 +172,7 @@ static int
 xfs_report_verify_inode(
 	struct scrub_ctx		*ctx,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat,
+	struct xfs_bulkstat		*bstat,
 	void				*arg)
 {
 	char				descr[DESCR_BUFSZ];
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 17e8f34f..b02c5658 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -432,7 +432,7 @@ unicrash_init(
  */
 static bool
 is_only_root_writable(
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	if (bstat->bs_uid != 0 || bstat->bs_gid != 0)
 		return false;
@@ -444,7 +444,7 @@ bool
 unicrash_dir_init(
 	struct unicrash		**ucp,
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	/*
 	 * Assume 64 bytes per dentry, clamp buckets between 16 and 64k.
@@ -459,7 +459,7 @@ bool
 unicrash_xattr_init(
 	struct unicrash		**ucp,
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	/* Assume 16 attributes per extent for lack of a better idea. */
 	return unicrash_init(ucp, ctx, false, 16 * (1 + bstat->bs_aextents),
diff --git a/scrub/unicrash.h b/scrub/unicrash.h
index fb8f5f72..feb9cc86 100644
--- a/scrub/unicrash.h
+++ b/scrub/unicrash.h
@@ -14,9 +14,9 @@ struct unicrash;
 struct dirent;
 
 bool unicrash_dir_init(struct unicrash **ucp, struct scrub_ctx *ctx,
-		struct xfs_bstat *bstat);
+		struct xfs_bulkstat *bstat);
 bool unicrash_xattr_init(struct unicrash **ucp, struct scrub_ctx *ctx,
-		struct xfs_bstat *bstat);
+		struct xfs_bulkstat *bstat);
 bool unicrash_fs_label_init(struct unicrash **ucp, struct scrub_ctx *ctx);
 void unicrash_free(struct unicrash *uc);
 bool unicrash_check_dir_name(struct unicrash *uc, const char *descr,
diff --git a/spaceman/health.c b/spaceman/health.c
index a8bd3f3e..b195a229 100644
--- a/spaceman/health.c
+++ b/spaceman/health.c
@@ -208,7 +208,7 @@ report_inode_health(
 	unsigned long long	ino,
 	const char		*descr)
 {
-	struct xfs_bstat	bs;
+	struct xfs_bulkstat	bs;
 	char			d[256];
 	int			ret;
 
@@ -217,7 +217,7 @@ report_inode_health(
 		descr = d;
 	}
 
-	ret = xfrog_bulkstat_single(&file->xfd, ino, &bs);
+	ret = xfrog_bulkstat_single(&file->xfd, ino, 0, &bs);
 	if (ret) {
 		errno = ret;
 		perror(descr);
@@ -266,11 +266,10 @@ static int
 report_bulkstat_health(
 	xfs_agnumber_t		agno)
 {
-	struct xfs_bstat	bstat[BULKSTAT_NR];
+	struct xfs_bulkstat_req	*breq;
 	char			descr[256];
 	uint64_t		startino = 0;
 	uint64_t		lastino = -1ULL;
-	uint32_t		ocount;
 	uint32_t		i;
 	int			error;
 
@@ -279,26 +278,34 @@ report_bulkstat_health(
 		lastino = cvt_agino_to_ino(&file->xfd, agno + 1, 0) - 1;
 	}
 
+	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, startino);
+	if (!breq) {
+		perror("bulk alloc req");
+		exitcode = 1;
+		return 1;
+	}
+
 	do {
-		error = xfrog_bulkstat(&file->xfd, &startino, BULKSTAT_NR,
-				bstat, &ocount);
+		error = xfrog_bulkstat(&file->xfd, breq);
 		if (error)
 			break;
-		for (i = 0; i < ocount; i++) {
-			if (bstat[i].bs_ino > lastino)
+		for (i = 0; i < breq->hdr.ocount; i++) {
+			if (breq->bulkstat[i].bs_ino > lastino)
 				goto out;
-			snprintf(descr, sizeof(descr) - 1, _("inode %llu"),
-					bstat[i].bs_ino);
-			report_sick(descr, inode_flags, bstat[i].bs_sick,
-					bstat[i].bs_checked);
+			snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
+					breq->bulkstat[i].bs_ino);
+			report_sick(descr, inode_flags,
+					breq->bulkstat[i].bs_sick,
+					breq->bulkstat[i].bs_checked);
 		}
-	} while (ocount > 0);
+	} while (breq->hdr.ocount > 0);
 
 	if (error) {
 		errno = error;
 		perror("bulkstat");
 	}
 out:
+	free(breq);
 	return error;
 }
 


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

* [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS
  2019-09-25 21:32 [PATCH 0/4] xfsprogs: port utilities to bulkstat v5 Darrick J. Wong
                   ` (2 preceding siblings ...)
  2019-09-25 21:32 ` [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics Darrick J. Wong
@ 2019-09-25 21:32 ` Darrick J. Wong
  2019-09-26 21:48   ` Eric Sandeen
  2019-09-27 20:15   ` [PATCH v2 " Darrick J. Wong
  3 siblings, 2 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-25 21:32 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Convert all programs to use the v5 inumbers ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/imap.c          |   26 +++++-----
 io/open.c          |   34 ++++++++-----
 libfrog/bulkstat.c |  132 ++++++++++++++++++++++++++++++++++++++++++++++------
 libfrog/bulkstat.h |   10 +++-
 scrub/fscounters.c |   21 +++++---
 scrub/inodes.c     |   46 ++++++++++--------
 6 files changed, 198 insertions(+), 71 deletions(-)


diff --git a/io/imap.c b/io/imap.c
index 472c1fda..fa69676e 100644
--- a/io/imap.c
+++ b/io/imap.c
@@ -17,9 +17,7 @@ static int
 imap_f(int argc, char **argv)
 {
 	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
-	struct xfs_inogrp	*t;
-	uint64_t		last = 0;
-	uint32_t		count;
+	struct xfs_inumbers_req	*ireq;
 	uint32_t		nent;
 	int			i;
 	int			error;
@@ -29,17 +27,19 @@ imap_f(int argc, char **argv)
 	else
 		nent = atoi(argv[1]);
 
-	t = malloc(nent * sizeof(*t));
-	if (!t)
+	ireq = xfrog_inumbers_alloc_req(nent, 0);
+	if (!ireq) {
+		perror("alloc req");
 		return 0;
+	}
 
-	while ((error = xfrog_inumbers(&xfd, &last, nent, t, &count)) == 0 &&
-	       count > 0) {
-		for (i = 0; i < count; i++) {
-			printf(_("ino %10llu count %2d mask %016llx\n"),
-				(unsigned long long)t[i].xi_startino,
-				t[i].xi_alloccount,
-				(unsigned long long)t[i].xi_allocmask);
+	while ((error = xfrog_inumbers(&xfd, ireq)) == 0 &&
+	       ireq->hdr.ocount > 0) {
+		for (i = 0; i < ireq->hdr.ocount; i++) {
+			printf(_("ino %10"PRIu64" count %2d mask %016"PRIx64"\n"),
+				ireq->inumbers[i].xi_startino,
+				ireq->inumbers[i].xi_alloccount,
+				ireq->inumbers[i].xi_allocmask);
 		}
 	}
 
@@ -48,7 +48,7 @@ imap_f(int argc, char **argv)
 		perror("xfsctl(XFS_IOC_FSINUMBERS)");
 		exitcode = 1;
 	}
-	free(t);
+	free(ireq);
 	return 0;
 }
 
diff --git a/io/open.c b/io/open.c
index e0e7fb3e..3c6113a1 100644
--- a/io/open.c
+++ b/io/open.c
@@ -681,39 +681,47 @@ static __u64
 get_last_inode(void)
 {
 	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
-	uint64_t		lastip = 0;
+	struct xfs_inumbers_req	*ireq;
 	uint32_t		lastgrp = 0;
-	uint32_t		ocount = 0;
-	__u64			last_ino;
-	struct xfs_inogrp	igroup[IGROUP_NR];
+	__u64			last_ino = 0;
+
+	ireq = xfrog_inumbers_alloc_req(IGROUP_NR, 0);
+	if (!ireq) {
+		perror("alloc req");
+		return 0;
+	}
 
 	for (;;) {
 		int		ret;
 
-		ret = xfrog_inumbers(&xfd, &lastip, IGROUP_NR, igroup,
-				&ocount);
+		ret = xfrog_inumbers(&xfd, ireq);
 		if (ret) {
 			errno = ret;
 			perror("XFS_IOC_FSINUMBERS");
-			return 0;
+			free(ireq);
+			goto out;
 		}
 
 		/* Did we reach the last inode? */
-		if (ocount == 0)
+		if (ireq->hdr.ocount == 0)
 			break;
 
 		/* last inode in igroup table */
-		lastgrp = ocount;
+		lastgrp = ireq->hdr.ocount;
 	}
 
-	if (lastgrp == 0)
-		return 0;
+	if (lastgrp == 0) {
+		free(ireq);
+		goto out;
+	}
 
 	lastgrp--;
 
 	/* The last inode number in use */
-	last_ino = igroup[lastgrp].xi_startino +
-		  libxfs_highbit64(igroup[lastgrp].xi_allocmask);
+	last_ino = ireq->inumbers[lastgrp].xi_startino +
+		  libxfs_highbit64(ireq->inumbers[lastgrp].xi_allocmask);
+out:
+	free(ireq);
 
 	return last_ino;
 }
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index 300963f1..85594e5e 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -435,6 +435,86 @@ xfrog_bulkstat_alloc_req(
 	return breq;
 }
 
+/* Convert a inumbers data from v5 format to v1 format. */
+void
+xfrog_inumbers_v5_to_v1(
+	struct xfs_inogrp		*ig1,
+	const struct xfs_inumbers	*ig5)
+{
+	ig1->xi_startino = ig5->xi_startino;
+	ig1->xi_alloccount = ig5->xi_alloccount;
+	ig1->xi_allocmask = ig5->xi_allocmask;
+}
+
+/* Convert a inumbers data from v1 format to v5 format. */
+void
+xfrog_inumbers_v1_to_v5(
+	struct xfs_inumbers		*ig5,
+	const struct xfs_inogrp		*ig1)
+{
+	memset(ig5, 0, sizeof(*ig5));
+	ig5->xi_version = XFS_INUMBERS_VERSION_V1;
+
+	ig5->xi_startino = ig1->xi_startino;
+	ig5->xi_alloccount = ig1->xi_alloccount;
+	ig5->xi_allocmask = ig1->xi_allocmask;
+}
+
+static uint64_t xfrog_inum_ino(void *v1_rec)
+{
+	return ((struct xfs_inogrp *)v1_rec)->xi_startino;
+}
+
+static void xfrog_inum_cvt(struct xfs_fd *xfd, void *v5, void *v1)
+{
+	xfrog_inumbers_v1_to_v5(v5, v1);
+}
+
+/* Query inode allocation bitmask information using v5 ioctl. */
+static int
+xfrog_inumbers5(
+	struct xfs_fd		*xfd,
+	struct xfs_inumbers_req	*req)
+{
+	int			ret;
+
+	ret = ioctl(xfd->fd, XFS_IOC_INUMBERS, req);
+	if (ret)
+		return errno;
+	return 0;
+}
+
+/* Query inode allocation bitmask information using v1 ioctl. */
+static int
+xfrog_inumbers1(
+	struct xfs_fd		*xfd,
+	struct xfs_inumbers_req	*req)
+{
+	struct xfs_fsop_bulkreq	bulkreq = { 0 };
+	int			error;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	error = xfrog_bulk_req_v1_setup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_inogrp));
+	if (error == ECANCELED)
+		goto out_teardown;
+	if (error)
+		return error;
+
+	error = ioctl(xfd->fd, XFS_IOC_FSINUMBERS, &bulkreq);
+	if (error)
+		error = errno;
+
+out_teardown:
+	return xfrog_bulk_req_v1_cleanup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_inogrp), xfrog_inum_ino,
+			&req->inumbers, sizeof(struct xfs_inumbers),
+			xfrog_inum_cvt, 64, error);
+}
+
 /*
  * Query inode allocation bitmask information.  Returns zero or a positive
  * error code.
@@ -442,21 +522,43 @@ xfrog_bulkstat_alloc_req(
 int
 xfrog_inumbers(
 	struct xfs_fd		*xfd,
-	uint64_t		*lastino,
-	uint32_t		icount,
-	struct xfs_inogrp	*ubuffer,
-	uint32_t		*ocount)
+	struct xfs_inumbers_req	*req)
 {
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= (__u64 *)lastino,
-		.icount		= icount,
-		.ubuffer	= ubuffer,
-		.ocount		= (__s32 *)ocount,
-	};
-	int			ret;
+	int			error;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSINUMBERS, &bulkreq);
-	if (ret)
-		return errno;
-	return 0;
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_inumbers5(xfd, req);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_inumbers1(xfd, req);
+}
+
+/* Allocate a inumbers request.  On error returns NULL and sets errno. */
+struct xfs_inumbers_req *
+xfrog_inumbers_alloc_req(
+	uint32_t		nr,
+	uint64_t		startino)
+{
+	struct xfs_inumbers_req	*ireq;
+
+	ireq = calloc(1, XFS_INUMBERS_REQ_SIZE(nr));
+	if (!ireq)
+		return NULL;
+
+	ireq->hdr.icount = nr;
+	ireq->hdr.ino = startino;
+
+	return ireq;
 }
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index bbbc69a2..a085da3d 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -20,7 +20,13 @@ void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
 		const struct xfs_bstat *bs1);
 
 struct xfs_inogrp;
-int xfrog_inumbers(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
-		struct xfs_inogrp *ubuffer, uint32_t *ocount);
+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_v5_to_v1(struct xfs_inogrp *ig1,
+		const struct xfs_inumbers *ig);
+void xfrog_inumbers_v1_to_v5(struct xfs_inumbers *ig,
+		const struct xfs_inogrp *ig1);
 
 #endif	/* __LIBFROG_BULKSTAT_H__ */
diff --git a/scrub/fscounters.c b/scrub/fscounters.c
index 8e4b3467..2fdf658a 100644
--- a/scrub/fscounters.c
+++ b/scrub/fscounters.c
@@ -42,23 +42,28 @@ xfs_count_inodes_range(
 	uint64_t		last_ino,
 	uint64_t		*count)
 {
-	struct xfs_inogrp	inogrp;
-	uint64_t		igrp_ino;
+	struct xfs_inumbers_req	*ireq;
 	uint64_t		nr = 0;
-	uint32_t		igrplen = 0;
 	int			error;
 
 	ASSERT(!(first_ino & (XFS_INODES_PER_CHUNK - 1)));
 	ASSERT((last_ino & (XFS_INODES_PER_CHUNK - 1)));
 
-	igrp_ino = first_ino;
-	while (!(error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
-			&igrplen))) {
-		if (igrplen == 0 || inogrp.xi_startino >= last_ino)
+	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	if (!ireq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		return false;
+	}
+
+	while (!(error = xfrog_inumbers(&ctx->mnt, ireq))) {
+		if (ireq->hdr.ocount == 0 ||
+		    ireq->inumbers[0].xi_startino >= last_ino)
 			break;
-		nr += inogrp.xi_alloccount;
+		nr += ireq->inumbers[0].xi_alloccount;
 	}
 
+	free(ireq);
+
 	if (error) {
 		str_liberror(ctx, error, descr);
 		return false;
diff --git a/scrub/inodes.c b/scrub/inodes.c
index 2112c9d1..964647ce 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -49,7 +49,7 @@
 static void
 xfs_iterate_inodes_range_check(
 	struct scrub_ctx	*ctx,
-	struct xfs_inogrp	*inogrp,
+	struct xfs_inumbers	*inumbers,
 	struct xfs_bulkstat	*bstat)
 {
 	struct xfs_bulkstat	*bs;
@@ -57,19 +57,19 @@ xfs_iterate_inodes_range_check(
 	int			error;
 
 	for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
-		if (!(inogrp->xi_allocmask & (1ULL << i)))
+		if (!(inumbers->xi_allocmask & (1ULL << i)))
 			continue;
-		if (bs->bs_ino == inogrp->xi_startino + i) {
+		if (bs->bs_ino == inumbers->xi_startino + i) {
 			bs++;
 			continue;
 		}
 
 		/* Load the one inode. */
 		error = xfrog_bulkstat_single(&ctx->mnt,
-				inogrp->xi_startino + i, 0, bs);
-		if (error || bs->bs_ino != inogrp->xi_startino + i) {
+				inumbers->xi_startino + i, 0, bs);
+		if (error || bs->bs_ino != inumbers->xi_startino + i) {
 			memset(bs, 0, sizeof(struct xfs_bulkstat));
-			bs->bs_ino = inogrp->xi_startino + i;
+			bs->bs_ino = inumbers->xi_startino + i;
 			bs->bs_blksize = ctx->mnt_sv.f_frsize;
 		}
 		bs++;
@@ -92,12 +92,11 @@ xfs_iterate_inodes_range(
 	void			*arg)
 {
 	struct xfs_handle	handle;
-	struct xfs_inogrp	inogrp;
+	struct xfs_inumbers_req	*ireq;
 	struct xfs_bulkstat_req	*breq;
 	char			idescr[DESCR_BUFSZ];
 	struct xfs_bulkstat	*bs;
-	uint64_t		igrp_ino;
-	uint32_t		igrplen = 0;
+	struct xfs_inumbers	*inumbers;
 	bool			moveon = true;
 	int			i;
 	int			error;
@@ -114,19 +113,26 @@ xfs_iterate_inodes_range(
 		return false;
 	}
 
+	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	if (!ireq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		free(breq);
+		return false;
+	}
+	inumbers = &ireq->inumbers[0];
+
 	/* Find the inode chunk & alloc mask */
-	igrp_ino = first_ino;
-	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
-	while (!error && igrplen) {
+	error = xfrog_inumbers(&ctx->mnt, ireq);
+	while (!error && ireq->hdr.ocount > 0) {
 		/*
 		 * We can have totally empty inode chunks on filesystems where
 		 * there are more than 64 inodes per block.  Skip these.
 		 */
-		if (inogrp.xi_alloccount == 0)
+		if (inumbers->xi_alloccount == 0)
 			goto igrp_retry;
 
-		breq->hdr.ino = inogrp.xi_startino;
-		breq->hdr.icount = inogrp.xi_alloccount;
+		breq->hdr.ino = inumbers->xi_startino;
+		breq->hdr.icount = inumbers->xi_alloccount;
 		error = xfrog_bulkstat(&ctx->mnt, breq);
 		if (error) {
 			char	errbuf[DESCR_BUFSZ];
@@ -135,11 +141,11 @@ xfs_iterate_inodes_range(
 						errbuf, DESCR_BUFSZ));
 		}
 
-		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
+		xfs_iterate_inodes_range_check(ctx, inumbers, breq->bulkstat);
 
 		/* Iterate all the inodes. */
 		for (i = 0, bs = breq->bulkstat;
-		     i < inogrp.xi_alloccount;
+		     i < inumbers->xi_alloccount;
 		     i++, bs++) {
 			if (bs->bs_ino > last_ino)
 				goto out;
@@ -153,7 +159,7 @@ xfs_iterate_inodes_range(
 			case ESTALE:
 				stale_count++;
 				if (stale_count < 30) {
-					igrp_ino = inogrp.xi_startino;
+					ireq->hdr.ino = inumbers->xi_startino;
 					goto igrp_retry;
 				}
 				snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
@@ -177,8 +183,7 @@ _("Changed too many times during scan; giving up."));
 
 		stale_count = 0;
 igrp_retry:
-		error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
-				&igrplen);
+		error = xfrog_inumbers(&ctx->mnt, ireq);
 	}
 
 err:
@@ -186,6 +191,7 @@ _("Changed too many times during scan; giving up."));
 		str_liberror(ctx, error, descr);
 		moveon = false;
 	}
+	free(ireq);
 	free(breq);
 out:
 	return moveon;


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

* Re: [PATCH 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
@ 2019-09-26 18:18   ` Eric Sandeen
  2019-09-26 19:01     ` Darrick J. Wong
  2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 1 reply; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 18:18 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  man/man2/ioctl_xfs_bulkstat.2   |  330 +++++++++++++++++++++++++++++++++++++++
>  man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
>  2 files changed, 336 insertions(+)
>  create mode 100644 man/man2/ioctl_xfs_bulkstat.2
> 

couple errors/typos and a nitpick below

> diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
> new file mode 100644
> index 00000000..f687cfe8
> --- /dev/null
> +++ b/man/man2/ioctl_xfs_bulkstat.2
> @@ -0,0 +1,330 @@
> +.\" Copyright (c) 2019, Oracle.  All rights reserved.
> +.\"
> +.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
> +.\" SPDX-License-Identifier: GPL-2.0+
> +.\" %%%LICENSE_END
> +.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
> +.SH NAME
> +ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
> +.SH SYNOPSIS
> +.br
> +.B #include <xfs/xfs_fs.h>
> +.PP
> +.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
> +.SH DESCRIPTION
> +Query stat information for a group of XFS inodes.
> +This ioctl uses
> +.B struct xfs_bulkstat_req
> +to set up a bulk transfer with the kernel:
> +.PP
> +.in +4n
> +.nf
> +struct xfs_bulkstat_req {
> +	struct xfs_bulk_ireq    hdr;
> +	struct xfs_bulkstat     bulkstat[];
> +};
> +
> +struct xfs_bulk_ireq {
> +	uint64_t                ino;
> +	uint32_t                flags;
> +	uint32_t                icount;
> +	uint32_t                ocount;
> +	uint32_t                agno;
> +	uint64_t                reserved[5];
> +};

maybe a "see below for the xfs_bulkstat structure definition?"

> +.fi
> +.in
> +.PP
> +.I hdr.ino
> +should be set to the number of the first inode for which the caller wants
> +information, or zero to start with the first inode in the filesystem

, or to a special value if XFS_BULK_IREQ_SPECIAL is set in the flags field.

> +Note that this is a different semantic than the
> +.B lastip
> +in the old
> +.B FSBULKSTAT
> +ioctl.
> +After the call, this value will be set to the number of the next inode for
> +which information could supplied.
> +This sets up the next call for an iteration loop.
> +.PP
> +If the
> +.B XFS_BULK_REQ_SPECIAL

XFS_BULK_IREQ_SPECIAL

> +flag is set, this field is interpreted as follows:
> +.RS 0.4i
> +.TP
> +.B XFS_BULK_IREQ_SPECIAL_ROOT
> +Return stat information for the root directory inode.

How about something like:

---

If the XFS_BULK_IREQ_SPECIAL flag is set in the flags field, then this ino field
is interpreted according to the following special values:

XFS_BULK_IREQ_SPECIAL_ROOT
    Return stat information for the root directory inode.

---

or

If the XFS_BULK_REQ_SPECIAL flag is set, this field is interpreted according
to the following special values:

XFS_BULK_IREQ_SPECIAL_ROOT
    Return stat information for the root directory inode.

----

> +.RE
> +.PP
> +.PP
> +.I hdr.flags
> +is a bit set of operational flags:
> +.RS 0.4i
> +.TP
> +.B XFS_BULK_REQ_AGNO

XFS_BULK_IREQ_AGNO

> +If this is set, the call will only return results for the allocation group (AG)
> +set in
> +.BR hdr.agno .
> +If
> +.B hdr.ino
> +is set to zero, results will be returned starting with the first inode in the
> +AG.
> +This flag may not be set at the same time as the
> +.B XFS_BULK_REQ_SPECIAL

XFS_BULK_IREQ_SPECIAL

> +flag.
> +.TP
> +.B XFS_BULK_REQ_SPECIAL

XFS_BULK_IREQ_SPECIAL ...

> +If this is set, results will be returned for only the special inode
> +specified in the
> +.B hdr.ino
> +field.
> +This flag may not be set at the same time as the
> +.B XFS_BULK_REQ_AGNO
> +flag.
> +.RE
> +.PP
> +.I hdr.icount
> +is the number of inodes to examine.
> +.PP
> +.I hdr.ocount
> +will be set to the number of records returned.
> +.PP
> +.I hdr.agno
> +is the number of the allocation group (AG) for which we want results.
> +If the
> +.B XFS_BULK_REQ_AGNO
> +flag is not set, this field is ignored.
> +.PP
> +.I hdr.reserved
> +must be set to zero.
> +
> +.PP
> +.I bulkstat
> +is an array of
> +.B struct xfs_bulkstat
> +which is described below.
> +The array must have at least
> +.I icount
> +elements.
> +.PP
> +.in +4n
> +.nf
> +struct xfs_bulkstat {
> +	uint64_t                bs_ino;
> +	uint64_t                bs_size;
> +
> +	uint64_t                bs_blocks;
> +	uint64_t                bs_xflags;
> +
> +	uint64_t                bs_atime;
> +	uint64_t                bs_mtime;
> +
> +	uint64_t                bs_ctime;
> +	uint64_t                bs_btime;
> +
> +	uint32_t                bs_gen;
> +	uint32_t                bs_uid;
> +	uint32_t                bs_gid;
> +	uint32_t                bs_projectid;
> +
> +	uint32_t                bs_atime_nsec;
> +	uint32_t                bs_mtime_nsec;
> +	uint32_t                bs_ctime_nsec;
> +	uint32_t                bs_btime_nsec;
> +
> +	uint32_t                bs_blksize;
> +	uint32_t                bs_rdev;
> +	uint32_t                bs_cowextsize_blks;
> +	uint32_t                bs_extsize_blks;
> +
> +	uint32_t                bs_nlink;
> +	uint32_t                bs_extents;
> +	uint32_t                bs_aextents;
> +	uint16_t                bs_version;
> +	uint16_t                bs_forkoff;
> +
> +	uint16_t                bs_sick;
> +	uint16_t                bs_checked;
> +	uint16_t                bs_mode;
> +	uint16_t                bs_pad2;
> +
> +	uint64_t                bs_pad[7];
> +};
> +.fi
> +.in
> +.PP
> +.I bs_ino
> +is the inode number of this record.
> +.PP
> +.I bs_size
> +is the size of the file, in bytes.
> +.PP
> +.I bs_blocks
> +is the number of filesystem blocks allocated to this file, including metadata.
> +.PP
> +.I bs_xflags
> +tell us what extended flags are set this inode.
> +These flags are the same values as those defined in the
> +.B XFS INODE FLAGS
> +section of the
> +.BR ioctl_xfs_fsgetxattr (2)
> +manpage.
> +.PP
> +.I bs_atime
> +is the last time this file was accessed, in seconds
> +.PP
> +.I bs_mtime
> +is the last time the contents of this file were modified, in seconds.
> +.PP
> +.I bs_ctime
> +is the last time this inode record was modified, in seconds.
> +.PP
> +.I bs_btime
> +is the time this inode record was created, in seconds.
> +.PP
> +.I bs_gen
> +is the generation number of the inode record.
> +.PP
> +.I bs_uid
> +is the user id.
> +.PP
> +.I bs_gid
> +is the group id.
> +.PP
> +.I bs_projectid
> +is the the project id.
> +.PP
> +.I bs_atime_nsec
> +is the nanoseconds component of the last time this file was accessed.
> +.PP
> +.I bs_mtime_nsec
> +is the nanoseconds component of the last time the contents of this file were
> +modified.
> +.PP
> +.I bs_ctime_nsec
> +is the nanoseconds component of the last time this inode record was modified.
> +.PP
> +.I bs_btime_nsec
> +is the nanoseconds component of the time this inode record was created.
> +.PP
> +.I bs_blksize
> +is the size of a data block for this file, in units of bytes.
> +.PP
> +.I bs_rdev
> +is the encoded device id if this is a special file.
> +.PP
> +.I bs_cowextsize_blks
> +is the Copy on Write extent size hint for this file, in units of data blocks.
> +.PP
> +.I bs_extsize_blks
> +is the extent size hint for this file, in units of data blocks.
> +.PP
> +.I bs_nlink
> +is the number of hard links to this inode.
> +.PP
> +.I bs_extents
> +is the number of storage mappings associated with this file's data.
> +.PP
> +.I bs_aextents
> +is the number of storage mappings associated with this file's extended
> +attributes.
> +.PP
> +.I bs_version
> +is the version of this data structure.
> +Currently, only 1 or 5 are supported.
> +.PP
> +.I bs_forkoff
> +is the offset of the attribute fork in the inode record, in bytes.
> +.PP
> +The fields
> +.IR bs_sick " and " bs_checked
> +indicate the relative health of various allocation group metadata.
> +Please see the section
> +.B XFS INODE METADATA HEALTH REPORTING
> +for more information.
> +.PP
> +.I bs_mode
> +is the file type and mode.
> +.PP
> +.I bs_pad[7]
> +is zeroed.
> +.SH RETURN VALUE
> +On error, \-1 is returned, and
> +.I errno
> +is set to indicate the error.
> +.PP
> +.SH XFS INODE METADATA HEALTH REPORTING
> +.PP
> +The online filesystem checking utility scans inode metadata and records what it
> +finds in the kernel incore state.
> +The following scheme is used for userspace to read the incore health status of
> +an inode:
> +.IP \[bu] 2
> +If a given sick flag is set in
> +.IR bs_sick ,
> +then that piece of metadata has been observed to be damaged.
> +The same bit should be set in
> +.IR bs_checked .
> +.IP \[bu]
> +If a given sick flag is set in
> +.I bs_checked
> +but is not set in
> +.IR bs_sick ,
> +then that piece of metadata has been checked and is not faulty.
> +.IP \[bu]
> +If a given sick flag is not set in
> +.IR bs_checked ,
> +then no conclusion can be made.
> +.PP
> +The following flags apply to these fields:
> +.RS 0.4i
> +.TP
> +.B XFS_BS_SICK_INODE
> +The inode's record itself.
> +.TP
> +.B XFS_BS_SICK_BMBTD
> +File data extent mappings.
> +.TP
> +.B XFS_BS_SICK_BMBTA
> +Extended attribute extent mappings.
> +.TP
> +.B XFS_BS_SICK_BMBTC
> +Copy on Write staging extent mappings.
> +.TP
> +.B XFS_BS_SICK_DIR
> +Directory information.
> +.TP
> +.B XFS_BS_SICK_XATTR
> +Extended attribute data.
> +.TP
> +.B XFS_BS_SICK_SYMLINK
> +Symbolic link target.
> +.TP
> +.B XFS_BS_SICK_PARENT
> +Parent pointers.
> +.RE
> +.SH ERRORS
> +Error codes can be one of, but are not limited to, the following:
> +.TP
> +.B EFAULT
> +The kernel was not able to copy into the userspace buffer.
> +.TP
> +.B EFSBADCRC
> +Metadata checksum validation failed while performing the query.
> +.TP
> +.B EFSCORRUPTED
> +Metadata corruption was encountered while performing the query.
> +.TP
> +.B EINVAL
> +One of the arguments was not valid.
> +.TP
> +.B EIO
> +An I/O error was encountered while performing the query.
> +.TP
> +.B ENOMEM
> +There was insufficient memory to perform the query.
> +.SH CONFORMING TO
> +This API is specific to XFS filesystem on the Linux kernel.
> +.SH SEE ALSO
> +.BR ioctl (2),
> +.BR ioctl_xfs_fsgetxattr (2)
> diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
> index 8f880c5a..3f059942 100644
> --- a/man/man2/ioctl_xfs_fsbulkstat.2
> +++ b/man/man2/ioctl_xfs_fsbulkstat.2
> @@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
>  .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
>  .SH DESCRIPTION
>  Query stat information for a group of XFS inodes.
> +.PP
> +NOTE: This ioctl has been superseded.
> +Please see the
> +.BR ioctl_xfs_bulkstat (2)
> +manpage for information about its replacement.
> +.PP
>  These ioctls use
>  .B struct xfs_fsop_bulkreq
>  to set up a bulk transfer with the kernel:
> 

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

* Re: [PATCH 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
@ 2019-09-26 18:36   ` Eric Sandeen
  2019-09-26 18:49     ` Eric Sandeen
  2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 1 reply; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 18:36 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a manpage describing the new v5 XFS_IOC_INUMBERS ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  man/man2/ioctl_xfs_inumbers.2 |  118 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 118 insertions(+)
>  create mode 100644 man/man2/ioctl_xfs_inumbers.2
> 
> 
> diff --git a/man/man2/ioctl_xfs_inumbers.2 b/man/man2/ioctl_xfs_inumbers.2
> new file mode 100644
> index 00000000..b1e854d3
> --- /dev/null
> +++ b/man/man2/ioctl_xfs_inumbers.2
> @@ -0,0 +1,118 @@
> +.\" Copyright (c) 2019, Oracle.  All rights reserved.
> +.\"
> +.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
> +.\" SPDX-License-Identifier: GPL-2.0+
> +.\" %%%LICENSE_END
> +.TH IOCTL-XFS-INUMBERS 2 2019-05-23 "XFS"
> +.SH NAME
> +ioctl_xfs_inumbers \- query allocation information for groups of XFS inodes
> +.SH SYNOPSIS
> +.br
> +.B #include <xfs/xfs_fs.h>
> +.PP
> +.BI "int ioctl(int " fd ", XFS_IOC_INUMBERS, struct xfs_inumbers_req *" arg );
> +.SH DESCRIPTION
> +Query inode allocation information for groups of XFS inodes.
> +This ioctl uses
> +.B struct xfs_inumbers_req
> +to set up a bulk transfer with the kernel:
> +.PP
> +.in +4n
> +.nf
> +struct xfs_inumbers_req {
> +	struct xfs_bulk_ireq    hdr;
> +	struct xfs_inumbers     inumbers[];
> +};
> +
> +struct xfs_bulk_ireq {
> +	uint64_t                ino;
> +	uint32_t                flags;
> +	uint32_t                icount;
> +	uint32_t                ocount;
> +	uint32_t                agno;
> +	uint64_t                reserved[5];
> +};
> +.fi
> +.in
> +.PP
> +.I hdr
> +describes the information to query.
> +The layout and behavior are documented in the
> +.BR ioctl_xfs_bulkstat (2)
> +manpage and will not be discussed further here.

it needs to be, though, because icount has a different meaning here; it's
not number of inodes, it's number-of-batches-of-64-inodes, right?

and the bulkstat manpage says "hdr.icount is the number of inodes to examine."

Ok, you suggested on IRC< changing the bulkstat manpage to say "records" not
"inodes" which would work too.

> +
> +.PP
> +.I inumbers
> +is an array of
> +.B struct xfs_inumbers
> +which is described below.
> +The array must have at least
> +.I icount
> +elements.
> +.PP
> +.in +4n
> +.nf
> +struct xfs_inumbers {
> +	uint64_t                xi_startino;
> +	uint64_t                xi_allocmask;
> +	uint8_t                 xi_alloccount;
> +	uint8_t                 xi_version;
> +	uint8_t                 xi_padding[6];
> +};
> +.fi
> +.in
> +.PP
> +This structure describes inode usage information for a group of 64 consecutive
> +inode numbers.
> +.PP
> +.I xi_startino
> +is the first inode number of this group.
> +.PP
> +.I xi_allocmask
> +is a bitmask telling which inodes in this group are allocated.
> +To clarify, bit
> +.B N
> +is set if inode
> +.BR xi_startino + N
> +is allocated.
> +.PP
> +.I xi_alloccount
> +is the number of inodes in this group that are allocated.
> +This should be equal to popcnt(xi_allocmask).
> +.PP
> +.I xi_version
> +is the version of this data structure.
> +Currently, only 1 or 5 are supported.

I find myself wondering what the differences between these versions
are...?

> +.PP
> +.I xi_padding[6]
> +is zeroed.
> +.SH RETURN VALUE
> +On error, \-1 is returned, and
> +.I errno
> +is set to indicate the error.
> +.PP
> +.SH ERRORS
> +Error codes can be one of, but are not limited to, the following:
> +.TP
> +.B EFAULT
> +The kernel was not able to copy into the userspace buffer.
> +.TP
> +.B EFSBADCRC
> +Metadata checksum validation failed while performing the query.
> +.TP
> +.B EFSCORRUPTED
> +Metadata corruption was encountered while performing the query.
> +.TP
> +.B EINVAL
> +One of the arguments was not valid.
> +.TP
> +.B EIO
> +An I/O error was encountered while performing the query.
> +.TP
> +.B ENOMEM
> +There was insufficient memory to perform the query.
> +.SH CONFORMING TO
> +This API is specific to XFS filesystem on the Linux kernel.
> +.SH SEE ALSO
> +.BR ioctl (2),
> +.BR ioctl_xfs_bulkstat (2).
> 

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

* Re: [PATCH 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-26 18:36   ` Eric Sandeen
@ 2019-09-26 18:49     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 18:49 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 9/26/19 1:36 PM, Eric Sandeen wrote:
>> +.I xi_version
>> +is the version of this data structure.
>> +Currently, only 1 or 5 are supported.
> I find myself wondering what the differences between these versions
> are...?
> 

ok, the structure defined in this manpage is v5; the kernel will never return
xi_version = 1.  So I think maybe:

"... is the version of this data structure.  The structure defined here is
version 5."

-Eric

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

* Re: [PATCH 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-26 18:18   ` Eric Sandeen
@ 2019-09-26 19:01     ` Darrick J. Wong
  0 siblings, 0 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-26 19:01 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs

On Thu, Sep 26, 2019 at 01:18:52PM -0500, Eric Sandeen wrote:
> On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  man/man2/ioctl_xfs_bulkstat.2   |  330 +++++++++++++++++++++++++++++++++++++++
> >  man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
> >  2 files changed, 336 insertions(+)
> >  create mode 100644 man/man2/ioctl_xfs_bulkstat.2
> > 
> 
> couple errors/typos and a nitpick below
> 
> > diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
> > new file mode 100644
> > index 00000000..f687cfe8
> > --- /dev/null
> > +++ b/man/man2/ioctl_xfs_bulkstat.2
> > @@ -0,0 +1,330 @@
> > +.\" Copyright (c) 2019, Oracle.  All rights reserved.
> > +.\"
> > +.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
> > +.\" SPDX-License-Identifier: GPL-2.0+
> > +.\" %%%LICENSE_END
> > +.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
> > +.SH NAME
> > +ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
> > +.SH SYNOPSIS
> > +.br
> > +.B #include <xfs/xfs_fs.h>
> > +.PP
> > +.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
> > +.SH DESCRIPTION
> > +Query stat information for a group of XFS inodes.
> > +This ioctl uses
> > +.B struct xfs_bulkstat_req
> > +to set up a bulk transfer with the kernel:
> > +.PP
> > +.in +4n
> > +.nf
> > +struct xfs_bulkstat_req {
> > +	struct xfs_bulk_ireq    hdr;
> > +	struct xfs_bulkstat     bulkstat[];
> > +};
> > +
> > +struct xfs_bulk_ireq {
> > +	uint64_t                ino;
> > +	uint32_t                flags;
> > +	uint32_t                icount;
> > +	uint32_t                ocount;
> > +	uint32_t                agno;
> > +	uint64_t                reserved[5];
> > +};
> 
> maybe a "see below for the xfs_bulkstat structure definition?"

Dunno?  Seems fairly obvious to me?  <shrug> I'll put it in.

> > +.fi
> > +.in
> > +.PP
> > +.I hdr.ino
> > +should be set to the number of the first inode for which the caller wants
> > +information, or zero to start with the first inode in the filesystem
> 
> , or to a special value if XFS_BULK_IREQ_SPECIAL is set in the flags field.

Ok.

> > +Note that this is a different semantic than the
> > +.B lastip
> > +in the old
> > +.B FSBULKSTAT
> > +ioctl.
> > +After the call, this value will be set to the number of the next inode for
> > +which information could supplied.
> > +This sets up the next call for an iteration loop.
> > +.PP
> > +If the
> > +.B XFS_BULK_REQ_SPECIAL
> 
> XFS_BULK_IREQ_SPECIAL
> 
> > +flag is set, this field is interpreted as follows:
> > +.RS 0.4i
> > +.TP
> > +.B XFS_BULK_IREQ_SPECIAL_ROOT
> > +Return stat information for the root directory inode.
> 
> How about something like:
> 
> ---
> 
> If the XFS_BULK_IREQ_SPECIAL flag is set in the flags field, then this ino field
> is interpreted according to the following special values:
> 
> XFS_BULK_IREQ_SPECIAL_ROOT
>     Return stat information for the root directory inode.
> 
> ---
> 
> or
> 
> If the XFS_BULK_REQ_SPECIAL flag is set, this field is interpreted according
> to the following special values:
> 
> XFS_BULK_IREQ_SPECIAL_ROOT
>     Return stat information for the root directory inode.

I'll go do that.

> ----
> 
> > +.RE
> > +.PP
> > +.PP
> > +.I hdr.flags
> > +is a bit set of operational flags:
> > +.RS 0.4i
> > +.TP
> > +.B XFS_BULK_REQ_AGNO
> 
> XFS_BULK_IREQ_AGNO
> 
> > +If this is set, the call will only return results for the allocation group (AG)
> > +set in
> > +.BR hdr.agno .
> > +If
> > +.B hdr.ino
> > +is set to zero, results will be returned starting with the first inode in the
> > +AG.
> > +This flag may not be set at the same time as the
> > +.B XFS_BULK_REQ_SPECIAL
> 
> XFS_BULK_IREQ_SPECIAL
> 
> > +flag.
> > +.TP
> > +.B XFS_BULK_REQ_SPECIAL
> 
> XFS_BULK_IREQ_SPECIAL ...

Fixed all this stuff up.

--D

> 
> > +If this is set, results will be returned for only the special inode
> > +specified in the
> > +.B hdr.ino
> > +field.
> > +This flag may not be set at the same time as the
> > +.B XFS_BULK_REQ_AGNO
> > +flag.
> > +.RE
> > +.PP
> > +.I hdr.icount
> > +is the number of inodes to examine.

"hdr.icount  is the maximum number of records to return.  This should be
the size of the array that comes after the header."

> > +.PP
> > +.I hdr.ocount
> > +will be set to the number of records returned.
> > +.PP
> > +.I hdr.agno
> > +is the number of the allocation group (AG) for which we want results.
> > +If the
> > +.B XFS_BULK_REQ_AGNO
> > +flag is not set, this field is ignored.
> > +.PP
> > +.I hdr.reserved
> > +must be set to zero.
> > +
> > +.PP
> > +.I bulkstat
> > +is an array of
> > +.B struct xfs_bulkstat
> > +which is described below.
> > +The array must have at least
> > +.I icount
> > +elements.
> > +.PP
> > +.in +4n
> > +.nf
> > +struct xfs_bulkstat {
> > +	uint64_t                bs_ino;
> > +	uint64_t                bs_size;
> > +
> > +	uint64_t                bs_blocks;
> > +	uint64_t                bs_xflags;
> > +
> > +	uint64_t                bs_atime;
> > +	uint64_t                bs_mtime;
> > +
> > +	uint64_t                bs_ctime;
> > +	uint64_t                bs_btime;
> > +
> > +	uint32_t                bs_gen;
> > +	uint32_t                bs_uid;
> > +	uint32_t                bs_gid;
> > +	uint32_t                bs_projectid;
> > +
> > +	uint32_t                bs_atime_nsec;
> > +	uint32_t                bs_mtime_nsec;
> > +	uint32_t                bs_ctime_nsec;
> > +	uint32_t                bs_btime_nsec;
> > +
> > +	uint32_t                bs_blksize;
> > +	uint32_t                bs_rdev;
> > +	uint32_t                bs_cowextsize_blks;
> > +	uint32_t                bs_extsize_blks;
> > +
> > +	uint32_t                bs_nlink;
> > +	uint32_t                bs_extents;
> > +	uint32_t                bs_aextents;
> > +	uint16_t                bs_version;
> > +	uint16_t                bs_forkoff;
> > +
> > +	uint16_t                bs_sick;
> > +	uint16_t                bs_checked;
> > +	uint16_t                bs_mode;
> > +	uint16_t                bs_pad2;
> > +
> > +	uint64_t                bs_pad[7];
> > +};
> > +.fi
> > +.in
> > +.PP
> > +.I bs_ino
> > +is the inode number of this record.
> > +.PP
> > +.I bs_size
> > +is the size of the file, in bytes.
> > +.PP
> > +.I bs_blocks
> > +is the number of filesystem blocks allocated to this file, including metadata.
> > +.PP
> > +.I bs_xflags
> > +tell us what extended flags are set this inode.
> > +These flags are the same values as those defined in the
> > +.B XFS INODE FLAGS
> > +section of the
> > +.BR ioctl_xfs_fsgetxattr (2)
> > +manpage.
> > +.PP
> > +.I bs_atime
> > +is the last time this file was accessed, in seconds
> > +.PP
> > +.I bs_mtime
> > +is the last time the contents of this file were modified, in seconds.
> > +.PP
> > +.I bs_ctime
> > +is the last time this inode record was modified, in seconds.
> > +.PP
> > +.I bs_btime
> > +is the time this inode record was created, in seconds.
> > +.PP
> > +.I bs_gen
> > +is the generation number of the inode record.
> > +.PP
> > +.I bs_uid
> > +is the user id.
> > +.PP
> > +.I bs_gid
> > +is the group id.
> > +.PP
> > +.I bs_projectid
> > +is the the project id.
> > +.PP
> > +.I bs_atime_nsec
> > +is the nanoseconds component of the last time this file was accessed.
> > +.PP
> > +.I bs_mtime_nsec
> > +is the nanoseconds component of the last time the contents of this file were
> > +modified.
> > +.PP
> > +.I bs_ctime_nsec
> > +is the nanoseconds component of the last time this inode record was modified.
> > +.PP
> > +.I bs_btime_nsec
> > +is the nanoseconds component of the time this inode record was created.
> > +.PP
> > +.I bs_blksize
> > +is the size of a data block for this file, in units of bytes.
> > +.PP
> > +.I bs_rdev
> > +is the encoded device id if this is a special file.
> > +.PP
> > +.I bs_cowextsize_blks
> > +is the Copy on Write extent size hint for this file, in units of data blocks.
> > +.PP
> > +.I bs_extsize_blks
> > +is the extent size hint for this file, in units of data blocks.
> > +.PP
> > +.I bs_nlink
> > +is the number of hard links to this inode.
> > +.PP
> > +.I bs_extents
> > +is the number of storage mappings associated with this file's data.
> > +.PP
> > +.I bs_aextents
> > +is the number of storage mappings associated with this file's extended
> > +attributes.
> > +.PP
> > +.I bs_version
> > +is the version of this data structure.
> > +Currently, only 1 or 5 are supported.
> > +.PP
> > +.I bs_forkoff
> > +is the offset of the attribute fork in the inode record, in bytes.
> > +.PP
> > +The fields
> > +.IR bs_sick " and " bs_checked
> > +indicate the relative health of various allocation group metadata.
> > +Please see the section
> > +.B XFS INODE METADATA HEALTH REPORTING
> > +for more information.
> > +.PP
> > +.I bs_mode
> > +is the file type and mode.
> > +.PP
> > +.I bs_pad[7]
> > +is zeroed.
> > +.SH RETURN VALUE
> > +On error, \-1 is returned, and
> > +.I errno
> > +is set to indicate the error.
> > +.PP
> > +.SH XFS INODE METADATA HEALTH REPORTING
> > +.PP
> > +The online filesystem checking utility scans inode metadata and records what it
> > +finds in the kernel incore state.
> > +The following scheme is used for userspace to read the incore health status of
> > +an inode:
> > +.IP \[bu] 2
> > +If a given sick flag is set in
> > +.IR bs_sick ,
> > +then that piece of metadata has been observed to be damaged.
> > +The same bit should be set in
> > +.IR bs_checked .
> > +.IP \[bu]
> > +If a given sick flag is set in
> > +.I bs_checked
> > +but is not set in
> > +.IR bs_sick ,
> > +then that piece of metadata has been checked and is not faulty.
> > +.IP \[bu]
> > +If a given sick flag is not set in
> > +.IR bs_checked ,
> > +then no conclusion can be made.
> > +.PP
> > +The following flags apply to these fields:
> > +.RS 0.4i
> > +.TP
> > +.B XFS_BS_SICK_INODE
> > +The inode's record itself.
> > +.TP
> > +.B XFS_BS_SICK_BMBTD
> > +File data extent mappings.
> > +.TP
> > +.B XFS_BS_SICK_BMBTA
> > +Extended attribute extent mappings.
> > +.TP
> > +.B XFS_BS_SICK_BMBTC
> > +Copy on Write staging extent mappings.
> > +.TP
> > +.B XFS_BS_SICK_DIR
> > +Directory information.
> > +.TP
> > +.B XFS_BS_SICK_XATTR
> > +Extended attribute data.
> > +.TP
> > +.B XFS_BS_SICK_SYMLINK
> > +Symbolic link target.
> > +.TP
> > +.B XFS_BS_SICK_PARENT
> > +Parent pointers.
> > +.RE
> > +.SH ERRORS
> > +Error codes can be one of, but are not limited to, the following:
> > +.TP
> > +.B EFAULT
> > +The kernel was not able to copy into the userspace buffer.
> > +.TP
> > +.B EFSBADCRC
> > +Metadata checksum validation failed while performing the query.
> > +.TP
> > +.B EFSCORRUPTED
> > +Metadata corruption was encountered while performing the query.
> > +.TP
> > +.B EINVAL
> > +One of the arguments was not valid.
> > +.TP
> > +.B EIO
> > +An I/O error was encountered while performing the query.
> > +.TP
> > +.B ENOMEM
> > +There was insufficient memory to perform the query.
> > +.SH CONFORMING TO
> > +This API is specific to XFS filesystem on the Linux kernel.
> > +.SH SEE ALSO
> > +.BR ioctl (2),
> > +.BR ioctl_xfs_fsgetxattr (2)
> > diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
> > index 8f880c5a..3f059942 100644
> > --- a/man/man2/ioctl_xfs_fsbulkstat.2
> > +++ b/man/man2/ioctl_xfs_fsbulkstat.2
> > @@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
> >  .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
> >  .SH DESCRIPTION
> >  Query stat information for a group of XFS inodes.
> > +.PP
> > +NOTE: This ioctl has been superseded.
> > +Please see the
> > +.BR ioctl_xfs_bulkstat (2)
> > +manpage for information about its replacement.
> > +.PP
> >  These ioctls use
> >  .B struct xfs_fsop_bulkreq
> >  to set up a bulk transfer with the kernel:
> > 

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

* [PATCH v2 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
  2019-09-26 18:18   ` Eric Sandeen
@ 2019-09-26 19:10   ` Darrick J. Wong
  2019-09-26 19:11     ` Eric Sandeen
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 1 reply; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-26 19:10 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_bulkstat.2   |  330 +++++++++++++++++++++++++++++++++++++++
 man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
 2 files changed, 336 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_bulkstat.2

diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
new file mode 100644
index 00000000..f687cfe8
--- /dev/null
+++ b/man/man2/ioctl_xfs_bulkstat.2
@@ -0,0 +1,330 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
+.SH DESCRIPTION
+Query stat information for a group of XFS inodes.
+This ioctl uses
+.B struct xfs_bulkstat_req
+to set up a bulk transfer with the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_bulkstat     bulkstat[];
+};
+
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr.ino
+should be set to the number of the first inode for which the caller wants
+information, or zero to start with the first inode in the filesystem.
+Note that this is a different semantic than the
+.B lastip
+in the old
+.B FSBULKSTAT
+ioctl.
+After the call, this value will be set to the number of the next inode for
+which information could supplied.
+This sets up the next call for an iteration loop.
+.PP
+If the
+.B XFS_BULK_REQ_SPECIAL
+flag is set, this field is interpreted as follows:
+.RS 0.4i
+.TP
+.B XFS_BULK_IREQ_SPECIAL_ROOT
+Return stat information for the root directory inode.
+.RE
+.PP
+.PP
+.I hdr.flags
+is a bit set of operational flags:
+.RS 0.4i
+.TP
+.B XFS_BULK_REQ_AGNO
+If this is set, the call will only return results for the allocation group (AG)
+set in
+.BR hdr.agno .
+If
+.B hdr.ino
+is set to zero, results will be returned starting with the first inode in the
+AG.
+This flag may not be set at the same time as the
+.B XFS_BULK_REQ_SPECIAL
+flag.
+.TP
+.B XFS_BULK_REQ_SPECIAL
+If this is set, results will be returned for only the special inode
+specified in the
+.B hdr.ino
+field.
+This flag may not be set at the same time as the
+.B XFS_BULK_REQ_AGNO
+flag.
+.RE
+.PP
+.I hdr.icount
+is the number of inodes to examine.
+.PP
+.I hdr.ocount
+will be set to the number of records returned.
+.PP
+.I hdr.agno
+is the number of the allocation group (AG) for which we want results.
+If the
+.B XFS_BULK_REQ_AGNO
+flag is not set, this field is ignored.
+.PP
+.I hdr.reserved
+must be set to zero.
+
+.PP
+.I bulkstat
+is an array of
+.B struct xfs_bulkstat
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat {
+	uint64_t                bs_ino;
+	uint64_t                bs_size;
+
+	uint64_t                bs_blocks;
+	uint64_t                bs_xflags;
+
+	uint64_t                bs_atime;
+	uint64_t                bs_mtime;
+
+	uint64_t                bs_ctime;
+	uint64_t                bs_btime;
+
+	uint32_t                bs_gen;
+	uint32_t                bs_uid;
+	uint32_t                bs_gid;
+	uint32_t                bs_projectid;
+
+	uint32_t                bs_atime_nsec;
+	uint32_t                bs_mtime_nsec;
+	uint32_t                bs_ctime_nsec;
+	uint32_t                bs_btime_nsec;
+
+	uint32_t                bs_blksize;
+	uint32_t                bs_rdev;
+	uint32_t                bs_cowextsize_blks;
+	uint32_t                bs_extsize_blks;
+
+	uint32_t                bs_nlink;
+	uint32_t                bs_extents;
+	uint32_t                bs_aextents;
+	uint16_t                bs_version;
+	uint16_t                bs_forkoff;
+
+	uint16_t                bs_sick;
+	uint16_t                bs_checked;
+	uint16_t                bs_mode;
+	uint16_t                bs_pad2;
+
+	uint64_t                bs_pad[7];
+};
+.fi
+.in
+.PP
+.I bs_ino
+is the inode number of this record.
+.PP
+.I bs_size
+is the size of the file, in bytes.
+.PP
+.I bs_blocks
+is the number of filesystem blocks allocated to this file, including metadata.
+.PP
+.I bs_xflags
+tell us what extended flags are set this inode.
+These flags are the same values as those defined in the
+.B XFS INODE FLAGS
+section of the
+.BR ioctl_xfs_fsgetxattr (2)
+manpage.
+.PP
+.I bs_atime
+is the last time this file was accessed, in seconds.
+.PP
+.I bs_mtime
+is the last time the contents of this file were modified, in seconds.
+.PP
+.I bs_ctime
+is the last time this inode record was modified, in seconds.
+.PP
+.I bs_btime
+is the time this inode record was created, in seconds.
+.PP
+.I bs_gen
+is the generation number of the inode record.
+.PP
+.I bs_uid
+is the user id.
+.PP
+.I bs_gid
+is the group id.
+.PP
+.I bs_projectid
+is the the project id.
+.PP
+.I bs_atime_nsec
+is the nanoseconds component of the last time this file was accessed.
+.PP
+.I bs_mtime_nsec
+is the nanoseconds component of the last time the contents of this file were
+modified.
+.PP
+.I bs_ctime_nsec
+is the nanoseconds component of the last time this inode record was modified.
+.PP
+.I bs_btime_nsec
+is the nanoseconds component of the time this inode record was created.
+.PP
+.I bs_blksize
+is the size of a data block for this file, in units of bytes.
+.PP
+.I bs_rdev
+is the encoded device id if this is a special file.
+.PP
+.I bs_cowextsize_blks
+is the Copy on Write extent size hint for this file, in units of data blocks.
+.PP
+.I bs_extsize_blks
+is the extent size hint for this file, in units of data blocks.
+.PP
+.I bs_nlink
+is the number of hard links to this inode.
+.PP
+.I bs_extents
+is the number of storage mappings associated with this file's data.
+.PP
+.I bs_aextents
+is the number of storage mappings associated with this file's extended
+attributes.
+.PP
+.I bs_version
+is the version of this data structure.
+Currently, only 1 or 5 are supported.
+.PP
+.I bs_forkoff
+is the offset of the attribute fork in the inode record, in bytes.
+.PP
+The fields
+.IR bs_sick " and " bs_checked
+indicate the relative health of various allocation group metadata.
+Please see the section
+.B XFS INODE METADATA HEALTH REPORTING
+for more information.
+.PP
+.I bs_mode
+is the file type and mode.
+.PP
+.I bs_pad[7]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH XFS INODE METADATA HEALTH REPORTING
+.PP
+The online filesystem checking utility scans inode metadata and records what it
+finds in the kernel incore state.
+The following scheme is used for userspace to read the incore health status of
+an inode:
+.IP \[bu] 2
+If a given sick flag is set in
+.IR bs_sick ,
+then that piece of metadata has been observed to be damaged.
+The same bit should be set in
+.IR bs_checked .
+.IP \[bu]
+If a given sick flag is set in
+.I bs_checked
+but is not set in
+.IR bs_sick ,
+then that piece of metadata has been checked and is not faulty.
+.IP \[bu]
+If a given sick flag is not set in
+.IR bs_checked ,
+then no conclusion can be made.
+.PP
+The following flags apply to these fields:
+.RS 0.4i
+.TP
+.B XFS_BS_SICK_INODE
+The inode's record itself.
+.TP
+.B XFS_BS_SICK_BMBTD
+File data extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTA
+Extended attribute extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTC
+Copy on Write staging extent mappings.
+.TP
+.B XFS_BS_SICK_DIR
+Directory information.
+.TP
+.B XFS_BS_SICK_XATTR
+Extended attribute data.
+.TP
+.B XFS_BS_SICK_SYMLINK
+Symbolic link target.
+.TP
+.B XFS_BS_SICK_PARENT
+Parent pointers.
+.RE
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_fsgetxattr (2)
diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
index 8f880c5a..3f059942 100644
--- a/man/man2/ioctl_xfs_fsbulkstat.2
+++ b/man/man2/ioctl_xfs_fsbulkstat.2
@@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
 .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
 .SH DESCRIPTION
 Query stat information for a group of XFS inodes.
+.PP
+NOTE: This ioctl has been superseded.
+Please see the
+.BR ioctl_xfs_bulkstat (2)
+manpage for information about its replacement.
+.PP
 These ioctls use
 .B struct xfs_fsop_bulkreq
 to set up a bulk transfer with the kernel:

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

* [PATCH v2 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
  2019-09-26 18:36   ` Eric Sandeen
@ 2019-09-26 19:10   ` Darrick J. Wong
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
  2 siblings, 0 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-26 19:10 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Add a manpage describing the new v5 XFS_IOC_INUMBERS ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_inumbers.2 |  118 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_inumbers.2

diff --git a/man/man2/ioctl_xfs_inumbers.2 b/man/man2/ioctl_xfs_inumbers.2
new file mode 100644
index 00000000..b1e854d3
--- /dev/null
+++ b/man/man2/ioctl_xfs_inumbers.2
@@ -0,0 +1,118 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-INUMBERS 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_inumbers \- query allocation information for groups of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_INUMBERS, struct xfs_inumbers_req *" arg );
+.SH DESCRIPTION
+Query inode allocation information for groups of XFS inodes.
+This ioctl uses
+.B struct xfs_inumbers_req
+to set up a bulk transfer with the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_inumbers_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_inumbers     inumbers[];
+};
+
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr
+describes the information to query.
+The layout and behavior are documented in the
+.BR ioctl_xfs_bulkstat (2)
+manpage and will not be discussed further here.
+
+.PP
+.I inumbers
+is an array of
+.B struct xfs_inumbers
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_inumbers {
+	uint64_t                xi_startino;
+	uint64_t                xi_allocmask;
+	uint8_t                 xi_alloccount;
+	uint8_t                 xi_version;
+	uint8_t                 xi_padding[6];
+};
+.fi
+.in
+.PP
+This structure describes inode usage information for a group of 64 consecutive
+inode numbers.
+.PP
+.I xi_startino
+is the first inode number of this group.
+.PP
+.I xi_allocmask
+is a bitmask telling which inodes in this group are allocated.
+To clarify, bit
+.B N
+is set if inode
+.BR xi_startino + N
+is allocated.
+.PP
+.I xi_alloccount
+is the number of inodes in this group that are allocated.
+This should be equal to popcnt(xi_allocmask).
+.PP
+.I xi_version
+is the version of this data structure.
+Currently, only 1 or 5 are supported.
+.PP
+.I xi_padding[6]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_bulkstat (2).

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

* Re: [PATCH v2 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
@ 2019-09-26 19:11     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 19:11 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/26/19 2:10 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  man/man2/ioctl_xfs_bulkstat.2   |  330 +++++++++++++++++++++++++++++++++++++++
>  man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
>  2 files changed, 336 insertions(+)
>  create mode 100644 man/man2/ioctl_xfs_bulkstat.2

V2:  No changes (?)


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

* Re: [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics
  2019-09-25 21:32 ` [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics Darrick J. Wong
@ 2019-09-26 21:01   ` Eric Sandeen
  2019-09-27  3:50     ` Darrick J. Wong
  2019-09-27 20:14   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 21:01 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Convert xfrog_bulkstat() and xfrog_bulkstat_single() to take arguments
> using v5 bulkstat semantics and return bulkstat information in v5
> structures.  If the v5 ioctl is not available, the xfrog wrapper should
> use the v1 ioctl to emulate v5 behaviors.  Add flags to the xfs_fd
> structure to constrain emulation for debugging purposes.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

gawd this is a lot of frobnication of ioctl results but I ... guess there's
no way around it.

Presumably we want all callers to use v5 for the bigger fields, right,
so it's not like we can just leave some old v1 callers as-is if they don't
need new fields ....

> ---
>  fsr/xfs_fsr.c      |   64 ++++++--
>  io/open.c          |   27 ++-
>  io/swapext.c       |    9 +
>  libfrog/bulkstat.c |  431 +++++++++++++++++++++++++++++++++++++++++++++++++---
>  libfrog/bulkstat.h |   14 +-
>  libfrog/fsgeom.h   |    9 +
>  quota/quot.c       |   29 ++-
>  scrub/inodes.c     |   39 +++--
>  scrub/inodes.h     |    2 
>  scrub/phase3.c     |    6 -
>  scrub/phase5.c     |    8 -
>  scrub/phase6.c     |    2 
>  scrub/unicrash.c   |    6 -
>  scrub/unicrash.h   |    4 
>  spaceman/health.c  |   33 ++--
>  15 files changed, 572 insertions(+), 111 deletions(-)
> 
> 
> diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
> index a53eb924..af5d6169 100644
> --- a/fsr/xfs_fsr.c
> +++ b/fsr/xfs_fsr.c
> @@ -466,6 +466,17 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
>  				ptr = strchr(ptr, ' ');
>  				if (ptr) {
>  					startino = strtoull(++ptr, NULL, 10);
> +					/*
> +					 * NOTE: The inode number read in from
> +					 * the leftoff file is the last inode
> +					 * to have been fsr'd.  Since the v5
> +					 * xfrog_bulkstat function wants to be
> +					 * passed the first inode that we want
> +					 * to examine, increment the value that
> +					 * we read in.  The debug message below
> +					 * prints the lastoff value.
> +					 */
> +					startino++;
>  				}
>  			}
>  			if (startpass < 0)
> @@ -484,7 +495,7 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
>  
>  	if (vflag) {
>  		fsrprintf(_("START: pass=%d ino=%llu %s %s\n"),
> -			  fs->npass, (unsigned long long)startino,
> +			  fs->npass, (unsigned long long)startino - 1,
>  			  fs->dev, fs->mnt);
>  	}
>  
> @@ -576,12 +587,10 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  	int	fd;
>  	int	count = 0;
>  	int	ret;
> -	uint32_t buflenout;
> -	struct xfs_bstat buf[GRABSZ];
>  	char	fname[64];
>  	char	*tname;
>  	jdm_fshandle_t	*fshandlep;
> -	xfs_ino_t	lastino = startino;
> +	struct xfs_bulkstat_req	*breq;
>  
>  	fsrprintf(_("%s start inode=%llu\n"), mntdir,
>  		(unsigned long long)startino);
> @@ -604,10 +613,21 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  
>  	tmp_init(mntdir);
>  
> -	while ((ret = xfrog_bulkstat(&fsxfd, &lastino, GRABSZ, &buf[0],
> -				&buflenout)) == 0) {
> -		struct xfs_bstat *p;
> -		struct xfs_bstat *endp;
> +	breq = xfrog_bulkstat_alloc_req(GRABSZ, startino);
> +	if (!breq) {
> +		fsrprintf(_("Skipping %s: not enough memory\n"),
> +			  mntdir);
> +		xfd_close(&fsxfd);
> +		free(fshandlep);
> +		return -1;
> +	}
> +
> +	while ((ret = xfrog_bulkstat(&fsxfd, breq) == 0)) {
> +		struct xfs_bstat	bs1;
> +		struct xfs_bulkstat	*buf = breq->bulkstat;
> +		struct xfs_bulkstat	*p;
> +		struct xfs_bulkstat	*endp;
> +		uint32_t		buflenout = breq->hdr.ocount;
>  
>  		if (buflenout == 0)
>  			goto out0;
> @@ -615,7 +635,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  		/* Each loop through, defrag targetrange percent of the files */
>  		count = (buflenout * targetrange) / 100;
>  
> -		qsort((char *)buf, buflenout, sizeof(struct xfs_bstat), cmp);
> +		qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
>  
>  		for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
>  			/* Do some obvious checks now */
> @@ -623,7 +643,14 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  			     (p->bs_extents < 2))
>  				continue;
>  
> -			fd = jdm_open(fshandlep, p, O_RDWR|O_DIRECT);
> +			ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);

ew.  In the long run, I guess I'd rather convert these to take v5 when needed
but alas.  I guess that has xfsdump implications too.  :(

> +			if (ret) {
> +				fsrprintf(_("bstat conversion error: %s\n"),
> +						strerror(ret));
> +				continue;
> +			}

... but then we wouldn't potential fail on bstats w/ big numbers :(

Oh well, another day.

> +
> +			fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
>  			if (fd < 0) {
>  				/* This probably means the file was
>  				 * removed while in progress of handling
> @@ -641,7 +668,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  			/* Get a tmp file name */
>  			tname = tmp_next(mntdir);
>  
> -			ret = fsrfile_common(fname, tname, mntdir, fd, p);
> +			ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
>  
>  			leftoffino = p->bs_ino;
>  
> @@ -653,6 +680,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  			}
>  		}
>  		if (endtime && endtime < time(NULL)) {
> +			free(breq);
>  			tmp_close(mntdir);
>  			xfd_close(&fsxfd);
>  			fsrall_cleanup(1);
> @@ -662,6 +690,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
>  	if (ret)
>  		fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
>  out0:
> +	free(breq);
>  	tmp_close(mntdir);
>  	xfd_close(&fsxfd);
>  	free(fshandlep);
> @@ -701,6 +730,7 @@ fsrfile(
>  	xfs_ino_t		ino)
>  {
>  	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
> +	struct xfs_bulkstat	bulkstat;
>  	struct xfs_bstat	statbuf;
>  	jdm_fshandle_t		*fshandlep;
>  	int			fd = -1;
> @@ -725,12 +755,18 @@ fsrfile(
>  		goto out;
>  	}
>  
> -	error = xfrog_bulkstat_single(&fsxfd, ino, &statbuf);
> +	error = xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
>  	if (error) {
>  		fsrprintf(_("unable to get bstat on %s: %s\n"),
>  			fname, strerror(error));
>  		goto out;
>  	}
> +	error = xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
> +	if (error) {
> +		fsrprintf(_("bstat conversion error on %s: %s\n"),
> +			fname, strerror(error));
> +		goto out;
> +	}
>  
>  	fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
>  	if (fd < 0) {
> @@ -951,7 +987,7 @@ fsr_setup_attr_fork(
>  
>  	i = 0;
>  	do {
> -		struct xfs_bstat tbstat;
> +		struct xfs_bulkstat	tbstat;
>  		char		name[64];
>  		int		ret;
>  
> @@ -960,7 +996,7 @@ fsr_setup_attr_fork(
>  		 * this to compare against the target and determine what we
>  		 * need to do.
>  		 */
> -		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, &tbstat);
> +		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0, &tbstat);
>  		if (ret) {
>  			fsrprintf(_("unable to get bstat on temp file: %s\n"),
>  						strerror(ret));
> diff --git a/io/open.c b/io/open.c
> index 99ca0dd3..e0e7fb3e 100644
> --- a/io/open.c
> +++ b/io/open.c
> @@ -723,8 +723,7 @@ inode_f(
>  	int			argc,
>  	char			**argv)
>  {
> -	struct xfs_bstat	bstat;
> -	uint32_t		count = 0;
> +	struct xfs_bulkstat	bulkstat;
>  	uint64_t		result_ino = 0;
>  	uint64_t		userino = NULLFSINO;
>  	char			*p;
> @@ -775,26 +774,40 @@ inode_f(
>  		}
>  	} else if (ret_next) {
>  		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
> +		struct xfs_bulkstat_req	*breq;
> +
> +		/*
> +		 * The -n option means that the caller wants to know the number
> +		 * of the next allocated inode, so we need to increment here.
> +		 */
> +		breq = xfrog_bulkstat_alloc_req(1, userino + 1);
> +		if (!breq) {
> +			perror("alloc bulkstat");
> +			exitcode = 1;
> +			return 0;
> +		}
>  
>  		/* get next inode */
> -		ret = xfrog_bulkstat(&xfd, &userino, 1, &bstat, &count);
> +		ret = xfrog_bulkstat(&xfd, breq);
>  		if (ret) {
>  			errno = ret;
>  			perror("bulkstat");
> +			free(breq);
>  			exitcode = 1;
>  			return 0;
>  		}
>  
>  		/* The next inode in use, or 0 if none */
> -		if (count)
> -			result_ino = bstat.bs_ino;
> +		if (breq->hdr.ocount)
> +			result_ino = breq->bulkstat[0].bs_ino;
>  		else
>  			result_ino = 0;
> +		free(breq);
>  	} else {
>  		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
>  
>  		/* get this inode */
> -		ret = xfrog_bulkstat_single(&xfd, userino, &bstat);
> +		ret = xfrog_bulkstat_single(&xfd, userino, 0, &bulkstat);
>  		if (ret == EINVAL) {
>  			/* Not in use */
>  			result_ino = 0;
> @@ -804,7 +817,7 @@ inode_f(
>  			exitcode = 1;
>  			return 0;
>  		} else {
> -			result_ino = bstat.bs_ino;
> +			result_ino = bulkstat.bs_ino;
>  		}
>  	}
>  
> diff --git a/io/swapext.c b/io/swapext.c
> index 2b4918f8..1139cf21 100644
> --- a/io/swapext.c
> +++ b/io/swapext.c
> @@ -28,6 +28,7 @@ swapext_f(
>  	char			**argv)
>  {
>  	struct xfs_fd		fxfd = XFS_FD_INIT(file->fd);
> +	struct xfs_bulkstat	bulkstat;
>  	int			fd;
>  	int			error;
>  	struct xfs_swapext	sx;
> @@ -48,12 +49,18 @@ swapext_f(
>  		goto out;
>  	}
>  
> -	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, &sx.sx_stat);
> +	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, 0, &bulkstat);
>  	if (error) {
>  		errno = error;
>  		perror("bulkstat");
>  		goto out;
>  	}
> +	error = xfrog_bulkstat_v5_to_v1(&fxfd, &sx.sx_stat, &bulkstat);
> +	if (error) {
> +		errno = error;
> +		perror("bulkstat conversion");
> +		goto out;
> +	}
>  	sx.sx_version = XFS_SX_VERSION;
>  	sx.sx_fdtarget = file->fd;
>  	sx.sx_fdtmp = fd;
> diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
> index fa10f298..300963f1 100644
> --- a/libfrog/bulkstat.c
> +++ b/libfrog/bulkstat.c
> @@ -3,55 +3,438 @@
>   * Copyright (C) 2019 Oracle.  All Rights Reserved.
>   * Author: Darrick J. Wong <darrick.wong@oracle.com>
>   */
> +#include <string.h>
> +#include <strings.h>
>  #include "xfs.h"
>  #include "fsgeom.h"
>  #include "bulkstat.h"
>  
> +/*
> + * Wrapper functions for BULKSTAT and INUMBERS
> + * ===========================================
> + *
> + * The functions in this file are thin wrappers around the most recent version
> + * of the BULKSTAT and INUMBERS ioctls.  BULKSTAT is used to query XFS-specific
> + * stat information about a group of inodes.  INUMBERS is used to query
> + * allocation information about batches of XFS inodes.
> + *
> + * At the moment, the public xfrog_* functions provide all functionality of the
> + * V5 interface.  If the V5 interface is not available on the running kernel,
> + * the functions will emulate them as best they can with previous versions of
> + * the interface (currently V1).  If emulation is not possible, EINVAL will be
> + * returned.
> + *
> + * The XFROG_FLAG_BULKSTAT_FORCE_V[15] flags can be used to force use of a
> + * particular version of the kernel interface for testing.
> + */
> +
> +/*
> + * Grab the fs geometry information that is needed to needed to emulate v5 with
> + * v1 interfaces.
> + */
> +static inline int
> +xfrog_bulkstat_prep_v1_emulation(
> +	struct xfs_fd		*xfd)
> +{
> +	if (xfd->fsgeom.blocksize > 0)
> +		return 0;
> +
> +	return xfd_prepare_geometry(xfd);
> +}
> +
> +/* Bulkstat a single inode using v5 ioctl. */
> +static int
> +xfrog_bulkstat_single5(
> +	struct xfs_fd			*xfd,
> +	uint64_t			ino,
> +	unsigned int			flags,
> +	struct xfs_bulkstat		*bulkstat)
> +{
> +	struct xfs_bulkstat_req		*req;
> +	int				ret;
> +
> +	if (flags & ~(XFS_BULK_IREQ_SPECIAL))
> +		return EINVAL;
> +
> +	req = xfrog_bulkstat_alloc_req(1, ino);
> +	if (!req)
> +		return ENOMEM;
> +
> +	req->hdr.flags = flags;
> +	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
> +	if (ret) {
> +		ret = errno;
> +		goto free;
> +	}
> +
> +	if (req->hdr.ocount == 0) {
> +		ret = ENOENT;
> +		goto free;
> +	}
> +
> +	memcpy(bulkstat, req->bulkstat, sizeof(struct xfs_bulkstat));
> +free:
> +	free(req);
> +	return ret;
> +}
> +
> +/* Bulkstat a single inode using v1 ioctl. */
> +static int
> +xfrog_bulkstat_single1(
> +	struct xfs_fd			*xfd,
> +	uint64_t			ino,
> +	unsigned int			flags,
> +	struct xfs_bulkstat		*bulkstat)
> +{
> +	struct xfs_bstat		bstat;
> +	struct xfs_fsop_bulkreq		bulkreq = { 0 };
> +	int				error;
> +
> +	if (flags)
> +		return EINVAL;
> +
> +	error = xfrog_bulkstat_prep_v1_emulation(xfd);
> +	if (error)
> +		return error;
> +
> +	bulkreq.lastip = (__u64 *)&ino;
> +	bulkreq.icount = 1;
> +	bulkreq.ubuffer = &bstat;
> +	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
> +	if (error)
> +		return errno;
> +
> +	xfrog_bulkstat_v1_to_v5(xfd, bulkstat, &bstat);
> +	return 0;
> +}
> +
>  /* Bulkstat a single inode.  Returns zero or a positive error code. */
>  int
>  xfrog_bulkstat_single(
> +	struct xfs_fd			*xfd,
> +	uint64_t			ino,
> +	unsigned int			flags,
> +	struct xfs_bulkstat		*bulkstat)
> +{
> +	int				error;
> +
> +	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
> +		goto try_v1;
> +
> +	error = xfrog_bulkstat_single5(xfd, ino, flags, bulkstat);
> +	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
> +		return error;
> +
> +	/* If the v5 ioctl wasn't found, we punt to v1. */
> +	switch (error) {
> +	case EOPNOTSUPP:
> +	case ENOTTY:
> +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> +		break;
> +	}
> +
> +try_v1:
> +	return xfrog_bulkstat_single1(xfd, ino, flags, bulkstat);
> +}
> +
> +/*
> + * Set up the necessary control structures to emulate a V5 bulk request ioctl
> + * by calling a V1 bulk request ioctl.  This enables callers to run on older
> + * kernels.
> + *
> + * Returns 0 if the emulation should proceed; ECANCELED if there are no
> + * records; or a positive error code.
> + */
> +static int
> +xfrog_bulk_req_v1_setup(
>  	struct xfs_fd		*xfd,
> -	uint64_t		ino,
> -	struct xfs_bstat	*ubuffer)
> +	struct xfs_bulk_ireq	*hdr,
> +	struct xfs_fsop_bulkreq	*bulkreq,
> +	size_t			rec_size)
> +{
> +	void			*buf;
> +
> +	if (hdr->flags & XFS_BULK_IREQ_AGNO) {
> +		uint32_t	agno = cvt_ino_to_agno(xfd, hdr->ino);
> +
> +		if (hdr->ino == 0)
> +			hdr->ino = cvt_agino_to_ino(xfd, hdr->agno, 0);
> +		else if (agno < hdr->agno)
> +			return EINVAL;
> +		else if (agno > hdr->agno)
> +			goto no_results;
> +	}
> +
> +	if (cvt_ino_to_agno(xfd, hdr->ino) > xfd->fsgeom.agcount)
> +		goto no_results;
> +
> +	buf = malloc(hdr->icount * rec_size);
> +	if (!buf)
> +		return errno;
> +
> +	if (hdr->ino)
> +		hdr->ino--;
> +	bulkreq->lastip = (__u64 *)&hdr->ino,
> +	bulkreq->icount = hdr->icount,
> +	bulkreq->ocount = (__s32 *)&hdr->ocount,
> +	bulkreq->ubuffer = buf;
> +	return 0;
> +
> +no_results:
> +	hdr->ocount = 0;
> +	return ECANCELED;
> +}
> +
> +/*
> + * Clean up after using a V1 bulk request to emulate a V5 bulk request call.
> + *
> + * If the ioctl was successful, we need to convert the returned V1-format bulk
> + * request data into the V5-format bulk request data and copy it into the
> + * caller's buffer.  We also need to free all resources allocated during the
> + * setup setup.
> + */
> +static int
> +xfrog_bulk_req_v1_cleanup(
> +	struct xfs_fd		*xfd,
> +	struct xfs_bulk_ireq	*hdr,
> +	struct xfs_fsop_bulkreq	*bulkreq,
> +	size_t			v1_rec_size,
> +	uint64_t		(*v1_ino)(void *v1_rec),
> +	void			*v5_records,
> +	size_t			v5_rec_size,
> +	void			(*cvt)(struct xfs_fd *xfd, void *v5, void *v1),
> +	unsigned int		startino_adj,
> +	int			error)
> +{
> +	void			*v1_rec = bulkreq->ubuffer;
> +	void			*v5_rec = v5_records;
> +	unsigned int		i;
> +
> +	if (error == ECANCELED) {
> +		error = 0;
> +		goto free;
> +	}
> +	if (error)
> +		goto free;
> +
> +	/*
> +	 * Convert each record from v1 to v5 format, keeping the startino
> +	 * value up to date and (if desired) stopping at the end of the
> +	 * AG.
> +	 */
> +	for (i = 0;
> +	     i < hdr->ocount;
> +	     i++, v1_rec += v1_rec_size, v5_rec += v5_rec_size) {
> +		uint64_t	ino = v1_ino(v1_rec);
> +
> +		/* Stop if we hit a different AG. */
> +		if ((hdr->flags & XFS_BULK_IREQ_AGNO) &&
> +		    cvt_ino_to_agno(xfd, ino) != hdr->agno) {
> +			hdr->ocount = i;
> +			break;
> +		}
> +		cvt(xfd, v5_rec, v1_rec);
> +		hdr->ino = ino + startino_adj;
> +	}
> +
> +free:
> +	free(bulkreq->ubuffer);
> +	return error;
> +}
> +
> +static uint64_t xfrog_bstat_ino(void *v1_rec)
> +{
> +	return ((struct xfs_bstat *)v1_rec)->bs_ino;
> +}
> +
> +static void xfrog_bstat_cvt(struct xfs_fd *xfd, void *v5, void *v1)
> +{
> +	xfrog_bulkstat_v1_to_v5(xfd, v5, v1);
> +}
> +
> +/* Bulkstat a bunch of inodes using the v5 interface. */
> +static int
> +xfrog_bulkstat5(
> +	struct xfs_fd		*xfd,
> +	struct xfs_bulkstat_req	*req)
>  {
> -	__u64			i = ino;
> -	struct xfs_fsop_bulkreq	bulkreq = {
> -		.lastip		= &i,
> -		.icount		= 1,
> -		.ubuffer	= ubuffer,
> -		.ocount		= NULL,
> -	};
>  	int			ret;
>  
> -	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
> +	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
>  	if (ret)
>  		return errno;
>  	return 0;
>  }
>  
> +/* Bulkstat a bunch of inodes using the v1 interface. */
> +static int
> +xfrog_bulkstat1(
> +	struct xfs_fd		*xfd,
> +	struct xfs_bulkstat_req	*req)
> +{
> +	struct xfs_fsop_bulkreq	bulkreq = { 0 };
> +	int			error;
> +
> +	error = xfrog_bulkstat_prep_v1_emulation(xfd);
> +	if (error)
> +		return error;
> +
> +	error = xfrog_bulk_req_v1_setup(xfd, &req->hdr, &bulkreq,
> +			sizeof(struct xfs_bstat));
> +	if (error == ECANCELED)
> +		goto out_teardown;
> +	if (error)
> +		return error;
> +
> +	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
> +	if (error)
> +		error = errno;
> +
> +out_teardown:
> +	return xfrog_bulk_req_v1_cleanup(xfd, &req->hdr, &bulkreq,
> +			sizeof(struct xfs_bstat), xfrog_bstat_ino,
> +			&req->bulkstat, sizeof(struct xfs_bulkstat),
> +			xfrog_bstat_cvt, 1, error);
> +}
> +
>  /* Bulkstat a bunch of inodes.  Returns zero or a positive error code. */
>  int
>  xfrog_bulkstat(
>  	struct xfs_fd		*xfd,
> -	uint64_t		*lastino,
> -	uint32_t		icount,
> -	struct xfs_bstat	*ubuffer,
> -	uint32_t		*ocount)
> +	struct xfs_bulkstat_req	*req)
>  {
> -	struct xfs_fsop_bulkreq	bulkreq = {
> -		.lastip		= (__u64 *)lastino,
> -		.icount		= icount,
> -		.ubuffer	= ubuffer,
> -		.ocount		= (__s32 *)ocount,
> -	};
> -	int			ret;
> +	int			error;
>  
> -	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
> -	if (ret)
> -		return errno;
> +	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
> +		goto try_v1;
> +
> +	error = xfrog_bulkstat5(xfd, req);
> +	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
> +		return error;
> +
> +	/* If the v5 ioctl wasn't found, we punt to v1. */
> +	switch (error) {
> +	case EOPNOTSUPP:
> +	case ENOTTY:
> +		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
> +		break;
> +	}
> +
> +try_v1:
> +	return xfrog_bulkstat1(xfd, req);
> +}
> +
> +static bool
> +time_too_big(
> +	uint64_t	time)
> +{
> +	time_t		TIME_MAX;
> +
> +	memset(&TIME_MAX, 0xFF, sizeof(TIME_MAX));
> +	return time > TIME_MAX;
> +}
> +
> +/* Convert bulkstat data from v5 format to v1 format. */
> +int
> +xfrog_bulkstat_v5_to_v1(
> +	struct xfs_fd			*xfd,
> +	struct xfs_bstat		*bs1,
> +	const struct xfs_bulkstat	*bs5)
> +{
> +	if (bs5->bs_aextents > UINT16_MAX ||
> +	    cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks) > UINT32_MAX ||
> +	    cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks) > UINT32_MAX ||
> +	    time_too_big(bs5->bs_atime) ||
> +	    time_too_big(bs5->bs_ctime) ||
> +	    time_too_big(bs5->bs_mtime))
> +		return ERANGE;
> +
> +	bs1->bs_ino = bs5->bs_ino;
> +	bs1->bs_mode = bs5->bs_mode;
> +	bs1->bs_nlink = bs5->bs_nlink;
> +	bs1->bs_uid = bs5->bs_uid;
> +	bs1->bs_gid = bs5->bs_gid;
> +	bs1->bs_rdev = bs5->bs_rdev;
> +	bs1->bs_blksize = bs5->bs_blksize;
> +	bs1->bs_size = bs5->bs_size;
> +	bs1->bs_atime.tv_sec = bs5->bs_atime;
> +	bs1->bs_mtime.tv_sec = bs5->bs_mtime;
> +	bs1->bs_ctime.tv_sec = bs5->bs_ctime;
> +	bs1->bs_atime.tv_nsec = bs5->bs_atime_nsec;
> +	bs1->bs_mtime.tv_nsec = bs5->bs_mtime_nsec;
> +	bs1->bs_ctime.tv_nsec = bs5->bs_ctime_nsec;
> +	bs1->bs_blocks = bs5->bs_blocks;
> +	bs1->bs_xflags = bs5->bs_xflags;
> +	bs1->bs_extsize = cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks);
> +	bs1->bs_extents = bs5->bs_extents;
> +	bs1->bs_gen = bs5->bs_gen;
> +	bs1->bs_projid_lo = bs5->bs_projectid & 0xFFFF;
> +	bs1->bs_forkoff = bs5->bs_forkoff;
> +	bs1->bs_projid_hi = bs5->bs_projectid >> 16;
> +	bs1->bs_sick = bs5->bs_sick;
> +	bs1->bs_checked = bs5->bs_checked;
> +	bs1->bs_cowextsize = cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks);
> +	bs1->bs_dmevmask = 0;
> +	bs1->bs_dmstate = 0;
> +	bs1->bs_aextents = bs5->bs_aextents;
>  	return 0;
>  }
>  
> +/* Convert bulkstat data from v1 format to v5 format. */
> +void
> +xfrog_bulkstat_v1_to_v5(
> +	struct xfs_fd			*xfd,
> +	struct xfs_bulkstat		*bs5,
> +	const struct xfs_bstat		*bs1)
> +{
> +	memset(bs5, 0, sizeof(*bs5));
> +	bs5->bs_version = XFS_BULKSTAT_VERSION_V1;
> +
> +	bs5->bs_ino = bs1->bs_ino;
> +	bs5->bs_mode = bs1->bs_mode;
> +	bs5->bs_nlink = bs1->bs_nlink;
> +	bs5->bs_uid = bs1->bs_uid;
> +	bs5->bs_gid = bs1->bs_gid;
> +	bs5->bs_rdev = bs1->bs_rdev;
> +	bs5->bs_blksize = bs1->bs_blksize;
> +	bs5->bs_size = bs1->bs_size;
> +	bs5->bs_atime = bs1->bs_atime.tv_sec;
> +	bs5->bs_mtime = bs1->bs_mtime.tv_sec;
> +	bs5->bs_ctime = bs1->bs_ctime.tv_sec;
> +	bs5->bs_atime_nsec = bs1->bs_atime.tv_nsec;
> +	bs5->bs_mtime_nsec = bs1->bs_mtime.tv_nsec;
> +	bs5->bs_ctime_nsec = bs1->bs_ctime.tv_nsec;
> +	bs5->bs_blocks = bs1->bs_blocks;
> +	bs5->bs_xflags = bs1->bs_xflags;
> +	bs5->bs_extsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_extsize);
> +	bs5->bs_extents = bs1->bs_extents;
> +	bs5->bs_gen = bs1->bs_gen;
> +	bs5->bs_projectid = bstat_get_projid(bs1);
> +	bs5->bs_forkoff = bs1->bs_forkoff;
> +	bs5->bs_sick = bs1->bs_sick;
> +	bs5->bs_checked = bs1->bs_checked;
> +	bs5->bs_cowextsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_cowextsize);
> +	bs5->bs_aextents = bs1->bs_aextents;
> +}
> +
> +/* Allocate a bulkstat request.  On error returns NULL and sets errno. */
> +struct xfs_bulkstat_req *
> +xfrog_bulkstat_alloc_req(
> +	uint32_t		nr,
> +	uint64_t		startino)
> +{
> +	struct xfs_bulkstat_req	*breq;
> +
> +	breq = calloc(1, XFS_BULKSTAT_REQ_SIZE(nr));
> +	if (!breq)
> +		return NULL;
> +
> +	breq->hdr.icount = nr;
> +	breq->hdr.ino = startino;
> +
> +	return breq;
> +}
> +
>  /*
>   * Query inode allocation bitmask information.  Returns zero or a positive
>   * error code.
> diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
> index 83ac0e37..bbbc69a2 100644
> --- a/libfrog/bulkstat.h
> +++ b/libfrog/bulkstat.h
> @@ -8,10 +8,16 @@
>  
>  /* Bulkstat wrappers */
>  struct xfs_bstat;
> -int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino,
> -		struct xfs_bstat *ubuffer);
> -int xfrog_bulkstat(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
> -		struct xfs_bstat *ubuffer, uint32_t *ocount);
> +int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino, unsigned int flags,
> +		struct xfs_bulkstat *bulkstat);
> +int xfrog_bulkstat(struct xfs_fd *xfd, struct xfs_bulkstat_req *req);
> +
> +struct xfs_bulkstat_req *xfrog_bulkstat_alloc_req(uint32_t nr,
> +		uint64_t startino);
> +int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
> +		const struct xfs_bulkstat *bstat);
> +void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
> +		const struct xfs_bstat *bs1);
>  
>  struct xfs_inogrp;
>  int xfrog_inumbers(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
> diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
> index 55b14c2b..ca38324e 100644
> --- a/libfrog/fsgeom.h
> +++ b/libfrog/fsgeom.h
> @@ -39,8 +39,17 @@ struct xfs_fd {
>  
>  	/* log2 of sb_blocksize / sb_sectsize */
>  	unsigned int		blkbb_log;
> +
> +	/* XFROG_FLAG_* state flags */
> +	unsigned int		flags;
>  };
>  
> +/* Only use v1 bulkstat/inumbers ioctls. */
> +#define XFROG_FLAG_BULKSTAT_FORCE_V1	(1 << 0)
> +
> +/* Only use v5 bulkstat/inumbers ioctls. */
> +#define XFROG_FLAG_BULKSTAT_FORCE_V5	(1 << 1)
> +
>  /* Static initializers */
>  #define XFS_FD_INIT(_fd)	{ .fd = (_fd), }
>  #define XFS_FD_INIT_EMPTY	XFS_FD_INIT(-1)
> diff --git a/quota/quot.c b/quota/quot.c
> index 686b2726..7edfad16 100644
> --- a/quota/quot.c
> +++ b/quota/quot.c
> @@ -69,7 +69,7 @@ quot_help(void)
>  
>  static void
>  quot_bulkstat_add(
> -	struct xfs_bstat *p,
> +	struct xfs_bulkstat	*p,
>  	uint		flags)
>  {
>  	du_t		*dp;
> @@ -93,7 +93,7 @@ quot_bulkstat_add(
>  	}
>  	for (i = 0; i < 3; i++) {
>  		id = (i == 0) ? p->bs_uid : ((i == 1) ?
> -			p->bs_gid : bstat_get_projid(p));
> +			p->bs_gid : p->bs_projectid);
>  		hp = &duhash[i][id % DUHASH];
>  		for (dp = *hp; dp; dp = dp->next)
>  			if (dp->id == id)
> @@ -113,11 +113,11 @@ quot_bulkstat_add(
>  		}
>  		dp->blocks += size;
>  
> -		if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
> +		if (now - p->bs_atime > 30 * (60*60*24))
>  			dp->blocks30 += size;
> -		if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
> +		if (now - p->bs_atime > 60 * (60*60*24))
>  			dp->blocks60 += size;
> -		if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
> +		if (now - p->bs_atime > 90 * (60*60*24))
>  			dp->blocks90 += size;
>  		dp->nfiles++;
>  	}
> @@ -129,9 +129,7 @@ quot_bulkstat_mount(
>  	unsigned int		flags)
>  {
>  	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
> -	struct xfs_bstat	*buf;
> -	uint64_t		last = 0;
> -	uint32_t		count;
> +	struct xfs_bulkstat_req	*breq;
>  	int			i, sts, ret;
>  	du_t			**dp;
>  
> @@ -154,25 +152,24 @@ quot_bulkstat_mount(
>  		return;
>  	}
>  
> -	buf = (struct xfs_bstat *)calloc(NBSTAT, sizeof(struct xfs_bstat));
> -	if (!buf) {
> +	breq = xfrog_bulkstat_alloc_req(NBSTAT, 0);
> +	if (!breq) {
>  		perror("calloc");
>  		xfd_close(&fsxfd);
>  		return;
>  	}
>  
> -	while ((sts = xfrog_bulkstat(&fsxfd, &last, NBSTAT, buf,
> -				&count)) == 0) {
> -		if (count == 0)
> +	while ((sts = xfrog_bulkstat(&fsxfd, breq)) == 0) {
> +		if (breq->hdr.ocount == 0)
>  			break;
> -		for (i = 0; i < count; i++)
> -			quot_bulkstat_add(&buf[i], flags);
> +		for (i = 0; i < breq->hdr.ocount; i++)
> +			quot_bulkstat_add(&breq->bulkstat[i], flags);
>  	}
>  	if (sts < 0) {
>  		errno = sts;
>  		perror("XFS_IOC_FSBULKSTAT");
>  	}
> -	free(buf);
> +	free(breq);
>  	xfd_close(&fsxfd);
>  }
>  
> diff --git a/scrub/inodes.c b/scrub/inodes.c
> index 580a845e..2112c9d1 100644
> --- a/scrub/inodes.c
> +++ b/scrub/inodes.c
> @@ -50,9 +50,9 @@ static void
>  xfs_iterate_inodes_range_check(
>  	struct scrub_ctx	*ctx,
>  	struct xfs_inogrp	*inogrp,
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
> -	struct xfs_bstat	*bs;
> +	struct xfs_bulkstat	*bs;
>  	int			i;
>  	int			error;
>  
> @@ -66,9 +66,9 @@ xfs_iterate_inodes_range_check(
>  
>  		/* Load the one inode. */
>  		error = xfrog_bulkstat_single(&ctx->mnt,
> -				inogrp->xi_startino + i, bs);
> +				inogrp->xi_startino + i, 0, bs);
>  		if (error || bs->bs_ino != inogrp->xi_startino + i) {
> -			memset(bs, 0, sizeof(struct xfs_bstat));
> +			memset(bs, 0, sizeof(struct xfs_bulkstat));
>  			bs->bs_ino = inogrp->xi_startino + i;
>  			bs->bs_blksize = ctx->mnt_sv.f_frsize;
>  		}
> @@ -93,41 +93,41 @@ xfs_iterate_inodes_range(
>  {
>  	struct xfs_handle	handle;
>  	struct xfs_inogrp	inogrp;
> -	struct xfs_bstat	bstat[XFS_INODES_PER_CHUNK];
> +	struct xfs_bulkstat_req	*breq;
>  	char			idescr[DESCR_BUFSZ];
> -	struct xfs_bstat	*bs;
> +	struct xfs_bulkstat	*bs;
>  	uint64_t		igrp_ino;
> -	uint64_t		ino;
> -	uint32_t		bulklen = 0;
>  	uint32_t		igrplen = 0;
>  	bool			moveon = true;
>  	int			i;
>  	int			error;
>  	int			stale_count = 0;
>  
> -
> -	memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat));
> -
>  	memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid));
>  	handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
>  			sizeof(handle.ha_fid.fid_len);
>  	handle.ha_fid.fid_pad = 0;
>  
> +	breq = xfrog_bulkstat_alloc_req(XFS_INODES_PER_CHUNK, 0);
> +	if (!breq) {
> +		str_info(ctx, descr, _("Insufficient memory; giving up."));
> +		return false;
> +	}
> +
>  	/* Find the inode chunk & alloc mask */
>  	igrp_ino = first_ino;
>  	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
>  	while (!error && igrplen) {
> -		/* Load the inodes. */
> -		ino = inogrp.xi_startino - 1;
> -
>  		/*
>  		 * We can have totally empty inode chunks on filesystems where
>  		 * there are more than 64 inodes per block.  Skip these.
>  		 */
>  		if (inogrp.xi_alloccount == 0)
>  			goto igrp_retry;
> -		error = xfrog_bulkstat(&ctx->mnt, &ino, inogrp.xi_alloccount,
> -				bstat, &bulklen);
> +
> +		breq->hdr.ino = inogrp.xi_startino;
> +		breq->hdr.icount = inogrp.xi_alloccount;
> +		error = xfrog_bulkstat(&ctx->mnt, breq);
>  		if (error) {
>  			char	errbuf[DESCR_BUFSZ];
>  
> @@ -135,10 +135,12 @@ xfs_iterate_inodes_range(
>  						errbuf, DESCR_BUFSZ));
>  		}
>  
> -		xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
> +		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
>  
>  		/* Iterate all the inodes. */
> -		for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
> +		for (i = 0, bs = breq->bulkstat;
> +		     i < inogrp.xi_alloccount;
> +		     i++, bs++) {
>  			if (bs->bs_ino > last_ino)
>  				goto out;

leaks the breq here, no?

>  
> @@ -184,6 +186,7 @@ _("Changed too many times during scan; giving up."));
>  		str_liberror(ctx, error, descr);
>  		moveon = false;
>  	}
> +	free(breq);
>  out:

maybe free should be here?

>  	return moveon;
>  }
> diff --git a/scrub/inodes.h b/scrub/inodes.h
> index 631848c3..3341c6d9 100644
> --- a/scrub/inodes.h
> +++ b/scrub/inodes.h
> @@ -7,7 +7,7 @@
>  #define XFS_SCRUB_INODES_H_
>  
>  typedef int (*xfs_inode_iter_fn)(struct scrub_ctx *ctx,
> -		struct xfs_handle *handle, struct xfs_bstat *bs, void *arg);
> +		struct xfs_handle *handle, struct xfs_bulkstat *bs, void *arg);
>  
>  #define XFS_ITERATE_INODES_ABORT	(-1)
>  bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
> diff --git a/scrub/phase3.c b/scrub/phase3.c
> index 81c64cd1..a32d1ced 100644
> --- a/scrub/phase3.c
> +++ b/scrub/phase3.c
> @@ -30,7 +30,7 @@ xfs_scrub_fd(
>  	struct scrub_ctx	*ctx,
>  	bool			(*fn)(struct scrub_ctx *ctx, uint64_t ino,
>  				      uint32_t gen, struct xfs_action_list *a),
> -	struct xfs_bstat	*bs,
> +	struct xfs_bulkstat	*bs,
>  	struct xfs_action_list	*alist)
>  {
>  	return fn(ctx, bs->bs_ino, bs->bs_gen, alist);
> @@ -45,7 +45,7 @@ struct scrub_inode_ctx {
>  static void
>  xfs_scrub_inode_vfs_error(
>  	struct scrub_ctx	*ctx,
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
>  	char			descr[DESCR_BUFSZ];
>  	xfs_agnumber_t		agno;
> @@ -65,7 +65,7 @@ static int
>  xfs_scrub_inode(
>  	struct scrub_ctx	*ctx,
>  	struct xfs_handle	*handle,
> -	struct xfs_bstat	*bstat,
> +	struct xfs_bulkstat	*bstat,
>  	void			*arg)
>  {
>  	struct xfs_action_list	alist;
> diff --git a/scrub/phase5.c b/scrub/phase5.c
> index 3ff34251..99cd51b2 100644
> --- a/scrub/phase5.c
> +++ b/scrub/phase5.c
> @@ -80,7 +80,7 @@ xfs_scrub_scan_dirents(
>  	struct scrub_ctx	*ctx,
>  	const char		*descr,
>  	int			*fd,
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
>  	struct unicrash		*uc = NULL;
>  	DIR			*dir;
> @@ -140,7 +140,7 @@ xfs_scrub_scan_fhandle_namespace_xattrs(
>  	struct scrub_ctx		*ctx,
>  	const char			*descr,
>  	struct xfs_handle		*handle,
> -	struct xfs_bstat		*bstat,
> +	struct xfs_bulkstat		*bstat,
>  	const struct attrns_decode	*attr_ns)
>  {
>  	struct attrlist_cursor		cur;
> @@ -200,7 +200,7 @@ xfs_scrub_scan_fhandle_xattrs(
>  	struct scrub_ctx		*ctx,
>  	const char			*descr,
>  	struct xfs_handle		*handle,
> -	struct xfs_bstat		*bstat)
> +	struct xfs_bulkstat		*bstat)
>  {
>  	const struct attrns_decode	*ns;
>  	bool				moveon = true;
> @@ -228,7 +228,7 @@ static int
>  xfs_scrub_connections(
>  	struct scrub_ctx	*ctx,
>  	struct xfs_handle	*handle,
> -	struct xfs_bstat	*bstat,
> +	struct xfs_bulkstat	*bstat,
>  	void			*arg)
>  {
>  	bool			*pmoveon = arg;
> diff --git a/scrub/phase6.c b/scrub/phase6.c
> index 506e75d2..b41f90e0 100644
> --- a/scrub/phase6.c
> +++ b/scrub/phase6.c
> @@ -172,7 +172,7 @@ static int
>  xfs_report_verify_inode(
>  	struct scrub_ctx		*ctx,
>  	struct xfs_handle		*handle,
> -	struct xfs_bstat		*bstat,
> +	struct xfs_bulkstat		*bstat,
>  	void				*arg)
>  {
>  	char				descr[DESCR_BUFSZ];
> diff --git a/scrub/unicrash.c b/scrub/unicrash.c
> index 17e8f34f..b02c5658 100644
> --- a/scrub/unicrash.c
> +++ b/scrub/unicrash.c
> @@ -432,7 +432,7 @@ unicrash_init(
>   */
>  static bool
>  is_only_root_writable(
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
>  	if (bstat->bs_uid != 0 || bstat->bs_gid != 0)
>  		return false;
> @@ -444,7 +444,7 @@ bool
>  unicrash_dir_init(
>  	struct unicrash		**ucp,
>  	struct scrub_ctx	*ctx,
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
>  	/*
>  	 * Assume 64 bytes per dentry, clamp buckets between 16 and 64k.
> @@ -459,7 +459,7 @@ bool
>  unicrash_xattr_init(
>  	struct unicrash		**ucp,
>  	struct scrub_ctx	*ctx,
> -	struct xfs_bstat	*bstat)
> +	struct xfs_bulkstat	*bstat)
>  {
>  	/* Assume 16 attributes per extent for lack of a better idea. */
>  	return unicrash_init(ucp, ctx, false, 16 * (1 + bstat->bs_aextents),
> diff --git a/scrub/unicrash.h b/scrub/unicrash.h
> index fb8f5f72..feb9cc86 100644
> --- a/scrub/unicrash.h
> +++ b/scrub/unicrash.h
> @@ -14,9 +14,9 @@ struct unicrash;
>  struct dirent;
>  
>  bool unicrash_dir_init(struct unicrash **ucp, struct scrub_ctx *ctx,
> -		struct xfs_bstat *bstat);
> +		struct xfs_bulkstat *bstat);
>  bool unicrash_xattr_init(struct unicrash **ucp, struct scrub_ctx *ctx,
> -		struct xfs_bstat *bstat);
> +		struct xfs_bulkstat *bstat);
>  bool unicrash_fs_label_init(struct unicrash **ucp, struct scrub_ctx *ctx);
>  void unicrash_free(struct unicrash *uc);
>  bool unicrash_check_dir_name(struct unicrash *uc, const char *descr,
> diff --git a/spaceman/health.c b/spaceman/health.c
> index a8bd3f3e..b195a229 100644
> --- a/spaceman/health.c
> +++ b/spaceman/health.c
> @@ -208,7 +208,7 @@ report_inode_health(
>  	unsigned long long	ino,
>  	const char		*descr)
>  {
> -	struct xfs_bstat	bs;
> +	struct xfs_bulkstat	bs;
>  	char			d[256];
>  	int			ret;
>  
> @@ -217,7 +217,7 @@ report_inode_health(
>  		descr = d;
>  	}
>  
> -	ret = xfrog_bulkstat_single(&file->xfd, ino, &bs);
> +	ret = xfrog_bulkstat_single(&file->xfd, ino, 0, &bs);
>  	if (ret) {
>  		errno = ret;
>  		perror(descr);
> @@ -266,11 +266,10 @@ static int
>  report_bulkstat_health(
>  	xfs_agnumber_t		agno)
>  {
> -	struct xfs_bstat	bstat[BULKSTAT_NR];
> +	struct xfs_bulkstat_req	*breq;
>  	char			descr[256];
>  	uint64_t		startino = 0;
>  	uint64_t		lastino = -1ULL;
> -	uint32_t		ocount;
>  	uint32_t		i;
>  	int			error;
>  
> @@ -279,26 +278,34 @@ report_bulkstat_health(
>  		lastino = cvt_agino_to_ino(&file->xfd, agno + 1, 0) - 1;
>  	}
>  
> +	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, startino);
> +	if (!breq) {
> +		perror("bulk alloc req");
> +		exitcode = 1;
> +		return 1;
> +	}
> +
>  	do {
> -		error = xfrog_bulkstat(&file->xfd, &startino, BULKSTAT_NR,
> -				bstat, &ocount);
> +		error = xfrog_bulkstat(&file->xfd, breq);
>  		if (error)
>  			break;
> -		for (i = 0; i < ocount; i++) {
> -			if (bstat[i].bs_ino > lastino)
> +		for (i = 0; i < breq->hdr.ocount; i++) {
> +			if (breq->bulkstat[i].bs_ino > lastino)
>  				goto out;
> -			snprintf(descr, sizeof(descr) - 1, _("inode %llu"),
> -					bstat[i].bs_ino);
> -			report_sick(descr, inode_flags, bstat[i].bs_sick,
> -					bstat[i].bs_checked);
> +			snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
> +					breq->bulkstat[i].bs_ino);
> +			report_sick(descr, inode_flags,
> +					breq->bulkstat[i].bs_sick,
> +					breq->bulkstat[i].bs_checked);
>  		}
> -	} while (ocount > 0);
> +	} while (breq->hdr.ocount > 0);
>  
>  	if (error) {
>  		errno = error;
>  		perror("bulkstat");
>  	}
>  out:
> +	free(breq);
>  	return error;
>  }
>  
> 

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

* Re: [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS
  2019-09-25 21:32 ` [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS Darrick J. Wong
@ 2019-09-26 21:48   ` Eric Sandeen
  2019-09-27  3:54     ` Darrick J. Wong
  2019-09-27 20:15   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 24+ messages in thread
From: Eric Sandeen @ 2019-09-26 21:48 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Convert all programs to use the v5 inumbers ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  io/imap.c          |   26 +++++-----
>  io/open.c          |   34 ++++++++-----
>  libfrog/bulkstat.c |  132 ++++++++++++++++++++++++++++++++++++++++++++++------
>  libfrog/bulkstat.h |   10 +++-
>  scrub/fscounters.c |   21 +++++---
>  scrub/inodes.c     |   46 ++++++++++--------
>  6 files changed, 198 insertions(+), 71 deletions(-)

...

> diff --git a/io/open.c b/io/open.c
> index e0e7fb3e..3c6113a1 100644
> --- a/io/open.c
> +++ b/io/open.c
> @@ -681,39 +681,47 @@ static __u64
>  get_last_inode(void)
>  {
>  	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> -	uint64_t		lastip = 0;
> +	struct xfs_inumbers_req	*ireq;
>  	uint32_t		lastgrp = 0;
> -	uint32_t		ocount = 0;
> -	__u64			last_ino;
> -	struct xfs_inogrp	igroup[IGROUP_NR];
> +	__u64			last_ino = 0;
> +
> +	ireq = xfrog_inumbers_alloc_req(IGROUP_NR, 0);
> +	if (!ireq) {
> +		perror("alloc req");
> +		return 0;
> +	}
>  
>  	for (;;) {
>  		int		ret;
>  
> -		ret = xfrog_inumbers(&xfd, &lastip, IGROUP_NR, igroup,
> -				&ocount);
> +		ret = xfrog_inumbers(&xfd, ireq);
>  		if (ret) {
>  			errno = ret;
>  			perror("XFS_IOC_FSINUMBERS");
> -			return 0;
> +			free(ireq);

no need to free here

> +			goto out;
>  		}
>  
>  		/* Did we reach the last inode? */
> -		if (ocount == 0)
> +		if (ireq->hdr.ocount == 0)
>  			break;
>  
>  		/* last inode in igroup table */
> -		lastgrp = ocount;
> +		lastgrp = ireq->hdr.ocount;
>  	}
>  
> -	if (lastgrp == 0)
> -		return 0;
> +	if (lastgrp == 0) {
> +		free(ireq);

or here

> +		goto out;
> +	}
>  
>  	lastgrp--;
>  
>  	/* The last inode number in use */
> -	last_ino = igroup[lastgrp].xi_startino +
> -		  libxfs_highbit64(igroup[lastgrp].xi_allocmask);
> +	last_ino = ireq->inumbers[lastgrp].xi_startino +
> +		  libxfs_highbit64(ireq->inumbers[lastgrp].xi_allocmask);
> +out:
> +	free(ireq);

since you do it here

>  
>  	return last_ino;
>  }
> diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
> index 300963f1..85594e5e 100644
> --- a/libfrog/bulkstat.c
> +++ b/libfrog/bulkstat.c
> @@ -435,6 +435,86 @@ xfrog_bulkstat_alloc_req(
>  	return breq;
>  }
>  
> +/* Convert a inumbers data from v5 format to v1 format. */
> +void
> +xfrog_inumbers_v5_to_v1(
> +	struct xfs_inogrp		*ig1,
> +	const struct xfs_inumbers	*ig5)
> +{
> +	ig1->xi_startino = ig5->xi_startino;
> +	ig1->xi_alloccount = ig5->xi_alloccount;
> +	ig1->xi_allocmask = ig5->xi_allocmask;
> +}

nobody uses this?

...

> diff --git a/scrub/inodes.c b/scrub/inodes.c
> index 2112c9d1..964647ce 100644
> --- a/scrub/inodes.c
> +++ b/scrub/inodes.c
> @@ -49,7 +49,7 @@
>  static void
>  xfs_iterate_inodes_range_check(
>  	struct scrub_ctx	*ctx,
> -	struct xfs_inogrp	*inogrp,
> +	struct xfs_inumbers	*inumbers,
>  	struct xfs_bulkstat	*bstat)
>  {
>  	struct xfs_bulkstat	*bs;
> @@ -57,19 +57,19 @@ xfs_iterate_inodes_range_check(
>  	int			error;
>  
>  	for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
> -		if (!(inogrp->xi_allocmask & (1ULL << i)))
> +		if (!(inumbers->xi_allocmask & (1ULL << i)))
>  			continue;
> -		if (bs->bs_ino == inogrp->xi_startino + i) {
> +		if (bs->bs_ino == inumbers->xi_startino + i) {
>  			bs++;
>  			continue;
>  		}
>  
>  		/* Load the one inode. */
>  		error = xfrog_bulkstat_single(&ctx->mnt,
> -				inogrp->xi_startino + i, 0, bs);
> -		if (error || bs->bs_ino != inogrp->xi_startino + i) {
> +				inumbers->xi_startino + i, 0, bs);
> +		if (error || bs->bs_ino != inumbers->xi_startino + i) {
>  			memset(bs, 0, sizeof(struct xfs_bulkstat));
> -			bs->bs_ino = inogrp->xi_startino + i;
> +			bs->bs_ino = inumbers->xi_startino + i;
>  			bs->bs_blksize = ctx->mnt_sv.f_frsize;
>  		}
>  		bs++;
> @@ -92,12 +92,11 @@ xfs_iterate_inodes_range(
>  	void			*arg)
>  {
>  	struct xfs_handle	handle;
> -	struct xfs_inogrp	inogrp;
> +	struct xfs_inumbers_req	*ireq;
>  	struct xfs_bulkstat_req	*breq;
>  	char			idescr[DESCR_BUFSZ];
>  	struct xfs_bulkstat	*bs;
> -	uint64_t		igrp_ino;
> -	uint32_t		igrplen = 0;
> +	struct xfs_inumbers	*inumbers;
>  	bool			moveon = true;
>  	int			i;
>  	int			error;
> @@ -114,19 +113,26 @@ xfs_iterate_inodes_range(
>  		return false;
>  	}
>  
> +	ireq = xfrog_inumbers_alloc_req(1, first_ino);
> +	if (!ireq) {
> +		str_info(ctx, descr, _("Insufficient memory; giving up."));
> +		free(breq);
> +		return false;
> +	}
> +	inumbers = &ireq->inumbers[0];
> +
>  	/* Find the inode chunk & alloc mask */
> -	igrp_ino = first_ino;
> -	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
> -	while (!error && igrplen) {
> +	error = xfrog_inumbers(&ctx->mnt, ireq);
> +	while (!error && ireq->hdr.ocount > 0) {
>  		/*
>  		 * We can have totally empty inode chunks on filesystems where
>  		 * there are more than 64 inodes per block.  Skip these.
>  		 */
> -		if (inogrp.xi_alloccount == 0)
> +		if (inumbers->xi_alloccount == 0)
>  			goto igrp_retry;
>  
> -		breq->hdr.ino = inogrp.xi_startino;
> -		breq->hdr.icount = inogrp.xi_alloccount;
> +		breq->hdr.ino = inumbers->xi_startino;
> +		breq->hdr.icount = inumbers->xi_alloccount;
>  		error = xfrog_bulkstat(&ctx->mnt, breq);
>  		if (error) {
>  			char	errbuf[DESCR_BUFSZ];
> @@ -135,11 +141,11 @@ xfs_iterate_inodes_range(
>  						errbuf, DESCR_BUFSZ));
>  		}
>  
> -		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
> +		xfs_iterate_inodes_range_check(ctx, inumbers, breq->bulkstat);
>  
>  		/* Iterate all the inodes. */
>  		for (i = 0, bs = breq->bulkstat;
> -		     i < inogrp.xi_alloccount;
> +		     i < inumbers->xi_alloccount;
>  		     i++, bs++) {
>  			if (bs->bs_ino > last_ino)
>  				goto out;

same deal w/ leaking here

> @@ -153,7 +159,7 @@ xfs_iterate_inodes_range(
>  			case ESTALE:
>  				stale_count++;
>  				if (stale_count < 30) {
> -					igrp_ino = inogrp.xi_startino;
> +					ireq->hdr.ino = inumbers->xi_startino;
>  					goto igrp_retry;
>  				}
>  				snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
> @@ -177,8 +183,7 @@ _("Changed too many times during scan; giving up."));
>  
>  		stale_count = 0;
>  igrp_retry:
> -		error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
> -				&igrplen);
> +		error = xfrog_inumbers(&ctx->mnt, ireq);
>  	}
>  
>  err:
> @@ -186,6 +191,7 @@ _("Changed too many times during scan; giving up."));
>  		str_liberror(ctx, error, descr);
>  		moveon = false;
>  	}
> +	free(ireq);

since the free isn't under out:

>  	free(breq);
>  out:
>  	return moveon;
> 

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

* [PATCH v3 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
  2019-09-26 18:18   ` Eric Sandeen
  2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
@ 2019-09-27  3:44   ` Darrick J. Wong
  2019-09-27 17:23     ` Eric Sandeen
  2 siblings, 1 reply; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27  3:44 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_bulkstat.2   |  346 +++++++++++++++++++++++++++++++++++++++
 man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
 2 files changed, 352 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_bulkstat.2

diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
new file mode 100644
index 00000000..cd0a9b06
--- /dev/null
+++ b/man/man2/ioctl_xfs_bulkstat.2
@@ -0,0 +1,346 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
+.SH DESCRIPTION
+Query stat information for a group of XFS inodes.
+This ioctl uses
+.B struct xfs_bulkstat_req
+to set up a bulk transfer from the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_bulkstat     bulkstat[];
+};
+.fi
+.in
+.PP
+See below for the
+.B xfs_bulkstat
+structure definition.
+.PP
+.in +4n
+.nf
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr.ino
+should be set to the number of the first inode for which the caller wants
+information; or zero to start with the first inode in the filesystem;
+or a special value if
+.B XFS_BULK_IREQ_SPECIAL
+is set in the flags field.
+Note that this is a different semantic than the
+.B lastip
+in the old
+.B FSBULKSTAT
+ioctl.
+After the call, this value will be set to the number of the next inode for
+which information could supplied.
+This sets up the next call for an iteration loop.
+.PP
+If the
+.B XFS_BULK_IREQ_SPECIAL
+flag is set in the flags field, the
+.I ino
+field is interpreted according to the following special values:
+.RS 0.4i
+.TP
+.B XFS_BULK_IREQ_SPECIAL_ROOT
+Return stat information for the root directory inode.
+.RE
+.PP
+.PP
+.I hdr.flags
+is a bit set of operational flags:
+.RS 0.4i
+.TP
+.B XFS_BULK_IREQ_AGNO
+If this is set, the call will only return results for the allocation group (AG)
+set in
+.BR hdr.agno .
+If
+.B hdr.ino
+is set to zero, results will be returned starting with the first inode in the
+AG.
+This flag may not be set at the same time as the
+.B XFS_BULK_IREQ_SPECIAL
+flag.
+.TP
+.B XFS_BULK_IREQ_SPECIAL
+If this is set, results will be returned for only the special inode
+specified in the
+.B hdr.ino
+field.
+This flag may not be set at the same time as the
+.B XFS_BULK_IREQ_AGNO
+flag.
+.RE
+.PP
+.I hdr.icount
+is the maximum number of records to return.
+This should be the size of the array that comes after the header.
+.PP
+.I hdr.ocount
+will be set to the number of records actually returned.
+.PP
+.I hdr.agno
+is the number of the allocation group (AG) for which we want results.
+If the
+.B XFS_BULK_IREQ_AGNO
+flag is not set, this field is ignored.
+.PP
+.I hdr.reserved
+must be set to zero.
+
+.PP
+.I bulkstat
+is an array of
+.B struct xfs_bulkstat
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_bulkstat {
+	uint64_t                bs_ino;
+	uint64_t                bs_size;
+
+	uint64_t                bs_blocks;
+	uint64_t                bs_xflags;
+
+	uint64_t                bs_atime;
+	uint64_t                bs_mtime;
+
+	uint64_t                bs_ctime;
+	uint64_t                bs_btime;
+
+	uint32_t                bs_gen;
+	uint32_t                bs_uid;
+	uint32_t                bs_gid;
+	uint32_t                bs_projectid;
+
+	uint32_t                bs_atime_nsec;
+	uint32_t                bs_mtime_nsec;
+	uint32_t                bs_ctime_nsec;
+	uint32_t                bs_btime_nsec;
+
+	uint32_t                bs_blksize;
+	uint32_t                bs_rdev;
+	uint32_t                bs_cowextsize_blks;
+	uint32_t                bs_extsize_blks;
+
+	uint32_t                bs_nlink;
+	uint32_t                bs_extents;
+	uint32_t                bs_aextents;
+	uint16_t                bs_version;
+	uint16_t                bs_forkoff;
+
+	uint16_t                bs_sick;
+	uint16_t                bs_checked;
+	uint16_t                bs_mode;
+	uint16_t                bs_pad2;
+
+	uint64_t                bs_pad[7];
+};
+.fi
+.in
+.PP
+.I bs_ino
+is the inode number of this record.
+.PP
+.I bs_size
+is the size of the file, in bytes.
+.PP
+.I bs_blocks
+is the number of filesystem blocks allocated to this file, including metadata.
+.PP
+.I bs_xflags
+tell us what extended flags are set this inode.
+These flags are the same values as those defined in the
+.B XFS INODE FLAGS
+section of the
+.BR ioctl_xfs_fsgetxattr (2)
+manpage.
+.PP
+.I bs_atime
+is the last time this file was accessed, in seconds.
+.PP
+.I bs_mtime
+is the last time the contents of this file were modified, in seconds.
+.PP
+.I bs_ctime
+is the last time this inode record was modified, in seconds.
+.PP
+.I bs_btime
+is the time this inode record was created, in seconds.
+.PP
+.I bs_gen
+is the generation number of the inode record.
+.PP
+.I bs_uid
+is the user id.
+.PP
+.I bs_gid
+is the group id.
+.PP
+.I bs_projectid
+is the the project id.
+.PP
+.I bs_atime_nsec
+is the nanoseconds component of the last time this file was accessed.
+.PP
+.I bs_mtime_nsec
+is the nanoseconds component of the last time the contents of this file were
+modified.
+.PP
+.I bs_ctime_nsec
+is the nanoseconds component of the last time this inode record was modified.
+.PP
+.I bs_btime_nsec
+is the nanoseconds component of the time this inode record was created.
+.PP
+.I bs_blksize
+is the size of a data block for this file, in units of bytes.
+.PP
+.I bs_rdev
+is the encoded device id if this is a special file.
+.PP
+.I bs_cowextsize_blks
+is the Copy on Write extent size hint for this file, in units of data blocks.
+.PP
+.I bs_extsize_blks
+is the extent size hint for this file, in units of data blocks.
+.PP
+.I bs_nlink
+is the number of hard links to this inode.
+.PP
+.I bs_extents
+is the number of storage mappings associated with this file's data.
+.PP
+.I bs_aextents
+is the number of storage mappings associated with this file's extended
+attributes.
+.PP
+.I bs_version
+is the version of this data structure.
+This will be set to
+.I XFS_BULKSTAT_VERSION_V5
+by the kernel.
+.PP
+.I bs_forkoff
+is the offset of the attribute fork in the inode record, in bytes.
+.PP
+The fields
+.IR bs_sick " and " bs_checked
+indicate the relative health of various allocation group metadata.
+Please see the section
+.B XFS INODE METADATA HEALTH REPORTING
+for more information.
+.PP
+.I bs_mode
+is the file type and mode.
+.PP
+.I bs_pad[7]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH XFS INODE METADATA HEALTH REPORTING
+.PP
+The online filesystem checking utility scans inode metadata and records what it
+finds in the kernel incore state.
+The following scheme is used for userspace to read the incore health status of
+an inode:
+.IP \[bu] 2
+If a given sick flag is set in
+.IR bs_sick ,
+then that piece of metadata has been observed to be damaged.
+The same bit should be set in
+.IR bs_checked .
+.IP \[bu]
+If a given sick flag is set in
+.I bs_checked
+but is not set in
+.IR bs_sick ,
+then that piece of metadata has been checked and is not faulty.
+.IP \[bu]
+If a given sick flag is not set in
+.IR bs_checked ,
+then no conclusion can be made.
+.PP
+The following flags apply to these fields:
+.RS 0.4i
+.TP
+.B XFS_BS_SICK_INODE
+The inode's record itself.
+.TP
+.B XFS_BS_SICK_BMBTD
+File data extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTA
+Extended attribute extent mappings.
+.TP
+.B XFS_BS_SICK_BMBTC
+Copy on Write staging extent mappings.
+.TP
+.B XFS_BS_SICK_DIR
+Directory information.
+.TP
+.B XFS_BS_SICK_XATTR
+Extended attribute data.
+.TP
+.B XFS_BS_SICK_SYMLINK
+Symbolic link target.
+.TP
+.B XFS_BS_SICK_PARENT
+Parent pointers.
+.RE
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_fsgetxattr (2)
diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
index 8f880c5a..3f059942 100644
--- a/man/man2/ioctl_xfs_fsbulkstat.2
+++ b/man/man2/ioctl_xfs_fsbulkstat.2
@@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
 .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
 .SH DESCRIPTION
 Query stat information for a group of XFS inodes.
+.PP
+NOTE: This ioctl has been superseded.
+Please see the
+.BR ioctl_xfs_bulkstat (2)
+manpage for information about its replacement.
+.PP
 These ioctls use
 .B struct xfs_fsop_bulkreq
 to set up a bulk transfer with the kernel:

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

* [PATCH v3 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
  2019-09-26 18:36   ` Eric Sandeen
  2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
@ 2019-09-27  3:44   ` Darrick J. Wong
  2019-09-27 17:25     ` Eric Sandeen
  2 siblings, 1 reply; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27  3:44 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Add a manpage describing the new v5 XFS_IOC_INUMBERS ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man2/ioctl_xfs_inumbers.2 |  128 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)
 create mode 100644 man/man2/ioctl_xfs_inumbers.2

diff --git a/man/man2/ioctl_xfs_inumbers.2 b/man/man2/ioctl_xfs_inumbers.2
new file mode 100644
index 00000000..f495e73c
--- /dev/null
+++ b/man/man2/ioctl_xfs_inumbers.2
@@ -0,0 +1,128 @@
+.\" Copyright (c) 2019, Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
+.\" SPDX-License-Identifier: GPL-2.0+
+.\" %%%LICENSE_END
+.TH IOCTL-XFS-INUMBERS 2 2019-05-23 "XFS"
+.SH NAME
+ioctl_xfs_inumbers \- query allocation information for groups of XFS inodes
+.SH SYNOPSIS
+.br
+.B #include <xfs/xfs_fs.h>
+.PP
+.BI "int ioctl(int " fd ", XFS_IOC_INUMBERS, struct xfs_inumbers_req *" arg );
+.SH DESCRIPTION
+Query inode allocation information for groups of XFS inodes.
+This ioctl uses
+.B struct xfs_inumbers_req
+to set up a bulk transfer from the kernel:
+.PP
+.in +4n
+.nf
+struct xfs_inumbers_req {
+	struct xfs_bulk_ireq    hdr;
+	struct xfs_inumbers     inumbers[];
+};
+.fi
+.in
+.PP
+See below for the
+.B xfs_inumbers
+structure definition.
+.PP
+.in +4n
+.nf
+struct xfs_bulk_ireq {
+	uint64_t                ino;
+	uint32_t                flags;
+	uint32_t                icount;
+	uint32_t                ocount;
+	uint32_t                agno;
+	uint64_t                reserved[5];
+};
+.fi
+.in
+.PP
+.I hdr
+describes the information to query.
+The layout and behavior are documented in the
+.BR ioctl_xfs_bulkstat (2)
+manpage and will not be discussed further here.
+
+.PP
+.I inumbers
+is an array of
+.B struct xfs_inumbers
+which is described below.
+The array must have at least
+.I icount
+elements.
+.PP
+.in +4n
+.nf
+struct xfs_inumbers {
+	uint64_t                xi_startino;
+	uint64_t                xi_allocmask;
+	uint8_t                 xi_alloccount;
+	uint8_t                 xi_version;
+	uint8_t                 xi_padding[6];
+};
+.fi
+.in
+.PP
+This structure describes inode usage information for a group of 64 consecutive
+inode numbers.
+.PP
+.I xi_startino
+is the first inode number of this group.
+.PP
+.I xi_allocmask
+is a bitmask telling which inodes in this group are allocated.
+To clarify, bit
+.B N
+is set if inode
+.BR xi_startino + N
+is allocated.
+.PP
+.I xi_alloccount
+is the number of inodes in this group that are allocated.
+This should be equal to popcnt(xi_allocmask).
+.PP
+.I xi_version
+is the version of this data structure.
+This will be set to
+.I XFS_INUMBERS_VERSION_V5
+by the kernel.
+.PP
+.I xi_padding[6]
+is zeroed.
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EFAULT
+The kernel was not able to copy into the userspace buffer.
+.TP
+.B EFSBADCRC
+Metadata checksum validation failed while performing the query.
+.TP
+.B EFSCORRUPTED
+Metadata corruption was encountered while performing the query.
+.TP
+.B EINVAL
+One of the arguments was not valid.
+.TP
+.B EIO
+An I/O error was encountered while performing the query.
+.TP
+.B ENOMEM
+There was insufficient memory to perform the query.
+.SH CONFORMING TO
+This API is specific to XFS filesystem on the Linux kernel.
+.SH SEE ALSO
+.BR ioctl (2),
+.BR ioctl_xfs_bulkstat (2).

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

* Re: [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics
  2019-09-26 21:01   ` Eric Sandeen
@ 2019-09-27  3:50     ` Darrick J. Wong
  0 siblings, 0 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27  3:50 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs

On Thu, Sep 26, 2019 at 04:01:43PM -0500, Eric Sandeen wrote:
> On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Convert xfrog_bulkstat() and xfrog_bulkstat_single() to take arguments
> > using v5 bulkstat semantics and return bulkstat information in v5
> > structures.  If the v5 ioctl is not available, the xfrog wrapper should
> > use the v1 ioctl to emulate v5 behaviors.  Add flags to the xfs_fd
> > structure to constrain emulation for debugging purposes.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> 
> gawd this is a lot of frobnication of ioctl results but I ... guess there's
> no way around it.
> 
> Presumably we want all callers to use v5 for the bigger fields, right,
> so it's not like we can just leave some old v1 callers as-is if they don't
> need new fields ....

Well... in theory we could leave them, but eventually we'll either want
new functionality or we'll want to deprecate the old ones.

> > ---
> >  fsr/xfs_fsr.c      |   64 ++++++--
> >  io/open.c          |   27 ++-
> >  io/swapext.c       |    9 +
> >  libfrog/bulkstat.c |  431 +++++++++++++++++++++++++++++++++++++++++++++++++---
> >  libfrog/bulkstat.h |   14 +-
> >  libfrog/fsgeom.h   |    9 +
> >  quota/quot.c       |   29 ++-
> >  scrub/inodes.c     |   39 +++--
> >  scrub/inodes.h     |    2 
> >  scrub/phase3.c     |    6 -
> >  scrub/phase5.c     |    8 -
> >  scrub/phase6.c     |    2 
> >  scrub/unicrash.c   |    6 -
> >  scrub/unicrash.h   |    4 
> >  spaceman/health.c  |   33 ++--
> >  15 files changed, 572 insertions(+), 111 deletions(-)
> > 
> > 
> > diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
> > index a53eb924..af5d6169 100644
> > --- a/fsr/xfs_fsr.c
> > +++ b/fsr/xfs_fsr.c
<snip>
> > @@ -623,7 +643,14 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
> >  			     (p->bs_extents < 2))
> >  				continue;
> >  
> > -			fd = jdm_open(fshandlep, p, O_RDWR|O_DIRECT);
> > +			ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
> 
> ew.  In the long run, I guess I'd rather convert these to take v5 when needed
> but alas.  I guess that has xfsdump implications too.  :(

Worse -- these are public libhandle functions, so we'll have to upgrade
its interfaces very carefully.

> > +			if (ret) {
> > +				fsrprintf(_("bstat conversion error: %s\n"),
> > +						strerror(ret));
> > +				continue;
> > +			}
> 
> ... but then we wouldn't potential fail on bstats w/ big numbers :(
> 
> Oh well, another day.
> 
> > +
> > +			fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
> >  			if (fd < 0) {
> >  				/* This probably means the file was
> >  				 * removed while in progress of handling

<snip>

> > diff --git a/scrub/inodes.c b/scrub/inodes.c
> > index 580a845e..2112c9d1 100644
> > --- a/scrub/inodes.c
> > +++ b/scrub/inodes.c

<snip>

> > @@ -135,10 +135,12 @@ xfs_iterate_inodes_range(
> >  						errbuf, DESCR_BUFSZ));
> >  		}
> >  
> > -		xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
> > +		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
> >  
> >  		/* Iterate all the inodes. */
> > -		for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
> > +		for (i = 0, bs = breq->bulkstat;
> > +		     i < inogrp.xi_alloccount;
> > +		     i++, bs++) {
> >  			if (bs->bs_ino > last_ino)
> >  				goto out;
> 
> leaks the breq here, no?
> 
> >  
> > @@ -184,6 +186,7 @@ _("Changed too many times during scan; giving up."));
> >  		str_liberror(ctx, error, descr);
> >  		moveon = false;
> >  	}
> > +	free(breq);
> >  out:
> 
> maybe free should be here?

Ugh, I think I mismerged that.  Fixed. :(

--D

> > diff --git a/scrub/inodes.h b/scrub/inodes.h
> > index 631848c3..3341c6d9 100644
> > --- a/scrub/inodes.h
> > +++ b/scrub/inodes.h
> > @@ -7,7 +7,7 @@
> >  #define XFS_SCRUB_INODES_H_
> >  
> >  typedef int (*xfs_inode_iter_fn)(struct scrub_ctx *ctx,
> > -		struct xfs_handle *handle, struct xfs_bstat *bs, void *arg);
> > +		struct xfs_handle *handle, struct xfs_bulkstat *bs, void *arg);
> >  
> >  #define XFS_ITERATE_INODES_ABORT	(-1)
> >  bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
> > diff --git a/scrub/phase3.c b/scrub/phase3.c
> > index 81c64cd1..a32d1ced 100644
> > --- a/scrub/phase3.c
> > +++ b/scrub/phase3.c
> > @@ -30,7 +30,7 @@ xfs_scrub_fd(
> >  	struct scrub_ctx	*ctx,
> >  	bool			(*fn)(struct scrub_ctx *ctx, uint64_t ino,
> >  				      uint32_t gen, struct xfs_action_list *a),
> > -	struct xfs_bstat	*bs,
> > +	struct xfs_bulkstat	*bs,
> >  	struct xfs_action_list	*alist)
> >  {
> >  	return fn(ctx, bs->bs_ino, bs->bs_gen, alist);
> > @@ -45,7 +45,7 @@ struct scrub_inode_ctx {
> >  static void
> >  xfs_scrub_inode_vfs_error(
> >  	struct scrub_ctx	*ctx,
> > -	struct xfs_bstat	*bstat)
> > +	struct xfs_bulkstat	*bstat)
> >  {
> >  	char			descr[DESCR_BUFSZ];
> >  	xfs_agnumber_t		agno;
> > @@ -65,7 +65,7 @@ static int
> >  xfs_scrub_inode(
> >  	struct scrub_ctx	*ctx,
> >  	struct xfs_handle	*handle,
> > -	struct xfs_bstat	*bstat,
> > +	struct xfs_bulkstat	*bstat,
> >  	void			*arg)
> >  {
> >  	struct xfs_action_list	alist;
> > diff --git a/scrub/phase5.c b/scrub/phase5.c
> > index 3ff34251..99cd51b2 100644
> > --- a/scrub/phase5.c
> > +++ b/scrub/phase5.c
> > @@ -80,7 +80,7 @@ xfs_scrub_scan_dirents(
> >  	struct scrub_ctx	*ctx,
> >  	const char		*descr,
> >  	int			*fd,
> > -	struct xfs_bstat	*bstat)
> > +	struct xfs_bulkstat	*bstat)
> >  {
> >  	struct unicrash		*uc = NULL;
> >  	DIR			*dir;
> > @@ -140,7 +140,7 @@ xfs_scrub_scan_fhandle_namespace_xattrs(
> >  	struct scrub_ctx		*ctx,
> >  	const char			*descr,
> >  	struct xfs_handle		*handle,
> > -	struct xfs_bstat		*bstat,
> > +	struct xfs_bulkstat		*bstat,
> >  	const struct attrns_decode	*attr_ns)
> >  {
> >  	struct attrlist_cursor		cur;
> > @@ -200,7 +200,7 @@ xfs_scrub_scan_fhandle_xattrs(
> >  	struct scrub_ctx		*ctx,
> >  	const char			*descr,
> >  	struct xfs_handle		*handle,
> > -	struct xfs_bstat		*bstat)
> > +	struct xfs_bulkstat		*bstat)
> >  {
> >  	const struct attrns_decode	*ns;
> >  	bool				moveon = true;
> > @@ -228,7 +228,7 @@ static int
> >  xfs_scrub_connections(
> >  	struct scrub_ctx	*ctx,
> >  	struct xfs_handle	*handle,
> > -	struct xfs_bstat	*bstat,
> > +	struct xfs_bulkstat	*bstat,
> >  	void			*arg)
> >  {
> >  	bool			*pmoveon = arg;
> > diff --git a/scrub/phase6.c b/scrub/phase6.c
> > index 506e75d2..b41f90e0 100644
> > --- a/scrub/phase6.c
> > +++ b/scrub/phase6.c
> > @@ -172,7 +172,7 @@ static int
> >  xfs_report_verify_inode(
> >  	struct scrub_ctx		*ctx,
> >  	struct xfs_handle		*handle,
> > -	struct xfs_bstat		*bstat,
> > +	struct xfs_bulkstat		*bstat,
> >  	void				*arg)
> >  {
> >  	char				descr[DESCR_BUFSZ];
> > diff --git a/scrub/unicrash.c b/scrub/unicrash.c
> > index 17e8f34f..b02c5658 100644
> > --- a/scrub/unicrash.c
> > +++ b/scrub/unicrash.c
> > @@ -432,7 +432,7 @@ unicrash_init(
> >   */
> >  static bool
> >  is_only_root_writable(
> > -	struct xfs_bstat	*bstat)
> > +	struct xfs_bulkstat	*bstat)
> >  {
> >  	if (bstat->bs_uid != 0 || bstat->bs_gid != 0)
> >  		return false;
> > @@ -444,7 +444,7 @@ bool
> >  unicrash_dir_init(
> >  	struct unicrash		**ucp,
> >  	struct scrub_ctx	*ctx,
> > -	struct xfs_bstat	*bstat)
> > +	struct xfs_bulkstat	*bstat)
> >  {
> >  	/*
> >  	 * Assume 64 bytes per dentry, clamp buckets between 16 and 64k.
> > @@ -459,7 +459,7 @@ bool
> >  unicrash_xattr_init(
> >  	struct unicrash		**ucp,
> >  	struct scrub_ctx	*ctx,
> > -	struct xfs_bstat	*bstat)
> > +	struct xfs_bulkstat	*bstat)
> >  {
> >  	/* Assume 16 attributes per extent for lack of a better idea. */
> >  	return unicrash_init(ucp, ctx, false, 16 * (1 + bstat->bs_aextents),
> > diff --git a/scrub/unicrash.h b/scrub/unicrash.h
> > index fb8f5f72..feb9cc86 100644
> > --- a/scrub/unicrash.h
> > +++ b/scrub/unicrash.h
> > @@ -14,9 +14,9 @@ struct unicrash;
> >  struct dirent;
> >  
> >  bool unicrash_dir_init(struct unicrash **ucp, struct scrub_ctx *ctx,
> > -		struct xfs_bstat *bstat);
> > +		struct xfs_bulkstat *bstat);
> >  bool unicrash_xattr_init(struct unicrash **ucp, struct scrub_ctx *ctx,
> > -		struct xfs_bstat *bstat);
> > +		struct xfs_bulkstat *bstat);
> >  bool unicrash_fs_label_init(struct unicrash **ucp, struct scrub_ctx *ctx);
> >  void unicrash_free(struct unicrash *uc);
> >  bool unicrash_check_dir_name(struct unicrash *uc, const char *descr,
> > diff --git a/spaceman/health.c b/spaceman/health.c
> > index a8bd3f3e..b195a229 100644
> > --- a/spaceman/health.c
> > +++ b/spaceman/health.c
> > @@ -208,7 +208,7 @@ report_inode_health(
> >  	unsigned long long	ino,
> >  	const char		*descr)
> >  {
> > -	struct xfs_bstat	bs;
> > +	struct xfs_bulkstat	bs;
> >  	char			d[256];
> >  	int			ret;
> >  
> > @@ -217,7 +217,7 @@ report_inode_health(
> >  		descr = d;
> >  	}
> >  
> > -	ret = xfrog_bulkstat_single(&file->xfd, ino, &bs);
> > +	ret = xfrog_bulkstat_single(&file->xfd, ino, 0, &bs);
> >  	if (ret) {
> >  		errno = ret;
> >  		perror(descr);
> > @@ -266,11 +266,10 @@ static int
> >  report_bulkstat_health(
> >  	xfs_agnumber_t		agno)
> >  {
> > -	struct xfs_bstat	bstat[BULKSTAT_NR];
> > +	struct xfs_bulkstat_req	*breq;
> >  	char			descr[256];
> >  	uint64_t		startino = 0;
> >  	uint64_t		lastino = -1ULL;
> > -	uint32_t		ocount;
> >  	uint32_t		i;
> >  	int			error;
> >  
> > @@ -279,26 +278,34 @@ report_bulkstat_health(
> >  		lastino = cvt_agino_to_ino(&file->xfd, agno + 1, 0) - 1;
> >  	}
> >  
> > +	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, startino);
> > +	if (!breq) {
> > +		perror("bulk alloc req");
> > +		exitcode = 1;
> > +		return 1;
> > +	}
> > +
> >  	do {
> > -		error = xfrog_bulkstat(&file->xfd, &startino, BULKSTAT_NR,
> > -				bstat, &ocount);
> > +		error = xfrog_bulkstat(&file->xfd, breq);
> >  		if (error)
> >  			break;
> > -		for (i = 0; i < ocount; i++) {
> > -			if (bstat[i].bs_ino > lastino)
> > +		for (i = 0; i < breq->hdr.ocount; i++) {
> > +			if (breq->bulkstat[i].bs_ino > lastino)
> >  				goto out;
> > -			snprintf(descr, sizeof(descr) - 1, _("inode %llu"),
> > -					bstat[i].bs_ino);
> > -			report_sick(descr, inode_flags, bstat[i].bs_sick,
> > -					bstat[i].bs_checked);
> > +			snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
> > +					breq->bulkstat[i].bs_ino);
> > +			report_sick(descr, inode_flags,
> > +					breq->bulkstat[i].bs_sick,
> > +					breq->bulkstat[i].bs_checked);
> >  		}
> > -	} while (ocount > 0);
> > +	} while (breq->hdr.ocount > 0);
> >  
> >  	if (error) {
> >  		errno = error;
> >  		perror("bulkstat");
> >  	}
> >  out:
> > +	free(breq);
> >  	return error;
> >  }
> >  
> > 

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

* Re: [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS
  2019-09-26 21:48   ` Eric Sandeen
@ 2019-09-27  3:54     ` Darrick J. Wong
  0 siblings, 0 replies; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27  3:54 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs

On Thu, Sep 26, 2019 at 04:48:37PM -0500, Eric Sandeen wrote:
> On 9/25/19 4:32 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Convert all programs to use the v5 inumbers ioctl.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  io/imap.c          |   26 +++++-----
> >  io/open.c          |   34 ++++++++-----
> >  libfrog/bulkstat.c |  132 ++++++++++++++++++++++++++++++++++++++++++++++------
> >  libfrog/bulkstat.h |   10 +++-
> >  scrub/fscounters.c |   21 +++++---
> >  scrub/inodes.c     |   46 ++++++++++--------
> >  6 files changed, 198 insertions(+), 71 deletions(-)
> 
> ...
> 
> > diff --git a/io/open.c b/io/open.c
> > index e0e7fb3e..3c6113a1 100644
> > --- a/io/open.c
> > +++ b/io/open.c
> > @@ -681,39 +681,47 @@ static __u64
> >  get_last_inode(void)
> >  {
> >  	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
> > -	uint64_t		lastip = 0;
> > +	struct xfs_inumbers_req	*ireq;
> >  	uint32_t		lastgrp = 0;
> > -	uint32_t		ocount = 0;
> > -	__u64			last_ino;
> > -	struct xfs_inogrp	igroup[IGROUP_NR];
> > +	__u64			last_ino = 0;
> > +
> > +	ireq = xfrog_inumbers_alloc_req(IGROUP_NR, 0);
> > +	if (!ireq) {
> > +		perror("alloc req");
> > +		return 0;
> > +	}
> >  
> >  	for (;;) {
> >  		int		ret;
> >  
> > -		ret = xfrog_inumbers(&xfd, &lastip, IGROUP_NR, igroup,
> > -				&ocount);
> > +		ret = xfrog_inumbers(&xfd, ireq);
> >  		if (ret) {
> >  			errno = ret;
> >  			perror("XFS_IOC_FSINUMBERS");
> > -			return 0;
> > +			free(ireq);
> 
> no need to free here

Fixed.

> > +			goto out;
> >  		}
> >  
> >  		/* Did we reach the last inode? */
> > -		if (ocount == 0)
> > +		if (ireq->hdr.ocount == 0)
> >  			break;
> >  
> >  		/* last inode in igroup table */
> > -		lastgrp = ocount;
> > +		lastgrp = ireq->hdr.ocount;
> >  	}
> >  
> > -	if (lastgrp == 0)
> > -		return 0;
> > +	if (lastgrp == 0) {
> > +		free(ireq);
> 
> or here
> 
> > +		goto out;
> > +	}
> >  
> >  	lastgrp--;
> >  
> >  	/* The last inode number in use */
> > -	last_ino = igroup[lastgrp].xi_startino +
> > -		  libxfs_highbit64(igroup[lastgrp].xi_allocmask);
> > +	last_ino = ireq->inumbers[lastgrp].xi_startino +
> > +		  libxfs_highbit64(ireq->inumbers[lastgrp].xi_allocmask);
> > +out:
> > +	free(ireq);
> 
> since you do it here

Thanks for catching that. <sigh> :)

> >  
> >  	return last_ino;
> >  }
> > diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
> > index 300963f1..85594e5e 100644
> > --- a/libfrog/bulkstat.c
> > +++ b/libfrog/bulkstat.c
> > @@ -435,6 +435,86 @@ xfrog_bulkstat_alloc_req(
> >  	return breq;
> >  }
> >  
> > +/* Convert a inumbers data from v5 format to v1 format. */
> > +void
> > +xfrog_inumbers_v5_to_v1(
> > +	struct xfs_inogrp		*ig1,
> > +	const struct xfs_inumbers	*ig5)
> > +{
> > +	ig1->xi_startino = ig5->xi_startino;
> > +	ig1->xi_alloccount = ig5->xi_alloccount;
> > +	ig1->xi_allocmask = ig5->xi_allocmask;
> > +}
> 
> nobody uses this?

Right, it is (for now) an unused helper function.

> ...
> 
> > diff --git a/scrub/inodes.c b/scrub/inodes.c
> > index 2112c9d1..964647ce 100644
> > --- a/scrub/inodes.c
> > +++ b/scrub/inodes.c
> > @@ -49,7 +49,7 @@
> >  static void
> >  xfs_iterate_inodes_range_check(
> >  	struct scrub_ctx	*ctx,
> > -	struct xfs_inogrp	*inogrp,
> > +	struct xfs_inumbers	*inumbers,
> >  	struct xfs_bulkstat	*bstat)
> >  {
> >  	struct xfs_bulkstat	*bs;
> > @@ -57,19 +57,19 @@ xfs_iterate_inodes_range_check(
> >  	int			error;
> >  
> >  	for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
> > -		if (!(inogrp->xi_allocmask & (1ULL << i)))
> > +		if (!(inumbers->xi_allocmask & (1ULL << i)))
> >  			continue;
> > -		if (bs->bs_ino == inogrp->xi_startino + i) {
> > +		if (bs->bs_ino == inumbers->xi_startino + i) {
> >  			bs++;
> >  			continue;
> >  		}
> >  
> >  		/* Load the one inode. */
> >  		error = xfrog_bulkstat_single(&ctx->mnt,
> > -				inogrp->xi_startino + i, 0, bs);
> > -		if (error || bs->bs_ino != inogrp->xi_startino + i) {
> > +				inumbers->xi_startino + i, 0, bs);
> > +		if (error || bs->bs_ino != inumbers->xi_startino + i) {
> >  			memset(bs, 0, sizeof(struct xfs_bulkstat));
> > -			bs->bs_ino = inogrp->xi_startino + i;
> > +			bs->bs_ino = inumbers->xi_startino + i;
> >  			bs->bs_blksize = ctx->mnt_sv.f_frsize;
> >  		}
> >  		bs++;
> > @@ -92,12 +92,11 @@ xfs_iterate_inodes_range(
> >  	void			*arg)
> >  {
> >  	struct xfs_handle	handle;
> > -	struct xfs_inogrp	inogrp;
> > +	struct xfs_inumbers_req	*ireq;
> >  	struct xfs_bulkstat_req	*breq;
> >  	char			idescr[DESCR_BUFSZ];
> >  	struct xfs_bulkstat	*bs;
> > -	uint64_t		igrp_ino;
> > -	uint32_t		igrplen = 0;
> > +	struct xfs_inumbers	*inumbers;
> >  	bool			moveon = true;
> >  	int			i;
> >  	int			error;
> > @@ -114,19 +113,26 @@ xfs_iterate_inodes_range(
> >  		return false;
> >  	}
> >  
> > +	ireq = xfrog_inumbers_alloc_req(1, first_ino);
> > +	if (!ireq) {
> > +		str_info(ctx, descr, _("Insufficient memory; giving up."));
> > +		free(breq);
> > +		return false;
> > +	}
> > +	inumbers = &ireq->inumbers[0];
> > +
> >  	/* Find the inode chunk & alloc mask */
> > -	igrp_ino = first_ino;
> > -	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
> > -	while (!error && igrplen) {
> > +	error = xfrog_inumbers(&ctx->mnt, ireq);
> > +	while (!error && ireq->hdr.ocount > 0) {
> >  		/*
> >  		 * We can have totally empty inode chunks on filesystems where
> >  		 * there are more than 64 inodes per block.  Skip these.
> >  		 */
> > -		if (inogrp.xi_alloccount == 0)
> > +		if (inumbers->xi_alloccount == 0)
> >  			goto igrp_retry;
> >  
> > -		breq->hdr.ino = inogrp.xi_startino;
> > -		breq->hdr.icount = inogrp.xi_alloccount;
> > +		breq->hdr.ino = inumbers->xi_startino;
> > +		breq->hdr.icount = inumbers->xi_alloccount;
> >  		error = xfrog_bulkstat(&ctx->mnt, breq);
> >  		if (error) {
> >  			char	errbuf[DESCR_BUFSZ];
> > @@ -135,11 +141,11 @@ xfs_iterate_inodes_range(
> >  						errbuf, DESCR_BUFSZ));
> >  		}
> >  
> > -		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
> > +		xfs_iterate_inodes_range_check(ctx, inumbers, breq->bulkstat);
> >  
> >  		/* Iterate all the inodes. */
> >  		for (i = 0, bs = breq->bulkstat;
> > -		     i < inogrp.xi_alloccount;
> > +		     i < inumbers->xi_alloccount;
> >  		     i++, bs++) {
> >  			if (bs->bs_ino > last_ino)
> >  				goto out;
> 
> same deal w/ leaking here
> 
> > @@ -153,7 +159,7 @@ xfs_iterate_inodes_range(
> >  			case ESTALE:
> >  				stale_count++;
> >  				if (stale_count < 30) {
> > -					igrp_ino = inogrp.xi_startino;
> > +					ireq->hdr.ino = inumbers->xi_startino;
> >  					goto igrp_retry;
> >  				}
> >  				snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
> > @@ -177,8 +183,7 @@ _("Changed too many times during scan; giving up."));
> >  
> >  		stale_count = 0;
> >  igrp_retry:
> > -		error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
> > -				&igrplen);
> > +		error = xfrog_inumbers(&ctx->mnt, ireq);
> >  	}
> >  
> >  err:
> > @@ -186,6 +191,7 @@ _("Changed too many times during scan; giving up."));
> >  		str_liberror(ctx, error, descr);
> >  		moveon = false;
> >  	}
> > +	free(ireq);
> 
> since the free isn't under out:

Fixed that too.  Thanks for the review!

--D

> >  	free(breq);
> >  out:
> >  	return moveon;
> > 

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

* Re: [PATCH v3 1/4] man: add documentation for v5 bulkstat ioctl
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
@ 2019-09-27 17:23     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-27 17:23 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/26/19 10:44 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a new manpage describing the V5 XFS_IOC_BULKSTAT ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Beautiful, thanks.

Reviewed-by: Eric Sandeen <sandeen@redhat.com>

> ---
>  man/man2/ioctl_xfs_bulkstat.2   |  346 +++++++++++++++++++++++++++++++++++++++
>  man/man2/ioctl_xfs_fsbulkstat.2 |    6 +
>  2 files changed, 352 insertions(+)
>  create mode 100644 man/man2/ioctl_xfs_bulkstat.2
> 
> diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2
> new file mode 100644
> index 00000000..cd0a9b06
> --- /dev/null
> +++ b/man/man2/ioctl_xfs_bulkstat.2
> @@ -0,0 +1,346 @@
> +.\" Copyright (c) 2019, Oracle.  All rights reserved.
> +.\"
> +.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
> +.\" SPDX-License-Identifier: GPL-2.0+
> +.\" %%%LICENSE_END
> +.TH IOCTL-XFS-BULKSTAT 2 2019-05-23 "XFS"
> +.SH NAME
> +ioctl_xfs_bulkstat \- query information for a batch of XFS inodes
> +.SH SYNOPSIS
> +.br
> +.B #include <xfs/xfs_fs.h>
> +.PP
> +.BI "int ioctl(int " fd ", XFS_IOC_BULKSTAT, struct xfs_bulkstat_req *" arg );
> +.SH DESCRIPTION
> +Query stat information for a group of XFS inodes.
> +This ioctl uses
> +.B struct xfs_bulkstat_req
> +to set up a bulk transfer from the kernel:
> +.PP
> +.in +4n
> +.nf
> +struct xfs_bulkstat_req {
> +	struct xfs_bulk_ireq    hdr;
> +	struct xfs_bulkstat     bulkstat[];
> +};
> +.fi
> +.in
> +.PP
> +See below for the
> +.B xfs_bulkstat
> +structure definition.
> +.PP
> +.in +4n
> +.nf
> +struct xfs_bulk_ireq {
> +	uint64_t                ino;
> +	uint32_t                flags;
> +	uint32_t                icount;
> +	uint32_t                ocount;
> +	uint32_t                agno;
> +	uint64_t                reserved[5];
> +};
> +.fi
> +.in
> +.PP
> +.I hdr.ino
> +should be set to the number of the first inode for which the caller wants
> +information; or zero to start with the first inode in the filesystem;
> +or a special value if
> +.B XFS_BULK_IREQ_SPECIAL
> +is set in the flags field.
> +Note that this is a different semantic than the
> +.B lastip
> +in the old
> +.B FSBULKSTAT
> +ioctl.
> +After the call, this value will be set to the number of the next inode for
> +which information could supplied.
> +This sets up the next call for an iteration loop.
> +.PP
> +If the
> +.B XFS_BULK_IREQ_SPECIAL
> +flag is set in the flags field, the
> +.I ino
> +field is interpreted according to the following special values:
> +.RS 0.4i
> +.TP
> +.B XFS_BULK_IREQ_SPECIAL_ROOT
> +Return stat information for the root directory inode.
> +.RE
> +.PP
> +.PP
> +.I hdr.flags
> +is a bit set of operational flags:
> +.RS 0.4i
> +.TP
> +.B XFS_BULK_IREQ_AGNO
> +If this is set, the call will only return results for the allocation group (AG)
> +set in
> +.BR hdr.agno .
> +If
> +.B hdr.ino
> +is set to zero, results will be returned starting with the first inode in the
> +AG.
> +This flag may not be set at the same time as the
> +.B XFS_BULK_IREQ_SPECIAL
> +flag.
> +.TP
> +.B XFS_BULK_IREQ_SPECIAL
> +If this is set, results will be returned for only the special inode
> +specified in the
> +.B hdr.ino
> +field.
> +This flag may not be set at the same time as the
> +.B XFS_BULK_IREQ_AGNO
> +flag.
> +.RE
> +.PP
> +.I hdr.icount
> +is the maximum number of records to return.
> +This should be the size of the array that comes after the header.
> +.PP
> +.I hdr.ocount
> +will be set to the number of records actually returned.
> +.PP
> +.I hdr.agno
> +is the number of the allocation group (AG) for which we want results.
> +If the
> +.B XFS_BULK_IREQ_AGNO
> +flag is not set, this field is ignored.
> +.PP
> +.I hdr.reserved
> +must be set to zero.
> +
> +.PP
> +.I bulkstat
> +is an array of
> +.B struct xfs_bulkstat
> +which is described below.
> +The array must have at least
> +.I icount
> +elements.
> +.PP
> +.in +4n
> +.nf
> +struct xfs_bulkstat {
> +	uint64_t                bs_ino;
> +	uint64_t                bs_size;
> +
> +	uint64_t                bs_blocks;
> +	uint64_t                bs_xflags;
> +
> +	uint64_t                bs_atime;
> +	uint64_t                bs_mtime;
> +
> +	uint64_t                bs_ctime;
> +	uint64_t                bs_btime;
> +
> +	uint32_t                bs_gen;
> +	uint32_t                bs_uid;
> +	uint32_t                bs_gid;
> +	uint32_t                bs_projectid;
> +
> +	uint32_t                bs_atime_nsec;
> +	uint32_t                bs_mtime_nsec;
> +	uint32_t                bs_ctime_nsec;
> +	uint32_t                bs_btime_nsec;
> +
> +	uint32_t                bs_blksize;
> +	uint32_t                bs_rdev;
> +	uint32_t                bs_cowextsize_blks;
> +	uint32_t                bs_extsize_blks;
> +
> +	uint32_t                bs_nlink;
> +	uint32_t                bs_extents;
> +	uint32_t                bs_aextents;
> +	uint16_t                bs_version;
> +	uint16_t                bs_forkoff;
> +
> +	uint16_t                bs_sick;
> +	uint16_t                bs_checked;
> +	uint16_t                bs_mode;
> +	uint16_t                bs_pad2;
> +
> +	uint64_t                bs_pad[7];
> +};
> +.fi
> +.in
> +.PP
> +.I bs_ino
> +is the inode number of this record.
> +.PP
> +.I bs_size
> +is the size of the file, in bytes.
> +.PP
> +.I bs_blocks
> +is the number of filesystem blocks allocated to this file, including metadata.
> +.PP
> +.I bs_xflags
> +tell us what extended flags are set this inode.
> +These flags are the same values as those defined in the
> +.B XFS INODE FLAGS
> +section of the
> +.BR ioctl_xfs_fsgetxattr (2)
> +manpage.
> +.PP
> +.I bs_atime
> +is the last time this file was accessed, in seconds.
> +.PP
> +.I bs_mtime
> +is the last time the contents of this file were modified, in seconds.
> +.PP
> +.I bs_ctime
> +is the last time this inode record was modified, in seconds.
> +.PP
> +.I bs_btime
> +is the time this inode record was created, in seconds.
> +.PP
> +.I bs_gen
> +is the generation number of the inode record.
> +.PP
> +.I bs_uid
> +is the user id.
> +.PP
> +.I bs_gid
> +is the group id.
> +.PP
> +.I bs_projectid
> +is the the project id.
> +.PP
> +.I bs_atime_nsec
> +is the nanoseconds component of the last time this file was accessed.
> +.PP
> +.I bs_mtime_nsec
> +is the nanoseconds component of the last time the contents of this file were
> +modified.
> +.PP
> +.I bs_ctime_nsec
> +is the nanoseconds component of the last time this inode record was modified.
> +.PP
> +.I bs_btime_nsec
> +is the nanoseconds component of the time this inode record was created.
> +.PP
> +.I bs_blksize
> +is the size of a data block for this file, in units of bytes.
> +.PP
> +.I bs_rdev
> +is the encoded device id if this is a special file.
> +.PP
> +.I bs_cowextsize_blks
> +is the Copy on Write extent size hint for this file, in units of data blocks.
> +.PP
> +.I bs_extsize_blks
> +is the extent size hint for this file, in units of data blocks.
> +.PP
> +.I bs_nlink
> +is the number of hard links to this inode.
> +.PP
> +.I bs_extents
> +is the number of storage mappings associated with this file's data.
> +.PP
> +.I bs_aextents
> +is the number of storage mappings associated with this file's extended
> +attributes.
> +.PP
> +.I bs_version
> +is the version of this data structure.
> +This will be set to
> +.I XFS_BULKSTAT_VERSION_V5
> +by the kernel.
> +.PP
> +.I bs_forkoff
> +is the offset of the attribute fork in the inode record, in bytes.
> +.PP
> +The fields
> +.IR bs_sick " and " bs_checked
> +indicate the relative health of various allocation group metadata.
> +Please see the section
> +.B XFS INODE METADATA HEALTH REPORTING
> +for more information.
> +.PP
> +.I bs_mode
> +is the file type and mode.
> +.PP
> +.I bs_pad[7]
> +is zeroed.
> +.SH RETURN VALUE
> +On error, \-1 is returned, and
> +.I errno
> +is set to indicate the error.
> +.PP
> +.SH XFS INODE METADATA HEALTH REPORTING
> +.PP
> +The online filesystem checking utility scans inode metadata and records what it
> +finds in the kernel incore state.
> +The following scheme is used for userspace to read the incore health status of
> +an inode:
> +.IP \[bu] 2
> +If a given sick flag is set in
> +.IR bs_sick ,
> +then that piece of metadata has been observed to be damaged.
> +The same bit should be set in
> +.IR bs_checked .
> +.IP \[bu]
> +If a given sick flag is set in
> +.I bs_checked
> +but is not set in
> +.IR bs_sick ,
> +then that piece of metadata has been checked and is not faulty.
> +.IP \[bu]
> +If a given sick flag is not set in
> +.IR bs_checked ,
> +then no conclusion can be made.
> +.PP
> +The following flags apply to these fields:
> +.RS 0.4i
> +.TP
> +.B XFS_BS_SICK_INODE
> +The inode's record itself.
> +.TP
> +.B XFS_BS_SICK_BMBTD
> +File data extent mappings.
> +.TP
> +.B XFS_BS_SICK_BMBTA
> +Extended attribute extent mappings.
> +.TP
> +.B XFS_BS_SICK_BMBTC
> +Copy on Write staging extent mappings.
> +.TP
> +.B XFS_BS_SICK_DIR
> +Directory information.
> +.TP
> +.B XFS_BS_SICK_XATTR
> +Extended attribute data.
> +.TP
> +.B XFS_BS_SICK_SYMLINK
> +Symbolic link target.
> +.TP
> +.B XFS_BS_SICK_PARENT
> +Parent pointers.
> +.RE
> +.SH ERRORS
> +Error codes can be one of, but are not limited to, the following:
> +.TP
> +.B EFAULT
> +The kernel was not able to copy into the userspace buffer.
> +.TP
> +.B EFSBADCRC
> +Metadata checksum validation failed while performing the query.
> +.TP
> +.B EFSCORRUPTED
> +Metadata corruption was encountered while performing the query.
> +.TP
> +.B EINVAL
> +One of the arguments was not valid.
> +.TP
> +.B EIO
> +An I/O error was encountered while performing the query.
> +.TP
> +.B ENOMEM
> +There was insufficient memory to perform the query.
> +.SH CONFORMING TO
> +This API is specific to XFS filesystem on the Linux kernel.
> +.SH SEE ALSO
> +.BR ioctl (2),
> +.BR ioctl_xfs_fsgetxattr (2)
> diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2
> index 8f880c5a..3f059942 100644
> --- a/man/man2/ioctl_xfs_fsbulkstat.2
> +++ b/man/man2/ioctl_xfs_fsbulkstat.2
> @@ -15,6 +15,12 @@ ioctl_xfs_fsbulkstat \- query information for a batch of XFS inodes
>  .BI "int ioctl(int " fd ", XFS_IOC_FSBULKSTAT_SINGLE, struct xfs_fsop_bulkreq *" arg );
>  .SH DESCRIPTION
>  Query stat information for a group of XFS inodes.
> +.PP
> +NOTE: This ioctl has been superseded.
> +Please see the
> +.BR ioctl_xfs_bulkstat (2)
> +manpage for information about its replacement.
> +.PP
>  These ioctls use
>  .B struct xfs_fsop_bulkreq
>  to set up a bulk transfer with the kernel:
> 

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

* Re: [PATCH v3 2/4] man: add documentation for v5 inumbers ioctl
  2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
@ 2019-09-27 17:25     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-27 17:25 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/26/19 10:44 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a manpage describing the new v5 XFS_IOC_INUMBERS ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Reviewed-by: Eric Sandeen <sandeen@redhat.com>

> ---
>  man/man2/ioctl_xfs_inumbers.2 |  128 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 128 insertions(+)
>  create mode 100644 man/man2/ioctl_xfs_inumbers.2

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

* [PATCH v2 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics
  2019-09-25 21:32 ` [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics Darrick J. Wong
  2019-09-26 21:01   ` Eric Sandeen
@ 2019-09-27 20:14   ` Darrick J. Wong
  2019-09-27 20:29     ` Eric Sandeen
  1 sibling, 1 reply; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27 20:14 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Convert xfrog_bulkstat() and xfrog_bulkstat_single() to take arguments
using v5 bulkstat semantics and return bulkstat information in v5
structures.  If the v5 ioctl is not available, the xfrog wrapper should
use the v1 ioctl to emulate v5 behaviors.  Add flags to the xfs_fd
structure to constrain emulation for debugging purposes.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fsr/xfs_fsr.c      |   64 ++++++--
 io/open.c          |   27 ++-
 io/swapext.c       |    9 +
 libfrog/bulkstat.c |  431 +++++++++++++++++++++++++++++++++++++++++++++++++---
 libfrog/bulkstat.h |   14 +-
 libfrog/fsgeom.h   |    9 +
 quota/quot.c       |   29 ++-
 scrub/inodes.c     |   39 +++--
 scrub/inodes.h     |    2 
 scrub/phase3.c     |    6 -
 scrub/phase5.c     |    8 -
 scrub/phase6.c     |    2 
 scrub/unicrash.c   |    6 -
 scrub/unicrash.h   |    4 
 spaceman/health.c  |   33 ++--
 15 files changed, 572 insertions(+), 111 deletions(-)

diff --git a/fsr/xfs_fsr.c b/fsr/xfs_fsr.c
index a53eb924..af5d6169 100644
--- a/fsr/xfs_fsr.c
+++ b/fsr/xfs_fsr.c
@@ -466,6 +466,17 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
 				ptr = strchr(ptr, ' ');
 				if (ptr) {
 					startino = strtoull(++ptr, NULL, 10);
+					/*
+					 * NOTE: The inode number read in from
+					 * the leftoff file is the last inode
+					 * to have been fsr'd.  Since the v5
+					 * xfrog_bulkstat function wants to be
+					 * passed the first inode that we want
+					 * to examine, increment the value that
+					 * we read in.  The debug message below
+					 * prints the lastoff value.
+					 */
+					startino++;
 				}
 			}
 			if (startpass < 0)
@@ -484,7 +495,7 @@ fsrallfs(char *mtab, int howlong, char *leftofffile)
 
 	if (vflag) {
 		fsrprintf(_("START: pass=%d ino=%llu %s %s\n"),
-			  fs->npass, (unsigned long long)startino,
+			  fs->npass, (unsigned long long)startino - 1,
 			  fs->dev, fs->mnt);
 	}
 
@@ -576,12 +587,10 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 	int	fd;
 	int	count = 0;
 	int	ret;
-	uint32_t buflenout;
-	struct xfs_bstat buf[GRABSZ];
 	char	fname[64];
 	char	*tname;
 	jdm_fshandle_t	*fshandlep;
-	xfs_ino_t	lastino = startino;
+	struct xfs_bulkstat_req	*breq;
 
 	fsrprintf(_("%s start inode=%llu\n"), mntdir,
 		(unsigned long long)startino);
@@ -604,10 +613,21 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 
 	tmp_init(mntdir);
 
-	while ((ret = xfrog_bulkstat(&fsxfd, &lastino, GRABSZ, &buf[0],
-				&buflenout)) == 0) {
-		struct xfs_bstat *p;
-		struct xfs_bstat *endp;
+	breq = xfrog_bulkstat_alloc_req(GRABSZ, startino);
+	if (!breq) {
+		fsrprintf(_("Skipping %s: not enough memory\n"),
+			  mntdir);
+		xfd_close(&fsxfd);
+		free(fshandlep);
+		return -1;
+	}
+
+	while ((ret = xfrog_bulkstat(&fsxfd, breq) == 0)) {
+		struct xfs_bstat	bs1;
+		struct xfs_bulkstat	*buf = breq->bulkstat;
+		struct xfs_bulkstat	*p;
+		struct xfs_bulkstat	*endp;
+		uint32_t		buflenout = breq->hdr.ocount;
 
 		if (buflenout == 0)
 			goto out0;
@@ -615,7 +635,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 		/* Each loop through, defrag targetrange percent of the files */
 		count = (buflenout * targetrange) / 100;
 
-		qsort((char *)buf, buflenout, sizeof(struct xfs_bstat), cmp);
+		qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
 
 		for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
 			/* Do some obvious checks now */
@@ -623,7 +643,14 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			     (p->bs_extents < 2))
 				continue;
 
-			fd = jdm_open(fshandlep, p, O_RDWR|O_DIRECT);
+			ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
+			if (ret) {
+				fsrprintf(_("bstat conversion error: %s\n"),
+						strerror(ret));
+				continue;
+			}
+
+			fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
 			if (fd < 0) {
 				/* This probably means the file was
 				 * removed while in progress of handling
@@ -641,7 +668,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			/* Get a tmp file name */
 			tname = tmp_next(mntdir);
 
-			ret = fsrfile_common(fname, tname, mntdir, fd, p);
+			ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
 
 			leftoffino = p->bs_ino;
 
@@ -653,6 +680,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 			}
 		}
 		if (endtime && endtime < time(NULL)) {
+			free(breq);
 			tmp_close(mntdir);
 			xfd_close(&fsxfd);
 			fsrall_cleanup(1);
@@ -662,6 +690,7 @@ fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
 	if (ret)
 		fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
 out0:
+	free(breq);
 	tmp_close(mntdir);
 	xfd_close(&fsxfd);
 	free(fshandlep);
@@ -701,6 +730,7 @@ fsrfile(
 	xfs_ino_t		ino)
 {
 	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
+	struct xfs_bulkstat	bulkstat;
 	struct xfs_bstat	statbuf;
 	jdm_fshandle_t		*fshandlep;
 	int			fd = -1;
@@ -725,12 +755,18 @@ fsrfile(
 		goto out;
 	}
 
-	error = xfrog_bulkstat_single(&fsxfd, ino, &statbuf);
+	error = xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
 	if (error) {
 		fsrprintf(_("unable to get bstat on %s: %s\n"),
 			fname, strerror(error));
 		goto out;
 	}
+	error = xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
+	if (error) {
+		fsrprintf(_("bstat conversion error on %s: %s\n"),
+			fname, strerror(error));
+		goto out;
+	}
 
 	fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
 	if (fd < 0) {
@@ -951,7 +987,7 @@ fsr_setup_attr_fork(
 
 	i = 0;
 	do {
-		struct xfs_bstat tbstat;
+		struct xfs_bulkstat	tbstat;
 		char		name[64];
 		int		ret;
 
@@ -960,7 +996,7 @@ fsr_setup_attr_fork(
 		 * this to compare against the target and determine what we
 		 * need to do.
 		 */
-		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, &tbstat);
+		ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0, &tbstat);
 		if (ret) {
 			fsrprintf(_("unable to get bstat on temp file: %s\n"),
 						strerror(ret));
diff --git a/io/open.c b/io/open.c
index 99ca0dd3..e0e7fb3e 100644
--- a/io/open.c
+++ b/io/open.c
@@ -723,8 +723,7 @@ inode_f(
 	int			argc,
 	char			**argv)
 {
-	struct xfs_bstat	bstat;
-	uint32_t		count = 0;
+	struct xfs_bulkstat	bulkstat;
 	uint64_t		result_ino = 0;
 	uint64_t		userino = NULLFSINO;
 	char			*p;
@@ -775,26 +774,40 @@ inode_f(
 		}
 	} else if (ret_next) {
 		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
+		struct xfs_bulkstat_req	*breq;
+
+		/*
+		 * The -n option means that the caller wants to know the number
+		 * of the next allocated inode, so we need to increment here.
+		 */
+		breq = xfrog_bulkstat_alloc_req(1, userino + 1);
+		if (!breq) {
+			perror("alloc bulkstat");
+			exitcode = 1;
+			return 0;
+		}
 
 		/* get next inode */
-		ret = xfrog_bulkstat(&xfd, &userino, 1, &bstat, &count);
+		ret = xfrog_bulkstat(&xfd, breq);
 		if (ret) {
 			errno = ret;
 			perror("bulkstat");
+			free(breq);
 			exitcode = 1;
 			return 0;
 		}
 
 		/* The next inode in use, or 0 if none */
-		if (count)
-			result_ino = bstat.bs_ino;
+		if (breq->hdr.ocount)
+			result_ino = breq->bulkstat[0].bs_ino;
 		else
 			result_ino = 0;
+		free(breq);
 	} else {
 		struct xfs_fd	xfd = XFS_FD_INIT(file->fd);
 
 		/* get this inode */
-		ret = xfrog_bulkstat_single(&xfd, userino, &bstat);
+		ret = xfrog_bulkstat_single(&xfd, userino, 0, &bulkstat);
 		if (ret == EINVAL) {
 			/* Not in use */
 			result_ino = 0;
@@ -804,7 +817,7 @@ inode_f(
 			exitcode = 1;
 			return 0;
 		} else {
-			result_ino = bstat.bs_ino;
+			result_ino = bulkstat.bs_ino;
 		}
 	}
 
diff --git a/io/swapext.c b/io/swapext.c
index 2b4918f8..1139cf21 100644
--- a/io/swapext.c
+++ b/io/swapext.c
@@ -28,6 +28,7 @@ swapext_f(
 	char			**argv)
 {
 	struct xfs_fd		fxfd = XFS_FD_INIT(file->fd);
+	struct xfs_bulkstat	bulkstat;
 	int			fd;
 	int			error;
 	struct xfs_swapext	sx;
@@ -48,12 +49,18 @@ swapext_f(
 		goto out;
 	}
 
-	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, &sx.sx_stat);
+	error = xfrog_bulkstat_single(&fxfd, stat.st_ino, 0, &bulkstat);
 	if (error) {
 		errno = error;
 		perror("bulkstat");
 		goto out;
 	}
+	error = xfrog_bulkstat_v5_to_v1(&fxfd, &sx.sx_stat, &bulkstat);
+	if (error) {
+		errno = error;
+		perror("bulkstat conversion");
+		goto out;
+	}
 	sx.sx_version = XFS_SX_VERSION;
 	sx.sx_fdtarget = file->fd;
 	sx.sx_fdtmp = fd;
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index fa10f298..300963f1 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -3,55 +3,438 @@
  * Copyright (C) 2019 Oracle.  All Rights Reserved.
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
  */
+#include <string.h>
+#include <strings.h>
 #include "xfs.h"
 #include "fsgeom.h"
 #include "bulkstat.h"
 
+/*
+ * Wrapper functions for BULKSTAT and INUMBERS
+ * ===========================================
+ *
+ * The functions in this file are thin wrappers around the most recent version
+ * of the BULKSTAT and INUMBERS ioctls.  BULKSTAT is used to query XFS-specific
+ * stat information about a group of inodes.  INUMBERS is used to query
+ * allocation information about batches of XFS inodes.
+ *
+ * At the moment, the public xfrog_* functions provide all functionality of the
+ * V5 interface.  If the V5 interface is not available on the running kernel,
+ * the functions will emulate them as best they can with previous versions of
+ * the interface (currently V1).  If emulation is not possible, EINVAL will be
+ * returned.
+ *
+ * The XFROG_FLAG_BULKSTAT_FORCE_V[15] flags can be used to force use of a
+ * particular version of the kernel interface for testing.
+ */
+
+/*
+ * Grab the fs geometry information that is needed to needed to emulate v5 with
+ * v1 interfaces.
+ */
+static inline int
+xfrog_bulkstat_prep_v1_emulation(
+	struct xfs_fd		*xfd)
+{
+	if (xfd->fsgeom.blocksize > 0)
+		return 0;
+
+	return xfd_prepare_geometry(xfd);
+}
+
+/* Bulkstat a single inode using v5 ioctl. */
+static int
+xfrog_bulkstat_single5(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	struct xfs_bulkstat_req		*req;
+	int				ret;
+
+	if (flags & ~(XFS_BULK_IREQ_SPECIAL))
+		return EINVAL;
+
+	req = xfrog_bulkstat_alloc_req(1, ino);
+	if (!req)
+		return ENOMEM;
+
+	req->hdr.flags = flags;
+	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
+	if (ret) {
+		ret = errno;
+		goto free;
+	}
+
+	if (req->hdr.ocount == 0) {
+		ret = ENOENT;
+		goto free;
+	}
+
+	memcpy(bulkstat, req->bulkstat, sizeof(struct xfs_bulkstat));
+free:
+	free(req);
+	return ret;
+}
+
+/* Bulkstat a single inode using v1 ioctl. */
+static int
+xfrog_bulkstat_single1(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	struct xfs_bstat		bstat;
+	struct xfs_fsop_bulkreq		bulkreq = { 0 };
+	int				error;
+
+	if (flags)
+		return EINVAL;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	bulkreq.lastip = (__u64 *)&ino;
+	bulkreq.icount = 1;
+	bulkreq.ubuffer = &bstat;
+	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+	if (error)
+		return errno;
+
+	xfrog_bulkstat_v1_to_v5(xfd, bulkstat, &bstat);
+	return 0;
+}
+
 /* Bulkstat a single inode.  Returns zero or a positive error code. */
 int
 xfrog_bulkstat_single(
+	struct xfs_fd			*xfd,
+	uint64_t			ino,
+	unsigned int			flags,
+	struct xfs_bulkstat		*bulkstat)
+{
+	int				error;
+
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_bulkstat_single5(xfd, ino, flags, bulkstat);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_bulkstat_single1(xfd, ino, flags, bulkstat);
+}
+
+/*
+ * Set up the necessary control structures to emulate a V5 bulk request ioctl
+ * by calling a V1 bulk request ioctl.  This enables callers to run on older
+ * kernels.
+ *
+ * Returns 0 if the emulation should proceed; ECANCELED if there are no
+ * records; or a positive error code.
+ */
+static int
+xfrog_bulk_req_v1_setup(
 	struct xfs_fd		*xfd,
-	uint64_t		ino,
-	struct xfs_bstat	*ubuffer)
+	struct xfs_bulk_ireq	*hdr,
+	struct xfs_fsop_bulkreq	*bulkreq,
+	size_t			rec_size)
+{
+	void			*buf;
+
+	if (hdr->flags & XFS_BULK_IREQ_AGNO) {
+		uint32_t	agno = cvt_ino_to_agno(xfd, hdr->ino);
+
+		if (hdr->ino == 0)
+			hdr->ino = cvt_agino_to_ino(xfd, hdr->agno, 0);
+		else if (agno < hdr->agno)
+			return EINVAL;
+		else if (agno > hdr->agno)
+			goto no_results;
+	}
+
+	if (cvt_ino_to_agno(xfd, hdr->ino) > xfd->fsgeom.agcount)
+		goto no_results;
+
+	buf = malloc(hdr->icount * rec_size);
+	if (!buf)
+		return errno;
+
+	if (hdr->ino)
+		hdr->ino--;
+	bulkreq->lastip = (__u64 *)&hdr->ino,
+	bulkreq->icount = hdr->icount,
+	bulkreq->ocount = (__s32 *)&hdr->ocount,
+	bulkreq->ubuffer = buf;
+	return 0;
+
+no_results:
+	hdr->ocount = 0;
+	return ECANCELED;
+}
+
+/*
+ * Clean up after using a V1 bulk request to emulate a V5 bulk request call.
+ *
+ * If the ioctl was successful, we need to convert the returned V1-format bulk
+ * request data into the V5-format bulk request data and copy it into the
+ * caller's buffer.  We also need to free all resources allocated during the
+ * setup setup.
+ */
+static int
+xfrog_bulk_req_v1_cleanup(
+	struct xfs_fd		*xfd,
+	struct xfs_bulk_ireq	*hdr,
+	struct xfs_fsop_bulkreq	*bulkreq,
+	size_t			v1_rec_size,
+	uint64_t		(*v1_ino)(void *v1_rec),
+	void			*v5_records,
+	size_t			v5_rec_size,
+	void			(*cvt)(struct xfs_fd *xfd, void *v5, void *v1),
+	unsigned int		startino_adj,
+	int			error)
+{
+	void			*v1_rec = bulkreq->ubuffer;
+	void			*v5_rec = v5_records;
+	unsigned int		i;
+
+	if (error == ECANCELED) {
+		error = 0;
+		goto free;
+	}
+	if (error)
+		goto free;
+
+	/*
+	 * Convert each record from v1 to v5 format, keeping the startino
+	 * value up to date and (if desired) stopping at the end of the
+	 * AG.
+	 */
+	for (i = 0;
+	     i < hdr->ocount;
+	     i++, v1_rec += v1_rec_size, v5_rec += v5_rec_size) {
+		uint64_t	ino = v1_ino(v1_rec);
+
+		/* Stop if we hit a different AG. */
+		if ((hdr->flags & XFS_BULK_IREQ_AGNO) &&
+		    cvt_ino_to_agno(xfd, ino) != hdr->agno) {
+			hdr->ocount = i;
+			break;
+		}
+		cvt(xfd, v5_rec, v1_rec);
+		hdr->ino = ino + startino_adj;
+	}
+
+free:
+	free(bulkreq->ubuffer);
+	return error;
+}
+
+static uint64_t xfrog_bstat_ino(void *v1_rec)
+{
+	return ((struct xfs_bstat *)v1_rec)->bs_ino;
+}
+
+static void xfrog_bstat_cvt(struct xfs_fd *xfd, void *v5, void *v1)
+{
+	xfrog_bulkstat_v1_to_v5(xfd, v5, v1);
+}
+
+/* Bulkstat a bunch of inodes using the v5 interface. */
+static int
+xfrog_bulkstat5(
+	struct xfs_fd		*xfd,
+	struct xfs_bulkstat_req	*req)
 {
-	__u64			i = ino;
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= &i,
-		.icount		= 1,
-		.ubuffer	= ubuffer,
-		.ocount		= NULL,
-	};
 	int			ret;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+	ret = ioctl(xfd->fd, XFS_IOC_BULKSTAT, req);
 	if (ret)
 		return errno;
 	return 0;
 }
 
+/* Bulkstat a bunch of inodes using the v1 interface. */
+static int
+xfrog_bulkstat1(
+	struct xfs_fd		*xfd,
+	struct xfs_bulkstat_req	*req)
+{
+	struct xfs_fsop_bulkreq	bulkreq = { 0 };
+	int			error;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	error = xfrog_bulk_req_v1_setup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_bstat));
+	if (error == ECANCELED)
+		goto out_teardown;
+	if (error)
+		return error;
+
+	error = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
+	if (error)
+		error = errno;
+
+out_teardown:
+	return xfrog_bulk_req_v1_cleanup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_bstat), xfrog_bstat_ino,
+			&req->bulkstat, sizeof(struct xfs_bulkstat),
+			xfrog_bstat_cvt, 1, error);
+}
+
 /* Bulkstat a bunch of inodes.  Returns zero or a positive error code. */
 int
 xfrog_bulkstat(
 	struct xfs_fd		*xfd,
-	uint64_t		*lastino,
-	uint32_t		icount,
-	struct xfs_bstat	*ubuffer,
-	uint32_t		*ocount)
+	struct xfs_bulkstat_req	*req)
 {
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= (__u64 *)lastino,
-		.icount		= icount,
-		.ubuffer	= ubuffer,
-		.ocount		= (__s32 *)ocount,
-	};
-	int			ret;
+	int			error;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSBULKSTAT, &bulkreq);
-	if (ret)
-		return errno;
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_bulkstat5(xfd, req);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_bulkstat1(xfd, req);
+}
+
+static bool
+time_too_big(
+	uint64_t	time)
+{
+	time_t		TIME_MAX;
+
+	memset(&TIME_MAX, 0xFF, sizeof(TIME_MAX));
+	return time > TIME_MAX;
+}
+
+/* Convert bulkstat data from v5 format to v1 format. */
+int
+xfrog_bulkstat_v5_to_v1(
+	struct xfs_fd			*xfd,
+	struct xfs_bstat		*bs1,
+	const struct xfs_bulkstat	*bs5)
+{
+	if (bs5->bs_aextents > UINT16_MAX ||
+	    cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks) > UINT32_MAX ||
+	    cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks) > UINT32_MAX ||
+	    time_too_big(bs5->bs_atime) ||
+	    time_too_big(bs5->bs_ctime) ||
+	    time_too_big(bs5->bs_mtime))
+		return ERANGE;
+
+	bs1->bs_ino = bs5->bs_ino;
+	bs1->bs_mode = bs5->bs_mode;
+	bs1->bs_nlink = bs5->bs_nlink;
+	bs1->bs_uid = bs5->bs_uid;
+	bs1->bs_gid = bs5->bs_gid;
+	bs1->bs_rdev = bs5->bs_rdev;
+	bs1->bs_blksize = bs5->bs_blksize;
+	bs1->bs_size = bs5->bs_size;
+	bs1->bs_atime.tv_sec = bs5->bs_atime;
+	bs1->bs_mtime.tv_sec = bs5->bs_mtime;
+	bs1->bs_ctime.tv_sec = bs5->bs_ctime;
+	bs1->bs_atime.tv_nsec = bs5->bs_atime_nsec;
+	bs1->bs_mtime.tv_nsec = bs5->bs_mtime_nsec;
+	bs1->bs_ctime.tv_nsec = bs5->bs_ctime_nsec;
+	bs1->bs_blocks = bs5->bs_blocks;
+	bs1->bs_xflags = bs5->bs_xflags;
+	bs1->bs_extsize = cvt_off_fsb_to_b(xfd, bs5->bs_extsize_blks);
+	bs1->bs_extents = bs5->bs_extents;
+	bs1->bs_gen = bs5->bs_gen;
+	bs1->bs_projid_lo = bs5->bs_projectid & 0xFFFF;
+	bs1->bs_forkoff = bs5->bs_forkoff;
+	bs1->bs_projid_hi = bs5->bs_projectid >> 16;
+	bs1->bs_sick = bs5->bs_sick;
+	bs1->bs_checked = bs5->bs_checked;
+	bs1->bs_cowextsize = cvt_off_fsb_to_b(xfd, bs5->bs_cowextsize_blks);
+	bs1->bs_dmevmask = 0;
+	bs1->bs_dmstate = 0;
+	bs1->bs_aextents = bs5->bs_aextents;
 	return 0;
 }
 
+/* Convert bulkstat data from v1 format to v5 format. */
+void
+xfrog_bulkstat_v1_to_v5(
+	struct xfs_fd			*xfd,
+	struct xfs_bulkstat		*bs5,
+	const struct xfs_bstat		*bs1)
+{
+	memset(bs5, 0, sizeof(*bs5));
+	bs5->bs_version = XFS_BULKSTAT_VERSION_V1;
+
+	bs5->bs_ino = bs1->bs_ino;
+	bs5->bs_mode = bs1->bs_mode;
+	bs5->bs_nlink = bs1->bs_nlink;
+	bs5->bs_uid = bs1->bs_uid;
+	bs5->bs_gid = bs1->bs_gid;
+	bs5->bs_rdev = bs1->bs_rdev;
+	bs5->bs_blksize = bs1->bs_blksize;
+	bs5->bs_size = bs1->bs_size;
+	bs5->bs_atime = bs1->bs_atime.tv_sec;
+	bs5->bs_mtime = bs1->bs_mtime.tv_sec;
+	bs5->bs_ctime = bs1->bs_ctime.tv_sec;
+	bs5->bs_atime_nsec = bs1->bs_atime.tv_nsec;
+	bs5->bs_mtime_nsec = bs1->bs_mtime.tv_nsec;
+	bs5->bs_ctime_nsec = bs1->bs_ctime.tv_nsec;
+	bs5->bs_blocks = bs1->bs_blocks;
+	bs5->bs_xflags = bs1->bs_xflags;
+	bs5->bs_extsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_extsize);
+	bs5->bs_extents = bs1->bs_extents;
+	bs5->bs_gen = bs1->bs_gen;
+	bs5->bs_projectid = bstat_get_projid(bs1);
+	bs5->bs_forkoff = bs1->bs_forkoff;
+	bs5->bs_sick = bs1->bs_sick;
+	bs5->bs_checked = bs1->bs_checked;
+	bs5->bs_cowextsize_blks = cvt_b_to_off_fsbt(xfd, bs1->bs_cowextsize);
+	bs5->bs_aextents = bs1->bs_aextents;
+}
+
+/* Allocate a bulkstat request.  On error returns NULL and sets errno. */
+struct xfs_bulkstat_req *
+xfrog_bulkstat_alloc_req(
+	uint32_t		nr,
+	uint64_t		startino)
+{
+	struct xfs_bulkstat_req	*breq;
+
+	breq = calloc(1, XFS_BULKSTAT_REQ_SIZE(nr));
+	if (!breq)
+		return NULL;
+
+	breq->hdr.icount = nr;
+	breq->hdr.ino = startino;
+
+	return breq;
+}
+
 /*
  * Query inode allocation bitmask information.  Returns zero or a positive
  * error code.
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index 83ac0e37..bbbc69a2 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -8,10 +8,16 @@
 
 /* Bulkstat wrappers */
 struct xfs_bstat;
-int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino,
-		struct xfs_bstat *ubuffer);
-int xfrog_bulkstat(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
-		struct xfs_bstat *ubuffer, uint32_t *ocount);
+int xfrog_bulkstat_single(struct xfs_fd *xfd, uint64_t ino, unsigned int flags,
+		struct xfs_bulkstat *bulkstat);
+int xfrog_bulkstat(struct xfs_fd *xfd, struct xfs_bulkstat_req *req);
+
+struct xfs_bulkstat_req *xfrog_bulkstat_alloc_req(uint32_t nr,
+		uint64_t startino);
+int xfrog_bulkstat_v5_to_v1(struct xfs_fd *xfd, struct xfs_bstat *bs1,
+		const struct xfs_bulkstat *bstat);
+void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
+		const struct xfs_bstat *bs1);
 
 struct xfs_inogrp;
 int xfrog_inumbers(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h
index 55b14c2b..ca38324e 100644
--- a/libfrog/fsgeom.h
+++ b/libfrog/fsgeom.h
@@ -39,8 +39,17 @@ struct xfs_fd {
 
 	/* log2 of sb_blocksize / sb_sectsize */
 	unsigned int		blkbb_log;
+
+	/* XFROG_FLAG_* state flags */
+	unsigned int		flags;
 };
 
+/* Only use v1 bulkstat/inumbers ioctls. */
+#define XFROG_FLAG_BULKSTAT_FORCE_V1	(1 << 0)
+
+/* Only use v5 bulkstat/inumbers ioctls. */
+#define XFROG_FLAG_BULKSTAT_FORCE_V5	(1 << 1)
+
 /* Static initializers */
 #define XFS_FD_INIT(_fd)	{ .fd = (_fd), }
 #define XFS_FD_INIT_EMPTY	XFS_FD_INIT(-1)
diff --git a/quota/quot.c b/quota/quot.c
index 686b2726..7edfad16 100644
--- a/quota/quot.c
+++ b/quota/quot.c
@@ -69,7 +69,7 @@ quot_help(void)
 
 static void
 quot_bulkstat_add(
-	struct xfs_bstat *p,
+	struct xfs_bulkstat	*p,
 	uint		flags)
 {
 	du_t		*dp;
@@ -93,7 +93,7 @@ quot_bulkstat_add(
 	}
 	for (i = 0; i < 3; i++) {
 		id = (i == 0) ? p->bs_uid : ((i == 1) ?
-			p->bs_gid : bstat_get_projid(p));
+			p->bs_gid : p->bs_projectid);
 		hp = &duhash[i][id % DUHASH];
 		for (dp = *hp; dp; dp = dp->next)
 			if (dp->id == id)
@@ -113,11 +113,11 @@ quot_bulkstat_add(
 		}
 		dp->blocks += size;
 
-		if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
+		if (now - p->bs_atime > 30 * (60*60*24))
 			dp->blocks30 += size;
-		if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
+		if (now - p->bs_atime > 60 * (60*60*24))
 			dp->blocks60 += size;
-		if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
+		if (now - p->bs_atime > 90 * (60*60*24))
 			dp->blocks90 += size;
 		dp->nfiles++;
 	}
@@ -129,9 +129,7 @@ quot_bulkstat_mount(
 	unsigned int		flags)
 {
 	struct xfs_fd		fsxfd = XFS_FD_INIT_EMPTY;
-	struct xfs_bstat	*buf;
-	uint64_t		last = 0;
-	uint32_t		count;
+	struct xfs_bulkstat_req	*breq;
 	int			i, sts, ret;
 	du_t			**dp;
 
@@ -154,25 +152,24 @@ quot_bulkstat_mount(
 		return;
 	}
 
-	buf = (struct xfs_bstat *)calloc(NBSTAT, sizeof(struct xfs_bstat));
-	if (!buf) {
+	breq = xfrog_bulkstat_alloc_req(NBSTAT, 0);
+	if (!breq) {
 		perror("calloc");
 		xfd_close(&fsxfd);
 		return;
 	}
 
-	while ((sts = xfrog_bulkstat(&fsxfd, &last, NBSTAT, buf,
-				&count)) == 0) {
-		if (count == 0)
+	while ((sts = xfrog_bulkstat(&fsxfd, breq)) == 0) {
+		if (breq->hdr.ocount == 0)
 			break;
-		for (i = 0; i < count; i++)
-			quot_bulkstat_add(&buf[i], flags);
+		for (i = 0; i < breq->hdr.ocount; i++)
+			quot_bulkstat_add(&breq->bulkstat[i], flags);
 	}
 	if (sts < 0) {
 		errno = sts;
 		perror("XFS_IOC_FSBULKSTAT");
 	}
-	free(buf);
+	free(breq);
 	xfd_close(&fsxfd);
 }
 
diff --git a/scrub/inodes.c b/scrub/inodes.c
index 580a845e..4c95f635 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -50,9 +50,9 @@ static void
 xfs_iterate_inodes_range_check(
 	struct scrub_ctx	*ctx,
 	struct xfs_inogrp	*inogrp,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
-	struct xfs_bstat	*bs;
+	struct xfs_bulkstat	*bs;
 	int			i;
 	int			error;
 
@@ -66,9 +66,9 @@ xfs_iterate_inodes_range_check(
 
 		/* Load the one inode. */
 		error = xfrog_bulkstat_single(&ctx->mnt,
-				inogrp->xi_startino + i, bs);
+				inogrp->xi_startino + i, 0, bs);
 		if (error || bs->bs_ino != inogrp->xi_startino + i) {
-			memset(bs, 0, sizeof(struct xfs_bstat));
+			memset(bs, 0, sizeof(struct xfs_bulkstat));
 			bs->bs_ino = inogrp->xi_startino + i;
 			bs->bs_blksize = ctx->mnt_sv.f_frsize;
 		}
@@ -93,41 +93,41 @@ xfs_iterate_inodes_range(
 {
 	struct xfs_handle	handle;
 	struct xfs_inogrp	inogrp;
-	struct xfs_bstat	bstat[XFS_INODES_PER_CHUNK];
+	struct xfs_bulkstat_req	*breq;
 	char			idescr[DESCR_BUFSZ];
-	struct xfs_bstat	*bs;
+	struct xfs_bulkstat	*bs;
 	uint64_t		igrp_ino;
-	uint64_t		ino;
-	uint32_t		bulklen = 0;
 	uint32_t		igrplen = 0;
 	bool			moveon = true;
 	int			i;
 	int			error;
 	int			stale_count = 0;
 
-
-	memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat));
-
 	memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid));
 	handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
 			sizeof(handle.ha_fid.fid_len);
 	handle.ha_fid.fid_pad = 0;
 
+	breq = xfrog_bulkstat_alloc_req(XFS_INODES_PER_CHUNK, 0);
+	if (!breq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		return false;
+	}
+
 	/* Find the inode chunk & alloc mask */
 	igrp_ino = first_ino;
 	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
 	while (!error && igrplen) {
-		/* Load the inodes. */
-		ino = inogrp.xi_startino - 1;
-
 		/*
 		 * We can have totally empty inode chunks on filesystems where
 		 * there are more than 64 inodes per block.  Skip these.
 		 */
 		if (inogrp.xi_alloccount == 0)
 			goto igrp_retry;
-		error = xfrog_bulkstat(&ctx->mnt, &ino, inogrp.xi_alloccount,
-				bstat, &bulklen);
+
+		breq->hdr.ino = inogrp.xi_startino;
+		breq->hdr.icount = inogrp.xi_alloccount;
+		error = xfrog_bulkstat(&ctx->mnt, breq);
 		if (error) {
 			char	errbuf[DESCR_BUFSZ];
 
@@ -135,10 +135,12 @@ xfs_iterate_inodes_range(
 						errbuf, DESCR_BUFSZ));
 		}
 
-		xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
+		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
 
 		/* Iterate all the inodes. */
-		for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
+		for (i = 0, bs = breq->bulkstat;
+		     i < inogrp.xi_alloccount;
+		     i++, bs++) {
 			if (bs->bs_ino > last_ino)
 				goto out;
 
@@ -185,6 +187,7 @@ _("Changed too many times during scan; giving up."));
 		moveon = false;
 	}
 out:
+	free(breq);
 	return moveon;
 }
 
diff --git a/scrub/inodes.h b/scrub/inodes.h
index 631848c3..3341c6d9 100644
--- a/scrub/inodes.h
+++ b/scrub/inodes.h
@@ -7,7 +7,7 @@
 #define XFS_SCRUB_INODES_H_
 
 typedef int (*xfs_inode_iter_fn)(struct scrub_ctx *ctx,
-		struct xfs_handle *handle, struct xfs_bstat *bs, void *arg);
+		struct xfs_handle *handle, struct xfs_bulkstat *bs, void *arg);
 
 #define XFS_ITERATE_INODES_ABORT	(-1)
 bool xfs_scan_all_inodes(struct scrub_ctx *ctx, xfs_inode_iter_fn fn,
diff --git a/scrub/phase3.c b/scrub/phase3.c
index 81c64cd1..a32d1ced 100644
--- a/scrub/phase3.c
+++ b/scrub/phase3.c
@@ -30,7 +30,7 @@ xfs_scrub_fd(
 	struct scrub_ctx	*ctx,
 	bool			(*fn)(struct scrub_ctx *ctx, uint64_t ino,
 				      uint32_t gen, struct xfs_action_list *a),
-	struct xfs_bstat	*bs,
+	struct xfs_bulkstat	*bs,
 	struct xfs_action_list	*alist)
 {
 	return fn(ctx, bs->bs_ino, bs->bs_gen, alist);
@@ -45,7 +45,7 @@ struct scrub_inode_ctx {
 static void
 xfs_scrub_inode_vfs_error(
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	char			descr[DESCR_BUFSZ];
 	xfs_agnumber_t		agno;
@@ -65,7 +65,7 @@ static int
 xfs_scrub_inode(
 	struct scrub_ctx	*ctx,
 	struct xfs_handle	*handle,
-	struct xfs_bstat	*bstat,
+	struct xfs_bulkstat	*bstat,
 	void			*arg)
 {
 	struct xfs_action_list	alist;
diff --git a/scrub/phase5.c b/scrub/phase5.c
index 3ff34251..99cd51b2 100644
--- a/scrub/phase5.c
+++ b/scrub/phase5.c
@@ -80,7 +80,7 @@ xfs_scrub_scan_dirents(
 	struct scrub_ctx	*ctx,
 	const char		*descr,
 	int			*fd,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	struct unicrash		*uc = NULL;
 	DIR			*dir;
@@ -140,7 +140,7 @@ xfs_scrub_scan_fhandle_namespace_xattrs(
 	struct scrub_ctx		*ctx,
 	const char			*descr,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat,
+	struct xfs_bulkstat		*bstat,
 	const struct attrns_decode	*attr_ns)
 {
 	struct attrlist_cursor		cur;
@@ -200,7 +200,7 @@ xfs_scrub_scan_fhandle_xattrs(
 	struct scrub_ctx		*ctx,
 	const char			*descr,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat)
+	struct xfs_bulkstat		*bstat)
 {
 	const struct attrns_decode	*ns;
 	bool				moveon = true;
@@ -228,7 +228,7 @@ static int
 xfs_scrub_connections(
 	struct scrub_ctx	*ctx,
 	struct xfs_handle	*handle,
-	struct xfs_bstat	*bstat,
+	struct xfs_bulkstat	*bstat,
 	void			*arg)
 {
 	bool			*pmoveon = arg;
diff --git a/scrub/phase6.c b/scrub/phase6.c
index 506e75d2..b41f90e0 100644
--- a/scrub/phase6.c
+++ b/scrub/phase6.c
@@ -172,7 +172,7 @@ static int
 xfs_report_verify_inode(
 	struct scrub_ctx		*ctx,
 	struct xfs_handle		*handle,
-	struct xfs_bstat		*bstat,
+	struct xfs_bulkstat		*bstat,
 	void				*arg)
 {
 	char				descr[DESCR_BUFSZ];
diff --git a/scrub/unicrash.c b/scrub/unicrash.c
index 17e8f34f..b02c5658 100644
--- a/scrub/unicrash.c
+++ b/scrub/unicrash.c
@@ -432,7 +432,7 @@ unicrash_init(
  */
 static bool
 is_only_root_writable(
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	if (bstat->bs_uid != 0 || bstat->bs_gid != 0)
 		return false;
@@ -444,7 +444,7 @@ bool
 unicrash_dir_init(
 	struct unicrash		**ucp,
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	/*
 	 * Assume 64 bytes per dentry, clamp buckets between 16 and 64k.
@@ -459,7 +459,7 @@ bool
 unicrash_xattr_init(
 	struct unicrash		**ucp,
 	struct scrub_ctx	*ctx,
-	struct xfs_bstat	*bstat)
+	struct xfs_bulkstat	*bstat)
 {
 	/* Assume 16 attributes per extent for lack of a better idea. */
 	return unicrash_init(ucp, ctx, false, 16 * (1 + bstat->bs_aextents),
diff --git a/scrub/unicrash.h b/scrub/unicrash.h
index fb8f5f72..feb9cc86 100644
--- a/scrub/unicrash.h
+++ b/scrub/unicrash.h
@@ -14,9 +14,9 @@ struct unicrash;
 struct dirent;
 
 bool unicrash_dir_init(struct unicrash **ucp, struct scrub_ctx *ctx,
-		struct xfs_bstat *bstat);
+		struct xfs_bulkstat *bstat);
 bool unicrash_xattr_init(struct unicrash **ucp, struct scrub_ctx *ctx,
-		struct xfs_bstat *bstat);
+		struct xfs_bulkstat *bstat);
 bool unicrash_fs_label_init(struct unicrash **ucp, struct scrub_ctx *ctx);
 void unicrash_free(struct unicrash *uc);
 bool unicrash_check_dir_name(struct unicrash *uc, const char *descr,
diff --git a/spaceman/health.c b/spaceman/health.c
index a8bd3f3e..b195a229 100644
--- a/spaceman/health.c
+++ b/spaceman/health.c
@@ -208,7 +208,7 @@ report_inode_health(
 	unsigned long long	ino,
 	const char		*descr)
 {
-	struct xfs_bstat	bs;
+	struct xfs_bulkstat	bs;
 	char			d[256];
 	int			ret;
 
@@ -217,7 +217,7 @@ report_inode_health(
 		descr = d;
 	}
 
-	ret = xfrog_bulkstat_single(&file->xfd, ino, &bs);
+	ret = xfrog_bulkstat_single(&file->xfd, ino, 0, &bs);
 	if (ret) {
 		errno = ret;
 		perror(descr);
@@ -266,11 +266,10 @@ static int
 report_bulkstat_health(
 	xfs_agnumber_t		agno)
 {
-	struct xfs_bstat	bstat[BULKSTAT_NR];
+	struct xfs_bulkstat_req	*breq;
 	char			descr[256];
 	uint64_t		startino = 0;
 	uint64_t		lastino = -1ULL;
-	uint32_t		ocount;
 	uint32_t		i;
 	int			error;
 
@@ -279,26 +278,34 @@ report_bulkstat_health(
 		lastino = cvt_agino_to_ino(&file->xfd, agno + 1, 0) - 1;
 	}
 
+	breq = xfrog_bulkstat_alloc_req(BULKSTAT_NR, startino);
+	if (!breq) {
+		perror("bulk alloc req");
+		exitcode = 1;
+		return 1;
+	}
+
 	do {
-		error = xfrog_bulkstat(&file->xfd, &startino, BULKSTAT_NR,
-				bstat, &ocount);
+		error = xfrog_bulkstat(&file->xfd, breq);
 		if (error)
 			break;
-		for (i = 0; i < ocount; i++) {
-			if (bstat[i].bs_ino > lastino)
+		for (i = 0; i < breq->hdr.ocount; i++) {
+			if (breq->bulkstat[i].bs_ino > lastino)
 				goto out;
-			snprintf(descr, sizeof(descr) - 1, _("inode %llu"),
-					bstat[i].bs_ino);
-			report_sick(descr, inode_flags, bstat[i].bs_sick,
-					bstat[i].bs_checked);
+			snprintf(descr, sizeof(descr) - 1, _("inode %"PRIu64),
+					breq->bulkstat[i].bs_ino);
+			report_sick(descr, inode_flags,
+					breq->bulkstat[i].bs_sick,
+					breq->bulkstat[i].bs_checked);
 		}
-	} while (ocount > 0);
+	} while (breq->hdr.ocount > 0);
 
 	if (error) {
 		errno = error;
 		perror("bulkstat");
 	}
 out:
+	free(breq);
 	return error;
 }
 

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

* [PATCH v2 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS
  2019-09-25 21:32 ` [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS Darrick J. Wong
  2019-09-26 21:48   ` Eric Sandeen
@ 2019-09-27 20:15   ` Darrick J. Wong
  2019-09-27 20:30     ` Eric Sandeen
  1 sibling, 1 reply; 24+ messages in thread
From: Darrick J. Wong @ 2019-09-27 20:15 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

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

Convert all programs to use the v5 inumbers ioctl.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/imap.c          |   26 +++++-----
 io/open.c          |   29 +++++++----
 libfrog/bulkstat.c |  132 ++++++++++++++++++++++++++++++++++++++++++++++------
 libfrog/bulkstat.h |   10 +++-
 scrub/fscounters.c |   21 +++++---
 scrub/inodes.c     |   46 ++++++++++--------
 6 files changed, 194 insertions(+), 70 deletions(-)

diff --git a/io/imap.c b/io/imap.c
index 472c1fda..fa69676e 100644
--- a/io/imap.c
+++ b/io/imap.c
@@ -17,9 +17,7 @@ static int
 imap_f(int argc, char **argv)
 {
 	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
-	struct xfs_inogrp	*t;
-	uint64_t		last = 0;
-	uint32_t		count;
+	struct xfs_inumbers_req	*ireq;
 	uint32_t		nent;
 	int			i;
 	int			error;
@@ -29,17 +27,19 @@ imap_f(int argc, char **argv)
 	else
 		nent = atoi(argv[1]);
 
-	t = malloc(nent * sizeof(*t));
-	if (!t)
+	ireq = xfrog_inumbers_alloc_req(nent, 0);
+	if (!ireq) {
+		perror("alloc req");
 		return 0;
+	}
 
-	while ((error = xfrog_inumbers(&xfd, &last, nent, t, &count)) == 0 &&
-	       count > 0) {
-		for (i = 0; i < count; i++) {
-			printf(_("ino %10llu count %2d mask %016llx\n"),
-				(unsigned long long)t[i].xi_startino,
-				t[i].xi_alloccount,
-				(unsigned long long)t[i].xi_allocmask);
+	while ((error = xfrog_inumbers(&xfd, ireq)) == 0 &&
+	       ireq->hdr.ocount > 0) {
+		for (i = 0; i < ireq->hdr.ocount; i++) {
+			printf(_("ino %10"PRIu64" count %2d mask %016"PRIx64"\n"),
+				ireq->inumbers[i].xi_startino,
+				ireq->inumbers[i].xi_alloccount,
+				ireq->inumbers[i].xi_allocmask);
 		}
 	}
 
@@ -48,7 +48,7 @@ imap_f(int argc, char **argv)
 		perror("xfsctl(XFS_IOC_FSINUMBERS)");
 		exitcode = 1;
 	}
-	free(t);
+	free(ireq);
 	return 0;
 }
 
diff --git a/io/open.c b/io/open.c
index e0e7fb3e..fa9ca01a 100644
--- a/io/open.c
+++ b/io/open.c
@@ -681,39 +681,44 @@ static __u64
 get_last_inode(void)
 {
 	struct xfs_fd		xfd = XFS_FD_INIT(file->fd);
-	uint64_t		lastip = 0;
+	struct xfs_inumbers_req	*ireq;
 	uint32_t		lastgrp = 0;
-	uint32_t		ocount = 0;
-	__u64			last_ino;
-	struct xfs_inogrp	igroup[IGROUP_NR];
+	__u64			last_ino = 0;
+
+	ireq = xfrog_inumbers_alloc_req(IGROUP_NR, 0);
+	if (!ireq) {
+		perror("alloc req");
+		return 0;
+	}
 
 	for (;;) {
 		int		ret;
 
-		ret = xfrog_inumbers(&xfd, &lastip, IGROUP_NR, igroup,
-				&ocount);
+		ret = xfrog_inumbers(&xfd, ireq);
 		if (ret) {
 			errno = ret;
 			perror("XFS_IOC_FSINUMBERS");
-			return 0;
+			goto out;
 		}
 
 		/* Did we reach the last inode? */
-		if (ocount == 0)
+		if (ireq->hdr.ocount == 0)
 			break;
 
 		/* last inode in igroup table */
-		lastgrp = ocount;
+		lastgrp = ireq->hdr.ocount;
 	}
 
 	if (lastgrp == 0)
-		return 0;
+		goto out;
 
 	lastgrp--;
 
 	/* The last inode number in use */
-	last_ino = igroup[lastgrp].xi_startino +
-		  libxfs_highbit64(igroup[lastgrp].xi_allocmask);
+	last_ino = ireq->inumbers[lastgrp].xi_startino +
+		  libxfs_highbit64(ireq->inumbers[lastgrp].xi_allocmask);
+out:
+	free(ireq);
 
 	return last_ino;
 }
diff --git a/libfrog/bulkstat.c b/libfrog/bulkstat.c
index 300963f1..85594e5e 100644
--- a/libfrog/bulkstat.c
+++ b/libfrog/bulkstat.c
@@ -435,6 +435,86 @@ xfrog_bulkstat_alloc_req(
 	return breq;
 }
 
+/* Convert a inumbers data from v5 format to v1 format. */
+void
+xfrog_inumbers_v5_to_v1(
+	struct xfs_inogrp		*ig1,
+	const struct xfs_inumbers	*ig5)
+{
+	ig1->xi_startino = ig5->xi_startino;
+	ig1->xi_alloccount = ig5->xi_alloccount;
+	ig1->xi_allocmask = ig5->xi_allocmask;
+}
+
+/* Convert a inumbers data from v1 format to v5 format. */
+void
+xfrog_inumbers_v1_to_v5(
+	struct xfs_inumbers		*ig5,
+	const struct xfs_inogrp		*ig1)
+{
+	memset(ig5, 0, sizeof(*ig5));
+	ig5->xi_version = XFS_INUMBERS_VERSION_V1;
+
+	ig5->xi_startino = ig1->xi_startino;
+	ig5->xi_alloccount = ig1->xi_alloccount;
+	ig5->xi_allocmask = ig1->xi_allocmask;
+}
+
+static uint64_t xfrog_inum_ino(void *v1_rec)
+{
+	return ((struct xfs_inogrp *)v1_rec)->xi_startino;
+}
+
+static void xfrog_inum_cvt(struct xfs_fd *xfd, void *v5, void *v1)
+{
+	xfrog_inumbers_v1_to_v5(v5, v1);
+}
+
+/* Query inode allocation bitmask information using v5 ioctl. */
+static int
+xfrog_inumbers5(
+	struct xfs_fd		*xfd,
+	struct xfs_inumbers_req	*req)
+{
+	int			ret;
+
+	ret = ioctl(xfd->fd, XFS_IOC_INUMBERS, req);
+	if (ret)
+		return errno;
+	return 0;
+}
+
+/* Query inode allocation bitmask information using v1 ioctl. */
+static int
+xfrog_inumbers1(
+	struct xfs_fd		*xfd,
+	struct xfs_inumbers_req	*req)
+{
+	struct xfs_fsop_bulkreq	bulkreq = { 0 };
+	int			error;
+
+	error = xfrog_bulkstat_prep_v1_emulation(xfd);
+	if (error)
+		return error;
+
+	error = xfrog_bulk_req_v1_setup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_inogrp));
+	if (error == ECANCELED)
+		goto out_teardown;
+	if (error)
+		return error;
+
+	error = ioctl(xfd->fd, XFS_IOC_FSINUMBERS, &bulkreq);
+	if (error)
+		error = errno;
+
+out_teardown:
+	return xfrog_bulk_req_v1_cleanup(xfd, &req->hdr, &bulkreq,
+			sizeof(struct xfs_inogrp), xfrog_inum_ino,
+			&req->inumbers, sizeof(struct xfs_inumbers),
+			xfrog_inum_cvt, 64, error);
+}
+
 /*
  * Query inode allocation bitmask information.  Returns zero or a positive
  * error code.
@@ -442,21 +522,43 @@ xfrog_bulkstat_alloc_req(
 int
 xfrog_inumbers(
 	struct xfs_fd		*xfd,
-	uint64_t		*lastino,
-	uint32_t		icount,
-	struct xfs_inogrp	*ubuffer,
-	uint32_t		*ocount)
+	struct xfs_inumbers_req	*req)
 {
-	struct xfs_fsop_bulkreq	bulkreq = {
-		.lastip		= (__u64 *)lastino,
-		.icount		= icount,
-		.ubuffer	= ubuffer,
-		.ocount		= (__s32 *)ocount,
-	};
-	int			ret;
+	int			error;
 
-	ret = ioctl(xfd->fd, XFS_IOC_FSINUMBERS, &bulkreq);
-	if (ret)
-		return errno;
-	return 0;
+	if (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V1)
+		goto try_v1;
+
+	error = xfrog_inumbers5(xfd, req);
+	if (error == 0 || (xfd->flags & XFROG_FLAG_BULKSTAT_FORCE_V5))
+		return error;
+
+	/* If the v5 ioctl wasn't found, we punt to v1. */
+	switch (error) {
+	case EOPNOTSUPP:
+	case ENOTTY:
+		xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
+		break;
+	}
+
+try_v1:
+	return xfrog_inumbers1(xfd, req);
+}
+
+/* Allocate a inumbers request.  On error returns NULL and sets errno. */
+struct xfs_inumbers_req *
+xfrog_inumbers_alloc_req(
+	uint32_t		nr,
+	uint64_t		startino)
+{
+	struct xfs_inumbers_req	*ireq;
+
+	ireq = calloc(1, XFS_INUMBERS_REQ_SIZE(nr));
+	if (!ireq)
+		return NULL;
+
+	ireq->hdr.icount = nr;
+	ireq->hdr.ino = startino;
+
+	return ireq;
 }
diff --git a/libfrog/bulkstat.h b/libfrog/bulkstat.h
index bbbc69a2..a085da3d 100644
--- a/libfrog/bulkstat.h
+++ b/libfrog/bulkstat.h
@@ -20,7 +20,13 @@ void xfrog_bulkstat_v1_to_v5(struct xfs_fd *xfd, struct xfs_bulkstat *bstat,
 		const struct xfs_bstat *bs1);
 
 struct xfs_inogrp;
-int xfrog_inumbers(struct xfs_fd *xfd, uint64_t *lastino, uint32_t icount,
-		struct xfs_inogrp *ubuffer, uint32_t *ocount);
+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_v5_to_v1(struct xfs_inogrp *ig1,
+		const struct xfs_inumbers *ig);
+void xfrog_inumbers_v1_to_v5(struct xfs_inumbers *ig,
+		const struct xfs_inogrp *ig1);
 
 #endif	/* __LIBFROG_BULKSTAT_H__ */
diff --git a/scrub/fscounters.c b/scrub/fscounters.c
index 8e4b3467..2fdf658a 100644
--- a/scrub/fscounters.c
+++ b/scrub/fscounters.c
@@ -42,23 +42,28 @@ xfs_count_inodes_range(
 	uint64_t		last_ino,
 	uint64_t		*count)
 {
-	struct xfs_inogrp	inogrp;
-	uint64_t		igrp_ino;
+	struct xfs_inumbers_req	*ireq;
 	uint64_t		nr = 0;
-	uint32_t		igrplen = 0;
 	int			error;
 
 	ASSERT(!(first_ino & (XFS_INODES_PER_CHUNK - 1)));
 	ASSERT((last_ino & (XFS_INODES_PER_CHUNK - 1)));
 
-	igrp_ino = first_ino;
-	while (!(error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
-			&igrplen))) {
-		if (igrplen == 0 || inogrp.xi_startino >= last_ino)
+	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	if (!ireq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		return false;
+	}
+
+	while (!(error = xfrog_inumbers(&ctx->mnt, ireq))) {
+		if (ireq->hdr.ocount == 0 ||
+		    ireq->inumbers[0].xi_startino >= last_ino)
 			break;
-		nr += inogrp.xi_alloccount;
+		nr += ireq->inumbers[0].xi_alloccount;
 	}
 
+	free(ireq);
+
 	if (error) {
 		str_liberror(ctx, error, descr);
 		return false;
diff --git a/scrub/inodes.c b/scrub/inodes.c
index 4c95f635..c50f2de6 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -49,7 +49,7 @@
 static void
 xfs_iterate_inodes_range_check(
 	struct scrub_ctx	*ctx,
-	struct xfs_inogrp	*inogrp,
+	struct xfs_inumbers	*inumbers,
 	struct xfs_bulkstat	*bstat)
 {
 	struct xfs_bulkstat	*bs;
@@ -57,19 +57,19 @@ xfs_iterate_inodes_range_check(
 	int			error;
 
 	for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
-		if (!(inogrp->xi_allocmask & (1ULL << i)))
+		if (!(inumbers->xi_allocmask & (1ULL << i)))
 			continue;
-		if (bs->bs_ino == inogrp->xi_startino + i) {
+		if (bs->bs_ino == inumbers->xi_startino + i) {
 			bs++;
 			continue;
 		}
 
 		/* Load the one inode. */
 		error = xfrog_bulkstat_single(&ctx->mnt,
-				inogrp->xi_startino + i, 0, bs);
-		if (error || bs->bs_ino != inogrp->xi_startino + i) {
+				inumbers->xi_startino + i, 0, bs);
+		if (error || bs->bs_ino != inumbers->xi_startino + i) {
 			memset(bs, 0, sizeof(struct xfs_bulkstat));
-			bs->bs_ino = inogrp->xi_startino + i;
+			bs->bs_ino = inumbers->xi_startino + i;
 			bs->bs_blksize = ctx->mnt_sv.f_frsize;
 		}
 		bs++;
@@ -92,12 +92,11 @@ xfs_iterate_inodes_range(
 	void			*arg)
 {
 	struct xfs_handle	handle;
-	struct xfs_inogrp	inogrp;
+	struct xfs_inumbers_req	*ireq;
 	struct xfs_bulkstat_req	*breq;
 	char			idescr[DESCR_BUFSZ];
 	struct xfs_bulkstat	*bs;
-	uint64_t		igrp_ino;
-	uint32_t		igrplen = 0;
+	struct xfs_inumbers	*inumbers;
 	bool			moveon = true;
 	int			i;
 	int			error;
@@ -114,19 +113,26 @@ xfs_iterate_inodes_range(
 		return false;
 	}
 
+	ireq = xfrog_inumbers_alloc_req(1, first_ino);
+	if (!ireq) {
+		str_info(ctx, descr, _("Insufficient memory; giving up."));
+		free(breq);
+		return false;
+	}
+	inumbers = &ireq->inumbers[0];
+
 	/* Find the inode chunk & alloc mask */
-	igrp_ino = first_ino;
-	error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen);
-	while (!error && igrplen) {
+	error = xfrog_inumbers(&ctx->mnt, ireq);
+	while (!error && ireq->hdr.ocount > 0) {
 		/*
 		 * We can have totally empty inode chunks on filesystems where
 		 * there are more than 64 inodes per block.  Skip these.
 		 */
-		if (inogrp.xi_alloccount == 0)
+		if (inumbers->xi_alloccount == 0)
 			goto igrp_retry;
 
-		breq->hdr.ino = inogrp.xi_startino;
-		breq->hdr.icount = inogrp.xi_alloccount;
+		breq->hdr.ino = inumbers->xi_startino;
+		breq->hdr.icount = inumbers->xi_alloccount;
 		error = xfrog_bulkstat(&ctx->mnt, breq);
 		if (error) {
 			char	errbuf[DESCR_BUFSZ];
@@ -135,11 +141,11 @@ xfs_iterate_inodes_range(
 						errbuf, DESCR_BUFSZ));
 		}
 
-		xfs_iterate_inodes_range_check(ctx, &inogrp, breq->bulkstat);
+		xfs_iterate_inodes_range_check(ctx, inumbers, breq->bulkstat);
 
 		/* Iterate all the inodes. */
 		for (i = 0, bs = breq->bulkstat;
-		     i < inogrp.xi_alloccount;
+		     i < inumbers->xi_alloccount;
 		     i++, bs++) {
 			if (bs->bs_ino > last_ino)
 				goto out;
@@ -153,7 +159,7 @@ xfs_iterate_inodes_range(
 			case ESTALE:
 				stale_count++;
 				if (stale_count < 30) {
-					igrp_ino = inogrp.xi_startino;
+					ireq->hdr.ino = inumbers->xi_startino;
 					goto igrp_retry;
 				}
 				snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
@@ -177,8 +183,7 @@ _("Changed too many times during scan; giving up."));
 
 		stale_count = 0;
 igrp_retry:
-		error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp,
-				&igrplen);
+		error = xfrog_inumbers(&ctx->mnt, ireq);
 	}
 
 err:
@@ -187,6 +192,7 @@ _("Changed too many times during scan; giving up."));
 		moveon = false;
 	}
 out:
+	free(ireq);
 	free(breq);
 	return moveon;
 }

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

* Re: [PATCH v2 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics
  2019-09-27 20:14   ` [PATCH v2 " Darrick J. Wong
@ 2019-09-27 20:29     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-27 20:29 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 9/27/19 3:14 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Convert xfrog_bulkstat() and xfrog_bulkstat_single() to take arguments
> using v5 bulkstat semantics and return bulkstat information in v5
> structures.  If the v5 ioctl is not available, the xfrog wrapper should
> use the v1 ioctl to emulate v5 behaviors.  Add flags to the xfs_fd
> structure to constrain emulation for debugging purposes.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Reviewed-by: Eric Sandeen <sandeen@redhat.com>

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

* Re: [PATCH v2 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS
  2019-09-27 20:15   ` [PATCH v2 " Darrick J. Wong
@ 2019-09-27 20:30     ` Eric Sandeen
  0 siblings, 0 replies; 24+ messages in thread
From: Eric Sandeen @ 2019-09-27 20:30 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 9/27/19 3:15 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Convert all programs to use the v5 inumbers ioctl.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Reviewed-by: Eric Sandeen <sandeen@redhat.com>


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

end of thread, other threads:[~2019-09-27 20:30 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-25 21:32 [PATCH 0/4] xfsprogs: port utilities to bulkstat v5 Darrick J. Wong
2019-09-25 21:32 ` [PATCH 1/4] man: add documentation for v5 bulkstat ioctl Darrick J. Wong
2019-09-26 18:18   ` Eric Sandeen
2019-09-26 19:01     ` Darrick J. Wong
2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
2019-09-26 19:11     ` Eric Sandeen
2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
2019-09-27 17:23     ` Eric Sandeen
2019-09-25 21:32 ` [PATCH 2/4] man: add documentation for v5 inumbers ioctl Darrick J. Wong
2019-09-26 18:36   ` Eric Sandeen
2019-09-26 18:49     ` Eric Sandeen
2019-09-26 19:10   ` [PATCH v2 " Darrick J. Wong
2019-09-27  3:44   ` [PATCH v3 " Darrick J. Wong
2019-09-27 17:25     ` Eric Sandeen
2019-09-25 21:32 ` [PATCH 3/4] misc: convert xfrog_bulkstat functions to have v5 semantics Darrick J. Wong
2019-09-26 21:01   ` Eric Sandeen
2019-09-27  3:50     ` Darrick J. Wong
2019-09-27 20:14   ` [PATCH v2 " Darrick J. Wong
2019-09-27 20:29     ` Eric Sandeen
2019-09-25 21:32 ` [PATCH 4/4] misc: convert from XFS_IOC_FSINUMBERS to XFS_IOC_INUMBERS Darrick J. Wong
2019-09-26 21:48   ` Eric Sandeen
2019-09-27  3:54     ` Darrick J. Wong
2019-09-27 20:15   ` [PATCH v2 " Darrick J. Wong
2019-09-27 20:30     ` Eric Sandeen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.