All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support
@ 2017-06-15 20:35 Darrick J. Wong
  2017-06-15 20:35 ` [PATCH 01/12] xfs_io: refactor numlen into a library function Darrick J. Wong
                   ` (11 more replies)
  0 siblings, 12 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:35 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

Hi all,

This patchset implements the userspace components of the new GETFSMAP
ioctl that is now in upstream for 4.12.  This release contains various
cleanups that came up in the discussion of the v8 patchset; it should
apply directly to for-next.

The first two patches refactor string-to-number conversion routines.

The third patch chooses a faster crc32c algorithm, which provides
a small (~5%) decrease in xfs_repair runtime for metadata-heavy fses.

The fourth patch adds some autoconf magic so that client programs can
detect a platform supporting FSMAP (currently Linux) and stuff in our
own header if the system headers do not provide it.

The fifth patch introduce a 'fsmap' command to xfs_io so that users can
query the raw reverse mapping data that is exported by fsmap.  If the
experimental rmapbt feature is enabled, the returned results will be
straight out of the rmapbt; otherwise, the extent information is
synthesized out of the free space btres.

The sixth patch delegates xfs_repair's rmap comparison function to the
new libxfs version that was introduced as a part of the getfsmap kernel
patches.

The remainder of the patch set introduces xfs_spaceman, which is a new
tool to manage disk space on a mounted XFS filesystem.  The new tool can
call FITRIM, manage space reservations, and display summary information
about the free space on the filesystem.  This information is probably
most useful for developers and people surveying free space
fragmentation.

Note: The primary user of the GETFSMAP information will be the media
scan phase of the upcoming xfs_scrub tool, which will be posted later.

Questions?  Comments?

--D

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

* [PATCH 01/12] xfs_io: refactor numlen into a library function
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
@ 2017-06-15 20:35 ` Darrick J. Wong
  2017-06-21 20:40   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long Darrick J. Wong
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:35 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Refactor the competing numlen implementations into a single library function.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/input.h |    1 +
 io/bmap.c       |   19 ++++---------------
 io/fiemap.c     |   14 +-------------
 libxcmd/input.c |   13 +++++++++++++
 4 files changed, 19 insertions(+), 28 deletions(-)


diff --git a/include/input.h b/include/input.h
index 221678e..82cd2f4 100644
--- a/include/input.h
+++ b/include/input.h
@@ -28,6 +28,7 @@ extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
+extern size_t numlen(uint64_t val, size_t base);
 extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
 extern void	cvtstr(double value, char *str, size_t sz);
 extern unsigned long cvttime(char *s);
diff --git a/io/bmap.c b/io/bmap.c
index 2333244..2e4ff7b 100644
--- a/io/bmap.c
+++ b/io/bmap.c
@@ -18,6 +18,7 @@
 
 #include "platform_defs.h"
 #include "command.h"
+#include "input.h"
 #include "init.h"
 #include "io.h"
 
@@ -53,18 +54,6 @@ bmap_help(void)
 "\n"));
 }
 
-static int
-numlen(
-	off64_t	val)
-{
-	off64_t	tmp;
-	int	len;
-
-	for (len = 0, tmp = val; tmp > 0; tmp = tmp/10)
-		len++;
-	return (len == 0 ? 1 : len);
-}
-
 int
 bmap_f(
 	int			argc,
@@ -323,7 +312,7 @@ bmap_f(
 			if (map[i + 1].bmv_block == -1) {
 				foff_w = max(foff_w, strlen(rbuf));
 				tot_w = max(tot_w,
-					numlen(map[i+1].bmv_length));
+					numlen(map[i+1].bmv_length, 10));
 			} else {
 				snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
 					(long long) map[i + 1].bmv_block,
@@ -344,10 +333,10 @@ bmap_f(
 					aoff_w = 0;
 				foff_w = max(foff_w, strlen(rbuf));
 				tot_w = max(tot_w,
-					numlen(map[i+1].bmv_length));
+					numlen(map[i+1].bmv_length, 10));
 			}
 		}
-		agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount));
+		agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount, 10));
 		printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
 			_("EXT"),
 			foff_w, _("FILE-OFFSET"),
diff --git a/io/fiemap.c b/io/fiemap.c
index bcbae49..75e8820 100644
--- a/io/fiemap.c
+++ b/io/fiemap.c
@@ -18,6 +18,7 @@
 
 #include "platform_defs.h"
 #include "command.h"
+#include "input.h"
 #include <linux/fiemap.h>
 #include "init.h"
 #include "io.h"
@@ -48,19 +49,6 @@ fiemap_help(void)
 "\n"));
 }
 
-static int
-numlen(
-	__u64	val,
-	int	base)
-{
-	__u64	tmp;
-	int	len;
-
-	for (len = 0, tmp = val; tmp > 0; tmp = tmp/base)
-		len++;
-	return (len == 0 ? 1 : len);
-}
-
 static void
 print_verbose(
 	struct fiemap_extent	*extent,
diff --git a/libxcmd/input.c b/libxcmd/input.c
index 8aeb3b0..9437be3 100644
--- a/libxcmd/input.c
+++ b/libxcmd/input.c
@@ -136,6 +136,19 @@ doneline(
 	free(vec);
 }
 
+size_t
+numlen(
+	uint64_t	val,
+	size_t		base)
+{
+	uint64_t	tmp;
+	size_t		len;
+
+	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
+		len++;
+	return len == 0 ? 1 : len;
+}
+
 #define EXABYTES(x)	((long long)(x) << 60)
 #define PETABYTES(x)	((long long)(x) << 50)
 #define TERABYTES(x)	((long long)(x) << 40)


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

* [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
  2017-06-15 20:35 ` [PATCH 01/12] xfs_io: refactor numlen into a library function Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 20:16   ` Eric Sandeen
  2017-06-21 21:19   ` [PATCH v2 " Darrick J. Wong
  2017-06-15 20:36 ` [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default Darrick J. Wong
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Create some helper functions to convert strings to int or long
in a standard way and work around problems in the C library
atoi/strtol functions.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/input.h |    8 +++
 libxcmd/input.c |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)


diff --git a/include/input.h b/include/input.h
index 82cd2f4..145114b 100644
--- a/include/input.h
+++ b/include/input.h
@@ -28,6 +28,14 @@ extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
+extern int64_t	cvt_s64(char *s, int base);
+extern int32_t	cvt_s32(char *s, int base);
+extern int16_t	cvt_s16(char *s, int base);
+
+extern uint64_t	cvt_u64(char *s, int base);
+extern uint32_t	cvt_u32(char *s, int base);
+extern uint16_t	cvt_u16(char *s, int base);
+
 extern size_t numlen(uint64_t val, size_t base);
 extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
 extern void	cvtstr(double value, char *str, size_t sz);
diff --git a/libxcmd/input.c b/libxcmd/input.c
index 9437be3..8dd232c 100644
--- a/libxcmd/input.c
+++ b/libxcmd/input.c
@@ -149,6 +149,140 @@ numlen(
 	return len == 0 ? 1 : len;
 }
 
+/*
+ * Convert string to int64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+int64_t
+cvt_s64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	if (*sp == '\0' && sp != s)
+		return i;
+bad:
+	errno = -ERANGE;
+	return INT64_MIN;
+}
+
+/*
+ * Convert string to int32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+int32_t
+cvt_s32(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT32_MAX || i < INT32_MIN) {
+		errno = -ERANGE;
+		return INT32_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to int16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+int16_t
+cvt_s16(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT16_MAX || i < INT16_MIN) {
+		errno = -ERANGE;
+		return INT16_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+uint64_t
+cvt_u64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	if (*sp == '\0' && sp != s)
+		return i;
+bad:
+	errno = -ERANGE;
+	return UINT64_MAX;
+}
+
+/*
+ * Convert string to uint32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+uint32_t
+cvt_u32(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT32_MAX) {
+		errno = -ERANGE;
+		return UINT32_MAX;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion.
+ */
+uint16_t
+cvt_u16(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT16_MAX) {
+		errno = -ERANGE;
+		return UINT16_MAX;
+	}
+	return i;
+}
+
 #define EXABYTES(x)	((long long)(x) << 60)
 #define PETABYTES(x)	((long long)(x) << 50)
 #define TERABYTES(x)	((long long)(x) << 40)


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

* [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
  2017-06-15 20:35 ` [PATCH 01/12] xfs_io: refactor numlen into a library function Darrick J. Wong
  2017-06-15 20:36 ` [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 20:42   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

The crc32c code used in xfsprogs was copied directly from the Linux
kernel.  However, that code selects slice-by-4 by default, which isn't
the fastest -- that's slice-by-8, which trades table size for speed.
Fix some makefile dependency problems and explicitly select the
algorithm we want.  With this patch applied, I see about a 10% drop in
CPU time running xfs_repair.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libxfs/Makefile    |    4 ++--
 libxfs/crc32defs.h |   34 ++++++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+), 2 deletions(-)


diff --git a/libxfs/Makefile b/libxfs/Makefile
index baba02f..d248c1f 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -122,7 +122,7 @@ LDIRT = gen_crc32table crc32table.h crc32selftest
 
 default: crc32selftest ltdepend $(LTLIBRARY)
 
-crc32table.h: gen_crc32table.c
+crc32table.h: gen_crc32table.c crc32defs.h
 	@echo "    [CC]     gen_crc32table"
 	$(Q) $(BUILD_CC) $(BUILD_CFLAGS) -o gen_crc32table $<
 	@echo "    [GENERATE] $@"
@@ -133,7 +133,7 @@ crc32table.h: gen_crc32table.c
 # systems/architectures. Hence we make sure that xfsprogs will never use a
 # busted CRC calculation at build time and hence avoid putting bad CRCs down on
 # disk.
-crc32selftest: gen_crc32table.c crc32table.h crc32.c
+crc32selftest: gen_crc32table.c crc32table.h crc32.c crc32defs.h
 	@echo "    [TEST]    CRC32"
 	$(Q) $(BUILD_CC) $(BUILD_CFLAGS) -D CRC32_SELFTEST=1 crc32.c -o $@
 	$(Q) ./$@
diff --git a/libxfs/crc32defs.h b/libxfs/crc32defs.h
index 64cba2c..2999782 100644
--- a/libxfs/crc32defs.h
+++ b/libxfs/crc32defs.h
@@ -1,4 +1,38 @@
 /*
+ * Use slice-by-8, which is the fastest variant.
+ *
+ * Calculate checksum 8 bytes at a time with a clever slicing algorithm.
+ * This is the fastest algorithm, but comes with a 8KiB lookup table.
+ * Most modern processors have enough cache to hold this table without
+ * thrashing the cache.
+ *
+ * The Linux kernel uses this as the default implementation "unless you
+ * have a good reason not to".  The reason why Kconfig urges you to pick
+ * SLICEBY8 is because people challenged the assertion that we should
+ * always use slice by 8, so Darrick wrote a crc microbenchmark utility
+ * and ran it on as many machines as he could get his hands on to show
+ * that sb8 was the fastest.
+ *
+ * Every 64-bit machine (and most of the 32-bit ones too) saw the best
+ * results with sb8.  Any machine with more than 4K of cache saw better
+ * results.  The spreadsheet still exists today[1]; note that
+ * 'crc32-kern-le' corresponds to the slice by 4 algorithm which is the
+ * default unless CRC_LE_BITS is defined explicitly.
+ *
+ * FWIW, there are a handful of board defconfigs in the kernel that
+ * don't pick sliceby8.  These are all embedded 32-bit mips/ppc systems
+ * with very small cache sizes which experience cache thrashing with the
+ * slice by 8 algorithm, and therefore chose to pick defaults that are
+ * saner for their particular board configuration.  For nearly all of
+ * XFS' perceived userbase (which we assume are 32 and 64-bit machines
+ * with sufficiently large CPU cache and largeish storage devices) slice
+ * by 8 is the right choice.
+ *
+ * [1] https://goo.gl/0LSzsG ("crc32c_bench")
+ */
+#define CRC_LE_BITS 64
+
+/*
  * There are multiple 16-bit CRC polynomials in common use, but this is
  * *the* standard CRC-32 polynomial, first popularized by Ethernet.
  * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0


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

* [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (2 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 20:43   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 05/12] xfs_io: support the new getfsmap ioctl Darrick J. Wong
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Introduce a new ioctl that uses the reverse mapping btree to return
information about the physical layout of the filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
[sandeen: rework to have less autoconf magic]
Modified-by: Eric Sandeen <sandeen@redhat.com>
---
v3: rework the whole mess to have less autoconf magic; now we only
track if the system headers actually define getfsmap.
v2: shorten the device field to u32 since that's all we need for
dev_t.  Support reporting reverse mapping information for all the
devices that XFS supports (data, log).
---
 configure.ac          |    1 
 include/builddefs.in  |    4 ++
 include/linux.h       |  103 +++++++++++++++++++++++++++++++++++++++++++++++++
 m4/package_libcdev.m4 |   20 ++++++++++
 4 files changed, 128 insertions(+)


diff --git a/configure.ac b/configure.ac
index aa102e4..9534986 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,7 @@ AC_HAVE_READDIR
 AC_HAVE_FSETXATTR
 AC_HAVE_MREMAP
 AC_NEED_INTERNAL_FSXATTR
+AC_HAVE_GETFSMAP
 
 if test "$enable_blkid" = yes; then
 AC_HAVE_BLKID_TOPO
diff --git a/include/builddefs.in b/include/builddefs.in
index 4d6bb2d..ec630bd 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -112,6 +112,7 @@ HAVE_FLS = @have_fls@
 HAVE_FSETXATTR = @have_fsetxattr@
 HAVE_MREMAP = @have_mremap@
 NEED_INTERNAL_FSXATTR = @need_internal_fsxattr@
+HAVE_GETFSMAP = @have_getfsmap@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
 #	   -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl
@@ -150,6 +151,9 @@ endif
 ifeq ($(NEED_INTERNAL_FSXATTR),yes)
 PCFLAGS+= -DOVERRIDE_SYSTEM_FSXATTR
 endif
+ifeq ($(HAVE_GETFSMAP),yes)
+PCFLAGS+= -DHAVE_GETFSMAP
+endif
 
 
 GCFLAGS = $(DEBUG) \
diff --git a/include/linux.h b/include/linux.h
index 6a676ca..38969d1 100644
--- a/include/linux.h
+++ b/include/linux.h
@@ -222,4 +222,107 @@ struct fsxattr {
 #define FS_XFLAG_COWEXTSIZE	0x00010000	/* CoW extent size allocator hint */
 #endif
 
+#ifndef HAVE_GETFSMAP
+/*
+ *	Structure for FS_IOC_GETFSMAP.
+ *
+ *	The memory layout for this call are the scalar values defined in
+ *	struct fsmap_head, followed by two struct fsmap that describe
+ *	the lower and upper bound of mappings to return, followed by an
+ *	array of struct fsmap mappings.
+ *
+ *	fmh_iflags control the output of the call, whereas fmh_oflags report
+ *	on the overall record output.  fmh_count should be set to the
+ *	length of the fmh_recs array, and fmh_entries will be set to the
+ *	number of entries filled out during each call.  If fmh_count is
+ *	zero, the number of reverse mappings will be returned in
+ *	fmh_entries, though no mappings will be returned.  fmh_reserved
+ *	must be set to zero.
+ *
+ *	The two elements in the fmh_keys array are used to constrain the
+ *	output.  The first element in the array should represent the
+ *	lowest disk mapping ("low key") that the user wants to learn
+ *	about.  If this value is all zeroes, the filesystem will return
+ *	the first entry it knows about.  For a subsequent call, the
+ *	contents of fsmap_head.fmh_recs[fsmap_head.fmh_count - 1] should be
+ *	copied into fmh_keys[0] to have the kernel start where it left off.
+ *
+ *	The second element in the fmh_keys array should represent the
+ *	highest disk mapping ("high key") that the user wants to learn
+ *	about.  If this value is all ones, the filesystem will not stop
+ *	until it runs out of mapping to return or runs out of space in
+ *	fmh_recs.
+ *
+ *	fmr_device can be either a 32-bit cookie representing a device, or
+ *	a 32-bit dev_t if the FMH_OF_DEV_T flag is set.  fmr_physical,
+ *	fmr_offset, and fmr_length are expressed in units of bytes.
+ *	fmr_owner is either an inode number, or a special value if
+ *	FMR_OF_SPECIAL_OWNER is set in fmr_flags.
+ */
+struct fsmap {
+	__u32		fmr_device;	/* device id */
+	__u32		fmr_flags;	/* mapping flags */
+	__u64		fmr_physical;	/* device offset of segment */
+	__u64		fmr_owner;	/* owner id */
+	__u64		fmr_offset;	/* file offset of segment */
+	__u64		fmr_length;	/* length of segment */
+	__u64		fmr_reserved[3];	/* must be zero */
+};
+
+struct fsmap_head {
+	__u32		fmh_iflags;	/* control flags */
+	__u32		fmh_oflags;	/* output flags */
+	__u32		fmh_count;	/* # of entries in array incl. input */
+	__u32		fmh_entries;	/* # of entries filled in (output). */
+	__u64		fmh_reserved[6];	/* must be zero */
+
+	struct fsmap	fmh_keys[2];	/* low and high keys for the mapping search */
+	struct fsmap	fmh_recs[];	/* returned records */
+};
+
+/* Size of an fsmap_head with room for nr records. */
+static inline size_t
+fsmap_sizeof(
+	unsigned int	nr)
+{
+	return sizeof(struct fsmap_head) + nr * sizeof(struct fsmap);
+}
+
+/* Start the next fsmap query at the end of the current query results. */
+static inline void
+fsmap_advance(
+	struct fsmap_head	*head)
+{
+	head->fmh_keys[0] = head->fmh_recs[head->fmh_entries - 1];
+}
+
+/*	fmh_iflags values - set by XFS_IOC_GETFSMAP caller in the header. */
+/* no flags defined yet */
+#define FMH_IF_VALID		0
+
+/*	fmh_oflags values - returned in the header segment only. */
+#define FMH_OF_DEV_T		0x1	/* fmr_device values will be dev_t */
+
+/*	fmr_flags values - returned for each non-header segment */
+#define FMR_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
+#define FMR_OF_ATTR_FORK	0x2	/* segment = attribute fork */
+#define FMR_OF_EXTENT_MAP	0x4	/* segment = extent map */
+#define FMR_OF_SHARED		0x8	/* segment = shared with another file */
+#define FMR_OF_SPECIAL_OWNER	0x10	/* owner is a special value */
+#define FMR_OF_LAST		0x20	/* segment is the last in the FS */
+
+/* Each FS gets to define its own special owner codes. */
+#define FMR_OWNER(type, code)	(((__u64)type << 32) | \
+				 ((__u64)code & 0xFFFFFFFFULL))
+#define FMR_OWNER_TYPE(owner)	((__u32)((__u64)owner >> 32))
+#define FMR_OWNER_CODE(owner)	((__u32)(((__u64)owner & 0xFFFFFFFFULL)))
+#define FMR_OWN_FREE		FMR_OWNER(0, 1) /* free space */
+#define FMR_OWN_UNKNOWN		FMR_OWNER(0, 2) /* unknown owner */
+#define FMR_OWN_METADATA	FMR_OWNER(0, 3) /* metadata */
+
+#define FS_IOC_GETFSMAP		_IOWR('X', 59, struct fsmap_head)
+
+#define HAVE_GETFSMAP
+#endif /* HAVE_GETFSMAP */
+
 #endif	/* __XFS_LINUX_H__ */
diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
index bc3b4ce..fa5b639 100644
--- a/m4/package_libcdev.m4
+++ b/m4/package_libcdev.m4
@@ -277,3 +277,23 @@ AC_DEFUN([AC_NEED_INTERNAL_FSXATTR],
     )
     AC_SUBST(need_internal_fsxattr)
   ])
+
+#
+# Check if we have a FS_IOC_GETFSMAP ioctl (Linux)
+#
+AC_DEFUN([AC_HAVE_GETFSMAP],
+  [ AC_MSG_CHECKING([for GETFSMAP])
+    AC_TRY_LINK([
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <linux/fs.h>
+#include <linux/fsmap.h>
+    ], [
+         unsigned long x = FS_IOC_GETFSMAP;
+         struct fsmap_head fh;
+    ], have_getfsmap=yes
+       AC_MSG_RESULT(yes),
+       AC_MSG_RESULT(no))
+    AC_SUBST(have_getfsmap)
+  ])


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

* [PATCH 05/12] xfs_io: support the new getfsmap ioctl
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (3 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 20:51   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 06/12] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Plumb in all the pieces we need to have xfs_io query the GETFSMAP ioctl
for an arbitrary filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/Makefile          |    7 +
 io/copy_file_range.c |    2 
 io/encrypt.c         |    1 
 io/fsmap.c           |  588 ++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c            |    8 +
 io/io.h              |   14 +
 io/open.c            |   21 +-
 io/pwrite.c          |    2 
 io/reflink.c         |    4 
 io/sendfile.c        |    2 
 man/man8/xfs_io.8    |   99 ++++++++
 11 files changed, 735 insertions(+), 13 deletions(-)
 create mode 100644 io/fsmap.c


diff --git a/io/Makefile b/io/Makefile
index 435ccff..47b0a66 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -99,6 +99,13 @@ ifeq ($(HAVE_MREMAP),yes)
 LCFLAGS += -DHAVE_MREMAP
 endif
 
+# On linux we get fsmap from the system or define it ourselves
+# so include this based on platform type.  If this reverts to only
+# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
+ifeq ($(PKG_PLATFORM),linux)
+CFILES += fsmap.c
+endif
+
 default: depend $(LTCOMMAND)
 
 include $(BUILDRULES)
diff --git a/io/copy_file_range.c b/io/copy_file_range.c
index 249c649..d1dfc5a 100644
--- a/io/copy_file_range.c
+++ b/io/copy_file_range.c
@@ -121,7 +121,7 @@ copy_range_f(int argc, char **argv)
 	if (optind != argc - 1)
 		return command_usage(&copy_range_cmd);
 
-	fd = openfile(argv[optind], NULL, IO_READONLY, 0);
+	fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
 	if (fd < 0)
 		return 0;
 
diff --git a/io/encrypt.c b/io/encrypt.c
index d844c5e..26ab97c 100644
--- a/io/encrypt.c
+++ b/io/encrypt.c
@@ -20,6 +20,7 @@
 #include "platform_defs.h"
 #include "command.h"
 #include "init.h"
+#include "path.h"
 #include "io.h"
 
 #ifndef ARRAY_SIZE
diff --git a/io/fsmap.c b/io/fsmap.c
new file mode 100644
index 0000000..25c0e76
--- /dev/null
+++ b/io/fsmap.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "path.h"
+#include "io.h"
+#include "input.h"
+
+static cmdinfo_t	fsmap_cmd;
+static dev_t		xfs_data_dev;
+
+static void
+fsmap_help(void)
+{
+	printf(_(
+"\n"
+" Prints the block mapping for a filesystem"
+"\n"
+" fsmap prints the map of disk blocks used by the whole filesystem.\n"
+" When possible, owner and offset information will be included in the\n"
+" sapce report.\n"
+"\n"
+" By default, each line of the listing takes the following form:\n"
+"     extent: major:minor [startblock..endblock]: owner startoffset..endoffset length\n"
+" The owner field is either an inode number or a special value.\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -d -- query only the data device (default).\n"
+" -l -- query only the log device.\n"
+" -r -- query only the realtime device.\n"
+" -n -- query n extents at a time.\n"
+" -v -- Verbose information, show AG and offsets.  Show flags legend on 2nd -v\n"
+"\n"));
+}
+
+#define OWNER_BUF_SZ	32
+static const char *
+special_owner(
+	int64_t		owner,
+	char		*buf)
+{
+	switch (owner) {
+	case XFS_FMR_OWN_FREE:
+		return _("free space");
+	case XFS_FMR_OWN_UNKNOWN:
+		return _("unknown");
+	case XFS_FMR_OWN_FS:
+		return _("static fs metadata");
+	case XFS_FMR_OWN_LOG:
+		return _("journalling log");
+	case XFS_FMR_OWN_AG:
+		return _("per-AG metadata");
+	case XFS_FMR_OWN_INOBT:
+		return _("inode btree");
+	case XFS_FMR_OWN_INODES:
+		return _("inodes");
+	case XFS_FMR_OWN_REFC:
+		return _("refcount btree");
+	case XFS_FMR_OWN_COW:
+		return _("cow reservation");
+	case XFS_FMR_OWN_DEFECTIVE:
+		return _("defective");
+	default:
+		snprintf(buf, OWNER_BUF_SZ, _("special %u:%u"),
+				FMR_OWNER_TYPE(owner), FMR_OWNER_CODE(owner));
+		return buf;
+	}
+}
+
+static void
+dump_map(
+	unsigned long long	*nr,
+	struct fsmap_head	*head)
+{
+	unsigned long long	i;
+	struct fsmap		*p;
+	char			owner[OWNER_BUF_SZ];
+	char			*fork;
+
+	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+		printf("\t%llu: %u:%u [%lld..%lld]: ", i + (*nr),
+			major(p->fmr_device), minor(p->fmr_device),
+			(long long)BTOBBT(p->fmr_physical),
+			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? _("attr") : _("data");
+		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+			printf("%s", special_owner(p->fmr_owner, owner));
+		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+			printf(_("inode %lld %s extent map"),
+				(long long) p->fmr_owner, fork);
+		else
+			printf(_("inode %lld %s %lld..%lld"),
+				(long long)p->fmr_owner, fork,
+				(long long)BTOBBT(p->fmr_offset),
+				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+		printf(_(" %lld\n"),
+			(long long)BTOBBT(p->fmr_length));
+	}
+
+	(*nr) += head->fmh_entries;
+}
+
+static void
+dump_map_machine(
+	unsigned long long	*nr,
+	struct fsmap_head	*head)
+{
+	unsigned long long	i;
+	struct fsmap		*p;
+	char			*fork;
+
+	printf(_("EXT,MAJOR,MINOR,PSTART,PEND,OWNER,OSTART,OEND,LENGTH\n"));
+	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+		printf("%llu,%u,%u,%lld,%lld,", i + (*nr),
+			major(p->fmr_device), minor(p->fmr_device),
+			(long long)BTOBBT(p->fmr_physical),
+			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? "attr" : "data";
+		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+			printf("special_%u:%u,,,", FMR_OWNER_TYPE(p->fmr_owner),
+				FMR_OWNER_CODE(p->fmr_owner));
+		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+			printf(_("inode_%lld_%s_bmbt,,,"),
+				(long long) p->fmr_owner, fork);
+		else
+			printf(_("inode_%lld_%s,%lld,%lld,"),
+				(long long)p->fmr_owner, fork,
+				(long long)BTOBBT(p->fmr_offset),
+				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+		printf("%lld\n",
+			(long long)BTOBBT(p->fmr_length));
+	}
+
+	(*nr) += head->fmh_entries;
+}
+
+/*
+ * Verbose mode displays:
+ *   extent: major:minor [startblock..endblock]: startoffset..endoffset \
+ *	ag# (agoffset..agendoffset) totalbbs flags
+ */
+#define MINRANGE_WIDTH	16
+#define MINAG_WIDTH	2
+#define MINTOT_WIDTH	5
+#define NFLG		7		/* count of flags */
+#define	FLG_NULL	00000000	/* Null flag */
+#define	FLG_ATTR_FORK	01000000	/* attribute fork */
+#define	FLG_SHARED	00100000	/* shared extent */
+#define	FLG_PRE		00010000	/* Unwritten extent */
+#define	FLG_BSU		00001000	/* Not on begin of stripe unit  */
+#define	FLG_ESU		00000100	/* Not on end   of stripe unit  */
+#define	FLG_BSW		00000010	/* Not on begin of stripe width */
+#define	FLG_ESW		00000001	/* Not on end   of stripe width */
+static void
+dump_map_verbose(
+	unsigned long long	*nr,
+	struct fsmap_head	*head,
+	bool			*dumped_flags,
+	struct xfs_fsop_geom	*fsgeo)
+{
+	unsigned long long	i;
+	struct fsmap		*p;
+	int			agno;
+	off64_t			agoff, bperag;
+	int			foff_w, boff_w, aoff_w, tot_w, agno_w, own_w;
+	int			nr_w, dev_w;
+	char			rbuf[32], bbuf[32], abuf[32], obuf[32];
+	char			nbuf[32], dbuf[32], gbuf[32];
+	char			owner[OWNER_BUF_SZ];
+	int			sunit, swidth;
+	int			flg = 0;
+
+	foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
+	dev_w = 3;
+	nr_w = 4;
+	tot_w = MINTOT_WIDTH;
+	bperag = (off64_t)fsgeo->agblocks *
+		  (off64_t)fsgeo->blocksize;
+	sunit = (fsgeo->sunit * fsgeo->blocksize);
+	swidth = (fsgeo->swidth * fsgeo->blocksize);
+
+	/*
+	 * Go through the extents and figure out the width
+	 * needed for all columns.
+	 */
+	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+		if (p->fmr_flags & FMR_OF_PREALLOC ||
+		    p->fmr_flags & FMR_OF_ATTR_FORK ||
+		    p->fmr_flags & FMR_OF_SHARED)
+			flg = 1;
+		if (sunit &&
+		    (p->fmr_physical  % sunit != 0 ||
+		     ((p->fmr_physical + p->fmr_length) % sunit) != 0 ||
+		     p->fmr_physical % swidth != 0 ||
+		     ((p->fmr_physical + p->fmr_length) % swidth) != 0))
+			flg = 1;
+		if (flg)
+			*dumped_flags = true;
+		snprintf(nbuf, sizeof(nbuf), "%llu", (*nr) + i);
+		nr_w = max(nr_w, strlen(nbuf));
+		if (head->fmh_oflags & FMH_OF_DEV_T)
+			snprintf(dbuf, sizeof(dbuf), "%u:%u",
+				major(p->fmr_device),
+				minor(p->fmr_device));
+		else
+			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
+		dev_w = max(dev_w, strlen(dbuf));
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long)BTOBBT(p->fmr_physical),
+			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+		boff_w = max(boff_w, strlen(bbuf));
+		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+			own_w = max(own_w, strlen(
+					special_owner(p->fmr_owner, owner)));
+		else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmr_owner);
+			own_w = max(own_w, strlen(obuf));
+		}
+		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+			foff_w = max(foff_w, strlen(_("extent_map")));
+		else if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
+			;
+		else {
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long)BTOBBT(p->fmr_offset),
+				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+			foff_w = max(foff_w, strlen(rbuf));
+		}
+		if (p->fmr_device == xfs_data_dev) {
+			agno = p->fmr_physical / bperag;
+			agoff = p->fmr_physical - (agno * bperag);
+			snprintf(abuf, sizeof(abuf),
+				"(%lld..%lld)",
+				(long long)BTOBBT(agoff),
+				(long long)BTOBBT(agoff + p->fmr_length - 1));
+		} else
+			abuf[0] = 0;
+		aoff_w = max(aoff_w, strlen(abuf));
+		tot_w = max(tot_w,
+			numlen(BTOBBT(p->fmr_length), 10));
+	}
+	agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount, 10));
+	if (*nr == 0)
+		printf("%*s: %-*s %-*s %-*s %-*s %*s %-*s %*s%s\n",
+			nr_w, _("EXT"),
+			dev_w, _("DEV"),
+			boff_w, _("BLOCK-RANGE"),
+			own_w, _("OWNER"),
+			foff_w, _("FILE-OFFSET"),
+			agno_w, _("AG"),
+			aoff_w, _("AG-OFFSET"),
+			tot_w, _("TOTAL"),
+			flg ? _(" FLAGS") : "");
+	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
+		flg = FLG_NULL;
+		if (p->fmr_flags & FMR_OF_PREALLOC)
+			flg |= FLG_PRE;
+		if (p->fmr_flags & FMR_OF_ATTR_FORK)
+			flg |= FLG_ATTR_FORK;
+		if (p->fmr_flags & FMR_OF_SHARED)
+			flg |= FLG_SHARED;
+		/*
+		 * If striping enabled, determine if extent starts/ends
+		 * on a stripe unit boundary.
+		 */
+		if (sunit) {
+			if (p->fmr_physical  % sunit != 0)
+				flg |= FLG_BSU;
+			if (((p->fmr_physical +
+			      p->fmr_length ) % sunit ) != 0)
+				flg |= FLG_ESU;
+			if (p->fmr_physical % swidth != 0)
+				flg |= FLG_BSW;
+			if (((p->fmr_physical +
+			      p->fmr_length ) % swidth ) != 0)
+				flg |= FLG_ESW;
+		}
+		if (head->fmh_oflags & FMH_OF_DEV_T)
+			snprintf(dbuf, sizeof(dbuf), "%u:%u",
+				major(p->fmr_device),
+				minor(p->fmr_device));
+		else
+			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long)BTOBBT(p->fmr_physical),
+			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
+		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER) {
+			snprintf(obuf, sizeof(obuf), "%s",
+				special_owner(p->fmr_owner, owner));
+			snprintf(rbuf, sizeof(rbuf), " ");
+		} else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmr_owner);
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long)BTOBBT(p->fmr_offset),
+				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+		}
+		if (p->fmr_device == xfs_data_dev) {
+			agno = p->fmr_physical / bperag;
+			agoff = p->fmr_physical - (agno * bperag);
+			snprintf(abuf, sizeof(abuf),
+				"(%lld..%lld)",
+				(long long)BTOBBT(agoff),
+				(long long)BTOBBT(agoff + p->fmr_length - 1));
+			snprintf(gbuf, sizeof(gbuf),
+				"%lld",
+				(long long)agno);
+		} else {
+			abuf[0] = 0;
+			gbuf[0] = 0;
+		}
+		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
+			printf("%*llu: %-*s %-*s %-*s %-*s %-*s %-*s %*lld\n",
+				nr_w, (*nr) + i,
+				dev_w, dbuf,
+				boff_w, bbuf,
+				own_w, obuf,
+				foff_w, _("extent map"),
+				agno_w, gbuf,
+				aoff_w, abuf,
+				tot_w, (long long)BTOBBT(p->fmr_length));
+		else {
+			printf("%*llu: %-*s %-*s %-*s %-*s", nr_w, (*nr) + i,
+				dev_w, dbuf, boff_w, bbuf, own_w, obuf,
+				foff_w, rbuf);
+			printf(" %-*s %-*s", agno_w, gbuf,
+				aoff_w, abuf);
+			printf(" %*lld", tot_w,
+				(long long)BTOBBT(p->fmr_length));
+			if (flg == FLG_NULL)
+				printf("\n");
+			else
+				printf(" %-*.*o\n", NFLG, NFLG, flg);
+		}
+	}
+
+	(*nr) += head->fmh_entries;
+}
+
+static void
+dump_verbose_key(void)
+{
+	printf(_(" FLAG Values:\n"));
+	printf(_("    %*.*o Attribute fork\n"),
+		NFLG+1, NFLG+1, FLG_ATTR_FORK);
+	printf(_("    %*.*o Shared extent\n"),
+		NFLG+1, NFLG+1, FLG_SHARED);
+	printf(_("    %*.*o Unwritten preallocated extent\n"),
+		NFLG+1, NFLG+1, FLG_PRE);
+	printf(_("    %*.*o Doesn't begin on stripe unit\n"),
+		NFLG+1, NFLG+1, FLG_BSU);
+	printf(_("    %*.*o Doesn't end   on stripe unit\n"),
+		NFLG+1, NFLG+1, FLG_ESU);
+	printf(_("    %*.*o Doesn't begin on stripe width\n"),
+		NFLG+1, NFLG+1, FLG_BSW);
+	printf(_("    %*.*o Doesn't end   on stripe width\n"),
+		NFLG+1, NFLG+1, FLG_ESW);
+}
+
+int
+fsmap_f(
+	int			argc,
+	char			**argv)
+{
+	struct fsmap		*p;
+	struct fsmap_head	*nhead;
+	struct fsmap_head	*head;
+	struct fsmap		*l, *h;
+	struct xfs_fsop_geom	fsgeo;
+	long long		start = 0;
+	long long		end = -1;
+	int			nmap_size;
+	int			map_size;
+	int			nflag = 0;
+	int			vflag = 0;
+	int			mflag = 0;
+	int			i = 0;
+	int			c;
+	unsigned long long	nr = 0;
+	size_t			fsblocksize, fssectsize;
+	struct fs_path		*fs;
+	static bool		tab_init;
+	bool			dumped_flags = false;
+	int			dflag, lflag, rflag;
+
+	init_cvtnum(&fsblocksize, &fssectsize);
+
+	dflag = lflag = rflag = 0;
+	while ((c = getopt(argc, argv, "dlmn:rv")) != EOF) {
+		switch (c) {
+		case 'd':	/* data device */
+			dflag = 1;
+			break;
+		case 'l':	/* log device */
+			lflag = 1;
+			break;
+		case 'm':	/* machine readable format */
+			mflag++;
+			break;
+		case 'n':	/* number of extents specified */
+			nflag = cvt_u32(optarg, 10);
+			if (errno)
+				return command_usage(&fsmap_cmd);
+			break;
+		case 'r':	/* rt device */
+			rflag = 1;
+			break;
+		case 'v':	/* Verbose output */
+			vflag++;
+			break;
+		default:
+			return command_usage(&fsmap_cmd);
+		}
+	}
+
+	if ((dflag + lflag + rflag > 1) || (mflag > 0 && vflag > 0) ||
+	    (argc > optind && dflag + lflag + rflag == 0))
+		return command_usage(&fsmap_cmd);
+
+	if (argc > optind) {
+		start = cvtnum(fsblocksize, fssectsize, argv[optind]);
+		if (start < 0) {
+			fprintf(stderr,
+				_("Bad rmap start_bblock %s.\n"),
+				argv[optind]);
+			return 0;
+		}
+		start <<= BBSHIFT;
+	}
+
+	if (argc > optind + 1) {
+		end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
+		if (end < 0) {
+			fprintf(stderr,
+				_("Bad rmap end_bblock %s.\n"),
+				argv[optind + 1]);
+			return 0;
+		}
+		end <<= BBSHIFT;
+	}
+
+	if (vflag) {
+		c = ioctl(file->fd, XFS_IOC_FSGEOMETRY, &fsgeo);
+		if (c < 0) {
+			fprintf(stderr,
+				_("%s: can't get geometry [\"%s\"]: %s\n"),
+				progname, file->name, strerror(errno));
+			exitcode = 1;
+			return 0;
+		}
+	}
+
+	map_size = nflag ? nflag : 131072 / sizeof(struct fsmap);
+	head = malloc(fsmap_sizeof(map_size));
+	if (head == NULL) {
+		fprintf(stderr, _("%s: malloc of %zu bytes failed.\n"),
+			progname, fsmap_sizeof(map_size));
+		exitcode = 1;
+		return 0;
+	}
+
+	memset(head, 0, sizeof(*head));
+	l = head->fmh_keys;
+	h = head->fmh_keys + 1;
+	if (dflag) {
+		l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
+	} else if (lflag) {
+		l->fmr_device = h->fmr_device = file->fs_path.fs_logdev;
+	} else if (rflag) {
+		l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
+	} else {
+		l->fmr_device = 0;
+		h->fmr_device = UINT_MAX;
+	}
+	l->fmr_physical = start;
+	h->fmr_physical = end;
+	h->fmr_owner = ULLONG_MAX;
+	h->fmr_flags = UINT_MAX;
+	h->fmr_offset = ULLONG_MAX;
+
+	/* Count mappings */
+	if (!nflag) {
+		head->fmh_count = 0;
+		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, head->fmh_iflags, file->name,
+				strerror(errno));
+			free(head);
+			exitcode = 1;
+			return 0;
+		}
+		if (head->fmh_entries > map_size + 2) {
+			map_size = 11ULL * head->fmh_entries / 10;
+			nmap_size = map_size > (1 << 24) ? (1 << 24) : map_size;
+			nhead = realloc(head, fsmap_sizeof(nmap_size));
+			if (nhead == NULL) {
+				fprintf(stderr,
+					_("%s: cannot realloc %zu bytes\n"),
+					progname, fsmap_sizeof(nmap_size));
+			} else {
+				head = nhead;
+				map_size = nmap_size;
+			}
+		}
+	}
+
+	/*
+	 * If this is an XFS filesystem, remember the data device.
+	 * (We report AG number/block for data device extents on XFS).
+	 */
+	if (!tab_init) {
+		fs_table_initialise(0, NULL, 0, NULL);
+		tab_init = true;
+	}
+	fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
+	xfs_data_dev = fs ? fs->fs_datadev : 0;
+
+	head->fmh_count = map_size;
+	do {
+		/* Get some extents */
+		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, head->fmh_iflags, file->name,
+				strerror(errno));
+			free(head);
+			exitcode = 1;
+			return 0;
+		}
+
+		if (head->fmh_entries == 0)
+			break;
+
+		if (vflag)
+			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
+		else if (mflag)
+			dump_map_machine(&nr, head);
+		else
+			dump_map(&nr, head);
+
+		p = &head->fmh_recs[head->fmh_entries - 1];
+		if (p->fmr_flags & FMR_OF_LAST)
+			break;
+		fsmap_advance(head);
+	} while (true);
+
+	if (dumped_flags)
+		dump_verbose_key();
+
+	free(head);
+	return 0;
+}
+
+void
+fsmap_init(void)
+{
+	fsmap_cmd.name = "fsmap";
+	fsmap_cmd.cfunc = fsmap_f;
+	fsmap_cmd.argmin = 0;
+	fsmap_cmd.argmax = -1;
+	fsmap_cmd.flags = CMD_NOMAP_OK | CMD_FLAG_FOREIGN_OK;
+	fsmap_cmd.args = _("[-d|-l|-r] [-m|-v] [-n nx] [start] [end]");
+	fsmap_cmd.oneline = _("print filesystem mapping for a range of blocks");
+	fsmap_cmd.help = fsmap_help;
+
+	add_command(&fsmap_cmd);
+}
diff --git a/io/init.c b/io/init.c
index c15a1e1..20d5f80 100644
--- a/io/init.c
+++ b/io/init.c
@@ -66,6 +66,7 @@ init_commands(void)
 	file_init();
 	flink_init();
 	freeze_init();
+	fsmap_init();
 	fsync_init();
 	getrusage_init();
 	help_init();
@@ -139,6 +140,7 @@ init(
 	char		*sp;
 	mode_t		mode = 0600;
 	xfs_fsop_geom_t	geometry = { 0 };
+	struct fs_path	fsp;
 
 	progname = basename(argv[0]);
 	setlocale(LC_ALL, "");
@@ -148,6 +150,7 @@ init(
 	pagesize = getpagesize();
 	gettimeofday(&stopwatch, NULL);
 
+	fs_table_initialise(0, NULL, 0, NULL);
 	while ((c = getopt(argc, argv, "ac:C:dFfim:p:nrRstTVx")) != EOF) {
 		switch (c) {
 		case 'a':
@@ -212,11 +215,12 @@ init(
 	}
 
 	while (optind < argc) {
-		if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
+		c = openfile(argv[optind], &geometry, flags, mode, &fsp);
+		if (c < 0)
 			exit(1);
 		if (!platform_test_xfs_fd(c))
 			flags |= IO_FOREIGN;
-		if (addfile(argv[optind], c, &geometry, flags) < 0)
+		if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
 			exit(1);
 		optind++;
 	}
diff --git a/io/io.h b/io/io.h
index 952bdb8..6a0fe65 100644
--- a/io/io.h
+++ b/io/io.h
@@ -17,6 +17,7 @@
  */
 
 #include "xfs.h"
+#include "path.h"
 
 /*
  * Read/write patterns (default is always "forward")
@@ -47,6 +48,7 @@ typedef struct fileio {
 	int		flags;		/* flags describing file state */
 	char		*name;		/* file name at time of open */
 	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
+	struct fs_path	fs_path;	/* XFS path information */
 } fileio_t;
 
 extern fileio_t		*filetable;	/* open file table */
@@ -76,8 +78,10 @@ extern void *check_mapping_range(mmap_region_t *, off64_t, size_t, int);
  */
 
 extern off64_t		filesize(void);
-extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t);
-extern int		addfile(char *, int , xfs_fsop_geom_t *, int);
+extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t,
+				 struct fs_path *);
+extern int		addfile(char *, int , xfs_fsop_geom_t *, int,
+				struct fs_path *);
 extern void		printxattr(uint, int, int, const char *, int, int);
 
 extern unsigned int	recurse_all;
@@ -174,3 +178,9 @@ extern void		readdir_init(void);
 extern void		reflink_init(void);
 
 extern void		cowextsize_init(void);
+
+#ifdef HAVE_GETFSMAP
+extern void		fsmap_init(void);
+#else
+# define fsmap_init()	do { } while (0)
+#endif
diff --git a/io/open.c b/io/open.c
index 2ed55cf..b50f068 100644
--- a/io/open.c
+++ b/io/open.c
@@ -52,8 +52,10 @@ openfile(
 	char		*path,
 	xfs_fsop_geom_t	*geom,
 	int		flags,
-	mode_t		mode)
+	mode_t		mode,
+	struct fs_path	*fs_path)
 {
+	struct fs_path	*fsp;
 	int		fd;
 	int		oflags;
 
@@ -118,6 +120,14 @@ openfile(
 			}
 		}
 	}
+
+	if (fs_path) {
+		fsp = fs_table_lookup(path, FS_MOUNT_POINT);
+		if (!fsp)
+			memset(fs_path, 0, sizeof(*fs_path));
+		else
+			*fs_path = *fsp;
+	}
 	return fd;
 }
 
@@ -126,7 +136,8 @@ addfile(
 	char		*name,
 	int		fd,
 	xfs_fsop_geom_t	*geometry,
-	int		flags)
+	int		flags,
+	struct fs_path	*fs_path)
 {
 	char		*filename;
 
@@ -154,6 +165,7 @@ addfile(
 	file->flags = flags;
 	file->name = filename;
 	file->geom = *geometry;
+	file->fs_path = *fs_path;
 	return 0;
 }
 
@@ -195,6 +207,7 @@ open_f(
 	char		*sp;
 	mode_t		mode = 0600;
 	xfs_fsop_geom_t	geometry = { 0 };
+	struct fs_path	fsp;
 
 	if (argc == 1) {
 		if (file)
@@ -257,14 +270,14 @@ open_f(
 		return -1;
 	}
 
-	fd = openfile(argv[optind], &geometry, flags, mode);
+	fd = openfile(argv[optind], &geometry, flags, mode, &fsp);
 	if (fd < 0)
 		return 0;
 
 	if (!platform_test_xfs_fd(fd))
 		flags |= IO_FOREIGN;
 
-	addfile(argv[optind], fd, &geometry, flags);
+	addfile(argv[optind], fd, &geometry, flags, &fsp);
 	return 0;
 }
 
diff --git a/io/pwrite.c b/io/pwrite.c
index 7c0bb7f..1c5dfca 100644
--- a/io/pwrite.c
+++ b/io/pwrite.c
@@ -357,7 +357,7 @@ pwrite_f(
 		return 0;
 
 	c = IO_READONLY | (dflag ? IO_DIRECT : 0);
-	if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0))
+	if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0))
 		return 0;
 
 	gettimeofday(&t1, NULL);
diff --git a/io/reflink.c b/io/reflink.c
index fe05d1e..f584e8f 100644
--- a/io/reflink.c
+++ b/io/reflink.c
@@ -154,7 +154,7 @@ dedupe_f(
 		return 0;
 	}
 
-	fd = openfile(infile, NULL, IO_READONLY, 0);
+	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
 	if (fd < 0)
 		return 0;
 
@@ -278,7 +278,7 @@ reflink_f(
 	}
 
 clone_all:
-	fd = openfile(infile, NULL, IO_READONLY, 0);
+	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
 	if (fd < 0)
 		return 0;
 
diff --git a/io/sendfile.c b/io/sendfile.c
index edd31c9..063fa7f 100644
--- a/io/sendfile.c
+++ b/io/sendfile.c
@@ -115,7 +115,7 @@ sendfile_f(
 
 	if (!infile)
 		fd = filetable[fd].fd;
-	else if ((fd = openfile(infile, NULL, IO_READONLY, 0)) < 0)
+	else if ((fd = openfile(infile, NULL, IO_READONLY, 0, NULL)) < 0)
 		return 0;
 
 	if (optind == argc - 2) {
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 29a036c..7b921e5 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -301,6 +301,105 @@ ioctl.  Options behave as described in the
 .BR xfs_bmap (8)
 manual page.
 .TP
+.BI "fsmap [ \-d | \-l | \-r ] [ \-m | \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
+Prints the mapping of disk blocks used by a filesystem.
+The map lists each extent used by files, allocation group metadata,
+journalling logs, and static filesystem metadata, as well as any
+regions that are unused.
+Each line of the listings takes the following form:
+.PP
+.RS
+.IR extent ": " major ":" minor " [" startblock .. endblock "]: " owner " " startoffset .. endoffset " " length
+.PP
+Static filesystem metadata, allocation group metadata, btrees,
+journalling logs, and free space are marked by replacing the
+.IR startoffset .. endoffset
+with the appropriate marker.
+All blocks, offsets, and lengths are specified in units of 512-byte
+blocks, no matter what the filesystem's block size is.
+The optional
+.I start
+and
+.I end
+arguments can be used to constrain the output to a particular range of
+disk blocks.
+.RE
+.RS 1.0i
+.PD 0
+.TP
+.BI \-d
+Display only extents from the data device.
+This option only applies for XFS filesystems.
+.TP
+.BI \-l
+Display only extents from the external log device.
+This option only applies to XFS filesystems.
+.TP
+.BI \-r
+Display only extents from the realtime device.
+This option only applies to XFS filesystems.
+.TP
+.BI \-m
+Display results in a machine readable format (CSV).
+This option is not compatible with the
+.B \-v
+flag.
+The columns of the output are: extent number, device major, device minor,
+physical start, physical end, owner, offset start, offset end, length.
+The start, end, and length numbers are provided in units of 512b.
+The owner field is a special string that takes the form:
+
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.I inode_%lld_data
+for inode data.
+.TP
+.I inode_%lld_data_bmbt
+for inode data extent maps.
+.TP
+.I inode_%lld_attr
+for inode extended attribute data.
+.TP
+.I inode_%lld_attr_bmbt
+for inode extended attribute extent maps.
+.TP
+.I special_%u:%u
+for other filesystem metadata.
+.PD
+.RE
+
+.TP
+.BI \-n " num_extents"
+If this option is given,
+.B xfs_fsmap
+obtains the extent list of the file in groups of
+.I num_extents
+extents.
+In the absence of
+.BR \-n ", " xfs_fsmap
+queries the system for extents in groups of 131,072 records.
+.TP
+.B \-v
+Shows verbose information.
+When this flag is specified, additional AG specific information is
+appended to each line in the following form:
+.IP
+.RS 1.2i
+.IR agno " (" startagblock .. endagblock ") " nblocks " " flags
+.RE
+.IP
+A second
+.B \-v
+option will print out the
+.I flags
+legend.
+This option is not compatible with the
+.B \-m
+flag.
+.RE
+.PD
+.TP
 .BI "extsize [ \-R | \-D ] [ " value " ]"
 Display and/or modify the preferred extent size used when allocating
 space for the currently open file. If the


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

* [PATCH 06/12] xfs_repair: replace rmap_compare with libxfs version
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (4 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 05/12] xfs_io: support the new getfsmap ioctl Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-15 20:36 ` [PATCH 07/12] xfs_spaceman: space management tool Darrick J. Wong
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Now that libxfs has a function to compare rmaps, replace xfs_repair's
helper function with that.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
---
 libxfs/libxfs_api_defs.h |    1 +
 repair/rmap.c            |   32 ++------------------------------
 2 files changed, 3 insertions(+), 30 deletions(-)


diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 34716c4..d299b7a 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -145,5 +145,6 @@
 #define xfs_refcount_get_rec		libxfs_refcount_get_rec
 #define xfs_rmap_lookup_le_range	libxfs_rmap_lookup_le_range
 #define xfs_refc_block			libxfs_refc_block
+#define xfs_rmap_compare		libxfs_rmap_compare
 
 #endif /* __LIBXFS_API_DEFS_H__ */
diff --git a/repair/rmap.c b/repair/rmap.c
index 7508973..ab6e583 100644
--- a/repair/rmap.c
+++ b/repair/rmap.c
@@ -49,37 +49,9 @@ static struct xfs_ag_rmap *ag_rmaps;
 static bool rmapbt_suspect;
 static bool refcbt_suspect;
 
-/*
- * Compare rmap observations for array sorting.
- */
-static int
-rmap_compare(
-	const void		*a,
-	const void		*b)
+static inline int rmap_compare(const void *a, const void *b)
 {
-	const struct xfs_rmap_irec	*pa;
-	const struct xfs_rmap_irec	*pb;
-	__u64			oa;
-	__u64			ob;
-
-	pa = a; pb = b;
-	oa = libxfs_rmap_irec_offset_pack(pa);
-	ob = libxfs_rmap_irec_offset_pack(pb);
-
-	if (pa->rm_startblock < pb->rm_startblock)
-		return -1;
-	else if (pa->rm_startblock > pb->rm_startblock)
-		return 1;
-	else if (pa->rm_owner < pb->rm_owner)
-		return -1;
-	else if (pa->rm_owner > pb->rm_owner)
-		return 1;
-	else if (oa < ob)
-		return -1;
-	else if (oa > ob)
-		return 1;
-	else
-		return 0;
+	return libxfs_rmap_compare(a, b);
 }
 
 /*


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

* [PATCH 07/12] xfs_spaceman: space management tool
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (5 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 06/12] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 21:12   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 08/12] xfs_spaceman: add FITRIM support Darrick J. Wong
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs, Dave Chinner

From: Dave Chinner <dchinner@redhat.com>

xfs_spaceman is intended as a diagnostic and control tool for space
management operations within XFS. Operations like examining free
space, managing allocation policies, issuing block discards on free
space, etc.

The tool is modelled on the xfs_io interface, allowing both
interactive and command line control of the tool, enabling it to be
used in scripts and automated management tools.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
[darrick: change xfsctl to ioctl]
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 Makefile          |    3 +
 spaceman/Makefile |   33 ++++++++++++++
 spaceman/file.c   |  122 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/init.c   |  113 +++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/init.h   |   24 ++++++++++
 spaceman/space.h  |   38 +++++++++++++++++
 6 files changed, 332 insertions(+), 1 deletion(-)
 create mode 100644 spaceman/Makefile
 create mode 100644 spaceman/file.c
 create mode 100644 spaceman/init.c
 create mode 100644 spaceman/init.h
 create mode 100644 spaceman/space.h


diff --git a/Makefile b/Makefile
index ba87327..72d0044 100644
--- a/Makefile
+++ b/Makefile
@@ -47,7 +47,7 @@ HDR_SUBDIRS = include libxfs
 DLIB_SUBDIRS = libxlog libxcmd libhandle
 LIB_SUBDIRS = libxfs $(DLIB_SUBDIRS)
 TOOL_SUBDIRS = copy db estimate fsck growfs io logprint mkfs quota \
-		mdrestore repair rtcp m4 man doc debian
+		mdrestore repair rtcp m4 man doc debian spaceman
 
 ifneq ("$(PKG_PLATFORM)","darwin")
 TOOL_SUBDIRS += fsr
@@ -88,6 +88,7 @@ quota: libxcmd
 repair: libxlog libxcmd
 copy: libxlog
 mkfs: libxcmd
+spaceman: libxcmd
 
 ifeq ($(HAVE_BUILDDEFS), yes)
 include $(BUILDRULES)
diff --git a/spaceman/Makefile b/spaceman/Makefile
new file mode 100644
index 0000000..df59edf
--- /dev/null
+++ b/spaceman/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2012 Red Hat, Inc.  All Rights Reserved.
+#
+
+TOPDIR = ..
+include $(TOPDIR)/include/builddefs
+
+LTCOMMAND = xfs_spaceman
+HFILES = init.h space.h
+CFILES = init.c file.c
+
+LLDLIBS = $(LIBXCMD)
+LTDEPENDENCIES = $(LIBXCMD)
+LLDFLAGS = -static
+
+ifeq ($(ENABLE_READLINE),yes)
+LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
+endif
+
+ifeq ($(ENABLE_EDITLINE),yes)
+LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
+endif
+
+default: depend $(LTCOMMAND)
+
+include $(BUILDRULES)
+
+install: default
+	$(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+	$(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
+install-dev:
+
+-include .dep
diff --git a/spaceman/file.c b/spaceman/file.c
new file mode 100644
index 0000000..beca5ba
--- /dev/null
+++ b/spaceman/file.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2004-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include <sys/mman.h>
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "space.h"
+
+static cmdinfo_t print_cmd;
+
+fileio_t	*filetable;
+int		filecount;
+fileio_t	*file;
+
+static void
+print_fileio(
+	fileio_t	*file,
+	int		index,
+	int		braces)
+{
+	printf(_("%c%03d%c %-14s\n"), braces? '[' : ' ', index,
+			braces? ']' : ' ', file->name);
+}
+
+static int
+print_f(
+	int		argc,
+	char		**argv)
+{
+	int		i;
+
+	for (i = 0; i < filecount; i++)
+		print_fileio(&filetable[i], i, &filetable[i] == file);
+	return 0;
+}
+
+int
+openfile(
+	char		*path,
+	xfs_fsop_geom_t	*geom)
+{
+	int		fd;
+
+	fd = open(path, 0);
+	if (fd < 0) {
+		perror(path);
+		return -1;
+	}
+
+	if (ioctl(fd, XFS_IOC_FSGEOMETRY, geom) < 0) {
+		perror("XFS_IOC_FSGEOMETRY");
+		close(fd);
+		return -1;
+	}
+	return fd;
+}
+
+int
+addfile(
+	char		*name,
+	int		fd,
+	xfs_fsop_geom_t	*geometry)
+{
+	char		*filename;
+
+	filename = strdup(name);
+	if (!filename) {
+		perror("strdup");
+		close(fd);
+		return -1;
+	}
+
+	/* Extend the table of currently open files */
+	filetable = (fileio_t *)realloc(filetable,	/* growing */
+					++filecount * sizeof(fileio_t));
+	if (!filetable) {
+		perror("realloc");
+		filecount = 0;
+		free(filename);
+		close(fd);
+		return -1;
+	}
+
+	/* Finally, make this the new active open file */
+	file = &filetable[filecount - 1];
+	file->fd = fd;
+	file->name = filename;
+	file->geom = *geometry;
+	return 0;
+}
+
+void
+print_init(void)
+{
+	print_cmd.name = "print";
+	print_cmd.altname = "p";
+	print_cmd.cfunc = print_f;
+	print_cmd.argmin = 0;
+	print_cmd.argmax = 0;
+	print_cmd.flags = CMD_FLAG_ONESHOT;
+	print_cmd.oneline = _("list current open files");
+
+	add_command(&print_cmd);
+}
diff --git a/spaceman/init.c b/spaceman/init.c
new file mode 100644
index 0000000..8cbfbda
--- /dev/null
+++ b/spaceman/init.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "space.h"
+
+char	*progname;
+int	exitcode;
+
+void
+usage(void)
+{
+	fprintf(stderr,
+		_("Usage: %s [-c cmd] file\n"),
+		progname);
+	exit(1);
+}
+
+static void
+init_commands(void)
+{
+	print_init();
+	help_init();
+	quit_init();
+}
+
+static int
+init_args_command(
+	int	index)
+{
+	if (index >= filecount)
+		return 0;
+	file = &filetable[index++];
+	return index;
+}
+
+static int
+init_check_command(
+	const cmdinfo_t	*ct)
+{
+	if (!(ct->flags & CMD_FLAG_ONESHOT))
+		return 0;
+	return 1;
+}
+
+void
+init(
+	int		argc,
+	char		**argv)
+{
+	int		c;
+	xfs_fsop_geom_t	geometry = { 0 };
+
+	progname = basename(argv[0]);
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+	while ((c = getopt(argc, argv, "c:V")) != EOF) {
+		switch (c) {
+		case 'c':
+			add_user_command(optarg);
+			break;
+		case 'V':
+			printf(_("%s version %s\n"), progname, VERSION);
+			exit(0);
+		default:
+			usage();
+		}
+	}
+
+	if (optind != argc - 1)
+		usage();
+
+	if ((c = openfile(argv[optind], &geometry)) < 0)
+		exit(1);
+	if (!platform_test_xfs_fd(c))
+		printf(_("Not an XFS filesystem!\n"));
+	if (addfile(argv[optind], c, &geometry) < 0)
+		exit(1);
+
+	init_commands();
+	add_command_iterator(init_args_command);
+	add_check_command(init_check_command);
+}
+
+int
+main(
+	int	argc,
+	char	**argv)
+{
+	init(argc, argv);
+	command_loop();
+	return exitcode;
+}
diff --git a/spaceman/init.h b/spaceman/init.h
new file mode 100644
index 0000000..e9c25be
--- /dev/null
+++ b/spaceman/init.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef XFS_SPACEMAN_INIT_H_
+#define XFS_SPACEMAN_INIT_H_
+
+extern char	*progname;
+extern int	exitcode;
+
+#endif /* XFS_SPACEMAN_INIT_H_ */
diff --git a/spaceman/space.h b/spaceman/space.h
new file mode 100644
index 0000000..7bfe874
--- /dev/null
+++ b/spaceman/space.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef XFS_SPACEMAN_SPACE_H_
+#define XFS_SPACEMAN_SPACE_H_
+
+typedef struct fileio {
+	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
+	char		*name;		/* file name at time of open */
+	int		fd;		/* open file descriptor */
+} fileio_t;
+
+extern fileio_t		*filetable;	/* open file table */
+extern int		filecount;	/* number of open files */
+extern fileio_t		*file;		/* active file in file table */
+
+extern int	openfile(char *, xfs_fsop_geom_t *);
+extern int	addfile(char *, int , xfs_fsop_geom_t *);
+
+extern void	print_init(void);
+extern void	help_init(void);
+extern void	quit_init(void);
+
+#endif /* XFS_SPACEMAN_SPACE_H_ */


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

* [PATCH 08/12] xfs_spaceman: add FITRIM support
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (6 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 07/12] xfs_spaceman: space management tool Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 21:21   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 09/12] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs, Dave Chinner

From: Dave Chinner <dchinner@redhat.com>

Add support for discarding free space extents via the FITRIM
command. Make it easy to discard a single range, an entire AG or all
the freespace in the filesystem.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 spaceman/Makefile |    2 -
 spaceman/init.c   |    1 
 spaceman/space.h  |    1 
 spaceman/trim.c   |  131 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 134 insertions(+), 1 deletion(-)
 create mode 100644 spaceman/trim.c


diff --git a/spaceman/Makefile b/spaceman/Makefile
index df59edf..c63a4fc 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -7,7 +7,7 @@ include $(TOPDIR)/include/builddefs
 
 LTCOMMAND = xfs_spaceman
 HFILES = init.h space.h
-CFILES = init.c file.c
+CFILES = init.c file.c trim.c
 
 LLDLIBS = $(LIBXCMD)
 LTDEPENDENCIES = $(LIBXCMD)
diff --git a/spaceman/init.c b/spaceman/init.c
index 8cbfbda..107a692 100644
--- a/spaceman/init.c
+++ b/spaceman/init.c
@@ -40,6 +40,7 @@ init_commands(void)
 	print_init();
 	help_init();
 	quit_init();
+	trim_init();
 }
 
 static int
diff --git a/spaceman/space.h b/spaceman/space.h
index 7bfe874..8cd3953 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -34,5 +34,6 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *);
 extern void	print_init(void);
 extern void	help_init(void);
 extern void	quit_init(void);
+extern void	trim_init(void);
 
 #endif /* XFS_SPACEMAN_SPACE_H_ */
diff --git a/spaceman/trim.c b/spaceman/trim.c
new file mode 100644
index 0000000..c0bc7f2
--- /dev/null
+++ b/spaceman/trim.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include <linux/fs.h>
+#include "command.h"
+#include "init.h"
+#include "space.h"
+#include "input.h"
+
+static cmdinfo_t trim_cmd;
+
+/*
+ * Trim unused space in xfs filesystem.
+ */
+static int
+trim_f(
+	int		argc,
+	char		**argv)
+{
+	struct fstrim_range trim = {0};
+	xfs_agnumber_t	agno = 0;
+	off64_t		offset = 0;
+	ssize_t		length = 0;
+	ssize_t		minlen = 0;
+	int		aflag = 0;
+	int		fflag = 0;
+	int		ret;
+	int		c;
+
+	while ((c = getopt(argc, argv, "a:fm:")) != EOF) {
+		switch (c) {
+		case 'a':
+			aflag = 1;
+			agno = cvt_u32(optarg, 10);
+			if (errno) {
+				printf(_("bad agno value %s\n"), optarg);
+				return command_usage(&trim_cmd);
+			}
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'm':
+			minlen = cvtnum(file->geom.blocksize,
+					file->geom.sectsize, optarg);
+			break;
+		default:
+			return command_usage(&trim_cmd);
+		}
+	}
+
+	if (aflag && fflag)
+		return command_usage(&trim_cmd);
+
+	if (optind != argc - 2 && !(aflag || fflag))
+		return command_usage(&trim_cmd);
+	if (optind != argc) {
+		offset = cvtnum(file->geom.blocksize, file->geom.sectsize,
+				argv[optind]);
+		length = cvtnum(file->geom.blocksize, file->geom.sectsize,
+				argv[optind + 1]);
+	} else if (agno) {
+		offset = agno * file->geom.agblocks * file->geom.blocksize;
+		length = file->geom.agblocks * file->geom.blocksize;
+	} else {
+		offset = 0;
+		length = file->geom.datablocks * file->geom.blocksize;
+	}
+
+	trim.start = offset;
+	trim.len = length;
+	trim.minlen = minlen;
+
+	ret = ioctl(file->fd, FITRIM, (unsigned long)&trim);
+	if (ret < 0) {
+		fprintf(stderr, "%s: ioctl(FITRIM) [\"%s\"]: "
+			"%s\n", progname, file->name, strerror(errno));
+		exitcode = 1;
+	}
+	return 0;
+}
+
+static void
+trim_help(void)
+{
+	printf(_(
+"\n"
+"Discard filesystem free space\n"
+"\n"
+" -a agno       -- trim all the freespace in the given AG agno\n"
+" -f            -- trim all the freespace in the entire filesystem\n"
+" offset length -- trim the freespace in the range {offset, length}\n"
+" -m minlen     -- skip freespace extents smaller than minlen\n"
+"\n"
+"One of -a, -f, or the offset/length pair are required.\n"
+"\n"));
+
+}
+
+void
+trim_init(void)
+{
+	trim_cmd.name = "trim";
+	trim_cmd.altname = "tr";
+	trim_cmd.cfunc = trim_f;
+	trim_cmd.argmin = 1;
+	trim_cmd.argmax = 4;
+	trim_cmd.args = "[-m minlen] ( -a agno | -f | offset length )";
+	trim_cmd.flags = CMD_FLAG_ONESHOT;
+	trim_cmd.oneline = _("Discard filesystem free space");
+	trim_cmd.help = trim_help;
+
+	add_command(&trim_cmd);
+}
+


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

* [PATCH 09/12] xfs_spaceman: add new speculative prealloc control
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (7 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 08/12] xfs_spaceman: add FITRIM support Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 21:26   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 10/12] xfs_spaceman: Free space mapping command Darrick J. Wong
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs, Dave Chinner

From: Dave Chinner <dchinner@redhat.com>

Add an control interface for purging speculative
preallocation via the new ioctls.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
[darrick: change xfsctl to ioctl]
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 spaceman/Makefile   |    2 -
 spaceman/init.c     |    1 
 spaceman/prealloc.c |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/space.h    |    1 
 4 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 spaceman/prealloc.c


diff --git a/spaceman/Makefile b/spaceman/Makefile
index c63a4fc..6aad746 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -7,7 +7,7 @@ include $(TOPDIR)/include/builddefs
 
 LTCOMMAND = xfs_spaceman
 HFILES = init.h space.h
-CFILES = init.c file.c trim.c
+CFILES = init.c file.c prealloc.c trim.c
 
 LLDLIBS = $(LIBXCMD)
 LTDEPENDENCIES = $(LIBXCMD)
diff --git a/spaceman/init.c b/spaceman/init.c
index 107a692..a51007d 100644
--- a/spaceman/init.c
+++ b/spaceman/init.c
@@ -39,6 +39,7 @@ init_commands(void)
 {
 	print_init();
 	help_init();
+	prealloc_init();
 	quit_init();
 	trim_init();
 }
diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
new file mode 100644
index 0000000..439a9e8
--- /dev/null
+++ b/spaceman/prealloc.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "space.h"
+
+static cmdinfo_t prealloc_cmd;
+
+/*
+ * Control preallocation amounts.
+ */
+static int
+prealloc_f(
+	int	argc,
+	char	**argv)
+{
+	struct xfs_fs_eofblocks eofb = {0};
+	int	c;
+
+	eofb.eof_version = XFS_EOFBLOCKS_VERSION;
+
+	while ((c = getopt(argc, argv, "g:m:p:su:")) != EOF) {
+		switch (c) {
+		case 'g':
+			eofb.eof_flags |= XFS_EOF_FLAGS_GID;
+			eofb.eof_gid = cvt_u32(optarg, 10);
+			if (errno)
+				return command_usage(&prealloc_cmd);
+			break;
+		case 'u':
+			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
+			eofb.eof_uid = cvt_u32(optarg, 10);
+			if (errno)
+				return command_usage(&prealloc_cmd);
+			break;
+		case 'p':
+			eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
+			eofb.eof_prid = cvt_u32(optarg, 10);
+			if (errno)
+				return command_usage(&prealloc_cmd);
+			break;
+		case 's':
+			eofb.eof_flags |= XFS_EOF_FLAGS_SYNC;
+			break;
+		case 'm':
+			eofb.eof_flags |= XFS_EOF_FLAGS_MINFILESIZE;
+			eofb.eof_min_file_size = cvtnum(file->geom.blocksize,
+							file->geom.sectsize,
+							optarg);
+			break;
+		case '?':
+		default:
+			return command_usage(&prealloc_cmd);
+		}
+	}
+	if (optind != argc)
+		return command_usage(&prealloc_cmd);
+
+	if (ioctl(file->fd, XFS_IOC_FREE_EOFBLOCKS, &eofb) < 0) {
+		fprintf(stderr, _("%s: XFS_IOC_FREE_EOFBLOCKS on %s: %s\n"),
+			progname, file->name, strerror(errno));
+	}
+	return 0;
+}
+
+static void
+prealloc_help(void)
+{
+	printf(_(
+"\n"
+"Control speculative preallocation\n"
+"\n"
+" -g gid    -- remove prealloc on files matching group <gid>\n"
+" -m minlen -- only consider files larger than <minlen>\n"
+" -p prid   -- remove prealloc on files matching project <prid>\n"
+" -s        -- wait for removal to complete\n"
+" -u uid    -- remove prealloc on files matching user <uid>\n"
+"\n"
+"If none of -u, -g, or -p are specified, this command acts on all files.\n"
+"minlen can take units.\n"
+"\n"));
+
+}
+
+void
+prealloc_init(void)
+{
+	prealloc_cmd.name = "prealloc";
+	prealloc_cmd.altname = "prealloc";
+	prealloc_cmd.cfunc = prealloc_f;
+	prealloc_cmd.argmin = 1;
+	prealloc_cmd.argmax = -1;
+	prealloc_cmd.args = "[-s] [-u id] [-g id] [-p id] [-m minlen]";
+	prealloc_cmd.flags = CMD_FLAG_ONESHOT;
+	prealloc_cmd.oneline = _("Control speculative preallocation");
+	prealloc_cmd.help = prealloc_help;
+
+	add_command(&prealloc_cmd);
+}
+
diff --git a/spaceman/space.h b/spaceman/space.h
index 8cd3953..de97139 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -33,6 +33,7 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *);
 
 extern void	print_init(void);
 extern void	help_init(void);
+extern void	prealloc_init(void);
 extern void	quit_init(void);
 extern void	trim_init(void);
 


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

* [PATCH 10/12] xfs_spaceman: Free space mapping command
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (8 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 09/12] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 21:32   ` Eric Sandeen
  2017-06-15 20:36 ` [PATCH 11/12] xfs_spaceman: add a man page Darrick J. Wong
  2017-06-15 20:37 ` [PATCH 12/12] xfs_spaceman: add group summary mode Darrick J. Wong
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs, Dave Chinner

From: Dave Chinner <dchinner@redhat.com>

Add freespace mapping tool modelled on the xfs_db freesp command.
The advantage of this command over xfs_db is that it can be done
online and is coherent with concurrent modifications to the
filesystem.

This requires the kernel to support the XFS_IOC_GETFSMAP ioctl to map
free space indexes.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
[darrick: port from FIEMAPFS to GETFSMAP]
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 spaceman/Makefile   |    7 +
 spaceman/file.c     |   19 ++
 spaceman/freesp.c   |  392 +++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/init.c     |    8 +
 spaceman/prealloc.c |    1 
 spaceman/space.h    |   10 +
 spaceman/trim.c     |    1 
 7 files changed, 432 insertions(+), 6 deletions(-)
 create mode 100644 spaceman/freesp.c


diff --git a/spaceman/Makefile b/spaceman/Makefile
index 6aad746..95ec3c0 100644
--- a/spaceman/Makefile
+++ b/spaceman/Makefile
@@ -21,6 +21,13 @@ ifeq ($(ENABLE_EDITLINE),yes)
 LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
 endif
 
+# On linux we get fsmap from the system or define it ourselves
+# so include this based on platform type.  If this reverts to only
+# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
+ifeq ($(PKG_PLATFORM),linux)
+CFILES += freesp.c
+endif
+
 default: depend $(LTCOMMAND)
 
 include $(BUILDRULES)
diff --git a/spaceman/file.c b/spaceman/file.c
index beca5ba..4ab3090 100644
--- a/spaceman/file.c
+++ b/spaceman/file.c
@@ -22,6 +22,7 @@
 #include "command.h"
 #include "input.h"
 #include "init.h"
+#include "path.h"
 #include "space.h"
 
 static cmdinfo_t print_cmd;
@@ -55,8 +56,10 @@ print_f(
 int
 openfile(
 	char		*path,
-	xfs_fsop_geom_t	*geom)
+	xfs_fsop_geom_t	*geom,
+	struct fs_path	*fs_path)
 {
+	struct fs_path	*fsp;
 	int		fd;
 
 	fd = open(path, 0);
@@ -70,6 +73,16 @@ openfile(
 		close(fd);
 		return -1;
 	}
+
+	if (fs_path) {
+		fsp = fs_table_lookup(path, FS_MOUNT_POINT);
+		if (!fsp) {
+			fprintf(stderr, _("%s: cannot find mount point."),
+				path);
+			return -1;
+		}
+		memcpy(fs_path, fsp, sizeof(struct fs_path));
+	}
 	return fd;
 }
 
@@ -77,7 +90,8 @@ int
 addfile(
 	char		*name,
 	int		fd,
-	xfs_fsop_geom_t	*geometry)
+	xfs_fsop_geom_t	*geometry,
+	struct fs_path	*fs_path)
 {
 	char		*filename;
 
@@ -104,6 +118,7 @@ addfile(
 	file->fd = fd;
 	file->name = filename;
 	file->geom = *geometry;
+	memcpy(&file->fs_path, fs_path, sizeof(file->fs_path));
 	return 0;
 }
 
diff --git a/spaceman/freesp.c b/spaceman/freesp.c
new file mode 100644
index 0000000..99e50cf
--- /dev/null
+++ b/spaceman/freesp.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2012 Red Hat, Inc.
+ * Copyright (c) 2017 Oracle.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include <linux/fiemap.h>
+#include "command.h"
+#include "init.h"
+#include "path.h"
+#include "space.h"
+#include "input.h"
+
+typedef struct histent
+{
+	long long	low;
+	long long	high;
+	long long	count;
+	long long	blocks;
+} histent_t;
+
+static int		agcount;
+static xfs_agnumber_t	*aglist;
+static histent_t	*hist;
+static int		dumpflag;
+static long long	equalsize;
+static long long	multsize;
+static int		histcount;
+static int		seen1;
+static int		summaryflag;
+static bool		rtflag;
+static long long	totblocks;
+static long long	totexts;
+
+static cmdinfo_t freesp_cmd;
+
+static void
+addhistent(
+	long long	h)
+{
+	if (histcount == INT_MAX) {
+		printf(_("Too many histogram buckets.\n"));
+		return;
+	}
+	hist = realloc(hist, (histcount + 1) * sizeof(*hist));
+	if (h == 0)
+		h = 1;
+	hist[histcount].low = h;
+	hist[histcount].count = hist[histcount].blocks = 0;
+	histcount++;
+	if (h == 1)
+		seen1 = 1;
+}
+
+static void
+addtohist(
+	xfs_agnumber_t	agno,
+	xfs_agblock_t	agbno,
+	off64_t		len)
+{
+	long		i;
+
+	if (dumpflag)
+		printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
+	totexts++;
+	totblocks += len;
+	for (i = 0; i < histcount; i++) {
+		if (hist[i].high >= len) {
+			hist[i].count++;
+			hist[i].blocks += len;
+			break;
+		}
+	}
+}
+
+static int
+hcmp(
+	const void	*a,
+	const void	*b)
+{
+	return ((histent_t *)a)->low - ((histent_t *)b)->low;
+}
+
+static void
+histinit(
+	long long	maxlen)
+{
+	long long	i;
+
+	if (equalsize) {
+		for (i = 1; i < maxlen; i += equalsize)
+			addhistent(i);
+	} else if (multsize) {
+		for (i = 1; i < maxlen; i *= multsize)
+			addhistent(i);
+	} else {
+		if (!seen1)
+			addhistent(1);
+		qsort(hist, histcount, sizeof(*hist), hcmp);
+	}
+	for (i = 0; i < histcount; i++) {
+		if (i < histcount - 1)
+			hist[i].high = hist[i + 1].low - 1;
+		else
+			hist[i].high = maxlen;
+	}
+}
+
+static void
+printhist(void)
+{
+	int	i;
+
+	printf("%7s %7s %7s %7s %6s\n",
+		_("from"), _("to"), _("extents"), _("blocks"), _("pct"));
+	for (i = 0; i < histcount; i++) {
+		if (hist[i].count)
+			printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
+				hist[i].high, hist[i].count, hist[i].blocks,
+				hist[i].blocks * 100.0 / totblocks);
+	}
+}
+
+static int
+inaglist(
+	xfs_agnumber_t	agno)
+{
+	int		i;
+
+	if (agcount == 0)
+		return 1;
+	for (i = 0; i < agcount; i++)
+		if (aglist[i] == agno)
+			return 1;
+	return 0;
+}
+
+#define NR_EXTENTS 128
+
+static void
+scan_ag(
+	xfs_agnumber_t		agno)
+{
+	struct fsmap_head	*fsmap;
+	struct fsmap		*extent;
+	struct fsmap		*l, *h;
+	struct fsmap		*p;
+	off64_t			blocksize = file->geom.blocksize;
+	off64_t			bperag;
+	off64_t			aglen;
+	xfs_agblock_t		agbno;
+	int			ret;
+	int			i;
+
+	bperag = (off64_t)file->geom.agblocks * blocksize;
+
+	fsmap = malloc(fsmap_sizeof(NR_EXTENTS));
+	if (!fsmap) {
+		fprintf(stderr, _("%s: fsmap malloc failed.\n"), progname);
+		exitcode = 1;
+		return;
+	}
+
+	memset(fsmap, 0, sizeof(*fsmap));
+	fsmap->fmh_count = NR_EXTENTS;
+	l = fsmap->fmh_keys;
+	h = fsmap->fmh_keys + 1;
+	if (agno != NULLAGNUMBER) {
+		l->fmr_physical = agno * bperag;
+		h->fmr_physical = ((agno + 1) * bperag) - 1;
+		l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
+	} else {
+		l->fmr_physical = 0;
+		h->fmr_physical = ULLONG_MAX;
+		l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
+	}
+	h->fmr_owner = ULLONG_MAX;
+	h->fmr_flags = UINT_MAX;
+	h->fmr_offset = ULLONG_MAX;
+
+	while (true) {
+		ret = ioctl(file->fd, FS_IOC_GETFSMAP, fsmap);
+		if (ret < 0) {
+			fprintf(stderr, _("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"),
+				progname, file->name, strerror(errno));
+			free(fsmap);
+			exitcode = 1;
+			return;
+		}
+
+		/* No more extents to map, exit */
+		if (!fsmap->fmh_entries)
+			break;
+
+		for (i = 0, extent = fsmap->fmh_recs;
+		     i < fsmap->fmh_entries;
+		     i++, extent++) {
+			if (!(extent->fmr_flags & FMR_OF_SPECIAL_OWNER) ||
+			    extent->fmr_owner != XFS_FMR_OWN_FREE)
+				continue;
+			agbno = (extent->fmr_physical - (bperag * agno)) /
+								blocksize;
+			aglen = extent->fmr_length / blocksize;
+
+			addtohist(agno, agbno, aglen);
+		}
+
+		p = &fsmap->fmh_recs[fsmap->fmh_entries - 1];
+		if (p->fmr_flags & FMR_OF_LAST)
+			break;
+		fsmap_advance(fsmap);
+	}
+}
+static void
+aglistadd(
+	char		*a)
+{
+	xfs_agnumber_t	x;
+
+	aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
+	x = cvt_u32(a, 0);
+	if (errno) {
+		printf(_("Unrecognized AG number: %s\n"), a);
+		return;
+	}
+	aglist[agcount] = x;
+	agcount++;
+}
+
+static int
+init(
+	int		argc,
+	char		**argv)
+{
+	long long	x;
+	int		c;
+	int		speced = 0;	/* only one of -b -e -h or -m */
+
+	agcount = dumpflag = equalsize = multsize = optind = 0;
+	histcount = seen1 = summaryflag = 0;
+	totblocks = totexts = 0;
+	aglist = NULL;
+	hist = NULL;
+	rtflag = false;
+
+	while ((c = getopt(argc, argv, "a:bde:h:m:rs")) != EOF) {
+		switch (c) {
+		case 'a':
+			aglistadd(optarg);
+			break;
+		case 'b':
+			if (speced)
+				goto many_spec;
+			multsize = 2;
+			speced = 1;
+			break;
+		case 'd':
+			dumpflag = 1;
+			break;
+		case 'e':
+			if (speced)
+				goto many_spec;
+			equalsize = cvt_s64(optarg, 0);
+			if (errno)
+				return command_usage(&freesp_cmd);
+			speced = 1;
+			break;
+		case 'h':
+			if (speced && !histcount)
+				goto many_spec;
+			/* addhistent increments histcount */
+			x = cvt_s64(optarg, 0);
+			if (errno)
+				return command_usage(&freesp_cmd);
+			addhistent(x);
+			speced = 1;
+			break;
+		case 'm':
+			if (speced)
+				goto many_spec;
+			multsize = cvt_s64(optarg, 0);
+			if (errno)
+				return command_usage(&freesp_cmd);
+			speced = 1;
+			break;
+		case 'r':
+			rtflag = true;
+			break;
+		case 's':
+			summaryflag = 1;
+			break;
+		case '?':
+		default:
+			return command_usage(&freesp_cmd);
+		}
+	}
+	if (optind != argc)
+		return 0;
+	if (!speced)
+		multsize = 2;
+	histinit(file->geom.agblocks);
+	return 1;
+many_spec:
+	return command_usage(&freesp_cmd);
+}
+
+/*
+ * Report on freespace usage in xfs filesystem.
+ */
+static int
+freesp_f(
+	int		argc,
+	char		**argv)
+{
+	xfs_agnumber_t	agno;
+
+	if (!init(argc, argv))
+		return 0;
+	if (rtflag)
+		scan_ag(NULLAGNUMBER);
+	for (agno = 0; !rtflag && agno < file->geom.agcount; agno++)  {
+		if (inaglist(agno))
+			scan_ag(agno);
+	}
+	if (histcount)
+		printhist();
+	if (summaryflag) {
+		printf(_("total free extents %lld\n"), totexts);
+		printf(_("total free blocks %lld\n"), totblocks);
+		printf(_("average free extent size %g\n"),
+			(double)totblocks / (double)totexts);
+	}
+	if (aglist)
+		free(aglist);
+	if (hist)
+		free(hist);
+	return 0;
+}
+
+static void
+freesp_help(void)
+{
+	printf(_(
+"\n"
+"Examine filesystem free space\n"
+"\n"
+" -a agno  -- Scan only the given AG agno.\n"
+" -b       -- binary histogram bin size\n"
+" -d       -- debug output\n"
+" -e bsize -- Use fixed histogram bin size of bsize\n"
+" -h hbsz  -- Use custom histogram bin size of h1.\n"
+"             Multiple specifications are allowed.\n"
+" -m bmult -- Use histogram bin size multiplier of bmult.\n"
+" -r       -- Display realtime device free space information.\n"
+" -s       -- Emit freespace summary information.\n"
+"\n"
+"Only one of -b, -e, -h, or -m may be specified.\n"
+"\n"));
+
+}
+
+void
+freesp_init(void)
+{
+	freesp_cmd.name = "freesp";
+	freesp_cmd.altname = "fsp";
+	freesp_cmd.cfunc = freesp_f;
+	freesp_cmd.argmin = 0;
+	freesp_cmd.argmax = -1;
+	freesp_cmd.args = "[-drs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
+	freesp_cmd.flags = CMD_FLAG_ONESHOT;
+	freesp_cmd.oneline = _("Examine filesystem free space");
+	freesp_cmd.help = freesp_help;
+
+	add_command(&freesp_cmd);
+}
+
diff --git a/spaceman/init.c b/spaceman/init.c
index a51007d..b3eface 100644
--- a/spaceman/init.c
+++ b/spaceman/init.c
@@ -20,6 +20,7 @@
 #include "command.h"
 #include "input.h"
 #include "init.h"
+#include "path.h"
 #include "space.h"
 
 char	*progname;
@@ -42,6 +43,7 @@ init_commands(void)
 	prealloc_init();
 	quit_init();
 	trim_init();
+	freesp_init();
 }
 
 static int
@@ -70,12 +72,14 @@ init(
 {
 	int		c;
 	xfs_fsop_geom_t	geometry = { 0 };
+	struct fs_path	fsp;
 
 	progname = basename(argv[0]);
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
 
+	fs_table_initialise(0, NULL, 0, NULL);
 	while ((c = getopt(argc, argv, "c:V")) != EOF) {
 		switch (c) {
 		case 'c':
@@ -92,11 +96,11 @@ init(
 	if (optind != argc - 1)
 		usage();
 
-	if ((c = openfile(argv[optind], &geometry)) < 0)
+	if ((c = openfile(argv[optind], &geometry, &fsp)) < 0)
 		exit(1);
 	if (!platform_test_xfs_fd(c))
 		printf(_("Not an XFS filesystem!\n"));
-	if (addfile(argv[optind], c, &geometry) < 0)
+	if (addfile(argv[optind], c, &geometry, &fsp) < 0)
 		exit(1);
 
 	init_commands();
diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
index 439a9e8..689ed05 100644
--- a/spaceman/prealloc.c
+++ b/spaceman/prealloc.c
@@ -20,6 +20,7 @@
 #include "command.h"
 #include "input.h"
 #include "init.h"
+#include "path.h"
 #include "space.h"
 
 static cmdinfo_t prealloc_cmd;
diff --git a/spaceman/space.h b/spaceman/space.h
index de97139..5f4a8a0 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -20,6 +20,7 @@
 
 typedef struct fileio {
 	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
+	struct fs_path	fs_path;	/* XFS path information */
 	char		*name;		/* file name at time of open */
 	int		fd;		/* open file descriptor */
 } fileio_t;
@@ -28,13 +29,18 @@ extern fileio_t		*filetable;	/* open file table */
 extern int		filecount;	/* number of open files */
 extern fileio_t		*file;		/* active file in file table */
 
-extern int	openfile(char *, xfs_fsop_geom_t *);
-extern int	addfile(char *, int , xfs_fsop_geom_t *);
+extern int	openfile(char *, xfs_fsop_geom_t *, struct fs_path *);
+extern int	addfile(char *, int , xfs_fsop_geom_t *, struct fs_path *);
 
 extern void	print_init(void);
 extern void	help_init(void);
 extern void	prealloc_init(void);
 extern void	quit_init(void);
 extern void	trim_init(void);
+#ifdef HAVE_GETFSMAP
+extern void	freesp_init(void);
+#else
+# define freesp_init()	do { } while (0)
+#endif
 
 #endif /* XFS_SPACEMAN_SPACE_H_ */
diff --git a/spaceman/trim.c b/spaceman/trim.c
index c0bc7f2..c20d2fe 100644
--- a/spaceman/trim.c
+++ b/spaceman/trim.c
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include "command.h"
 #include "init.h"
+#include "path.h"
 #include "space.h"
 #include "input.h"
 


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

* [PATCH 11/12] xfs_spaceman: add a man page
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (9 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 10/12] xfs_spaceman: Free space mapping command Darrick J. Wong
@ 2017-06-15 20:36 ` Darrick J. Wong
  2017-06-21 21:45   ` Eric Sandeen
  2017-06-15 20:37 ` [PATCH 12/12] xfs_spaceman: add group summary mode Darrick J. Wong
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:36 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Add a manual page describing xfs_spaceman's behavior.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man8/xfs_spaceman.8 |  171 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 171 insertions(+)
 create mode 100644 man/man8/xfs_spaceman.8


diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
new file mode 100644
index 0000000..f064360
--- /dev/null
+++ b/man/man8/xfs_spaceman.8
@@ -0,0 +1,171 @@
+.TH xfs_spaceman 8
+.SH NAME
+xfs_spaceman \- show free space information about an XFS filesystem
+.SH SYNOPSIS
+.B xfs_spaceman
+[
+.B \-c
+.I cmd
+]
+.I file
+.br
+.B xfs_spaceman \-V
+.SH DESCRIPTION
+.B xfs_spaceman
+reports and controls free space usage in an XFS filesystem.
+.SH OPTIONS
+.TP 1.0i
+.BI \-c " cmd"
+.B xfs_spaceman
+commands may be run interactively (the default) or as arguments on
+the command line. Multiple
+.B \-c
+arguments may be given. The commands are run in the sequence given,
+then the program exits.
+
+.SH COMMANDS
+.TP
+.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
+With no arguments,
+.B freesp
+shows a histogram of all free space extents in the filesystem.
+The command takes the following options:
+
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.B \-a agno
+Collect free space information from this allocation group.
+This option can be specified multiple times to collect from multiple groups.
+
+.TP
+.B \-b
+This argument establishes that the histogram bin sizes are successive powers of two.
+This is the default, and is mutually exclusive with the
+.BR "-e" ", " "-h" ", and " "-m" " options."
+
+.TP
+.B \-d
+Print debugging information such as the raw free space extent information.
+
+.TP
+.B \-e bsize
+Set all histogram bin sizes to a specific value.
+This option is mutually exclusive with the
+.BR "-b" ", " "-h" ", and " "-m" " options."
+
+.TP
+.B \-h bsize
+Create a histogram bin with a lower bound of this value.
+The upper bound of this bin will be one less than the lower bound of the
+next highest histogram bin.
+This option can be given multiple times to control the exact bin sizes.
+This option is mutually exclusive with the
+.BR "-b" ", " "-e" ", and " "-m" " options."
+
+.TP
+.B \-m factor
+Create each histogram bin with a size that is this many times the size
+of the prvious bin created.
+This option is mutually exclusive with the
+.BR "-b" ", " "-e" ", and " "-h" " options."
+
+.TP
+.B \-r
+Query the realtime device for free space information.
+
+.TP
+.B \-s
+Display a summary of the free space information found.
+.PD
+.RE
+.TP
+.BR "help [ " command " ]"
+Display a brief description of one or all commands.
+.TP
+.BI "prealloc [ \-u id ] [ \-g id ] [ -p id ] [ \-m minlen ] [ \-s ]"
+Controls speculative preallocation.
+If no
+.BR "-u" ", " "-g" ", or " "-p"
+options are given, this command acts on all files.
+The command takes the following options:
+
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.B \-u uid
+Clear all speculative preallocations for files with this user id.
+This option can be given in combination with the
+.B "-g" " and " "-p"
+options.
+
+.TP
+.B \-g gid
+Clear all speculative preallocations for files with this group id.
+This option can be given in combination with the
+.B "-u" " and " "-p"
+options.
+
+.TP
+.B \-p pid
+Clear all speculative preallocations for files with this project id.
+This option can be given in combination with the
+.B "-u" " and " "-g"
+options.
+
+.TP
+.B \-m minlen
+Ignore all files smaller than this size.
+Units can be supplied for this argument.
+
+.TP
+.B \-s
+Flush all dirty data and metadata to disk.
+.PD
+.RE
+.TP
+.B print
+Display a list of all open files.
+.TP
+.B quit
+Exit
+.BR xfs_spaceman .
+.TP
+.BI "trim ( \-a agno | \-f | " "offset" " " "length" " ) [ -m minlen ]"
+Instructs the underlying storage device to release all storage that may
+be backing free space in the filesystem.
+The command takes the following options:
+
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.B \-a agno
+Trim free space extents in the given allocation group.
+This option is mutually exclusive with the
+.BR "-f" " option and the "
+.IR "offset" "/" "length" " options."
+
+.TP
+.B \-f
+Trim all free space in the filesystem.
+This option is mutually exclusive with the
+.BR "-a" " option and the "
+.IR "offset" "/" "length" " options."
+
+.TP
+.IR "option" ", " "length"
+Trim all free space within the physical range defined by the
+.I offset
+and
+.I length
+from this filesystem.
+Units can be appended to these arguments.
+This option is mutually exclusive with the
+.BR "-a" " and " "-f" " options."
+
+.TP
+.B \-m minlen
+Do not trim free space extents shorter than this length.
+Units can be appended to this argument.
+.PD
+.RE


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

* [PATCH 12/12] xfs_spaceman: add group summary mode
  2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (10 preceding siblings ...)
  2017-06-15 20:36 ` [PATCH 11/12] xfs_spaceman: add a man page Darrick J. Wong
@ 2017-06-15 20:37 ` Darrick J. Wong
  2017-06-21 21:53   ` Eric Sandeen
  11 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-15 20:37 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

Add a -g switch to show only a per-group summary.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 man/man8/xfs_spaceman.8 |    6 +++++-
 spaceman/freesp.c       |   27 ++++++++++++++++++++++++---
 2 files changed, 29 insertions(+), 4 deletions(-)


diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
index f064360..0302196 100644
--- a/man/man8/xfs_spaceman.8
+++ b/man/man8/xfs_spaceman.8
@@ -25,7 +25,7 @@ then the program exits.
 
 .SH COMMANDS
 .TP
-.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
+.BI "freesp [ \-dgrs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
 With no arguments,
 .B freesp
 shows a histogram of all free space extents in the filesystem.
@@ -49,6 +49,10 @@ This is the default, and is mutually exclusive with the
 Print debugging information such as the raw free space extent information.
 
 .TP
+.B \-g
+Print the free space block and extent counts for each AG.
+
+.TP
 .B \-e bsize
 Set all histogram bin sizes to a specific value.
 This option is mutually exclusive with the
diff --git a/spaceman/freesp.c b/spaceman/freesp.c
index 99e50cf..79eea44 100644
--- a/spaceman/freesp.c
+++ b/spaceman/freesp.c
@@ -43,6 +43,7 @@ static long long	multsize;
 static int		histcount;
 static int		seen1;
 static int		summaryflag;
+static int		gflag;
 static bool		rtflag;
 static long long	totblocks;
 static long long	totexts;
@@ -164,6 +165,8 @@ scan_ag(
 	off64_t			bperag;
 	off64_t			aglen;
 	xfs_agblock_t		agbno;
+	unsigned long long	freeblks = 0;
+	unsigned long long	freeexts = 0;
 	int			ret;
 	int			i;
 
@@ -216,6 +219,8 @@ scan_ag(
 			agbno = (extent->fmr_physical - (bperag * agno)) /
 								blocksize;
 			aglen = extent->fmr_length / blocksize;
+			freeblks += aglen;
+			freeexts++;
 
 			addtohist(agno, agbno, aglen);
 		}
@@ -225,6 +230,15 @@ scan_ag(
 			break;
 		fsmap_advance(fsmap);
 	}
+
+	if (gflag) {
+		if (agno == NULLAGNUMBER)
+			printf(_("     rtdev %10llu %10llu\n"), freeexts,
+					freeblks);
+		else
+			printf(_("%10u %10llu %10llu\n"), agno, freeexts,
+					freeblks);
+	}
 }
 static void
 aglistadd(
@@ -258,7 +272,7 @@ init(
 	hist = NULL;
 	rtflag = false;
 
-	while ((c = getopt(argc, argv, "a:bde:h:m:rs")) != EOF) {
+	while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
 		switch (c) {
 		case 'a':
 			aglistadd(optarg);
@@ -280,6 +294,10 @@ init(
 				return command_usage(&freesp_cmd);
 			speced = 1;
 			break;
+		case 'g':
+			histcount = 0;
+			gflag++;
+			break;
 		case 'h':
 			if (speced && !histcount)
 				goto many_spec;
@@ -331,13 +349,15 @@ freesp_f(
 
 	if (!init(argc, argv))
 		return 0;
+	if (gflag)
+		printf(_("        AG    extents     blocks\n"));
 	if (rtflag)
 		scan_ag(NULLAGNUMBER);
 	for (agno = 0; !rtflag && agno < file->geom.agcount; agno++)  {
 		if (inaglist(agno))
 			scan_ag(agno);
 	}
-	if (histcount)
+	if (histcount && !gflag)
 		printhist();
 	if (summaryflag) {
 		printf(_("total free extents %lld\n"), totexts);
@@ -363,6 +383,7 @@ freesp_help(void)
 " -b       -- binary histogram bin size\n"
 " -d       -- debug output\n"
 " -e bsize -- Use fixed histogram bin size of bsize\n"
+" -g       -- Print only a per-AG summary.\n"
 " -h hbsz  -- Use custom histogram bin size of h1.\n"
 "             Multiple specifications are allowed.\n"
 " -m bmult -- Use histogram bin size multiplier of bmult.\n"
@@ -382,7 +403,7 @@ freesp_init(void)
 	freesp_cmd.cfunc = freesp_f;
 	freesp_cmd.argmin = 0;
 	freesp_cmd.argmax = -1;
-	freesp_cmd.args = "[-drs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
+	freesp_cmd.args = "[-dgrs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
 	freesp_cmd.flags = CMD_FLAG_ONESHOT;
 	freesp_cmd.oneline = _("Examine filesystem free space");
 	freesp_cmd.help = freesp_help;


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

* Re: [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-15 20:36 ` [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long Darrick J. Wong
@ 2017-06-21 20:16   ` Eric Sandeen
  2017-06-21 20:29     ` Darrick J. Wong
  2017-06-21 21:19   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:16 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Create some helper functions to convert strings to int or long
> in a standard way and work around problems in the C library
> atoi/strtol functions.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  include/input.h |    8 +++
>  libxcmd/input.c |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 142 insertions(+)
> 
> 
> diff --git a/include/input.h b/include/input.h
> index 82cd2f4..145114b 100644
> --- a/include/input.h
> +++ b/include/input.h
> @@ -28,6 +28,14 @@ extern char	**breakline(char *input, int *count);
>  extern void	doneline(char *input, char **vec);
>  extern char	*fetchline(void);
>  
> +extern int64_t	cvt_s64(char *s, int base);
> +extern int32_t	cvt_s32(char *s, int base);
> +extern int16_t	cvt_s16(char *s, int base);
> +
> +extern uint64_t	cvt_u64(char *s, int base);
> +extern uint32_t	cvt_u32(char *s, int base);
> +extern uint16_t	cvt_u16(char *s, int base);
> +
>  extern size_t numlen(uint64_t val, size_t base);
>  extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
>  extern void	cvtstr(double value, char *str, size_t sz);
> diff --git a/libxcmd/input.c b/libxcmd/input.c
> index 9437be3..8dd232c 100644
> --- a/libxcmd/input.c
> +++ b/libxcmd/input.c
> @@ -149,6 +149,140 @@ numlen(
>  	return len == 0 ? 1 : len;
>  }
>  
> +/*
> + * Convert string to int64_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +int64_t
> +cvt_s64(
> +	char		*s,
> +	int		base)
> +{
> +	long long	i;
> +	char		*sp;
> +	int		c;

unused var c

> +
> +	errno = 0;
> +	i = strtoll(s, &sp, base);
> +	if (*sp == '\0' && sp != s)
> +		return i;
> +bad:

label bad: defined but not used

> +	errno = -ERANGE;
> +	return INT64_MIN;

Hm, doesn't strtoll return LLONG_MIN or LLONG_MAX for underflows
and overflows?  Do you really want to return MIN even if this /overflowed/?
(Maybe it doesn't matter, gut its a bit of a departure from strtoll semantics)

> +}
> +
> +/*
> + * Convert string to int32_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +int32_t
> +cvt_s32(
> +	char		*s,
> +	int		base)
> +{
> +	int64_t		i;
> +
> +	i = cvt_s64(s, base);
> +	if (errno)
> +		return i;
> +	if (i > INT32_MAX || i < INT32_MIN) {
> +		errno = -ERANGE;
> +		return INT32_MIN;

same sort of Q here, wouldn't you want MIN or MAX
for underflow/overflow?  Same below as well for all
signed type conversions.

> +	}
> +	return i;
> +}
> +
> +/*
> + * Convert string to int16_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +int16_t
> +cvt_s16(
> +	char		*s,
> +	int		base)
> +{
> +	int64_t		i;
> +
> +	i = cvt_s64(s, base);
> +	if (errno)
> +		return i;
> +	if (i > INT16_MAX || i < INT16_MIN) {
> +		errno = -ERANGE;
> +		return INT16_MIN;
> +	}
> +	return i;
> +}
> +
> +/*
> + * Convert string to uint64_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +uint64_t
> +cvt_u64(
> +	char		*s,
> +	int		base)
> +{
> +	long long	i;
> +	char		*sp;
> +	int		c;

unused var c:

> +
> +	errno = 0;
> +	i = strtoll(s, &sp, base);
> +	if (*sp == '\0' && sp != s)
> +		return i;
> +bad:

unused label bad:

> +	errno = -ERANGE;
> +	return UINT64_MAX;
> +}
> +
> +/*
> + * Convert string to uint32_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +uint32_t
> +cvt_u32(
> +	char		*s,
> +	int		base)
> +{
> +	uint64_t	i;
> +
> +	i = cvt_u64(s, base);
> +	if (errno)
> +		return i;
> +	if (i > UINT32_MAX) {
> +		errno = -ERANGE;
> +		return UINT32_MAX;
> +	}
> +	return i;
> +}
> +
> +/*
> + * Convert string to uint16_t, set errno if the conversion fails or
> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> + * prior to conversion.
> + */
> +uint16_t
> +cvt_u16(
> +	char		*s,
> +	int		base)
> +{
> +	uint64_t	i;
> +
> +	i = cvt_u64(s, base);
> +	if (errno)
> +		return i;
> +	if (i > UINT16_MAX) {
> +		errno = -ERANGE;
> +		return UINT16_MAX;
> +	}
> +	return i;
> +}
> +
>  #define EXABYTES(x)	((long long)(x) << 60)
>  #define PETABYTES(x)	((long long)(x) << 50)
>  #define TERABYTES(x)	((long long)(x) << 40)
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-21 20:16   ` Eric Sandeen
@ 2017-06-21 20:29     ` Darrick J. Wong
  2017-06-21 20:33       ` Eric Sandeen
  0 siblings, 1 reply; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-21 20:29 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 21, 2017 at 03:16:37PM -0500, Eric Sandeen wrote:
> On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Create some helper functions to convert strings to int or long
> > in a standard way and work around problems in the C library
> > atoi/strtol functions.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  include/input.h |    8 +++
> >  libxcmd/input.c |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 142 insertions(+)
> > 
> > 
> > diff --git a/include/input.h b/include/input.h
> > index 82cd2f4..145114b 100644
> > --- a/include/input.h
> > +++ b/include/input.h
> > @@ -28,6 +28,14 @@ extern char	**breakline(char *input, int *count);
> >  extern void	doneline(char *input, char **vec);
> >  extern char	*fetchline(void);
> >  
> > +extern int64_t	cvt_s64(char *s, int base);
> > +extern int32_t	cvt_s32(char *s, int base);
> > +extern int16_t	cvt_s16(char *s, int base);
> > +
> > +extern uint64_t	cvt_u64(char *s, int base);
> > +extern uint32_t	cvt_u32(char *s, int base);
> > +extern uint16_t	cvt_u16(char *s, int base);
> > +
> >  extern size_t numlen(uint64_t val, size_t base);
> >  extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
> >  extern void	cvtstr(double value, char *str, size_t sz);
> > diff --git a/libxcmd/input.c b/libxcmd/input.c
> > index 9437be3..8dd232c 100644
> > --- a/libxcmd/input.c
> > +++ b/libxcmd/input.c
> > @@ -149,6 +149,140 @@ numlen(
> >  	return len == 0 ? 1 : len;
> >  }
> >  
> > +/*
> > + * Convert string to int64_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +int64_t
> > +cvt_s64(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	long long	i;
> > +	char		*sp;
> > +	int		c;
> 
> unused var c
> 
> > +
> > +	errno = 0;
> > +	i = strtoll(s, &sp, base);
> > +	if (*sp == '\0' && sp != s)
> > +		return i;
> > +bad:
> 
> label bad: defined but not used

Fixed.  Sorry about the stupidity. :(

> > +	errno = -ERANGE;
> > +	return INT64_MIN;
> 
> Hm, doesn't strtoll return LLONG_MIN or LLONG_MAX for underflows
> and overflows?  Do you really want to return MIN even if this /overflowed/?
> (Maybe it doesn't matter, gut its a bit of a departure from strtoll semantics)

True, it's a departure from the usual semantics.  The intent is to call
the function this way:

long foo = cvt_s64(...);
if (errno) {
	fprintf(stderr, "N|_|MB3Rz ARE HARDZ!!1!\n");
	exit(5);
}

So at least in theory it wouldn't matter what we actually set foo to.

> > +}
> > +
> > +/*
> > + * Convert string to int32_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +int32_t
> > +cvt_s32(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	int64_t		i;
> > +
> > +	i = cvt_s64(s, base);
> > +	if (errno)
> > +		return i;
> > +	if (i > INT32_MAX || i < INT32_MIN) {
> > +		errno = -ERANGE;
> > +		return INT32_MIN;
> 
> same sort of Q here, wouldn't you want MIN or MAX
> for underflow/overflow?  Same below as well for all
> signed type conversions.
> 
> > +	}
> > +	return i;
> > +}
> > +
> > +/*
> > + * Convert string to int16_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +int16_t
> > +cvt_s16(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	int64_t		i;
> > +
> > +	i = cvt_s64(s, base);
> > +	if (errno)
> > +		return i;
> > +	if (i > INT16_MAX || i < INT16_MIN) {
> > +		errno = -ERANGE;
> > +		return INT16_MIN;
> > +	}
> > +	return i;
> > +}
> > +
> > +/*
> > + * Convert string to uint64_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +uint64_t
> > +cvt_u64(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	long long	i;
> > +	char		*sp;
> > +	int		c;
> 
> unused var c:
> 
> > +
> > +	errno = 0;
> > +	i = strtoll(s, &sp, base);
> > +	if (*sp == '\0' && sp != s)
> > +		return i;
> > +bad:
> 
> unused label bad:

Yeah, fixed.

--D

> > +	errno = -ERANGE;
> > +	return UINT64_MAX;
> > +}
> > +
> > +/*
> > + * Convert string to uint32_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +uint32_t
> > +cvt_u32(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	uint64_t	i;
> > +
> > +	i = cvt_u64(s, base);
> > +	if (errno)
> > +		return i;
> > +	if (i > UINT32_MAX) {
> > +		errno = -ERANGE;
> > +		return UINT32_MAX;
> > +	}
> > +	return i;
> > +}
> > +
> > +/*
> > + * Convert string to uint16_t, set errno if the conversion fails or
> > + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> > + * prior to conversion.
> > + */
> > +uint16_t
> > +cvt_u16(
> > +	char		*s,
> > +	int		base)
> > +{
> > +	uint64_t	i;
> > +
> > +	i = cvt_u64(s, base);
> > +	if (errno)
> > +		return i;
> > +	if (i > UINT16_MAX) {
> > +		errno = -ERANGE;
> > +		return UINT16_MAX;
> > +	}
> > +	return i;
> > +}
> > +
> >  #define EXABYTES(x)	((long long)(x) << 60)
> >  #define PETABYTES(x)	((long long)(x) << 50)
> >  #define TERABYTES(x)	((long long)(x) << 40)
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-21 20:29     ` Darrick J. Wong
@ 2017-06-21 20:33       ` Eric Sandeen
  2017-06-21 20:38         ` Darrick J. Wong
  0 siblings, 1 reply; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:33 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: sandeen, linux-xfs

On 6/21/17 3:29 PM, Darrick J. Wong wrote:
> On Wed, Jun 21, 2017 at 03:16:37PM -0500, Eric Sandeen wrote:

...

>>> --- a/libxcmd/input.c
>>> +++ b/libxcmd/input.c
>>> @@ -149,6 +149,140 @@ numlen(
>>>  	return len == 0 ? 1 : len;
>>>  }
>>>  
>>> +/*
>>> + * Convert string to int64_t, set errno if the conversion fails or
>>> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
>>> + * prior to conversion.
>>> + */
>>> +int64_t
>>> +cvt_s64(
>>> +	char		*s,
>>> +	int		base)
>>> +{
>>> +	long long	i;
>>> +	char		*sp;
>>> +	int		c;
>>
>> unused var c
>>
>>> +
>>> +	errno = 0;
>>> +	i = strtoll(s, &sp, base);
>>> +	if (*sp == '\0' && sp != s)
>>> +		return i;
>>> +bad:
>>
>> label bad: defined but not used
> 
> Fixed.  Sorry about the stupidity. :(

no problem, things like this make me feel better :D

> 
>>> +	errno = -ERANGE;
>>> +	return INT64_MIN;
>>
>> Hm, doesn't strtoll return LLONG_MIN or LLONG_MAX for underflows
>> and overflows?  Do you really want to return MIN even if this /overflowed/?
>> (Maybe it doesn't matter, gut its a bit of a departure from strtoll semantics)
> 
> True, it's a departure from the usual semantics.  The intent is to call
> the function this way:
> 
> long foo = cvt_s64(...);
> if (errno) {
> 	fprintf(stderr, "N|_|MB3Rz ARE HARDZ!!1!\n");
> 	exit(5);
> }
> 
> So at least in theory it wouldn't matter what we actually set foo to.

well, that was my next question.  ;)

But setting it to one extreme value leaves me wondering why you'd not set it
to the other extreme.  *shrug*  Not that big a deal, just a thing that made me
go "hmmmm"

-Eric

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

* Re: [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-21 20:33       ` Eric Sandeen
@ 2017-06-21 20:38         ` Darrick J. Wong
  0 siblings, 0 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-21 20:38 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 21, 2017 at 03:33:16PM -0500, Eric Sandeen wrote:
> On 6/21/17 3:29 PM, Darrick J. Wong wrote:
> > On Wed, Jun 21, 2017 at 03:16:37PM -0500, Eric Sandeen wrote:
> 
> ...
> 
> >>> --- a/libxcmd/input.c
> >>> +++ b/libxcmd/input.c
> >>> @@ -149,6 +149,140 @@ numlen(
> >>>  	return len == 0 ? 1 : len;
> >>>  }
> >>>  
> >>> +/*
> >>> + * Convert string to int64_t, set errno if the conversion fails or
> >>> + * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
> >>> + * prior to conversion.
> >>> + */
> >>> +int64_t
> >>> +cvt_s64(
> >>> +	char		*s,
> >>> +	int		base)
> >>> +{
> >>> +	long long	i;
> >>> +	char		*sp;
> >>> +	int		c;
> >>
> >> unused var c
> >>
> >>> +
> >>> +	errno = 0;
> >>> +	i = strtoll(s, &sp, base);
> >>> +	if (*sp == '\0' && sp != s)
> >>> +		return i;
> >>> +bad:
> >>
> >> label bad: defined but not used
> > 
> > Fixed.  Sorry about the stupidity. :(
> 
> no problem, things like this make me feel better :D
> 
> > 
> >>> +	errno = -ERANGE;
> >>> +	return INT64_MIN;
> >>
> >> Hm, doesn't strtoll return LLONG_MIN or LLONG_MAX for underflows
> >> and overflows?  Do you really want to return MIN even if this /overflowed/?
> >> (Maybe it doesn't matter, gut its a bit of a departure from strtoll semantics)
> > 
> > True, it's a departure from the usual semantics.  The intent is to call
> > the function this way:
> > 
> > long foo = cvt_s64(...);
> > if (errno) {
> > 	fprintf(stderr, "N|_|MB3Rz ARE HARDZ!!1!\n");
> > 	exit(5);
> > }
> > 
> > So at least in theory it wouldn't matter what we actually set foo to.
> 
> well, that was my next question.  ;)
> 
> But setting it to one extreme value leaves me wondering why you'd not set it
> to the other extreme.  *shrug*  Not that big a deal, just a thing that made me
> go "hmmmm"

I'll just change it to:

errno = 0;
i = strtoll(s, &sp, base);
if (*sp == '\0' && sp != s)
	return i;
else if (errno)
	return i;
errno = -ERANGE;
return INT64_MAX;

That way if strtoll /did/ have something to complain about, we'll
just pass it straight back to the caller.  The last two lines will
handle the case that someone feeds us junk at the end of the number
string (e.g. "359urgh") in which case we also want to spit back
an error condition.

--D

> >>> +bad:
> >>
> >> label bad: defined but not used
> > 
> > Fixed.  Sorry about the stupidity. :(
> 
> no problem, things like this make me feel better :D
> 
> > 
> >>> +	errno = -ERANGE;

> 
> -Eric
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 01/12] xfs_io: refactor numlen into a library function
  2017-06-15 20:35 ` [PATCH 01/12] xfs_io: refactor numlen into a library function Darrick J. Wong
@ 2017-06-21 20:40   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:40 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:35 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Refactor the competing numlen implementations into a single library function.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Yay!

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

> ---
>  include/input.h |    1 +
>  io/bmap.c       |   19 ++++---------------
>  io/fiemap.c     |   14 +-------------
>  libxcmd/input.c |   13 +++++++++++++
>  4 files changed, 19 insertions(+), 28 deletions(-)
> 
> 
> diff --git a/include/input.h b/include/input.h
> index 221678e..82cd2f4 100644
> --- a/include/input.h
> +++ b/include/input.h
> @@ -28,6 +28,7 @@ extern char	**breakline(char *input, int *count);
>  extern void	doneline(char *input, char **vec);
>  extern char	*fetchline(void);
>  
> +extern size_t numlen(uint64_t val, size_t base);
>  extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
>  extern void	cvtstr(double value, char *str, size_t sz);
>  extern unsigned long cvttime(char *s);
> diff --git a/io/bmap.c b/io/bmap.c
> index 2333244..2e4ff7b 100644
> --- a/io/bmap.c
> +++ b/io/bmap.c
> @@ -18,6 +18,7 @@
>  
>  #include "platform_defs.h"
>  #include "command.h"
> +#include "input.h"
>  #include "init.h"
>  #include "io.h"
>  
> @@ -53,18 +54,6 @@ bmap_help(void)
>  "\n"));
>  }
>  
> -static int
> -numlen(
> -	off64_t	val)
> -{
> -	off64_t	tmp;
> -	int	len;
> -
> -	for (len = 0, tmp = val; tmp > 0; tmp = tmp/10)
> -		len++;
> -	return (len == 0 ? 1 : len);
> -}
> -
>  int
>  bmap_f(
>  	int			argc,
> @@ -323,7 +312,7 @@ bmap_f(
>  			if (map[i + 1].bmv_block == -1) {
>  				foff_w = max(foff_w, strlen(rbuf));
>  				tot_w = max(tot_w,
> -					numlen(map[i+1].bmv_length));
> +					numlen(map[i+1].bmv_length, 10));
>  			} else {
>  				snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
>  					(long long) map[i + 1].bmv_block,
> @@ -344,10 +333,10 @@ bmap_f(
>  					aoff_w = 0;
>  				foff_w = max(foff_w, strlen(rbuf));
>  				tot_w = max(tot_w,
> -					numlen(map[i+1].bmv_length));
> +					numlen(map[i+1].bmv_length, 10));
>  			}
>  		}
> -		agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount));
> +		agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount, 10));
>  		printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
>  			_("EXT"),
>  			foff_w, _("FILE-OFFSET"),
> diff --git a/io/fiemap.c b/io/fiemap.c
> index bcbae49..75e8820 100644
> --- a/io/fiemap.c
> +++ b/io/fiemap.c
> @@ -18,6 +18,7 @@
>  
>  #include "platform_defs.h"
>  #include "command.h"
> +#include "input.h"
>  #include <linux/fiemap.h>
>  #include "init.h"
>  #include "io.h"
> @@ -48,19 +49,6 @@ fiemap_help(void)
>  "\n"));
>  }
>  
> -static int
> -numlen(
> -	__u64	val,
> -	int	base)
> -{
> -	__u64	tmp;
> -	int	len;
> -
> -	for (len = 0, tmp = val; tmp > 0; tmp = tmp/base)
> -		len++;
> -	return (len == 0 ? 1 : len);
> -}
> -
>  static void
>  print_verbose(
>  	struct fiemap_extent	*extent,
> diff --git a/libxcmd/input.c b/libxcmd/input.c
> index 8aeb3b0..9437be3 100644
> --- a/libxcmd/input.c
> +++ b/libxcmd/input.c
> @@ -136,6 +136,19 @@ doneline(
>  	free(vec);
>  }
>  
> +size_t
> +numlen(
> +	uint64_t	val,
> +	size_t		base)
> +{
> +	uint64_t	tmp;
> +	size_t		len;
> +
> +	for (len = 0, tmp = val; tmp > 0; tmp = tmp / base)
> +		len++;
> +	return len == 0 ? 1 : len;
> +}
> +
>  #define EXABYTES(x)	((long long)(x) << 60)
>  #define PETABYTES(x)	((long long)(x) << 50)
>  #define TERABYTES(x)	((long long)(x) << 40)
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default
  2017-06-15 20:36 ` [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default Darrick J. Wong
@ 2017-06-21 20:42   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:42 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> The crc32c code used in xfsprogs was copied directly from the Linux
> kernel.  However, that code selects slice-by-4 by default, which isn't
> the fastest -- that's slice-by-8, which trades table size for speed.
> Fix some makefile dependency problems and explicitly select the
> algorithm we want.  With this patch applied, I see about a 10% drop in
> CPU time running xfs_repair.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Already said it for the prior verrsion, having missed this one, but for
posterity, since I'll merge this one:

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

> ---
>  libxfs/Makefile    |    4 ++--
>  libxfs/crc32defs.h |   34 ++++++++++++++++++++++++++++++++++
>  2 files changed, 36 insertions(+), 2 deletions(-)
> 
> 
> diff --git a/libxfs/Makefile b/libxfs/Makefile
> index baba02f..d248c1f 100644
> --- a/libxfs/Makefile
> +++ b/libxfs/Makefile
> @@ -122,7 +122,7 @@ LDIRT = gen_crc32table crc32table.h crc32selftest
>  
>  default: crc32selftest ltdepend $(LTLIBRARY)
>  
> -crc32table.h: gen_crc32table.c
> +crc32table.h: gen_crc32table.c crc32defs.h
>  	@echo "    [CC]     gen_crc32table"
>  	$(Q) $(BUILD_CC) $(BUILD_CFLAGS) -o gen_crc32table $<
>  	@echo "    [GENERATE] $@"
> @@ -133,7 +133,7 @@ crc32table.h: gen_crc32table.c
>  # systems/architectures. Hence we make sure that xfsprogs will never use a
>  # busted CRC calculation at build time and hence avoid putting bad CRCs down on
>  # disk.
> -crc32selftest: gen_crc32table.c crc32table.h crc32.c
> +crc32selftest: gen_crc32table.c crc32table.h crc32.c crc32defs.h
>  	@echo "    [TEST]    CRC32"
>  	$(Q) $(BUILD_CC) $(BUILD_CFLAGS) -D CRC32_SELFTEST=1 crc32.c -o $@
>  	$(Q) ./$@
> diff --git a/libxfs/crc32defs.h b/libxfs/crc32defs.h
> index 64cba2c..2999782 100644
> --- a/libxfs/crc32defs.h
> +++ b/libxfs/crc32defs.h
> @@ -1,4 +1,38 @@
>  /*
> + * Use slice-by-8, which is the fastest variant.
> + *
> + * Calculate checksum 8 bytes at a time with a clever slicing algorithm.
> + * This is the fastest algorithm, but comes with a 8KiB lookup table.
> + * Most modern processors have enough cache to hold this table without
> + * thrashing the cache.
> + *
> + * The Linux kernel uses this as the default implementation "unless you
> + * have a good reason not to".  The reason why Kconfig urges you to pick
> + * SLICEBY8 is because people challenged the assertion that we should
> + * always use slice by 8, so Darrick wrote a crc microbenchmark utility
> + * and ran it on as many machines as he could get his hands on to show
> + * that sb8 was the fastest.
> + *
> + * Every 64-bit machine (and most of the 32-bit ones too) saw the best
> + * results with sb8.  Any machine with more than 4K of cache saw better
> + * results.  The spreadsheet still exists today[1]; note that
> + * 'crc32-kern-le' corresponds to the slice by 4 algorithm which is the
> + * default unless CRC_LE_BITS is defined explicitly.
> + *
> + * FWIW, there are a handful of board defconfigs in the kernel that
> + * don't pick sliceby8.  These are all embedded 32-bit mips/ppc systems
> + * with very small cache sizes which experience cache thrashing with the
> + * slice by 8 algorithm, and therefore chose to pick defaults that are
> + * saner for their particular board configuration.  For nearly all of
> + * XFS' perceived userbase (which we assume are 32 and 64-bit machines
> + * with sufficiently large CPU cache and largeish storage devices) slice
> + * by 8 is the right choice.
> + *
> + * [1] https://goo.gl/0LSzsG ("crc32c_bench")
> + */
> +#define CRC_LE_BITS 64
> +
> +/*
>   * There are multiple 16-bit CRC polynomials in common use, but this is
>   * *the* standard CRC-32 polynomial, first popularized by Ethernet.
>   * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl
  2017-06-15 20:36 ` [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
@ 2017-06-21 20:43   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:43 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Introduce a new ioctl that uses the reverse mapping btree to return
> information about the physical layout of the filesystem.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> [sandeen: rework to have less autoconf magic]
> Modified-by: Eric Sandeen <sandeen@redhat.com>

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

> ---
> v3: rework the whole mess to have less autoconf magic; now we only
> track if the system headers actually define getfsmap.
> v2: shorten the device field to u32 since that's all we need for
> dev_t.  Support reporting reverse mapping information for all the
> devices that XFS supports (data, log).
> ---
>  configure.ac          |    1 
>  include/builddefs.in  |    4 ++
>  include/linux.h       |  103 +++++++++++++++++++++++++++++++++++++++++++++++++
>  m4/package_libcdev.m4 |   20 ++++++++++
>  4 files changed, 128 insertions(+)
> 
> 
> diff --git a/configure.ac b/configure.ac
> index aa102e4..9534986 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -142,6 +142,7 @@ AC_HAVE_READDIR
>  AC_HAVE_FSETXATTR
>  AC_HAVE_MREMAP
>  AC_NEED_INTERNAL_FSXATTR
> +AC_HAVE_GETFSMAP
>  
>  if test "$enable_blkid" = yes; then
>  AC_HAVE_BLKID_TOPO
> diff --git a/include/builddefs.in b/include/builddefs.in
> index 4d6bb2d..ec630bd 100644
> --- a/include/builddefs.in
> +++ b/include/builddefs.in
> @@ -112,6 +112,7 @@ HAVE_FLS = @have_fls@
>  HAVE_FSETXATTR = @have_fsetxattr@
>  HAVE_MREMAP = @have_mremap@
>  NEED_INTERNAL_FSXATTR = @need_internal_fsxattr@
> +HAVE_GETFSMAP = @have_getfsmap@
>  
>  GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
>  #	   -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl
> @@ -150,6 +151,9 @@ endif
>  ifeq ($(NEED_INTERNAL_FSXATTR),yes)
>  PCFLAGS+= -DOVERRIDE_SYSTEM_FSXATTR
>  endif
> +ifeq ($(HAVE_GETFSMAP),yes)
> +PCFLAGS+= -DHAVE_GETFSMAP
> +endif
>  
>  
>  GCFLAGS = $(DEBUG) \
> diff --git a/include/linux.h b/include/linux.h
> index 6a676ca..38969d1 100644
> --- a/include/linux.h
> +++ b/include/linux.h
> @@ -222,4 +222,107 @@ struct fsxattr {
>  #define FS_XFLAG_COWEXTSIZE	0x00010000	/* CoW extent size allocator hint */
>  #endif
>  
> +#ifndef HAVE_GETFSMAP
> +/*
> + *	Structure for FS_IOC_GETFSMAP.
> + *
> + *	The memory layout for this call are the scalar values defined in
> + *	struct fsmap_head, followed by two struct fsmap that describe
> + *	the lower and upper bound of mappings to return, followed by an
> + *	array of struct fsmap mappings.
> + *
> + *	fmh_iflags control the output of the call, whereas fmh_oflags report
> + *	on the overall record output.  fmh_count should be set to the
> + *	length of the fmh_recs array, and fmh_entries will be set to the
> + *	number of entries filled out during each call.  If fmh_count is
> + *	zero, the number of reverse mappings will be returned in
> + *	fmh_entries, though no mappings will be returned.  fmh_reserved
> + *	must be set to zero.
> + *
> + *	The two elements in the fmh_keys array are used to constrain the
> + *	output.  The first element in the array should represent the
> + *	lowest disk mapping ("low key") that the user wants to learn
> + *	about.  If this value is all zeroes, the filesystem will return
> + *	the first entry it knows about.  For a subsequent call, the
> + *	contents of fsmap_head.fmh_recs[fsmap_head.fmh_count - 1] should be
> + *	copied into fmh_keys[0] to have the kernel start where it left off.
> + *
> + *	The second element in the fmh_keys array should represent the
> + *	highest disk mapping ("high key") that the user wants to learn
> + *	about.  If this value is all ones, the filesystem will not stop
> + *	until it runs out of mapping to return or runs out of space in
> + *	fmh_recs.
> + *
> + *	fmr_device can be either a 32-bit cookie representing a device, or
> + *	a 32-bit dev_t if the FMH_OF_DEV_T flag is set.  fmr_physical,
> + *	fmr_offset, and fmr_length are expressed in units of bytes.
> + *	fmr_owner is either an inode number, or a special value if
> + *	FMR_OF_SPECIAL_OWNER is set in fmr_flags.
> + */
> +struct fsmap {
> +	__u32		fmr_device;	/* device id */
> +	__u32		fmr_flags;	/* mapping flags */
> +	__u64		fmr_physical;	/* device offset of segment */
> +	__u64		fmr_owner;	/* owner id */
> +	__u64		fmr_offset;	/* file offset of segment */
> +	__u64		fmr_length;	/* length of segment */
> +	__u64		fmr_reserved[3];	/* must be zero */
> +};
> +
> +struct fsmap_head {
> +	__u32		fmh_iflags;	/* control flags */
> +	__u32		fmh_oflags;	/* output flags */
> +	__u32		fmh_count;	/* # of entries in array incl. input */
> +	__u32		fmh_entries;	/* # of entries filled in (output). */
> +	__u64		fmh_reserved[6];	/* must be zero */
> +
> +	struct fsmap	fmh_keys[2];	/* low and high keys for the mapping search */
> +	struct fsmap	fmh_recs[];	/* returned records */
> +};
> +
> +/* Size of an fsmap_head with room for nr records. */
> +static inline size_t
> +fsmap_sizeof(
> +	unsigned int	nr)
> +{
> +	return sizeof(struct fsmap_head) + nr * sizeof(struct fsmap);
> +}
> +
> +/* Start the next fsmap query at the end of the current query results. */
> +static inline void
> +fsmap_advance(
> +	struct fsmap_head	*head)
> +{
> +	head->fmh_keys[0] = head->fmh_recs[head->fmh_entries - 1];
> +}
> +
> +/*	fmh_iflags values - set by XFS_IOC_GETFSMAP caller in the header. */
> +/* no flags defined yet */
> +#define FMH_IF_VALID		0
> +
> +/*	fmh_oflags values - returned in the header segment only. */
> +#define FMH_OF_DEV_T		0x1	/* fmr_device values will be dev_t */
> +
> +/*	fmr_flags values - returned for each non-header segment */
> +#define FMR_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
> +#define FMR_OF_ATTR_FORK	0x2	/* segment = attribute fork */
> +#define FMR_OF_EXTENT_MAP	0x4	/* segment = extent map */
> +#define FMR_OF_SHARED		0x8	/* segment = shared with another file */
> +#define FMR_OF_SPECIAL_OWNER	0x10	/* owner is a special value */
> +#define FMR_OF_LAST		0x20	/* segment is the last in the FS */
> +
> +/* Each FS gets to define its own special owner codes. */
> +#define FMR_OWNER(type, code)	(((__u64)type << 32) | \
> +				 ((__u64)code & 0xFFFFFFFFULL))
> +#define FMR_OWNER_TYPE(owner)	((__u32)((__u64)owner >> 32))
> +#define FMR_OWNER_CODE(owner)	((__u32)(((__u64)owner & 0xFFFFFFFFULL)))
> +#define FMR_OWN_FREE		FMR_OWNER(0, 1) /* free space */
> +#define FMR_OWN_UNKNOWN		FMR_OWNER(0, 2) /* unknown owner */
> +#define FMR_OWN_METADATA	FMR_OWNER(0, 3) /* metadata */
> +
> +#define FS_IOC_GETFSMAP		_IOWR('X', 59, struct fsmap_head)
> +
> +#define HAVE_GETFSMAP
> +#endif /* HAVE_GETFSMAP */
> +
>  #endif	/* __XFS_LINUX_H__ */
> diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
> index bc3b4ce..fa5b639 100644
> --- a/m4/package_libcdev.m4
> +++ b/m4/package_libcdev.m4
> @@ -277,3 +277,23 @@ AC_DEFUN([AC_NEED_INTERNAL_FSXATTR],
>      )
>      AC_SUBST(need_internal_fsxattr)
>    ])
> +
> +#
> +# Check if we have a FS_IOC_GETFSMAP ioctl (Linux)
> +#
> +AC_DEFUN([AC_HAVE_GETFSMAP],
> +  [ AC_MSG_CHECKING([for GETFSMAP])
> +    AC_TRY_LINK([
> +#define _GNU_SOURCE
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#include <linux/fs.h>
> +#include <linux/fsmap.h>
> +    ], [
> +         unsigned long x = FS_IOC_GETFSMAP;
> +         struct fsmap_head fh;
> +    ], have_getfsmap=yes
> +       AC_MSG_RESULT(yes),
> +       AC_MSG_RESULT(no))
> +    AC_SUBST(have_getfsmap)
> +  ])
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 05/12] xfs_io: support the new getfsmap ioctl
  2017-06-15 20:36 ` [PATCH 05/12] xfs_io: support the new getfsmap ioctl Darrick J. Wong
@ 2017-06-21 20:51   ` Eric Sandeen
  2017-06-21 20:54     ` Darrick J. Wong
  0 siblings, 1 reply; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 20:51 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Plumb in all the pieces we need to have xfs_io query the GETFSMAP ioctl
> for an arbitrary filesystem.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

With minor changes mentioned below which I'll take care of
if you approve,

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

> ---
>  io/Makefile          |    7 +
>  io/copy_file_range.c |    2 
>  io/encrypt.c         |    1 
>  io/fsmap.c           |  588 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  io/init.c            |    8 +
>  io/io.h              |   14 +
>  io/open.c            |   21 +-
>  io/pwrite.c          |    2 
>  io/reflink.c         |    4 
>  io/sendfile.c        |    2 
>  man/man8/xfs_io.8    |   99 ++++++++
>  11 files changed, 735 insertions(+), 13 deletions(-)
>  create mode 100644 io/fsmap.c
> 
> 
> diff --git a/io/Makefile b/io/Makefile
> index 435ccff..47b0a66 100644
> --- a/io/Makefile
> +++ b/io/Makefile
> @@ -99,6 +99,13 @@ ifeq ($(HAVE_MREMAP),yes)
>  LCFLAGS += -DHAVE_MREMAP
>  endif
>  
> +# On linux we get fsmap from the system or define it ourselves
> +# so include this based on platform type.  If this reverts to only
> +# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
> +ifeq ($(PKG_PLATFORM),linux)
> +CFILES += fsmap.c
> +endif
> +
>  default: depend $(LTCOMMAND)
>  
>  include $(BUILDRULES)
> diff --git a/io/copy_file_range.c b/io/copy_file_range.c
> index 249c649..d1dfc5a 100644
> --- a/io/copy_file_range.c
> +++ b/io/copy_file_range.c
> @@ -121,7 +121,7 @@ copy_range_f(int argc, char **argv)
>  	if (optind != argc - 1)
>  		return command_usage(&copy_range_cmd);
>  
> -	fd = openfile(argv[optind], NULL, IO_READONLY, 0);
> +	fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
>  	if (fd < 0)
>  		return 0;
>  
> diff --git a/io/encrypt.c b/io/encrypt.c
> index d844c5e..26ab97c 100644
> --- a/io/encrypt.c
> +++ b/io/encrypt.c
> @@ -20,6 +20,7 @@
>  #include "platform_defs.h"
>  #include "command.h"
>  #include "init.h"
> +#include "path.h"
>  #include "io.h"
>  
>  #ifndef ARRAY_SIZE
> diff --git a/io/fsmap.c b/io/fsmap.c
> new file mode 100644
> index 0000000..25c0e76
> --- /dev/null
> +++ b/io/fsmap.c
> @@ -0,0 +1,588 @@
> +/*
> + * Copyright (C) 2017 Oracle.  All Rights Reserved.
> + *
> + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +#include "platform_defs.h"
> +#include "command.h"
> +#include "init.h"
> +#include "path.h"
> +#include "io.h"
> +#include "input.h"
> +
> +static cmdinfo_t	fsmap_cmd;
> +static dev_t		xfs_data_dev;
> +
> +static void
> +fsmap_help(void)
> +{
> +	printf(_(
> +"\n"
> +" Prints the block mapping for a filesystem"

"for the filesystem hosting the current file?"

> +"\n"
> +" fsmap prints the map of disk blocks used by the whole filesystem.\n"
> +" When possible, owner and offset information will be included in the\n"
> +" sapce report.\n"

space report


> +"\n"
> +" By default, each line of the listing takes the following form:\n"
> +"     extent: major:minor [startblock..endblock]: owner startoffset..endoffset length\n"
> +" The owner field is either an inode number or a special value.\n"
> +" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
> +" -d -- query only the data device (default).\n"
> +" -l -- query only the log device.\n"
> +" -r -- query only the realtime device.\n"

+"-m -- machine readable output format.\n"

> +" -n -- query n extents at a time.\n"
> +" -v -- Verbose information, show AG and offsets.  Show flags legend on 2nd -v\n"
> +"\n"));
> +}
> +
> +#define OWNER_BUF_SZ	32
> +static const char *
> +special_owner(
> +	int64_t		owner,
> +	char		*buf)
> +{
> +	switch (owner) {
> +	case XFS_FMR_OWN_FREE:
> +		return _("free space");
> +	case XFS_FMR_OWN_UNKNOWN:
> +		return _("unknown");
> +	case XFS_FMR_OWN_FS:
> +		return _("static fs metadata");
> +	case XFS_FMR_OWN_LOG:
> +		return _("journalling log");
> +	case XFS_FMR_OWN_AG:
> +		return _("per-AG metadata");
> +	case XFS_FMR_OWN_INOBT:
> +		return _("inode btree");
> +	case XFS_FMR_OWN_INODES:
> +		return _("inodes");
> +	case XFS_FMR_OWN_REFC:
> +		return _("refcount btree");
> +	case XFS_FMR_OWN_COW:
> +		return _("cow reservation");
> +	case XFS_FMR_OWN_DEFECTIVE:
> +		return _("defective");
> +	default:
> +		snprintf(buf, OWNER_BUF_SZ, _("special %u:%u"),
> +				FMR_OWNER_TYPE(owner), FMR_OWNER_CODE(owner));
> +		return buf;
> +	}
> +}
> +
> +static void
> +dump_map(
> +	unsigned long long	*nr,
> +	struct fsmap_head	*head)
> +{
> +	unsigned long long	i;
> +	struct fsmap		*p;
> +	char			owner[OWNER_BUF_SZ];
> +	char			*fork;
> +
> +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> +		printf("\t%llu: %u:%u [%lld..%lld]: ", i + (*nr),
> +			major(p->fmr_device), minor(p->fmr_device),
> +			(long long)BTOBBT(p->fmr_physical),
> +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> +		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? _("attr") : _("data");
> +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> +			printf("%s", special_owner(p->fmr_owner, owner));
> +		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> +			printf(_("inode %lld %s extent map"),
> +				(long long) p->fmr_owner, fork);
> +		else
> +			printf(_("inode %lld %s %lld..%lld"),
> +				(long long)p->fmr_owner, fork,
> +				(long long)BTOBBT(p->fmr_offset),
> +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> +		printf(_(" %lld\n"),
> +			(long long)BTOBBT(p->fmr_length));
> +	}
> +
> +	(*nr) += head->fmh_entries;
> +}
> +
> +static void
> +dump_map_machine(
> +	unsigned long long	*nr,
> +	struct fsmap_head	*head)
> +{
> +	unsigned long long	i;
> +	struct fsmap		*p;
> +	char			*fork;
> +
> +	printf(_("EXT,MAJOR,MINOR,PSTART,PEND,OWNER,OSTART,OEND,LENGTH\n"));
> +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> +		printf("%llu,%u,%u,%lld,%lld,", i + (*nr),
> +			major(p->fmr_device), minor(p->fmr_device),
> +			(long long)BTOBBT(p->fmr_physical),
> +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> +		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? "attr" : "data";
> +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> +			printf("special_%u:%u,,,", FMR_OWNER_TYPE(p->fmr_owner),
> +				FMR_OWNER_CODE(p->fmr_owner));
> +		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> +			printf(_("inode_%lld_%s_bmbt,,,"),
> +				(long long) p->fmr_owner, fork);
> +		else
> +			printf(_("inode_%lld_%s,%lld,%lld,"),
> +				(long long)p->fmr_owner, fork,
> +				(long long)BTOBBT(p->fmr_offset),
> +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> +		printf("%lld\n",
> +			(long long)BTOBBT(p->fmr_length));
> +	}
> +
> +	(*nr) += head->fmh_entries;
> +}
> +
> +/*
> + * Verbose mode displays:
> + *   extent: major:minor [startblock..endblock]: startoffset..endoffset \
> + *	ag# (agoffset..agendoffset) totalbbs flags
> + */
> +#define MINRANGE_WIDTH	16
> +#define MINAG_WIDTH	2
> +#define MINTOT_WIDTH	5
> +#define NFLG		7		/* count of flags */
> +#define	FLG_NULL	00000000	/* Null flag */
> +#define	FLG_ATTR_FORK	01000000	/* attribute fork */
> +#define	FLG_SHARED	00100000	/* shared extent */
> +#define	FLG_PRE		00010000	/* Unwritten extent */
> +#define	FLG_BSU		00001000	/* Not on begin of stripe unit  */
> +#define	FLG_ESU		00000100	/* Not on end   of stripe unit  */
> +#define	FLG_BSW		00000010	/* Not on begin of stripe width */
> +#define	FLG_ESW		00000001	/* Not on end   of stripe width */
> +static void
> +dump_map_verbose(
> +	unsigned long long	*nr,
> +	struct fsmap_head	*head,
> +	bool			*dumped_flags,
> +	struct xfs_fsop_geom	*fsgeo)
> +{
> +	unsigned long long	i;
> +	struct fsmap		*p;
> +	int			agno;
> +	off64_t			agoff, bperag;
> +	int			foff_w, boff_w, aoff_w, tot_w, agno_w, own_w;
> +	int			nr_w, dev_w;
> +	char			rbuf[32], bbuf[32], abuf[32], obuf[32];
> +	char			nbuf[32], dbuf[32], gbuf[32];
> +	char			owner[OWNER_BUF_SZ];
> +	int			sunit, swidth;
> +	int			flg = 0;
> +
> +	foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
> +	dev_w = 3;
> +	nr_w = 4;
> +	tot_w = MINTOT_WIDTH;
> +	bperag = (off64_t)fsgeo->agblocks *
> +		  (off64_t)fsgeo->blocksize;
> +	sunit = (fsgeo->sunit * fsgeo->blocksize);
> +	swidth = (fsgeo->swidth * fsgeo->blocksize);
> +
> +	/*
> +	 * Go through the extents and figure out the width
> +	 * needed for all columns.
> +	 */
> +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> +		if (p->fmr_flags & FMR_OF_PREALLOC ||
> +		    p->fmr_flags & FMR_OF_ATTR_FORK ||
> +		    p->fmr_flags & FMR_OF_SHARED)
> +			flg = 1;
> +		if (sunit &&
> +		    (p->fmr_physical  % sunit != 0 ||
> +		     ((p->fmr_physical + p->fmr_length) % sunit) != 0 ||
> +		     p->fmr_physical % swidth != 0 ||
> +		     ((p->fmr_physical + p->fmr_length) % swidth) != 0))
> +			flg = 1;
> +		if (flg)
> +			*dumped_flags = true;
> +		snprintf(nbuf, sizeof(nbuf), "%llu", (*nr) + i);
> +		nr_w = max(nr_w, strlen(nbuf));
> +		if (head->fmh_oflags & FMH_OF_DEV_T)
> +			snprintf(dbuf, sizeof(dbuf), "%u:%u",
> +				major(p->fmr_device),
> +				minor(p->fmr_device));
> +		else
> +			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
> +		dev_w = max(dev_w, strlen(dbuf));
> +		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
> +			(long long)BTOBBT(p->fmr_physical),
> +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> +		boff_w = max(boff_w, strlen(bbuf));
> +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> +			own_w = max(own_w, strlen(
> +					special_owner(p->fmr_owner, owner)));
> +		else {
> +			snprintf(obuf, sizeof(obuf), "%lld",
> +				(long long)p->fmr_owner);
> +			own_w = max(own_w, strlen(obuf));
> +		}
> +		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> +			foff_w = max(foff_w, strlen(_("extent_map")));
> +		else if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> +			;
> +		else {
> +			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
> +				(long long)BTOBBT(p->fmr_offset),
> +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> +			foff_w = max(foff_w, strlen(rbuf));
> +		}
> +		if (p->fmr_device == xfs_data_dev) {
> +			agno = p->fmr_physical / bperag;
> +			agoff = p->fmr_physical - (agno * bperag);
> +			snprintf(abuf, sizeof(abuf),
> +				"(%lld..%lld)",
> +				(long long)BTOBBT(agoff),
> +				(long long)BTOBBT(agoff + p->fmr_length - 1));
> +		} else
> +			abuf[0] = 0;
> +		aoff_w = max(aoff_w, strlen(abuf));
> +		tot_w = max(tot_w,
> +			numlen(BTOBBT(p->fmr_length), 10));
> +	}
> +	agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount, 10));
> +	if (*nr == 0)
> +		printf("%*s: %-*s %-*s %-*s %-*s %*s %-*s %*s%s\n",
> +			nr_w, _("EXT"),
> +			dev_w, _("DEV"),
> +			boff_w, _("BLOCK-RANGE"),
> +			own_w, _("OWNER"),
> +			foff_w, _("FILE-OFFSET"),
> +			agno_w, _("AG"),
> +			aoff_w, _("AG-OFFSET"),
> +			tot_w, _("TOTAL"),
> +			flg ? _(" FLAGS") : "");
> +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> +		flg = FLG_NULL;
> +		if (p->fmr_flags & FMR_OF_PREALLOC)
> +			flg |= FLG_PRE;
> +		if (p->fmr_flags & FMR_OF_ATTR_FORK)
> +			flg |= FLG_ATTR_FORK;
> +		if (p->fmr_flags & FMR_OF_SHARED)
> +			flg |= FLG_SHARED;
> +		/*
> +		 * If striping enabled, determine if extent starts/ends
> +		 * on a stripe unit boundary.
> +		 */
> +		if (sunit) {
> +			if (p->fmr_physical  % sunit != 0)
> +				flg |= FLG_BSU;
> +			if (((p->fmr_physical +
> +			      p->fmr_length ) % sunit ) != 0)
> +				flg |= FLG_ESU;
> +			if (p->fmr_physical % swidth != 0)
> +				flg |= FLG_BSW;
> +			if (((p->fmr_physical +
> +			      p->fmr_length ) % swidth ) != 0)
> +				flg |= FLG_ESW;
> +		}
> +		if (head->fmh_oflags & FMH_OF_DEV_T)
> +			snprintf(dbuf, sizeof(dbuf), "%u:%u",
> +				major(p->fmr_device),
> +				minor(p->fmr_device));
> +		else
> +			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
> +		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
> +			(long long)BTOBBT(p->fmr_physical),
> +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER) {
> +			snprintf(obuf, sizeof(obuf), "%s",
> +				special_owner(p->fmr_owner, owner));
> +			snprintf(rbuf, sizeof(rbuf), " ");
> +		} else {
> +			snprintf(obuf, sizeof(obuf), "%lld",
> +				(long long)p->fmr_owner);
> +			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
> +				(long long)BTOBBT(p->fmr_offset),
> +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> +		}
> +		if (p->fmr_device == xfs_data_dev) {
> +			agno = p->fmr_physical / bperag;
> +			agoff = p->fmr_physical - (agno * bperag);
> +			snprintf(abuf, sizeof(abuf),
> +				"(%lld..%lld)",
> +				(long long)BTOBBT(agoff),
> +				(long long)BTOBBT(agoff + p->fmr_length - 1));
> +			snprintf(gbuf, sizeof(gbuf),
> +				"%lld",
> +				(long long)agno);
> +		} else {
> +			abuf[0] = 0;
> +			gbuf[0] = 0;
> +		}
> +		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> +			printf("%*llu: %-*s %-*s %-*s %-*s %-*s %-*s %*lld\n",
> +				nr_w, (*nr) + i,
> +				dev_w, dbuf,
> +				boff_w, bbuf,
> +				own_w, obuf,
> +				foff_w, _("extent map"),
> +				agno_w, gbuf,
> +				aoff_w, abuf,
> +				tot_w, (long long)BTOBBT(p->fmr_length));
> +		else {
> +			printf("%*llu: %-*s %-*s %-*s %-*s", nr_w, (*nr) + i,
> +				dev_w, dbuf, boff_w, bbuf, own_w, obuf,
> +				foff_w, rbuf);
> +			printf(" %-*s %-*s", agno_w, gbuf,
> +				aoff_w, abuf);
> +			printf(" %*lld", tot_w,
> +				(long long)BTOBBT(p->fmr_length));
> +			if (flg == FLG_NULL)
> +				printf("\n");
> +			else
> +				printf(" %-*.*o\n", NFLG, NFLG, flg);
> +		}
> +	}
> +
> +	(*nr) += head->fmh_entries;
> +}
> +
> +static void
> +dump_verbose_key(void)
> +{
> +	printf(_(" FLAG Values:\n"));
> +	printf(_("    %*.*o Attribute fork\n"),
> +		NFLG+1, NFLG+1, FLG_ATTR_FORK);
> +	printf(_("    %*.*o Shared extent\n"),
> +		NFLG+1, NFLG+1, FLG_SHARED);
> +	printf(_("    %*.*o Unwritten preallocated extent\n"),
> +		NFLG+1, NFLG+1, FLG_PRE);
> +	printf(_("    %*.*o Doesn't begin on stripe unit\n"),
> +		NFLG+1, NFLG+1, FLG_BSU);
> +	printf(_("    %*.*o Doesn't end   on stripe unit\n"),
> +		NFLG+1, NFLG+1, FLG_ESU);
> +	printf(_("    %*.*o Doesn't begin on stripe width\n"),
> +		NFLG+1, NFLG+1, FLG_BSW);
> +	printf(_("    %*.*o Doesn't end   on stripe width\n"),
> +		NFLG+1, NFLG+1, FLG_ESW);
> +}
> +
> +int
> +fsmap_f(
> +	int			argc,
> +	char			**argv)
> +{
> +	struct fsmap		*p;
> +	struct fsmap_head	*nhead;
> +	struct fsmap_head	*head;
> +	struct fsmap		*l, *h;
> +	struct xfs_fsop_geom	fsgeo;
> +	long long		start = 0;
> +	long long		end = -1;
> +	int			nmap_size;
> +	int			map_size;
> +	int			nflag = 0;
> +	int			vflag = 0;
> +	int			mflag = 0;
> +	int			i = 0;
> +	int			c;
> +	unsigned long long	nr = 0;
> +	size_t			fsblocksize, fssectsize;
> +	struct fs_path		*fs;
> +	static bool		tab_init;
> +	bool			dumped_flags = false;
> +	int			dflag, lflag, rflag;
> +
> +	init_cvtnum(&fsblocksize, &fssectsize);
> +
> +	dflag = lflag = rflag = 0;
> +	while ((c = getopt(argc, argv, "dlmn:rv")) != EOF) {
> +		switch (c) {
> +		case 'd':	/* data device */
> +			dflag = 1;
> +			break;
> +		case 'l':	/* log device */
> +			lflag = 1;
> +			break;
> +		case 'm':	/* machine readable format */
> +			mflag++;
> +			break;
> +		case 'n':	/* number of extents specified */
> +			nflag = cvt_u32(optarg, 10);
> +			if (errno)
> +				return command_usage(&fsmap_cmd);
> +			break;
> +		case 'r':	/* rt device */
> +			rflag = 1;
> +			break;
> +		case 'v':	/* Verbose output */
> +			vflag++;
> +			break;
> +		default:
> +			return command_usage(&fsmap_cmd);
> +		}
> +	}
> +
> +	if ((dflag + lflag + rflag > 1) || (mflag > 0 && vflag > 0) ||
> +	    (argc > optind && dflag + lflag + rflag == 0))
> +		return command_usage(&fsmap_cmd);
> +
> +	if (argc > optind) {
> +		start = cvtnum(fsblocksize, fssectsize, argv[optind]);
> +		if (start < 0) {
> +			fprintf(stderr,
> +				_("Bad rmap start_bblock %s.\n"),
> +				argv[optind]);
> +			return 0;
> +		}
> +		start <<= BBSHIFT;
> +	}
> +
> +	if (argc > optind + 1) {
> +		end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
> +		if (end < 0) {
> +			fprintf(stderr,
> +				_("Bad rmap end_bblock %s.\n"),
> +				argv[optind + 1]);
> +			return 0;
> +		}
> +		end <<= BBSHIFT;
> +	}
> +
> +	if (vflag) {
> +		c = ioctl(file->fd, XFS_IOC_FSGEOMETRY, &fsgeo);
> +		if (c < 0) {
> +			fprintf(stderr,
> +				_("%s: can't get geometry [\"%s\"]: %s\n"),
> +				progname, file->name, strerror(errno));
> +			exitcode = 1;
> +			return 0;
> +		}
> +	}
> +
> +	map_size = nflag ? nflag : 131072 / sizeof(struct fsmap);
> +	head = malloc(fsmap_sizeof(map_size));
> +	if (head == NULL) {
> +		fprintf(stderr, _("%s: malloc of %zu bytes failed.\n"),
> +			progname, fsmap_sizeof(map_size));
> +		exitcode = 1;
> +		return 0;
> +	}
> +
> +	memset(head, 0, sizeof(*head));
> +	l = head->fmh_keys;
> +	h = head->fmh_keys + 1;
> +	if (dflag) {
> +		l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
> +	} else if (lflag) {
> +		l->fmr_device = h->fmr_device = file->fs_path.fs_logdev;
> +	} else if (rflag) {
> +		l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
> +	} else {
> +		l->fmr_device = 0;
> +		h->fmr_device = UINT_MAX;
> +	}
> +	l->fmr_physical = start;
> +	h->fmr_physical = end;
> +	h->fmr_owner = ULLONG_MAX;
> +	h->fmr_flags = UINT_MAX;
> +	h->fmr_offset = ULLONG_MAX;
> +
> +	/* Count mappings */
> +	if (!nflag) {
> +		head->fmh_count = 0;
> +		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
> +		if (i < 0) {
> +			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
> +				" iflags=0x%x [\"%s\"]: %s\n"),
> +				progname, head->fmh_iflags, file->name,
> +				strerror(errno));
> +			free(head);
> +			exitcode = 1;
> +			return 0;
> +		}
> +		if (head->fmh_entries > map_size + 2) {
> +			map_size = 11ULL * head->fmh_entries / 10;
> +			nmap_size = map_size > (1 << 24) ? (1 << 24) : map_size;
> +			nhead = realloc(head, fsmap_sizeof(nmap_size));
> +			if (nhead == NULL) {
> +				fprintf(stderr,
> +					_("%s: cannot realloc %zu bytes\n"),
> +					progname, fsmap_sizeof(nmap_size));
> +			} else {
> +				head = nhead;
> +				map_size = nmap_size;
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * If this is an XFS filesystem, remember the data device.
> +	 * (We report AG number/block for data device extents on XFS).
> +	 */
> +	if (!tab_init) {
> +		fs_table_initialise(0, NULL, 0, NULL);
> +		tab_init = true;
> +	}
> +	fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
> +	xfs_data_dev = fs ? fs->fs_datadev : 0;
> +
> +	head->fmh_count = map_size;
> +	do {
> +		/* Get some extents */
> +		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
> +		if (i < 0) {
> +			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
> +				" iflags=0x%x [\"%s\"]: %s\n"),
> +				progname, head->fmh_iflags, file->name,
> +				strerror(errno));
> +			free(head);
> +			exitcode = 1;
> +			return 0;
> +		}
> +
> +		if (head->fmh_entries == 0)
> +			break;
> +
> +		if (vflag)
> +			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
> +		else if (mflag)
> +			dump_map_machine(&nr, head);
> +		else
> +			dump_map(&nr, head);
> +
> +		p = &head->fmh_recs[head->fmh_entries - 1];
> +		if (p->fmr_flags & FMR_OF_LAST)
> +			break;
> +		fsmap_advance(head);
> +	} while (true);
> +
> +	if (dumped_flags)
> +		dump_verbose_key();
> +
> +	free(head);
> +	return 0;
> +}
> +
> +void
> +fsmap_init(void)
> +{
> +	fsmap_cmd.name = "fsmap";
> +	fsmap_cmd.cfunc = fsmap_f;
> +	fsmap_cmd.argmin = 0;
> +	fsmap_cmd.argmax = -1;
> +	fsmap_cmd.flags = CMD_NOMAP_OK | CMD_FLAG_FOREIGN_OK;
> +	fsmap_cmd.args = _("[-d|-l|-r] [-m|-v] [-n nx] [start] [end]");
> +	fsmap_cmd.oneline = _("print filesystem mapping for a range of blocks");
> +	fsmap_cmd.help = fsmap_help;
> +
> +	add_command(&fsmap_cmd);
> +}
> diff --git a/io/init.c b/io/init.c
> index c15a1e1..20d5f80 100644
> --- a/io/init.c
> +++ b/io/init.c
> @@ -66,6 +66,7 @@ init_commands(void)
>  	file_init();
>  	flink_init();
>  	freeze_init();
> +	fsmap_init();
>  	fsync_init();
>  	getrusage_init();
>  	help_init();
> @@ -139,6 +140,7 @@ init(
>  	char		*sp;
>  	mode_t		mode = 0600;
>  	xfs_fsop_geom_t	geometry = { 0 };
> +	struct fs_path	fsp;
>  
>  	progname = basename(argv[0]);
>  	setlocale(LC_ALL, "");
> @@ -148,6 +150,7 @@ init(
>  	pagesize = getpagesize();
>  	gettimeofday(&stopwatch, NULL);
>  
> +	fs_table_initialise(0, NULL, 0, NULL);
>  	while ((c = getopt(argc, argv, "ac:C:dFfim:p:nrRstTVx")) != EOF) {
>  		switch (c) {
>  		case 'a':
> @@ -212,11 +215,12 @@ init(
>  	}
>  
>  	while (optind < argc) {
> -		if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
> +		c = openfile(argv[optind], &geometry, flags, mode, &fsp);
> +		if (c < 0)
>  			exit(1);
>  		if (!platform_test_xfs_fd(c))
>  			flags |= IO_FOREIGN;
> -		if (addfile(argv[optind], c, &geometry, flags) < 0)
> +		if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
>  			exit(1);
>  		optind++;
>  	}
> diff --git a/io/io.h b/io/io.h
> index 952bdb8..6a0fe65 100644
> --- a/io/io.h
> +++ b/io/io.h
> @@ -17,6 +17,7 @@
>   */
>  
>  #include "xfs.h"
> +#include "path.h"
>  
>  /*
>   * Read/write patterns (default is always "forward")
> @@ -47,6 +48,7 @@ typedef struct fileio {
>  	int		flags;		/* flags describing file state */
>  	char		*name;		/* file name at time of open */
>  	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> +	struct fs_path	fs_path;	/* XFS path information */
>  } fileio_t;
>  
>  extern fileio_t		*filetable;	/* open file table */
> @@ -76,8 +78,10 @@ extern void *check_mapping_range(mmap_region_t *, off64_t, size_t, int);
>   */
>  
>  extern off64_t		filesize(void);
> -extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t);
> -extern int		addfile(char *, int , xfs_fsop_geom_t *, int);
> +extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t,
> +				 struct fs_path *);
> +extern int		addfile(char *, int , xfs_fsop_geom_t *, int,
> +				struct fs_path *);
>  extern void		printxattr(uint, int, int, const char *, int, int);
>  
>  extern unsigned int	recurse_all;
> @@ -174,3 +178,9 @@ extern void		readdir_init(void);
>  extern void		reflink_init(void);
>  
>  extern void		cowextsize_init(void);
> +
> +#ifdef HAVE_GETFSMAP
> +extern void		fsmap_init(void);
> +#else
> +# define fsmap_init()	do { } while (0)
> +#endif
> diff --git a/io/open.c b/io/open.c
> index 2ed55cf..b50f068 100644
> --- a/io/open.c
> +++ b/io/open.c
> @@ -52,8 +52,10 @@ openfile(
>  	char		*path,
>  	xfs_fsop_geom_t	*geom,
>  	int		flags,
> -	mode_t		mode)
> +	mode_t		mode,
> +	struct fs_path	*fs_path)
>  {
> +	struct fs_path	*fsp;
>  	int		fd;
>  	int		oflags;
>  
> @@ -118,6 +120,14 @@ openfile(
>  			}
>  		}
>  	}
> +
> +	if (fs_path) {
> +		fsp = fs_table_lookup(path, FS_MOUNT_POINT);
> +		if (!fsp)
> +			memset(fs_path, 0, sizeof(*fs_path));
> +		else
> +			*fs_path = *fsp;
> +	}
>  	return fd;
>  }
>  
> @@ -126,7 +136,8 @@ addfile(
>  	char		*name,
>  	int		fd,
>  	xfs_fsop_geom_t	*geometry,
> -	int		flags)
> +	int		flags,
> +	struct fs_path	*fs_path)
>  {
>  	char		*filename;
>  
> @@ -154,6 +165,7 @@ addfile(
>  	file->flags = flags;
>  	file->name = filename;
>  	file->geom = *geometry;
> +	file->fs_path = *fs_path;
>  	return 0;
>  }
>  
> @@ -195,6 +207,7 @@ open_f(
>  	char		*sp;
>  	mode_t		mode = 0600;
>  	xfs_fsop_geom_t	geometry = { 0 };
> +	struct fs_path	fsp;
>  
>  	if (argc == 1) {
>  		if (file)
> @@ -257,14 +270,14 @@ open_f(
>  		return -1;
>  	}
>  
> -	fd = openfile(argv[optind], &geometry, flags, mode);
> +	fd = openfile(argv[optind], &geometry, flags, mode, &fsp);
>  	if (fd < 0)
>  		return 0;
>  
>  	if (!platform_test_xfs_fd(fd))
>  		flags |= IO_FOREIGN;
>  
> -	addfile(argv[optind], fd, &geometry, flags);
> +	addfile(argv[optind], fd, &geometry, flags, &fsp);
>  	return 0;
>  }
>  
> diff --git a/io/pwrite.c b/io/pwrite.c
> index 7c0bb7f..1c5dfca 100644
> --- a/io/pwrite.c
> +++ b/io/pwrite.c
> @@ -357,7 +357,7 @@ pwrite_f(
>  		return 0;
>  
>  	c = IO_READONLY | (dflag ? IO_DIRECT : 0);
> -	if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0))
> +	if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0))
>  		return 0;
>  
>  	gettimeofday(&t1, NULL);
> diff --git a/io/reflink.c b/io/reflink.c
> index fe05d1e..f584e8f 100644
> --- a/io/reflink.c
> +++ b/io/reflink.c
> @@ -154,7 +154,7 @@ dedupe_f(
>  		return 0;
>  	}
>  
> -	fd = openfile(infile, NULL, IO_READONLY, 0);
> +	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
>  	if (fd < 0)
>  		return 0;
>  
> @@ -278,7 +278,7 @@ reflink_f(
>  	}
>  
>  clone_all:
> -	fd = openfile(infile, NULL, IO_READONLY, 0);
> +	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
>  	if (fd < 0)
>  		return 0;
>  
> diff --git a/io/sendfile.c b/io/sendfile.c
> index edd31c9..063fa7f 100644
> --- a/io/sendfile.c
> +++ b/io/sendfile.c
> @@ -115,7 +115,7 @@ sendfile_f(
>  
>  	if (!infile)
>  		fd = filetable[fd].fd;
> -	else if ((fd = openfile(infile, NULL, IO_READONLY, 0)) < 0)
> +	else if ((fd = openfile(infile, NULL, IO_READONLY, 0, NULL)) < 0)
>  		return 0;
>  
>  	if (optind == argc - 2) {
> diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
> index 29a036c..7b921e5 100644
> --- a/man/man8/xfs_io.8
> +++ b/man/man8/xfs_io.8
> @@ -301,6 +301,105 @@ ioctl.  Options behave as described in the
>  .BR xfs_bmap (8)
>  manual page.
>  .TP
> +.BI "fsmap [ \-d | \-l | \-r ] [ \-m | \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
> +Prints the mapping of disk blocks used by a filesystem.
> +The map lists each extent used by files, allocation group metadata,
> +journalling logs, and static filesystem metadata, as well as any
> +regions that are unused.
> +Each line of the listings takes the following form:
> +.PP
> +.RS
> +.IR extent ": " major ":" minor " [" startblock .. endblock "]: " owner " " startoffset .. endoffset " " length
> +.PP
> +Static filesystem metadata, allocation group metadata, btrees,
> +journalling logs, and free space are marked by replacing the
> +.IR startoffset .. endoffset
> +with the appropriate marker.
> +All blocks, offsets, and lengths are specified in units of 512-byte
> +blocks, no matter what the filesystem's block size is.
> +The optional
> +.I start
> +and
> +.I end
> +arguments can be used to constrain the output to a particular range of
> +disk blocks.
> +.RE
> +.RS 1.0i
> +.PD 0
> +.TP
> +.BI \-d
> +Display only extents from the data device.
> +This option only applies for XFS filesystems.
> +.TP
> +.BI \-l
> +Display only extents from the external log device.
> +This option only applies to XFS filesystems.
> +.TP
> +.BI \-r
> +Display only extents from the realtime device.
> +This option only applies to XFS filesystems.
> +.TP
> +.BI \-m
> +Display results in a machine readable format (CSV).
> +This option is not compatible with the
> +.B \-v
> +flag.
> +The columns of the output are: extent number, device major, device minor,
> +physical start, physical end, owner, offset start, offset end, length.
> +The start, end, and length numbers are provided in units of 512b.
> +The owner field is a special string that takes the form:
> +
> +.RS 1.0i
> +.PD 0
> +.TP 0.4i
> +.I inode_%lld_data
> +for inode data.
> +.TP
> +.I inode_%lld_data_bmbt
> +for inode data extent maps.
> +.TP
> +.I inode_%lld_attr
> +for inode extended attribute data.
> +.TP
> +.I inode_%lld_attr_bmbt
> +for inode extended attribute extent maps.
> +.TP
> +.I special_%u:%u
> +for other filesystem metadata.
> +.PD
> +.RE
> +
> +.TP
> +.BI \-n " num_extents"
> +If this option is given,
> +.B xfs_fsmap
> +obtains the extent list of the file in groups of
> +.I num_extents
> +extents.
> +In the absence of
> +.BR \-n ", " xfs_fsmap
> +queries the system for extents in groups of 131,072 records.
> +.TP
> +.B \-v
> +Shows verbose information.
> +When this flag is specified, additional AG specific information is
> +appended to each line in the following form:
> +.IP
> +.RS 1.2i
> +.IR agno " (" startagblock .. endagblock ") " nblocks " " flags
> +.RE
> +.IP
> +A second
> +.B \-v
> +option will print out the
> +.I flags
> +legend.
> +This option is not compatible with the
> +.B \-m
> +flag.
> +.RE
> +.PD
> +.TP
>  .BI "extsize [ \-R | \-D ] [ " value " ]"
>  Display and/or modify the preferred extent size used when allocating
>  space for the currently open file. If the
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 05/12] xfs_io: support the new getfsmap ioctl
  2017-06-21 20:51   ` Eric Sandeen
@ 2017-06-21 20:54     ` Darrick J. Wong
  0 siblings, 0 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-21 20:54 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 21, 2017 at 03:51:24PM -0500, Eric Sandeen wrote:
> On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Plumb in all the pieces we need to have xfs_io query the GETFSMAP ioctl
> > for an arbitrary filesystem.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> 
> With minor changes mentioned below which I'll take care of
> if you approve,
> 
> Reviewed-by: Eric Sandeen <sandeen@redhat.com>
> 
> > ---
> >  io/Makefile          |    7 +
> >  io/copy_file_range.c |    2 
> >  io/encrypt.c         |    1 
> >  io/fsmap.c           |  588 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  io/init.c            |    8 +
> >  io/io.h              |   14 +
> >  io/open.c            |   21 +-
> >  io/pwrite.c          |    2 
> >  io/reflink.c         |    4 
> >  io/sendfile.c        |    2 
> >  man/man8/xfs_io.8    |   99 ++++++++
> >  11 files changed, 735 insertions(+), 13 deletions(-)
> >  create mode 100644 io/fsmap.c
> > 
> > 
> > diff --git a/io/Makefile b/io/Makefile
> > index 435ccff..47b0a66 100644
> > --- a/io/Makefile
> > +++ b/io/Makefile
> > @@ -99,6 +99,13 @@ ifeq ($(HAVE_MREMAP),yes)
> >  LCFLAGS += -DHAVE_MREMAP
> >  endif
> >  
> > +# On linux we get fsmap from the system or define it ourselves
> > +# so include this based on platform type.  If this reverts to only
> > +# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
> > +ifeq ($(PKG_PLATFORM),linux)
> > +CFILES += fsmap.c
> > +endif
> > +
> >  default: depend $(LTCOMMAND)
> >  
> >  include $(BUILDRULES)
> > diff --git a/io/copy_file_range.c b/io/copy_file_range.c
> > index 249c649..d1dfc5a 100644
> > --- a/io/copy_file_range.c
> > +++ b/io/copy_file_range.c
> > @@ -121,7 +121,7 @@ copy_range_f(int argc, char **argv)
> >  	if (optind != argc - 1)
> >  		return command_usage(&copy_range_cmd);
> >  
> > -	fd = openfile(argv[optind], NULL, IO_READONLY, 0);
> > +	fd = openfile(argv[optind], NULL, IO_READONLY, 0, NULL);
> >  	if (fd < 0)
> >  		return 0;
> >  
> > diff --git a/io/encrypt.c b/io/encrypt.c
> > index d844c5e..26ab97c 100644
> > --- a/io/encrypt.c
> > +++ b/io/encrypt.c
> > @@ -20,6 +20,7 @@
> >  #include "platform_defs.h"
> >  #include "command.h"
> >  #include "init.h"
> > +#include "path.h"
> >  #include "io.h"
> >  
> >  #ifndef ARRAY_SIZE
> > diff --git a/io/fsmap.c b/io/fsmap.c
> > new file mode 100644
> > index 0000000..25c0e76
> > --- /dev/null
> > +++ b/io/fsmap.c
> > @@ -0,0 +1,588 @@
> > +/*
> > + * Copyright (C) 2017 Oracle.  All Rights Reserved.
> > + *
> > + * Author: Darrick J. Wong <darrick.wong@oracle.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it would be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write the Free Software Foundation,
> > + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> > + */
> > +#include "platform_defs.h"
> > +#include "command.h"
> > +#include "init.h"
> > +#include "path.h"
> > +#include "io.h"
> > +#include "input.h"
> > +
> > +static cmdinfo_t	fsmap_cmd;
> > +static dev_t		xfs_data_dev;
> > +
> > +static void
> > +fsmap_help(void)
> > +{
> > +	printf(_(
> > +"\n"
> > +" Prints the block mapping for a filesystem"
> 
> "for the filesystem hosting the current file?"

Yes.

> > +"\n"
> > +" fsmap prints the map of disk blocks used by the whole filesystem.\n"
> > +" When possible, owner and offset information will be included in the\n"
> > +" sapce report.\n"
> 
> space report

Yep.

> > +"\n"
> > +" By default, each line of the listing takes the following form:\n"
> > +"     extent: major:minor [startblock..endblock]: owner startoffset..endoffset length\n"
> > +" The owner field is either an inode number or a special value.\n"
> > +" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
> > +" -d -- query only the data device (default).\n"
> > +" -l -- query only the log device.\n"
> > +" -r -- query only the realtime device.\n"
> 
> +"-m -- machine readable output format.\n"

Ugh, did this fall out again? <grugmgajghagh>

All looks fine to me.

--D

> > +" -n -- query n extents at a time.\n"
> > +" -v -- Verbose information, show AG and offsets.  Show flags legend on 2nd -v\n"
> > +"\n"));
> > +}
> > +
> > +#define OWNER_BUF_SZ	32
> > +static const char *
> > +special_owner(
> > +	int64_t		owner,
> > +	char		*buf)
> > +{
> > +	switch (owner) {
> > +	case XFS_FMR_OWN_FREE:
> > +		return _("free space");
> > +	case XFS_FMR_OWN_UNKNOWN:
> > +		return _("unknown");
> > +	case XFS_FMR_OWN_FS:
> > +		return _("static fs metadata");
> > +	case XFS_FMR_OWN_LOG:
> > +		return _("journalling log");
> > +	case XFS_FMR_OWN_AG:
> > +		return _("per-AG metadata");
> > +	case XFS_FMR_OWN_INOBT:
> > +		return _("inode btree");
> > +	case XFS_FMR_OWN_INODES:
> > +		return _("inodes");
> > +	case XFS_FMR_OWN_REFC:
> > +		return _("refcount btree");
> > +	case XFS_FMR_OWN_COW:
> > +		return _("cow reservation");
> > +	case XFS_FMR_OWN_DEFECTIVE:
> > +		return _("defective");
> > +	default:
> > +		snprintf(buf, OWNER_BUF_SZ, _("special %u:%u"),
> > +				FMR_OWNER_TYPE(owner), FMR_OWNER_CODE(owner));
> > +		return buf;
> > +	}
> > +}
> > +
> > +static void
> > +dump_map(
> > +	unsigned long long	*nr,
> > +	struct fsmap_head	*head)
> > +{
> > +	unsigned long long	i;
> > +	struct fsmap		*p;
> > +	char			owner[OWNER_BUF_SZ];
> > +	char			*fork;
> > +
> > +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> > +		printf("\t%llu: %u:%u [%lld..%lld]: ", i + (*nr),
> > +			major(p->fmr_device), minor(p->fmr_device),
> > +			(long long)BTOBBT(p->fmr_physical),
> > +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> > +		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? _("attr") : _("data");
> > +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> > +			printf("%s", special_owner(p->fmr_owner, owner));
> > +		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> > +			printf(_("inode %lld %s extent map"),
> > +				(long long) p->fmr_owner, fork);
> > +		else
> > +			printf(_("inode %lld %s %lld..%lld"),
> > +				(long long)p->fmr_owner, fork,
> > +				(long long)BTOBBT(p->fmr_offset),
> > +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> > +		printf(_(" %lld\n"),
> > +			(long long)BTOBBT(p->fmr_length));
> > +	}
> > +
> > +	(*nr) += head->fmh_entries;
> > +}
> > +
> > +static void
> > +dump_map_machine(
> > +	unsigned long long	*nr,
> > +	struct fsmap_head	*head)
> > +{
> > +	unsigned long long	i;
> > +	struct fsmap		*p;
> > +	char			*fork;
> > +
> > +	printf(_("EXT,MAJOR,MINOR,PSTART,PEND,OWNER,OSTART,OEND,LENGTH\n"));
> > +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> > +		printf("%llu,%u,%u,%lld,%lld,", i + (*nr),
> > +			major(p->fmr_device), minor(p->fmr_device),
> > +			(long long)BTOBBT(p->fmr_physical),
> > +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> > +		fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? "attr" : "data";
> > +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> > +			printf("special_%u:%u,,,", FMR_OWNER_TYPE(p->fmr_owner),
> > +				FMR_OWNER_CODE(p->fmr_owner));
> > +		else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> > +			printf(_("inode_%lld_%s_bmbt,,,"),
> > +				(long long) p->fmr_owner, fork);
> > +		else
> > +			printf(_("inode_%lld_%s,%lld,%lld,"),
> > +				(long long)p->fmr_owner, fork,
> > +				(long long)BTOBBT(p->fmr_offset),
> > +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> > +		printf("%lld\n",
> > +			(long long)BTOBBT(p->fmr_length));
> > +	}
> > +
> > +	(*nr) += head->fmh_entries;
> > +}
> > +
> > +/*
> > + * Verbose mode displays:
> > + *   extent: major:minor [startblock..endblock]: startoffset..endoffset \
> > + *	ag# (agoffset..agendoffset) totalbbs flags
> > + */
> > +#define MINRANGE_WIDTH	16
> > +#define MINAG_WIDTH	2
> > +#define MINTOT_WIDTH	5
> > +#define NFLG		7		/* count of flags */
> > +#define	FLG_NULL	00000000	/* Null flag */
> > +#define	FLG_ATTR_FORK	01000000	/* attribute fork */
> > +#define	FLG_SHARED	00100000	/* shared extent */
> > +#define	FLG_PRE		00010000	/* Unwritten extent */
> > +#define	FLG_BSU		00001000	/* Not on begin of stripe unit  */
> > +#define	FLG_ESU		00000100	/* Not on end   of stripe unit  */
> > +#define	FLG_BSW		00000010	/* Not on begin of stripe width */
> > +#define	FLG_ESW		00000001	/* Not on end   of stripe width */
> > +static void
> > +dump_map_verbose(
> > +	unsigned long long	*nr,
> > +	struct fsmap_head	*head,
> > +	bool			*dumped_flags,
> > +	struct xfs_fsop_geom	*fsgeo)
> > +{
> > +	unsigned long long	i;
> > +	struct fsmap		*p;
> > +	int			agno;
> > +	off64_t			agoff, bperag;
> > +	int			foff_w, boff_w, aoff_w, tot_w, agno_w, own_w;
> > +	int			nr_w, dev_w;
> > +	char			rbuf[32], bbuf[32], abuf[32], obuf[32];
> > +	char			nbuf[32], dbuf[32], gbuf[32];
> > +	char			owner[OWNER_BUF_SZ];
> > +	int			sunit, swidth;
> > +	int			flg = 0;
> > +
> > +	foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
> > +	dev_w = 3;
> > +	nr_w = 4;
> > +	tot_w = MINTOT_WIDTH;
> > +	bperag = (off64_t)fsgeo->agblocks *
> > +		  (off64_t)fsgeo->blocksize;
> > +	sunit = (fsgeo->sunit * fsgeo->blocksize);
> > +	swidth = (fsgeo->swidth * fsgeo->blocksize);
> > +
> > +	/*
> > +	 * Go through the extents and figure out the width
> > +	 * needed for all columns.
> > +	 */
> > +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> > +		if (p->fmr_flags & FMR_OF_PREALLOC ||
> > +		    p->fmr_flags & FMR_OF_ATTR_FORK ||
> > +		    p->fmr_flags & FMR_OF_SHARED)
> > +			flg = 1;
> > +		if (sunit &&
> > +		    (p->fmr_physical  % sunit != 0 ||
> > +		     ((p->fmr_physical + p->fmr_length) % sunit) != 0 ||
> > +		     p->fmr_physical % swidth != 0 ||
> > +		     ((p->fmr_physical + p->fmr_length) % swidth) != 0))
> > +			flg = 1;
> > +		if (flg)
> > +			*dumped_flags = true;
> > +		snprintf(nbuf, sizeof(nbuf), "%llu", (*nr) + i);
> > +		nr_w = max(nr_w, strlen(nbuf));
> > +		if (head->fmh_oflags & FMH_OF_DEV_T)
> > +			snprintf(dbuf, sizeof(dbuf), "%u:%u",
> > +				major(p->fmr_device),
> > +				minor(p->fmr_device));
> > +		else
> > +			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
> > +		dev_w = max(dev_w, strlen(dbuf));
> > +		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
> > +			(long long)BTOBBT(p->fmr_physical),
> > +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> > +		boff_w = max(boff_w, strlen(bbuf));
> > +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> > +			own_w = max(own_w, strlen(
> > +					special_owner(p->fmr_owner, owner)));
> > +		else {
> > +			snprintf(obuf, sizeof(obuf), "%lld",
> > +				(long long)p->fmr_owner);
> > +			own_w = max(own_w, strlen(obuf));
> > +		}
> > +		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> > +			foff_w = max(foff_w, strlen(_("extent_map")));
> > +		else if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
> > +			;
> > +		else {
> > +			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
> > +				(long long)BTOBBT(p->fmr_offset),
> > +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> > +			foff_w = max(foff_w, strlen(rbuf));
> > +		}
> > +		if (p->fmr_device == xfs_data_dev) {
> > +			agno = p->fmr_physical / bperag;
> > +			agoff = p->fmr_physical - (agno * bperag);
> > +			snprintf(abuf, sizeof(abuf),
> > +				"(%lld..%lld)",
> > +				(long long)BTOBBT(agoff),
> > +				(long long)BTOBBT(agoff + p->fmr_length - 1));
> > +		} else
> > +			abuf[0] = 0;
> > +		aoff_w = max(aoff_w, strlen(abuf));
> > +		tot_w = max(tot_w,
> > +			numlen(BTOBBT(p->fmr_length), 10));
> > +	}
> > +	agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount, 10));
> > +	if (*nr == 0)
> > +		printf("%*s: %-*s %-*s %-*s %-*s %*s %-*s %*s%s\n",
> > +			nr_w, _("EXT"),
> > +			dev_w, _("DEV"),
> > +			boff_w, _("BLOCK-RANGE"),
> > +			own_w, _("OWNER"),
> > +			foff_w, _("FILE-OFFSET"),
> > +			agno_w, _("AG"),
> > +			aoff_w, _("AG-OFFSET"),
> > +			tot_w, _("TOTAL"),
> > +			flg ? _(" FLAGS") : "");
> > +	for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
> > +		flg = FLG_NULL;
> > +		if (p->fmr_flags & FMR_OF_PREALLOC)
> > +			flg |= FLG_PRE;
> > +		if (p->fmr_flags & FMR_OF_ATTR_FORK)
> > +			flg |= FLG_ATTR_FORK;
> > +		if (p->fmr_flags & FMR_OF_SHARED)
> > +			flg |= FLG_SHARED;
> > +		/*
> > +		 * If striping enabled, determine if extent starts/ends
> > +		 * on a stripe unit boundary.
> > +		 */
> > +		if (sunit) {
> > +			if (p->fmr_physical  % sunit != 0)
> > +				flg |= FLG_BSU;
> > +			if (((p->fmr_physical +
> > +			      p->fmr_length ) % sunit ) != 0)
> > +				flg |= FLG_ESU;
> > +			if (p->fmr_physical % swidth != 0)
> > +				flg |= FLG_BSW;
> > +			if (((p->fmr_physical +
> > +			      p->fmr_length ) % swidth ) != 0)
> > +				flg |= FLG_ESW;
> > +		}
> > +		if (head->fmh_oflags & FMH_OF_DEV_T)
> > +			snprintf(dbuf, sizeof(dbuf), "%u:%u",
> > +				major(p->fmr_device),
> > +				minor(p->fmr_device));
> > +		else
> > +			snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
> > +		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
> > +			(long long)BTOBBT(p->fmr_physical),
> > +			(long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
> > +		if (p->fmr_flags & FMR_OF_SPECIAL_OWNER) {
> > +			snprintf(obuf, sizeof(obuf), "%s",
> > +				special_owner(p->fmr_owner, owner));
> > +			snprintf(rbuf, sizeof(rbuf), " ");
> > +		} else {
> > +			snprintf(obuf, sizeof(obuf), "%lld",
> > +				(long long)p->fmr_owner);
> > +			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
> > +				(long long)BTOBBT(p->fmr_offset),
> > +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> > +		}
> > +		if (p->fmr_device == xfs_data_dev) {
> > +			agno = p->fmr_physical / bperag;
> > +			agoff = p->fmr_physical - (agno * bperag);
> > +			snprintf(abuf, sizeof(abuf),
> > +				"(%lld..%lld)",
> > +				(long long)BTOBBT(agoff),
> > +				(long long)BTOBBT(agoff + p->fmr_length - 1));
> > +			snprintf(gbuf, sizeof(gbuf),
> > +				"%lld",
> > +				(long long)agno);
> > +		} else {
> > +			abuf[0] = 0;
> > +			gbuf[0] = 0;
> > +		}
> > +		if (p->fmr_flags & FMR_OF_EXTENT_MAP)
> > +			printf("%*llu: %-*s %-*s %-*s %-*s %-*s %-*s %*lld\n",
> > +				nr_w, (*nr) + i,
> > +				dev_w, dbuf,
> > +				boff_w, bbuf,
> > +				own_w, obuf,
> > +				foff_w, _("extent map"),
> > +				agno_w, gbuf,
> > +				aoff_w, abuf,
> > +				tot_w, (long long)BTOBBT(p->fmr_length));
> > +		else {
> > +			printf("%*llu: %-*s %-*s %-*s %-*s", nr_w, (*nr) + i,
> > +				dev_w, dbuf, boff_w, bbuf, own_w, obuf,
> > +				foff_w, rbuf);
> > +			printf(" %-*s %-*s", agno_w, gbuf,
> > +				aoff_w, abuf);
> > +			printf(" %*lld", tot_w,
> > +				(long long)BTOBBT(p->fmr_length));
> > +			if (flg == FLG_NULL)
> > +				printf("\n");
> > +			else
> > +				printf(" %-*.*o\n", NFLG, NFLG, flg);
> > +		}
> > +	}
> > +
> > +	(*nr) += head->fmh_entries;
> > +}
> > +
> > +static void
> > +dump_verbose_key(void)
> > +{
> > +	printf(_(" FLAG Values:\n"));
> > +	printf(_("    %*.*o Attribute fork\n"),
> > +		NFLG+1, NFLG+1, FLG_ATTR_FORK);
> > +	printf(_("    %*.*o Shared extent\n"),
> > +		NFLG+1, NFLG+1, FLG_SHARED);
> > +	printf(_("    %*.*o Unwritten preallocated extent\n"),
> > +		NFLG+1, NFLG+1, FLG_PRE);
> > +	printf(_("    %*.*o Doesn't begin on stripe unit\n"),
> > +		NFLG+1, NFLG+1, FLG_BSU);
> > +	printf(_("    %*.*o Doesn't end   on stripe unit\n"),
> > +		NFLG+1, NFLG+1, FLG_ESU);
> > +	printf(_("    %*.*o Doesn't begin on stripe width\n"),
> > +		NFLG+1, NFLG+1, FLG_BSW);
> > +	printf(_("    %*.*o Doesn't end   on stripe width\n"),
> > +		NFLG+1, NFLG+1, FLG_ESW);
> > +}
> > +
> > +int
> > +fsmap_f(
> > +	int			argc,
> > +	char			**argv)
> > +{
> > +	struct fsmap		*p;
> > +	struct fsmap_head	*nhead;
> > +	struct fsmap_head	*head;
> > +	struct fsmap		*l, *h;
> > +	struct xfs_fsop_geom	fsgeo;
> > +	long long		start = 0;
> > +	long long		end = -1;
> > +	int			nmap_size;
> > +	int			map_size;
> > +	int			nflag = 0;
> > +	int			vflag = 0;
> > +	int			mflag = 0;
> > +	int			i = 0;
> > +	int			c;
> > +	unsigned long long	nr = 0;
> > +	size_t			fsblocksize, fssectsize;
> > +	struct fs_path		*fs;
> > +	static bool		tab_init;
> > +	bool			dumped_flags = false;
> > +	int			dflag, lflag, rflag;
> > +
> > +	init_cvtnum(&fsblocksize, &fssectsize);
> > +
> > +	dflag = lflag = rflag = 0;
> > +	while ((c = getopt(argc, argv, "dlmn:rv")) != EOF) {
> > +		switch (c) {
> > +		case 'd':	/* data device */
> > +			dflag = 1;
> > +			break;
> > +		case 'l':	/* log device */
> > +			lflag = 1;
> > +			break;
> > +		case 'm':	/* machine readable format */
> > +			mflag++;
> > +			break;
> > +		case 'n':	/* number of extents specified */
> > +			nflag = cvt_u32(optarg, 10);
> > +			if (errno)
> > +				return command_usage(&fsmap_cmd);
> > +			break;
> > +		case 'r':	/* rt device */
> > +			rflag = 1;
> > +			break;
> > +		case 'v':	/* Verbose output */
> > +			vflag++;
> > +			break;
> > +		default:
> > +			return command_usage(&fsmap_cmd);
> > +		}
> > +	}
> > +
> > +	if ((dflag + lflag + rflag > 1) || (mflag > 0 && vflag > 0) ||
> > +	    (argc > optind && dflag + lflag + rflag == 0))
> > +		return command_usage(&fsmap_cmd);
> > +
> > +	if (argc > optind) {
> > +		start = cvtnum(fsblocksize, fssectsize, argv[optind]);
> > +		if (start < 0) {
> > +			fprintf(stderr,
> > +				_("Bad rmap start_bblock %s.\n"),
> > +				argv[optind]);
> > +			return 0;
> > +		}
> > +		start <<= BBSHIFT;
> > +	}
> > +
> > +	if (argc > optind + 1) {
> > +		end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
> > +		if (end < 0) {
> > +			fprintf(stderr,
> > +				_("Bad rmap end_bblock %s.\n"),
> > +				argv[optind + 1]);
> > +			return 0;
> > +		}
> > +		end <<= BBSHIFT;
> > +	}
> > +
> > +	if (vflag) {
> > +		c = ioctl(file->fd, XFS_IOC_FSGEOMETRY, &fsgeo);
> > +		if (c < 0) {
> > +			fprintf(stderr,
> > +				_("%s: can't get geometry [\"%s\"]: %s\n"),
> > +				progname, file->name, strerror(errno));
> > +			exitcode = 1;
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	map_size = nflag ? nflag : 131072 / sizeof(struct fsmap);
> > +	head = malloc(fsmap_sizeof(map_size));
> > +	if (head == NULL) {
> > +		fprintf(stderr, _("%s: malloc of %zu bytes failed.\n"),
> > +			progname, fsmap_sizeof(map_size));
> > +		exitcode = 1;
> > +		return 0;
> > +	}
> > +
> > +	memset(head, 0, sizeof(*head));
> > +	l = head->fmh_keys;
> > +	h = head->fmh_keys + 1;
> > +	if (dflag) {
> > +		l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
> > +	} else if (lflag) {
> > +		l->fmr_device = h->fmr_device = file->fs_path.fs_logdev;
> > +	} else if (rflag) {
> > +		l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
> > +	} else {
> > +		l->fmr_device = 0;
> > +		h->fmr_device = UINT_MAX;
> > +	}
> > +	l->fmr_physical = start;
> > +	h->fmr_physical = end;
> > +	h->fmr_owner = ULLONG_MAX;
> > +	h->fmr_flags = UINT_MAX;
> > +	h->fmr_offset = ULLONG_MAX;
> > +
> > +	/* Count mappings */
> > +	if (!nflag) {
> > +		head->fmh_count = 0;
> > +		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
> > +		if (i < 0) {
> > +			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
> > +				" iflags=0x%x [\"%s\"]: %s\n"),
> > +				progname, head->fmh_iflags, file->name,
> > +				strerror(errno));
> > +			free(head);
> > +			exitcode = 1;
> > +			return 0;
> > +		}
> > +		if (head->fmh_entries > map_size + 2) {
> > +			map_size = 11ULL * head->fmh_entries / 10;
> > +			nmap_size = map_size > (1 << 24) ? (1 << 24) : map_size;
> > +			nhead = realloc(head, fsmap_sizeof(nmap_size));
> > +			if (nhead == NULL) {
> > +				fprintf(stderr,
> > +					_("%s: cannot realloc %zu bytes\n"),
> > +					progname, fsmap_sizeof(nmap_size));
> > +			} else {
> > +				head = nhead;
> > +				map_size = nmap_size;
> > +			}
> > +		}
> > +	}
> > +
> > +	/*
> > +	 * If this is an XFS filesystem, remember the data device.
> > +	 * (We report AG number/block for data device extents on XFS).
> > +	 */
> > +	if (!tab_init) {
> > +		fs_table_initialise(0, NULL, 0, NULL);
> > +		tab_init = true;
> > +	}
> > +	fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
> > +	xfs_data_dev = fs ? fs->fs_datadev : 0;
> > +
> > +	head->fmh_count = map_size;
> > +	do {
> > +		/* Get some extents */
> > +		i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
> > +		if (i < 0) {
> > +			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
> > +				" iflags=0x%x [\"%s\"]: %s\n"),
> > +				progname, head->fmh_iflags, file->name,
> > +				strerror(errno));
> > +			free(head);
> > +			exitcode = 1;
> > +			return 0;
> > +		}
> > +
> > +		if (head->fmh_entries == 0)
> > +			break;
> > +
> > +		if (vflag)
> > +			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
> > +		else if (mflag)
> > +			dump_map_machine(&nr, head);
> > +		else
> > +			dump_map(&nr, head);
> > +
> > +		p = &head->fmh_recs[head->fmh_entries - 1];
> > +		if (p->fmr_flags & FMR_OF_LAST)
> > +			break;
> > +		fsmap_advance(head);
> > +	} while (true);
> > +
> > +	if (dumped_flags)
> > +		dump_verbose_key();
> > +
> > +	free(head);
> > +	return 0;
> > +}
> > +
> > +void
> > +fsmap_init(void)
> > +{
> > +	fsmap_cmd.name = "fsmap";
> > +	fsmap_cmd.cfunc = fsmap_f;
> > +	fsmap_cmd.argmin = 0;
> > +	fsmap_cmd.argmax = -1;
> > +	fsmap_cmd.flags = CMD_NOMAP_OK | CMD_FLAG_FOREIGN_OK;
> > +	fsmap_cmd.args = _("[-d|-l|-r] [-m|-v] [-n nx] [start] [end]");
> > +	fsmap_cmd.oneline = _("print filesystem mapping for a range of blocks");
> > +	fsmap_cmd.help = fsmap_help;
> > +
> > +	add_command(&fsmap_cmd);
> > +}
> > diff --git a/io/init.c b/io/init.c
> > index c15a1e1..20d5f80 100644
> > --- a/io/init.c
> > +++ b/io/init.c
> > @@ -66,6 +66,7 @@ init_commands(void)
> >  	file_init();
> >  	flink_init();
> >  	freeze_init();
> > +	fsmap_init();
> >  	fsync_init();
> >  	getrusage_init();
> >  	help_init();
> > @@ -139,6 +140,7 @@ init(
> >  	char		*sp;
> >  	mode_t		mode = 0600;
> >  	xfs_fsop_geom_t	geometry = { 0 };
> > +	struct fs_path	fsp;
> >  
> >  	progname = basename(argv[0]);
> >  	setlocale(LC_ALL, "");
> > @@ -148,6 +150,7 @@ init(
> >  	pagesize = getpagesize();
> >  	gettimeofday(&stopwatch, NULL);
> >  
> > +	fs_table_initialise(0, NULL, 0, NULL);
> >  	while ((c = getopt(argc, argv, "ac:C:dFfim:p:nrRstTVx")) != EOF) {
> >  		switch (c) {
> >  		case 'a':
> > @@ -212,11 +215,12 @@ init(
> >  	}
> >  
> >  	while (optind < argc) {
> > -		if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
> > +		c = openfile(argv[optind], &geometry, flags, mode, &fsp);
> > +		if (c < 0)
> >  			exit(1);
> >  		if (!platform_test_xfs_fd(c))
> >  			flags |= IO_FOREIGN;
> > -		if (addfile(argv[optind], c, &geometry, flags) < 0)
> > +		if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
> >  			exit(1);
> >  		optind++;
> >  	}
> > diff --git a/io/io.h b/io/io.h
> > index 952bdb8..6a0fe65 100644
> > --- a/io/io.h
> > +++ b/io/io.h
> > @@ -17,6 +17,7 @@
> >   */
> >  
> >  #include "xfs.h"
> > +#include "path.h"
> >  
> >  /*
> >   * Read/write patterns (default is always "forward")
> > @@ -47,6 +48,7 @@ typedef struct fileio {
> >  	int		flags;		/* flags describing file state */
> >  	char		*name;		/* file name at time of open */
> >  	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> > +	struct fs_path	fs_path;	/* XFS path information */
> >  } fileio_t;
> >  
> >  extern fileio_t		*filetable;	/* open file table */
> > @@ -76,8 +78,10 @@ extern void *check_mapping_range(mmap_region_t *, off64_t, size_t, int);
> >   */
> >  
> >  extern off64_t		filesize(void);
> > -extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t);
> > -extern int		addfile(char *, int , xfs_fsop_geom_t *, int);
> > +extern int		openfile(char *, xfs_fsop_geom_t *, int, mode_t,
> > +				 struct fs_path *);
> > +extern int		addfile(char *, int , xfs_fsop_geom_t *, int,
> > +				struct fs_path *);
> >  extern void		printxattr(uint, int, int, const char *, int, int);
> >  
> >  extern unsigned int	recurse_all;
> > @@ -174,3 +178,9 @@ extern void		readdir_init(void);
> >  extern void		reflink_init(void);
> >  
> >  extern void		cowextsize_init(void);
> > +
> > +#ifdef HAVE_GETFSMAP
> > +extern void		fsmap_init(void);
> > +#else
> > +# define fsmap_init()	do { } while (0)
> > +#endif
> > diff --git a/io/open.c b/io/open.c
> > index 2ed55cf..b50f068 100644
> > --- a/io/open.c
> > +++ b/io/open.c
> > @@ -52,8 +52,10 @@ openfile(
> >  	char		*path,
> >  	xfs_fsop_geom_t	*geom,
> >  	int		flags,
> > -	mode_t		mode)
> > +	mode_t		mode,
> > +	struct fs_path	*fs_path)
> >  {
> > +	struct fs_path	*fsp;
> >  	int		fd;
> >  	int		oflags;
> >  
> > @@ -118,6 +120,14 @@ openfile(
> >  			}
> >  		}
> >  	}
> > +
> > +	if (fs_path) {
> > +		fsp = fs_table_lookup(path, FS_MOUNT_POINT);
> > +		if (!fsp)
> > +			memset(fs_path, 0, sizeof(*fs_path));
> > +		else
> > +			*fs_path = *fsp;
> > +	}
> >  	return fd;
> >  }
> >  
> > @@ -126,7 +136,8 @@ addfile(
> >  	char		*name,
> >  	int		fd,
> >  	xfs_fsop_geom_t	*geometry,
> > -	int		flags)
> > +	int		flags,
> > +	struct fs_path	*fs_path)
> >  {
> >  	char		*filename;
> >  
> > @@ -154,6 +165,7 @@ addfile(
> >  	file->flags = flags;
> >  	file->name = filename;
> >  	file->geom = *geometry;
> > +	file->fs_path = *fs_path;
> >  	return 0;
> >  }
> >  
> > @@ -195,6 +207,7 @@ open_f(
> >  	char		*sp;
> >  	mode_t		mode = 0600;
> >  	xfs_fsop_geom_t	geometry = { 0 };
> > +	struct fs_path	fsp;
> >  
> >  	if (argc == 1) {
> >  		if (file)
> > @@ -257,14 +270,14 @@ open_f(
> >  		return -1;
> >  	}
> >  
> > -	fd = openfile(argv[optind], &geometry, flags, mode);
> > +	fd = openfile(argv[optind], &geometry, flags, mode, &fsp);
> >  	if (fd < 0)
> >  		return 0;
> >  
> >  	if (!platform_test_xfs_fd(fd))
> >  		flags |= IO_FOREIGN;
> >  
> > -	addfile(argv[optind], fd, &geometry, flags);
> > +	addfile(argv[optind], fd, &geometry, flags, &fsp);
> >  	return 0;
> >  }
> >  
> > diff --git a/io/pwrite.c b/io/pwrite.c
> > index 7c0bb7f..1c5dfca 100644
> > --- a/io/pwrite.c
> > +++ b/io/pwrite.c
> > @@ -357,7 +357,7 @@ pwrite_f(
> >  		return 0;
> >  
> >  	c = IO_READONLY | (dflag ? IO_DIRECT : 0);
> > -	if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0))
> > +	if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0))
> >  		return 0;
> >  
> >  	gettimeofday(&t1, NULL);
> > diff --git a/io/reflink.c b/io/reflink.c
> > index fe05d1e..f584e8f 100644
> > --- a/io/reflink.c
> > +++ b/io/reflink.c
> > @@ -154,7 +154,7 @@ dedupe_f(
> >  		return 0;
> >  	}
> >  
> > -	fd = openfile(infile, NULL, IO_READONLY, 0);
> > +	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
> >  	if (fd < 0)
> >  		return 0;
> >  
> > @@ -278,7 +278,7 @@ reflink_f(
> >  	}
> >  
> >  clone_all:
> > -	fd = openfile(infile, NULL, IO_READONLY, 0);
> > +	fd = openfile(infile, NULL, IO_READONLY, 0, NULL);
> >  	if (fd < 0)
> >  		return 0;
> >  
> > diff --git a/io/sendfile.c b/io/sendfile.c
> > index edd31c9..063fa7f 100644
> > --- a/io/sendfile.c
> > +++ b/io/sendfile.c
> > @@ -115,7 +115,7 @@ sendfile_f(
> >  
> >  	if (!infile)
> >  		fd = filetable[fd].fd;
> > -	else if ((fd = openfile(infile, NULL, IO_READONLY, 0)) < 0)
> > +	else if ((fd = openfile(infile, NULL, IO_READONLY, 0, NULL)) < 0)
> >  		return 0;
> >  
> >  	if (optind == argc - 2) {
> > diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
> > index 29a036c..7b921e5 100644
> > --- a/man/man8/xfs_io.8
> > +++ b/man/man8/xfs_io.8
> > @@ -301,6 +301,105 @@ ioctl.  Options behave as described in the
> >  .BR xfs_bmap (8)
> >  manual page.
> >  .TP
> > +.BI "fsmap [ \-d | \-l | \-r ] [ \-m | \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
> > +Prints the mapping of disk blocks used by a filesystem.
> > +The map lists each extent used by files, allocation group metadata,
> > +journalling logs, and static filesystem metadata, as well as any
> > +regions that are unused.
> > +Each line of the listings takes the following form:
> > +.PP
> > +.RS
> > +.IR extent ": " major ":" minor " [" startblock .. endblock "]: " owner " " startoffset .. endoffset " " length
> > +.PP
> > +Static filesystem metadata, allocation group metadata, btrees,
> > +journalling logs, and free space are marked by replacing the
> > +.IR startoffset .. endoffset
> > +with the appropriate marker.
> > +All blocks, offsets, and lengths are specified in units of 512-byte
> > +blocks, no matter what the filesystem's block size is.
> > +The optional
> > +.I start
> > +and
> > +.I end
> > +arguments can be used to constrain the output to a particular range of
> > +disk blocks.
> > +.RE
> > +.RS 1.0i
> > +.PD 0
> > +.TP
> > +.BI \-d
> > +Display only extents from the data device.
> > +This option only applies for XFS filesystems.
> > +.TP
> > +.BI \-l
> > +Display only extents from the external log device.
> > +This option only applies to XFS filesystems.
> > +.TP
> > +.BI \-r
> > +Display only extents from the realtime device.
> > +This option only applies to XFS filesystems.
> > +.TP
> > +.BI \-m
> > +Display results in a machine readable format (CSV).
> > +This option is not compatible with the
> > +.B \-v
> > +flag.
> > +The columns of the output are: extent number, device major, device minor,
> > +physical start, physical end, owner, offset start, offset end, length.
> > +The start, end, and length numbers are provided in units of 512b.
> > +The owner field is a special string that takes the form:
> > +
> > +.RS 1.0i
> > +.PD 0
> > +.TP 0.4i
> > +.I inode_%lld_data
> > +for inode data.
> > +.TP
> > +.I inode_%lld_data_bmbt
> > +for inode data extent maps.
> > +.TP
> > +.I inode_%lld_attr
> > +for inode extended attribute data.
> > +.TP
> > +.I inode_%lld_attr_bmbt
> > +for inode extended attribute extent maps.
> > +.TP
> > +.I special_%u:%u
> > +for other filesystem metadata.
> > +.PD
> > +.RE
> > +
> > +.TP
> > +.BI \-n " num_extents"
> > +If this option is given,
> > +.B xfs_fsmap
> > +obtains the extent list of the file in groups of
> > +.I num_extents
> > +extents.
> > +In the absence of
> > +.BR \-n ", " xfs_fsmap
> > +queries the system for extents in groups of 131,072 records.
> > +.TP
> > +.B \-v
> > +Shows verbose information.
> > +When this flag is specified, additional AG specific information is
> > +appended to each line in the following form:
> > +.IP
> > +.RS 1.2i
> > +.IR agno " (" startagblock .. endagblock ") " nblocks " " flags
> > +.RE
> > +.IP
> > +A second
> > +.B \-v
> > +option will print out the
> > +.I flags
> > +legend.
> > +This option is not compatible with the
> > +.B \-m
> > +flag.
> > +.RE
> > +.PD
> > +.TP
> >  .BI "extsize [ \-R | \-D ] [ " value " ]"
> >  Display and/or modify the preferred extent size used when allocating
> >  space for the currently open file. If the
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 07/12] xfs_spaceman: space management tool
  2017-06-15 20:36 ` [PATCH 07/12] xfs_spaceman: space management tool Darrick J. Wong
@ 2017-06-21 21:12   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:12 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> xfs_spaceman is intended as a diagnostic and control tool for space
> management operations within XFS. Operations like examining free
> space, managing allocation policies, issuing block discards on free
> space, etc.
> 
> The tool is modelled on the xfs_io interface, allowing both
> interactive and command line control of the tool, enabling it to be
> used in scripts and automated management tools.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> [darrick: change xfsctl to ioctl]
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

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

> ---
>  Makefile          |    3 +
>  spaceman/Makefile |   33 ++++++++++++++
>  spaceman/file.c   |  122 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.c   |  113 +++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.h   |   24 ++++++++++
>  spaceman/space.h  |   38 +++++++++++++++++
>  6 files changed, 332 insertions(+), 1 deletion(-)
>  create mode 100644 spaceman/Makefile
>  create mode 100644 spaceman/file.c
>  create mode 100644 spaceman/init.c
>  create mode 100644 spaceman/init.h
>  create mode 100644 spaceman/space.h
> 
> 
> diff --git a/Makefile b/Makefile
> index ba87327..72d0044 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -47,7 +47,7 @@ HDR_SUBDIRS = include libxfs
>  DLIB_SUBDIRS = libxlog libxcmd libhandle
>  LIB_SUBDIRS = libxfs $(DLIB_SUBDIRS)
>  TOOL_SUBDIRS = copy db estimate fsck growfs io logprint mkfs quota \
> -		mdrestore repair rtcp m4 man doc debian
> +		mdrestore repair rtcp m4 man doc debian spaceman
>  
>  ifneq ("$(PKG_PLATFORM)","darwin")
>  TOOL_SUBDIRS += fsr
> @@ -88,6 +88,7 @@ quota: libxcmd
>  repair: libxlog libxcmd
>  copy: libxlog
>  mkfs: libxcmd
> +spaceman: libxcmd
>  
>  ifeq ($(HAVE_BUILDDEFS), yes)
>  include $(BUILDRULES)
> diff --git a/spaceman/Makefile b/spaceman/Makefile
> new file mode 100644
> index 0000000..df59edf
> --- /dev/null
> +++ b/spaceman/Makefile
> @@ -0,0 +1,33 @@
> +#
> +# Copyright (c) 2012 Red Hat, Inc.  All Rights Reserved.
> +#
> +
> +TOPDIR = ..
> +include $(TOPDIR)/include/builddefs
> +
> +LTCOMMAND = xfs_spaceman
> +HFILES = init.h space.h
> +CFILES = init.c file.c
> +
> +LLDLIBS = $(LIBXCMD)
> +LTDEPENDENCIES = $(LIBXCMD)
> +LLDFLAGS = -static
> +
> +ifeq ($(ENABLE_READLINE),yes)
> +LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
> +endif
> +
> +ifeq ($(ENABLE_EDITLINE),yes)
> +LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
> +endif
> +
> +default: depend $(LTCOMMAND)
> +
> +include $(BUILDRULES)
> +
> +install: default
> +	$(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
> +	$(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
> +install-dev:
> +
> +-include .dep
> diff --git a/spaceman/file.c b/spaceman/file.c
> new file mode 100644
> index 0000000..beca5ba
> --- /dev/null
> +++ b/spaceman/file.c
> @@ -0,0 +1,122 @@
> +/*
> + * Copyright (c) 2004-2005 Silicon Graphics, Inc.
> + * Copyright (c) 2012 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include <sys/mman.h>
> +#include "command.h"
> +#include "input.h"
> +#include "init.h"
> +#include "space.h"
> +
> +static cmdinfo_t print_cmd;
> +
> +fileio_t	*filetable;
> +int		filecount;
> +fileio_t	*file;
> +
> +static void
> +print_fileio(
> +	fileio_t	*file,
> +	int		index,
> +	int		braces)
> +{
> +	printf(_("%c%03d%c %-14s\n"), braces? '[' : ' ', index,
> +			braces? ']' : ' ', file->name);
> +}
> +
> +static int
> +print_f(
> +	int		argc,
> +	char		**argv)
> +{
> +	int		i;
> +
> +	for (i = 0; i < filecount; i++)
> +		print_fileio(&filetable[i], i, &filetable[i] == file);
> +	return 0;
> +}
> +
> +int
> +openfile(
> +	char		*path,
> +	xfs_fsop_geom_t	*geom)
> +{
> +	int		fd;
> +
> +	fd = open(path, 0);
> +	if (fd < 0) {
> +		perror(path);
> +		return -1;
> +	}
> +
> +	if (ioctl(fd, XFS_IOC_FSGEOMETRY, geom) < 0) {
> +		perror("XFS_IOC_FSGEOMETRY");
> +		close(fd);
> +		return -1;
> +	}
> +	return fd;
> +}
> +
> +int
> +addfile(
> +	char		*name,
> +	int		fd,
> +	xfs_fsop_geom_t	*geometry)
> +{
> +	char		*filename;
> +
> +	filename = strdup(name);
> +	if (!filename) {
> +		perror("strdup");
> +		close(fd);
> +		return -1;
> +	}
> +
> +	/* Extend the table of currently open files */
> +	filetable = (fileio_t *)realloc(filetable,	/* growing */
> +					++filecount * sizeof(fileio_t));
> +	if (!filetable) {
> +		perror("realloc");
> +		filecount = 0;
> +		free(filename);
> +		close(fd);
> +		return -1;
> +	}
> +
> +	/* Finally, make this the new active open file */
> +	file = &filetable[filecount - 1];
> +	file->fd = fd;
> +	file->name = filename;
> +	file->geom = *geometry;
> +	return 0;
> +}
> +
> +void
> +print_init(void)
> +{
> +	print_cmd.name = "print";
> +	print_cmd.altname = "p";
> +	print_cmd.cfunc = print_f;
> +	print_cmd.argmin = 0;
> +	print_cmd.argmax = 0;
> +	print_cmd.flags = CMD_FLAG_ONESHOT;
> +	print_cmd.oneline = _("list current open files");
> +
> +	add_command(&print_cmd);
> +}
> diff --git a/spaceman/init.c b/spaceman/init.c
> new file mode 100644
> index 0000000..8cbfbda
> --- /dev/null
> +++ b/spaceman/init.c
> @@ -0,0 +1,113 @@
> +/*
> + * Copyright (c) 2012 Red Hat, Inc
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include "command.h"
> +#include "input.h"
> +#include "init.h"
> +#include "space.h"
> +
> +char	*progname;
> +int	exitcode;
> +
> +void
> +usage(void)
> +{
> +	fprintf(stderr,
> +		_("Usage: %s [-c cmd] file\n"),
> +		progname);
> +	exit(1);
> +}
> +
> +static void
> +init_commands(void)
> +{
> +	print_init();
> +	help_init();
> +	quit_init();
> +}
> +
> +static int
> +init_args_command(
> +	int	index)
> +{
> +	if (index >= filecount)
> +		return 0;
> +	file = &filetable[index++];
> +	return index;
> +}
> +
> +static int
> +init_check_command(
> +	const cmdinfo_t	*ct)
> +{
> +	if (!(ct->flags & CMD_FLAG_ONESHOT))
> +		return 0;
> +	return 1;
> +}
> +
> +void
> +init(
> +	int		argc,
> +	char		**argv)
> +{
> +	int		c;
> +	xfs_fsop_geom_t	geometry = { 0 };
> +
> +	progname = basename(argv[0]);
> +	setlocale(LC_ALL, "");
> +	bindtextdomain(PACKAGE, LOCALEDIR);
> +	textdomain(PACKAGE);
> +
> +	while ((c = getopt(argc, argv, "c:V")) != EOF) {
> +		switch (c) {
> +		case 'c':
> +			add_user_command(optarg);
> +			break;
> +		case 'V':
> +			printf(_("%s version %s\n"), progname, VERSION);
> +			exit(0);
> +		default:
> +			usage();
> +		}
> +	}
> +
> +	if (optind != argc - 1)
> +		usage();
> +
> +	if ((c = openfile(argv[optind], &geometry)) < 0)
> +		exit(1);
> +	if (!platform_test_xfs_fd(c))
> +		printf(_("Not an XFS filesystem!\n"));
> +	if (addfile(argv[optind], c, &geometry) < 0)
> +		exit(1);
> +
> +	init_commands();
> +	add_command_iterator(init_args_command);
> +	add_check_command(init_check_command);
> +}
> +
> +int
> +main(
> +	int	argc,
> +	char	**argv)
> +{
> +	init(argc, argv);
> +	command_loop();
> +	return exitcode;
> +}
> diff --git a/spaceman/init.h b/spaceman/init.h
> new file mode 100644
> index 0000000..e9c25be
> --- /dev/null
> +++ b/spaceman/init.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (c) 2012 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +#ifndef XFS_SPACEMAN_INIT_H_
> +#define XFS_SPACEMAN_INIT_H_
> +
> +extern char	*progname;
> +extern int	exitcode;
> +
> +#endif /* XFS_SPACEMAN_INIT_H_ */
> diff --git a/spaceman/space.h b/spaceman/space.h
> new file mode 100644
> index 0000000..7bfe874
> --- /dev/null
> +++ b/spaceman/space.h
> @@ -0,0 +1,38 @@
> +/*
> + * Copyright (c) 2012 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +#ifndef XFS_SPACEMAN_SPACE_H_
> +#define XFS_SPACEMAN_SPACE_H_
> +
> +typedef struct fileio {
> +	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> +	char		*name;		/* file name at time of open */
> +	int		fd;		/* open file descriptor */
> +} fileio_t;
> +
> +extern fileio_t		*filetable;	/* open file table */
> +extern int		filecount;	/* number of open files */
> +extern fileio_t		*file;		/* active file in file table */
> +
> +extern int	openfile(char *, xfs_fsop_geom_t *);
> +extern int	addfile(char *, int , xfs_fsop_geom_t *);
> +
> +extern void	print_init(void);
> +extern void	help_init(void);
> +extern void	quit_init(void);
> +
> +#endif /* XFS_SPACEMAN_SPACE_H_ */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* [PATCH v2 02/12] libxcmd: add cvt{int, long} to convert strings to int and long
  2017-06-15 20:36 ` [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long Darrick J. Wong
  2017-06-21 20:16   ` Eric Sandeen
@ 2017-06-21 21:19   ` Darrick J. Wong
  1 sibling, 0 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-21 21:19 UTC (permalink / raw)
  To: sandeen; +Cc: linux-xfs

Create some helper functions to convert strings to int or long
in a standard way and work around problems in the C library
atoi/strtol functions.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: various comment fixes requested by Eric Sandeen.
---
 include/input.h |    8 +++
 libxcmd/input.c |  152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)

diff --git a/include/input.h b/include/input.h
index 82cd2f4..145114b 100644
--- a/include/input.h
+++ b/include/input.h
@@ -28,6 +28,14 @@ extern char	**breakline(char *input, int *count);
 extern void	doneline(char *input, char **vec);
 extern char	*fetchline(void);
 
+extern int64_t	cvt_s64(char *s, int base);
+extern int32_t	cvt_s32(char *s, int base);
+extern int16_t	cvt_s16(char *s, int base);
+
+extern uint64_t	cvt_u64(char *s, int base);
+extern uint32_t	cvt_u32(char *s, int base);
+extern uint16_t	cvt_u16(char *s, int base);
+
 extern size_t numlen(uint64_t val, size_t base);
 extern long long cvtnum(size_t blocksize, size_t sectorsize, char *s);
 extern void	cvtstr(double value, char *str, size_t sz);
diff --git a/libxcmd/input.c b/libxcmd/input.c
index 9437be3..7a69dc1 100644
--- a/libxcmd/input.c
+++ b/libxcmd/input.c
@@ -149,6 +149,158 @@ numlen(
 	return len == 0 ? 1 : len;
 }
 
+/*
+ * Convert string to int64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int64_t
+cvt_s64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return INT64_MIN;
+}
+
+/*
+ * Convert string to int32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int32_t
+cvt_s32(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT32_MAX || i < INT32_MIN) {
+		errno = -ERANGE;
+		return INT32_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to int16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+int16_t
+cvt_s16(
+	char		*s,
+	int		base)
+{
+	int64_t		i;
+
+	i = cvt_s64(s, base);
+	if (errno)
+		return i;
+	if (i > INT16_MAX || i < INT16_MIN) {
+		errno = -ERANGE;
+		return INT16_MIN;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint64_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint64_t
+cvt_u64(
+	char		*s,
+	int		base)
+{
+	long long	i;
+	char		*sp;
+
+	errno = 0;
+	i = strtoll(s, &sp, base);
+	/*
+	 * If the input would over or underflow, return the clamped
+	 * value and let the user check errno.  If we went all the
+	 * way to the end of the input, return the converted value;
+	 * errno will be zero.
+	 */
+	if (errno || (*sp == '\0' && sp != s))
+		return i;
+
+	/* Not all the input was consumed, return error. */
+	errno = -ERANGE;
+	return UINT64_MAX;
+}
+
+/*
+ * Convert string to uint32_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint32_t
+cvt_u32(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT32_MAX) {
+		errno = -ERANGE;
+		return UINT32_MAX;
+	}
+	return i;
+}
+
+/*
+ * Convert string to uint16_t, set errno if the conversion fails or
+ * doesn't fit.  Does not allow unit specifiers.  Sets errno to zero
+ * prior to conversion so you can check for bad inputs by examining
+ * errno immediately after the call.
+ */
+uint16_t
+cvt_u16(
+	char		*s,
+	int		base)
+{
+	uint64_t	i;
+
+	i = cvt_u64(s, base);
+	if (errno)
+		return i;
+	if (i > UINT16_MAX) {
+		errno = -ERANGE;
+		return UINT16_MAX;
+	}
+	return i;
+}
+
 #define EXABYTES(x)	((long long)(x) << 60)
 #define PETABYTES(x)	((long long)(x) << 50)
 #define TERABYTES(x)	((long long)(x) << 40)

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

* Re: [PATCH 08/12] xfs_spaceman: add FITRIM support
  2017-06-15 20:36 ` [PATCH 08/12] xfs_spaceman: add FITRIM support Darrick J. Wong
@ 2017-06-21 21:21   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:21 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> Add support for discarding free space extents via the FITRIM
> command. Make it easy to discard a single range, an entire AG or all
> the freespace in the filesystem.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

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

> ---
>  spaceman/Makefile |    2 -
>  spaceman/init.c   |    1 
>  spaceman/space.h  |    1 
>  spaceman/trim.c   |  131 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 134 insertions(+), 1 deletion(-)
>  create mode 100644 spaceman/trim.c
> 
> 
> diff --git a/spaceman/Makefile b/spaceman/Makefile
> index df59edf..c63a4fc 100644
> --- a/spaceman/Makefile
> +++ b/spaceman/Makefile
> @@ -7,7 +7,7 @@ include $(TOPDIR)/include/builddefs
>  
>  LTCOMMAND = xfs_spaceman
>  HFILES = init.h space.h
> -CFILES = init.c file.c
> +CFILES = init.c file.c trim.c
>  
>  LLDLIBS = $(LIBXCMD)
>  LTDEPENDENCIES = $(LIBXCMD)
> diff --git a/spaceman/init.c b/spaceman/init.c
> index 8cbfbda..107a692 100644
> --- a/spaceman/init.c
> +++ b/spaceman/init.c
> @@ -40,6 +40,7 @@ init_commands(void)
>  	print_init();
>  	help_init();
>  	quit_init();
> +	trim_init();
>  }
>  
>  static int
> diff --git a/spaceman/space.h b/spaceman/space.h
> index 7bfe874..8cd3953 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -34,5 +34,6 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *);
>  extern void	print_init(void);
>  extern void	help_init(void);
>  extern void	quit_init(void);
> +extern void	trim_init(void);
>  
>  #endif /* XFS_SPACEMAN_SPACE_H_ */
> diff --git a/spaceman/trim.c b/spaceman/trim.c
> new file mode 100644
> index 0000000..c0bc7f2
> --- /dev/null
> +++ b/spaceman/trim.c
> @@ -0,0 +1,131 @@
> +/*
> + * Copyright (c) 2012 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include <linux/fs.h>
> +#include "command.h"
> +#include "init.h"
> +#include "space.h"
> +#include "input.h"
> +
> +static cmdinfo_t trim_cmd;
> +
> +/*
> + * Trim unused space in xfs filesystem.
> + */
> +static int
> +trim_f(
> +	int		argc,
> +	char		**argv)
> +{
> +	struct fstrim_range trim = {0};
> +	xfs_agnumber_t	agno = 0;
> +	off64_t		offset = 0;
> +	ssize_t		length = 0;
> +	ssize_t		minlen = 0;
> +	int		aflag = 0;
> +	int		fflag = 0;
> +	int		ret;
> +	int		c;
> +
> +	while ((c = getopt(argc, argv, "a:fm:")) != EOF) {
> +		switch (c) {
> +		case 'a':
> +			aflag = 1;
> +			agno = cvt_u32(optarg, 10);
> +			if (errno) {
> +				printf(_("bad agno value %s\n"), optarg);
> +				return command_usage(&trim_cmd);
> +			}
> +			break;
> +		case 'f':
> +			fflag = 1;
> +			break;
> +		case 'm':
> +			minlen = cvtnum(file->geom.blocksize,
> +					file->geom.sectsize, optarg);
> +			break;
> +		default:
> +			return command_usage(&trim_cmd);
> +		}
> +	}
> +
> +	if (aflag && fflag)
> +		return command_usage(&trim_cmd);
> +
> +	if (optind != argc - 2 && !(aflag || fflag))
> +		return command_usage(&trim_cmd);
> +	if (optind != argc) {
> +		offset = cvtnum(file->geom.blocksize, file->geom.sectsize,
> +				argv[optind]);
> +		length = cvtnum(file->geom.blocksize, file->geom.sectsize,
> +				argv[optind + 1]);
> +	} else if (agno) {
> +		offset = agno * file->geom.agblocks * file->geom.blocksize;
> +		length = file->geom.agblocks * file->geom.blocksize;
> +	} else {
> +		offset = 0;
> +		length = file->geom.datablocks * file->geom.blocksize;
> +	}
> +
> +	trim.start = offset;
> +	trim.len = length;
> +	trim.minlen = minlen;
> +
> +	ret = ioctl(file->fd, FITRIM, (unsigned long)&trim);
> +	if (ret < 0) {
> +		fprintf(stderr, "%s: ioctl(FITRIM) [\"%s\"]: "
> +			"%s\n", progname, file->name, strerror(errno));
> +		exitcode = 1;
> +	}
> +	return 0;
> +}
> +
> +static void
> +trim_help(void)
> +{
> +	printf(_(
> +"\n"
> +"Discard filesystem free space\n"
> +"\n"
> +" -a agno       -- trim all the freespace in the given AG agno\n"
> +" -f            -- trim all the freespace in the entire filesystem\n"
> +" offset length -- trim the freespace in the range {offset, length}\n"
> +" -m minlen     -- skip freespace extents smaller than minlen\n"
> +"\n"
> +"One of -a, -f, or the offset/length pair are required.\n"
> +"\n"));
> +
> +}
> +
> +void
> +trim_init(void)
> +{
> +	trim_cmd.name = "trim";
> +	trim_cmd.altname = "tr";
> +	trim_cmd.cfunc = trim_f;
> +	trim_cmd.argmin = 1;
> +	trim_cmd.argmax = 4;
> +	trim_cmd.args = "[-m minlen] ( -a agno | -f | offset length )";
> +	trim_cmd.flags = CMD_FLAG_ONESHOT;
> +	trim_cmd.oneline = _("Discard filesystem free space");
> +	trim_cmd.help = trim_help;
> +
> +	add_command(&trim_cmd);
> +}
> +
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 09/12] xfs_spaceman: add new speculative prealloc control
  2017-06-15 20:36 ` [PATCH 09/12] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
@ 2017-06-21 21:26   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:26 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> Add an control interface for purging speculative
> preallocation via the new ioctls.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> [darrick: change xfsctl to ioctl]
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

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

> ---
>  spaceman/Makefile   |    2 -
>  spaceman/init.c     |    1 
>  spaceman/prealloc.c |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/space.h    |    1 
>  4 files changed, 121 insertions(+), 1 deletion(-)
>  create mode 100644 spaceman/prealloc.c
> 
> 
> diff --git a/spaceman/Makefile b/spaceman/Makefile
> index c63a4fc..6aad746 100644
> --- a/spaceman/Makefile
> +++ b/spaceman/Makefile
> @@ -7,7 +7,7 @@ include $(TOPDIR)/include/builddefs
>  
>  LTCOMMAND = xfs_spaceman
>  HFILES = init.h space.h
> -CFILES = init.c file.c trim.c
> +CFILES = init.c file.c prealloc.c trim.c
>  
>  LLDLIBS = $(LIBXCMD)
>  LTDEPENDENCIES = $(LIBXCMD)
> diff --git a/spaceman/init.c b/spaceman/init.c
> index 107a692..a51007d 100644
> --- a/spaceman/init.c
> +++ b/spaceman/init.c
> @@ -39,6 +39,7 @@ init_commands(void)
>  {
>  	print_init();
>  	help_init();
> +	prealloc_init();
>  	quit_init();
>  	trim_init();
>  }
> diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
> new file mode 100644
> index 0000000..439a9e8
> --- /dev/null
> +++ b/spaceman/prealloc.c
> @@ -0,0 +1,118 @@
> +/*
> + * Copyright (c) 2012 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include "command.h"
> +#include "input.h"
> +#include "init.h"
> +#include "space.h"
> +
> +static cmdinfo_t prealloc_cmd;
> +
> +/*
> + * Control preallocation amounts.
> + */
> +static int
> +prealloc_f(
> +	int	argc,
> +	char	**argv)
> +{
> +	struct xfs_fs_eofblocks eofb = {0};
> +	int	c;
> +
> +	eofb.eof_version = XFS_EOFBLOCKS_VERSION;
> +
> +	while ((c = getopt(argc, argv, "g:m:p:su:")) != EOF) {
> +		switch (c) {
> +		case 'g':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_GID;
> +			eofb.eof_gid = cvt_u32(optarg, 10);
> +			if (errno)
> +				return command_usage(&prealloc_cmd);
> +			break;
> +		case 'u':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
> +			eofb.eof_uid = cvt_u32(optarg, 10);
> +			if (errno)
> +				return command_usage(&prealloc_cmd);
> +			break;
> +		case 'p':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
> +			eofb.eof_prid = cvt_u32(optarg, 10);
> +			if (errno)
> +				return command_usage(&prealloc_cmd);
> +			break;
> +		case 's':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_SYNC;
> +			break;
> +		case 'm':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_MINFILESIZE;
> +			eofb.eof_min_file_size = cvtnum(file->geom.blocksize,
> +							file->geom.sectsize,
> +							optarg);
> +			break;
> +		case '?':
> +		default:
> +			return command_usage(&prealloc_cmd);
> +		}
> +	}
> +	if (optind != argc)
> +		return command_usage(&prealloc_cmd);
> +
> +	if (ioctl(file->fd, XFS_IOC_FREE_EOFBLOCKS, &eofb) < 0) {
> +		fprintf(stderr, _("%s: XFS_IOC_FREE_EOFBLOCKS on %s: %s\n"),
> +			progname, file->name, strerror(errno));
> +	}
> +	return 0;
> +}
> +
> +static void
> +prealloc_help(void)
> +{
> +	printf(_(
> +"\n"
> +"Control speculative preallocation\n"
> +"\n"
> +" -g gid    -- remove prealloc on files matching group <gid>\n"
> +" -m minlen -- only consider files larger than <minlen>\n"
> +" -p prid   -- remove prealloc on files matching project <prid>\n"
> +" -s        -- wait for removal to complete\n"
> +" -u uid    -- remove prealloc on files matching user <uid>\n"
> +"\n"
> +"If none of -u, -g, or -p are specified, this command acts on all files.\n"
> +"minlen can take units.\n"
> +"\n"));
> +
> +}
> +
> +void
> +prealloc_init(void)
> +{
> +	prealloc_cmd.name = "prealloc";
> +	prealloc_cmd.altname = "prealloc";
> +	prealloc_cmd.cfunc = prealloc_f;
> +	prealloc_cmd.argmin = 1;
> +	prealloc_cmd.argmax = -1;
> +	prealloc_cmd.args = "[-s] [-u id] [-g id] [-p id] [-m minlen]";
> +	prealloc_cmd.flags = CMD_FLAG_ONESHOT;
> +	prealloc_cmd.oneline = _("Control speculative preallocation");
> +	prealloc_cmd.help = prealloc_help;
> +
> +	add_command(&prealloc_cmd);
> +}
> +
> diff --git a/spaceman/space.h b/spaceman/space.h
> index 8cd3953..de97139 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -33,6 +33,7 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *);
>  
>  extern void	print_init(void);
>  extern void	help_init(void);
> +extern void	prealloc_init(void);
>  extern void	quit_init(void);
>  extern void	trim_init(void);
>  
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 10/12] xfs_spaceman: Free space mapping command
  2017-06-15 20:36 ` [PATCH 10/12] xfs_spaceman: Free space mapping command Darrick J. Wong
@ 2017-06-21 21:32   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:32 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> Add freespace mapping tool modelled on the xfs_db freesp command.
> The advantage of this command over xfs_db is that it can be done
> online and is coherent with concurrent modifications to the
> filesystem.
> 
> This requires the kernel to support the XFS_IOC_GETFSMAP ioctl to map
> free space indexes.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> [darrick: port from FIEMAPFS to GETFSMAP]
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

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

> ---
>  spaceman/Makefile   |    7 +
>  spaceman/file.c     |   19 ++
>  spaceman/freesp.c   |  392 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.c     |    8 +
>  spaceman/prealloc.c |    1 
>  spaceman/space.h    |   10 +
>  spaceman/trim.c     |    1 
>  7 files changed, 432 insertions(+), 6 deletions(-)
>  create mode 100644 spaceman/freesp.c
> 
> 
> diff --git a/spaceman/Makefile b/spaceman/Makefile
> index 6aad746..95ec3c0 100644
> --- a/spaceman/Makefile
> +++ b/spaceman/Makefile
> @@ -21,6 +21,13 @@ ifeq ($(ENABLE_EDITLINE),yes)
>  LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
>  endif
>  
> +# On linux we get fsmap from the system or define it ourselves
> +# so include this based on platform type.  If this reverts to only
> +# the autoconf check w/o local definition, change to testing HAVE_GETFSMAP
> +ifeq ($(PKG_PLATFORM),linux)
> +CFILES += freesp.c
> +endif
> +
>  default: depend $(LTCOMMAND)
>  
>  include $(BUILDRULES)
> diff --git a/spaceman/file.c b/spaceman/file.c
> index beca5ba..4ab3090 100644
> --- a/spaceman/file.c
> +++ b/spaceman/file.c
> @@ -22,6 +22,7 @@
>  #include "command.h"
>  #include "input.h"
>  #include "init.h"
> +#include "path.h"
>  #include "space.h"
>  
>  static cmdinfo_t print_cmd;
> @@ -55,8 +56,10 @@ print_f(
>  int
>  openfile(
>  	char		*path,
> -	xfs_fsop_geom_t	*geom)
> +	xfs_fsop_geom_t	*geom,
> +	struct fs_path	*fs_path)
>  {
> +	struct fs_path	*fsp;
>  	int		fd;
>  
>  	fd = open(path, 0);
> @@ -70,6 +73,16 @@ openfile(
>  		close(fd);
>  		return -1;
>  	}
> +
> +	if (fs_path) {
> +		fsp = fs_table_lookup(path, FS_MOUNT_POINT);
> +		if (!fsp) {
> +			fprintf(stderr, _("%s: cannot find mount point."),
> +				path);
> +			return -1;
> +		}
> +		memcpy(fs_path, fsp, sizeof(struct fs_path));
> +	}
>  	return fd;
>  }
>  
> @@ -77,7 +90,8 @@ int
>  addfile(
>  	char		*name,
>  	int		fd,
> -	xfs_fsop_geom_t	*geometry)
> +	xfs_fsop_geom_t	*geometry,
> +	struct fs_path	*fs_path)
>  {
>  	char		*filename;
>  
> @@ -104,6 +118,7 @@ addfile(
>  	file->fd = fd;
>  	file->name = filename;
>  	file->geom = *geometry;
> +	memcpy(&file->fs_path, fs_path, sizeof(file->fs_path));
>  	return 0;
>  }
>  
> diff --git a/spaceman/freesp.c b/spaceman/freesp.c
> new file mode 100644
> index 0000000..99e50cf
> --- /dev/null
> +++ b/spaceman/freesp.c
> @@ -0,0 +1,392 @@
> +/*
> + * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
> + * Copyright (c) 2012 Red Hat, Inc.
> + * Copyright (c) 2017 Oracle.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation,
> + * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include "libxfs.h"
> +#include <linux/fiemap.h>
> +#include "command.h"
> +#include "init.h"
> +#include "path.h"
> +#include "space.h"
> +#include "input.h"
> +
> +typedef struct histent
> +{
> +	long long	low;
> +	long long	high;
> +	long long	count;
> +	long long	blocks;
> +} histent_t;
> +
> +static int		agcount;
> +static xfs_agnumber_t	*aglist;
> +static histent_t	*hist;
> +static int		dumpflag;
> +static long long	equalsize;
> +static long long	multsize;
> +static int		histcount;
> +static int		seen1;
> +static int		summaryflag;
> +static bool		rtflag;
> +static long long	totblocks;
> +static long long	totexts;
> +
> +static cmdinfo_t freesp_cmd;
> +
> +static void
> +addhistent(
> +	long long	h)
> +{
> +	if (histcount == INT_MAX) {
> +		printf(_("Too many histogram buckets.\n"));
> +		return;
> +	}
> +	hist = realloc(hist, (histcount + 1) * sizeof(*hist));
> +	if (h == 0)
> +		h = 1;
> +	hist[histcount].low = h;
> +	hist[histcount].count = hist[histcount].blocks = 0;
> +	histcount++;
> +	if (h == 1)
> +		seen1 = 1;
> +}
> +
> +static void
> +addtohist(
> +	xfs_agnumber_t	agno,
> +	xfs_agblock_t	agbno,
> +	off64_t		len)
> +{
> +	long		i;
> +
> +	if (dumpflag)
> +		printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
> +	totexts++;
> +	totblocks += len;
> +	for (i = 0; i < histcount; i++) {
> +		if (hist[i].high >= len) {
> +			hist[i].count++;
> +			hist[i].blocks += len;
> +			break;
> +		}
> +	}
> +}
> +
> +static int
> +hcmp(
> +	const void	*a,
> +	const void	*b)
> +{
> +	return ((histent_t *)a)->low - ((histent_t *)b)->low;
> +}
> +
> +static void
> +histinit(
> +	long long	maxlen)
> +{
> +	long long	i;
> +
> +	if (equalsize) {
> +		for (i = 1; i < maxlen; i += equalsize)
> +			addhistent(i);
> +	} else if (multsize) {
> +		for (i = 1; i < maxlen; i *= multsize)
> +			addhistent(i);
> +	} else {
> +		if (!seen1)
> +			addhistent(1);
> +		qsort(hist, histcount, sizeof(*hist), hcmp);
> +	}
> +	for (i = 0; i < histcount; i++) {
> +		if (i < histcount - 1)
> +			hist[i].high = hist[i + 1].low - 1;
> +		else
> +			hist[i].high = maxlen;
> +	}
> +}
> +
> +static void
> +printhist(void)
> +{
> +	int	i;
> +
> +	printf("%7s %7s %7s %7s %6s\n",
> +		_("from"), _("to"), _("extents"), _("blocks"), _("pct"));
> +	for (i = 0; i < histcount; i++) {
> +		if (hist[i].count)
> +			printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
> +				hist[i].high, hist[i].count, hist[i].blocks,
> +				hist[i].blocks * 100.0 / totblocks);
> +	}
> +}
> +
> +static int
> +inaglist(
> +	xfs_agnumber_t	agno)
> +{
> +	int		i;
> +
> +	if (agcount == 0)
> +		return 1;
> +	for (i = 0; i < agcount; i++)
> +		if (aglist[i] == agno)
> +			return 1;
> +	return 0;
> +}
> +
> +#define NR_EXTENTS 128
> +
> +static void
> +scan_ag(
> +	xfs_agnumber_t		agno)
> +{
> +	struct fsmap_head	*fsmap;
> +	struct fsmap		*extent;
> +	struct fsmap		*l, *h;
> +	struct fsmap		*p;
> +	off64_t			blocksize = file->geom.blocksize;
> +	off64_t			bperag;
> +	off64_t			aglen;
> +	xfs_agblock_t		agbno;
> +	int			ret;
> +	int			i;
> +
> +	bperag = (off64_t)file->geom.agblocks * blocksize;
> +
> +	fsmap = malloc(fsmap_sizeof(NR_EXTENTS));
> +	if (!fsmap) {
> +		fprintf(stderr, _("%s: fsmap malloc failed.\n"), progname);
> +		exitcode = 1;
> +		return;
> +	}
> +
> +	memset(fsmap, 0, sizeof(*fsmap));
> +	fsmap->fmh_count = NR_EXTENTS;
> +	l = fsmap->fmh_keys;
> +	h = fsmap->fmh_keys + 1;
> +	if (agno != NULLAGNUMBER) {
> +		l->fmr_physical = agno * bperag;
> +		h->fmr_physical = ((agno + 1) * bperag) - 1;
> +		l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
> +	} else {
> +		l->fmr_physical = 0;
> +		h->fmr_physical = ULLONG_MAX;
> +		l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
> +	}
> +	h->fmr_owner = ULLONG_MAX;
> +	h->fmr_flags = UINT_MAX;
> +	h->fmr_offset = ULLONG_MAX;
> +
> +	while (true) {
> +		ret = ioctl(file->fd, FS_IOC_GETFSMAP, fsmap);
> +		if (ret < 0) {
> +			fprintf(stderr, _("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"),
> +				progname, file->name, strerror(errno));
> +			free(fsmap);
> +			exitcode = 1;
> +			return;
> +		}
> +
> +		/* No more extents to map, exit */
> +		if (!fsmap->fmh_entries)
> +			break;
> +
> +		for (i = 0, extent = fsmap->fmh_recs;
> +		     i < fsmap->fmh_entries;
> +		     i++, extent++) {
> +			if (!(extent->fmr_flags & FMR_OF_SPECIAL_OWNER) ||
> +			    extent->fmr_owner != XFS_FMR_OWN_FREE)
> +				continue;
> +			agbno = (extent->fmr_physical - (bperag * agno)) /
> +								blocksize;
> +			aglen = extent->fmr_length / blocksize;
> +
> +			addtohist(agno, agbno, aglen);
> +		}
> +
> +		p = &fsmap->fmh_recs[fsmap->fmh_entries - 1];
> +		if (p->fmr_flags & FMR_OF_LAST)
> +			break;
> +		fsmap_advance(fsmap);
> +	}
> +}
> +static void
> +aglistadd(
> +	char		*a)
> +{
> +	xfs_agnumber_t	x;
> +
> +	aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
> +	x = cvt_u32(a, 0);
> +	if (errno) {
> +		printf(_("Unrecognized AG number: %s\n"), a);
> +		return;
> +	}
> +	aglist[agcount] = x;
> +	agcount++;
> +}
> +
> +static int
> +init(
> +	int		argc,
> +	char		**argv)
> +{
> +	long long	x;
> +	int		c;
> +	int		speced = 0;	/* only one of -b -e -h or -m */
> +
> +	agcount = dumpflag = equalsize = multsize = optind = 0;
> +	histcount = seen1 = summaryflag = 0;
> +	totblocks = totexts = 0;
> +	aglist = NULL;
> +	hist = NULL;
> +	rtflag = false;
> +
> +	while ((c = getopt(argc, argv, "a:bde:h:m:rs")) != EOF) {
> +		switch (c) {
> +		case 'a':
> +			aglistadd(optarg);
> +			break;
> +		case 'b':
> +			if (speced)
> +				goto many_spec;
> +			multsize = 2;
> +			speced = 1;
> +			break;
> +		case 'd':
> +			dumpflag = 1;
> +			break;
> +		case 'e':
> +			if (speced)
> +				goto many_spec;
> +			equalsize = cvt_s64(optarg, 0);
> +			if (errno)
> +				return command_usage(&freesp_cmd);
> +			speced = 1;
> +			break;
> +		case 'h':
> +			if (speced && !histcount)
> +				goto many_spec;
> +			/* addhistent increments histcount */
> +			x = cvt_s64(optarg, 0);
> +			if (errno)
> +				return command_usage(&freesp_cmd);
> +			addhistent(x);
> +			speced = 1;
> +			break;
> +		case 'm':
> +			if (speced)
> +				goto many_spec;
> +			multsize = cvt_s64(optarg, 0);
> +			if (errno)
> +				return command_usage(&freesp_cmd);
> +			speced = 1;
> +			break;
> +		case 'r':
> +			rtflag = true;
> +			break;
> +		case 's':
> +			summaryflag = 1;
> +			break;
> +		case '?':
> +		default:
> +			return command_usage(&freesp_cmd);
> +		}
> +	}
> +	if (optind != argc)
> +		return 0;
> +	if (!speced)
> +		multsize = 2;
> +	histinit(file->geom.agblocks);
> +	return 1;
> +many_spec:
> +	return command_usage(&freesp_cmd);
> +}
> +
> +/*
> + * Report on freespace usage in xfs filesystem.
> + */
> +static int
> +freesp_f(
> +	int		argc,
> +	char		**argv)
> +{
> +	xfs_agnumber_t	agno;
> +
> +	if (!init(argc, argv))
> +		return 0;
> +	if (rtflag)
> +		scan_ag(NULLAGNUMBER);
> +	for (agno = 0; !rtflag && agno < file->geom.agcount; agno++)  {
> +		if (inaglist(agno))
> +			scan_ag(agno);
> +	}
> +	if (histcount)
> +		printhist();
> +	if (summaryflag) {
> +		printf(_("total free extents %lld\n"), totexts);
> +		printf(_("total free blocks %lld\n"), totblocks);
> +		printf(_("average free extent size %g\n"),
> +			(double)totblocks / (double)totexts);
> +	}
> +	if (aglist)
> +		free(aglist);
> +	if (hist)
> +		free(hist);
> +	return 0;
> +}
> +
> +static void
> +freesp_help(void)
> +{
> +	printf(_(
> +"\n"
> +"Examine filesystem free space\n"
> +"\n"
> +" -a agno  -- Scan only the given AG agno.\n"
> +" -b       -- binary histogram bin size\n"
> +" -d       -- debug output\n"
> +" -e bsize -- Use fixed histogram bin size of bsize\n"
> +" -h hbsz  -- Use custom histogram bin size of h1.\n"
> +"             Multiple specifications are allowed.\n"
> +" -m bmult -- Use histogram bin size multiplier of bmult.\n"
> +" -r       -- Display realtime device free space information.\n"
> +" -s       -- Emit freespace summary information.\n"
> +"\n"
> +"Only one of -b, -e, -h, or -m may be specified.\n"
> +"\n"));
> +
> +}
> +
> +void
> +freesp_init(void)
> +{
> +	freesp_cmd.name = "freesp";
> +	freesp_cmd.altname = "fsp";
> +	freesp_cmd.cfunc = freesp_f;
> +	freesp_cmd.argmin = 0;
> +	freesp_cmd.argmax = -1;
> +	freesp_cmd.args = "[-drs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
> +	freesp_cmd.flags = CMD_FLAG_ONESHOT;
> +	freesp_cmd.oneline = _("Examine filesystem free space");
> +	freesp_cmd.help = freesp_help;
> +
> +	add_command(&freesp_cmd);
> +}
> +
> diff --git a/spaceman/init.c b/spaceman/init.c
> index a51007d..b3eface 100644
> --- a/spaceman/init.c
> +++ b/spaceman/init.c
> @@ -20,6 +20,7 @@
>  #include "command.h"
>  #include "input.h"
>  #include "init.h"
> +#include "path.h"
>  #include "space.h"
>  
>  char	*progname;
> @@ -42,6 +43,7 @@ init_commands(void)
>  	prealloc_init();
>  	quit_init();
>  	trim_init();
> +	freesp_init();
>  }
>  
>  static int
> @@ -70,12 +72,14 @@ init(
>  {
>  	int		c;
>  	xfs_fsop_geom_t	geometry = { 0 };
> +	struct fs_path	fsp;
>  
>  	progname = basename(argv[0]);
>  	setlocale(LC_ALL, "");
>  	bindtextdomain(PACKAGE, LOCALEDIR);
>  	textdomain(PACKAGE);
>  
> +	fs_table_initialise(0, NULL, 0, NULL);
>  	while ((c = getopt(argc, argv, "c:V")) != EOF) {
>  		switch (c) {
>  		case 'c':
> @@ -92,11 +96,11 @@ init(
>  	if (optind != argc - 1)
>  		usage();
>  
> -	if ((c = openfile(argv[optind], &geometry)) < 0)
> +	if ((c = openfile(argv[optind], &geometry, &fsp)) < 0)
>  		exit(1);
>  	if (!platform_test_xfs_fd(c))
>  		printf(_("Not an XFS filesystem!\n"));
> -	if (addfile(argv[optind], c, &geometry) < 0)
> +	if (addfile(argv[optind], c, &geometry, &fsp) < 0)
>  		exit(1);
>  
>  	init_commands();
> diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
> index 439a9e8..689ed05 100644
> --- a/spaceman/prealloc.c
> +++ b/spaceman/prealloc.c
> @@ -20,6 +20,7 @@
>  #include "command.h"
>  #include "input.h"
>  #include "init.h"
> +#include "path.h"
>  #include "space.h"
>  
>  static cmdinfo_t prealloc_cmd;
> diff --git a/spaceman/space.h b/spaceman/space.h
> index de97139..5f4a8a0 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -20,6 +20,7 @@
>  
>  typedef struct fileio {
>  	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> +	struct fs_path	fs_path;	/* XFS path information */
>  	char		*name;		/* file name at time of open */
>  	int		fd;		/* open file descriptor */
>  } fileio_t;
> @@ -28,13 +29,18 @@ extern fileio_t		*filetable;	/* open file table */
>  extern int		filecount;	/* number of open files */
>  extern fileio_t		*file;		/* active file in file table */
>  
> -extern int	openfile(char *, xfs_fsop_geom_t *);
> -extern int	addfile(char *, int , xfs_fsop_geom_t *);
> +extern int	openfile(char *, xfs_fsop_geom_t *, struct fs_path *);
> +extern int	addfile(char *, int , xfs_fsop_geom_t *, struct fs_path *);
>  
>  extern void	print_init(void);
>  extern void	help_init(void);
>  extern void	prealloc_init(void);
>  extern void	quit_init(void);
>  extern void	trim_init(void);
> +#ifdef HAVE_GETFSMAP
> +extern void	freesp_init(void);
> +#else
> +# define freesp_init()	do { } while (0)
> +#endif
>  
>  #endif /* XFS_SPACEMAN_SPACE_H_ */
> diff --git a/spaceman/trim.c b/spaceman/trim.c
> index c0bc7f2..c20d2fe 100644
> --- a/spaceman/trim.c
> +++ b/spaceman/trim.c
> @@ -20,6 +20,7 @@
>  #include <linux/fs.h>
>  #include "command.h"
>  #include "init.h"
> +#include "path.h"
>  #include "space.h"
>  #include "input.h"
>  
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 11/12] xfs_spaceman: add a man page
  2017-06-15 20:36 ` [PATCH 11/12] xfs_spaceman: add a man page Darrick J. Wong
@ 2017-06-21 21:45   ` Eric Sandeen
  0 siblings, 0 replies; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:45 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:36 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a manual page describing xfs_spaceman's behavior.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

With some minor edits:

freesp command:

-.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
+.BI "freesp [ \-drs ] [-a agno]... [ \-b | \-e bsize | \-h bsize | \-m factor ]"

prealloc command (did this in the utility help too):

-Controls speculative preallocation.
+Removes speculative preallocation.

prealloc command -s option:

-Flush all dirty data and metadata to disk.
+Wait for removal to complete.

trim command:

+(One of
+.BR -a ", " -f ", or the "
+.IR offset / length
+pair are required.)

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

> ---
>  man/man8/xfs_spaceman.8 |  171 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 171 insertions(+)
>  create mode 100644 man/man8/xfs_spaceman.8
> 
> 
> diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
> new file mode 100644
> index 0000000..f064360
> --- /dev/null
> +++ b/man/man8/xfs_spaceman.8
> @@ -0,0 +1,171 @@
> +.TH xfs_spaceman 8
> +.SH NAME
> +xfs_spaceman \- show free space information about an XFS filesystem
> +.SH SYNOPSIS
> +.B xfs_spaceman
> +[
> +.B \-c
> +.I cmd
> +]
> +.I file
> +.br
> +.B xfs_spaceman \-V
> +.SH DESCRIPTION
> +.B xfs_spaceman
> +reports and controls free space usage in an XFS filesystem.
> +.SH OPTIONS
> +.TP 1.0i
> +.BI \-c " cmd"
> +.B xfs_spaceman
> +commands may be run interactively (the default) or as arguments on
> +the command line. Multiple
> +.B \-c
> +arguments may be given. The commands are run in the sequence given,
> +then the program exits.
> +
> +.SH COMMANDS
> +.TP
> +.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
> +With no arguments,
> +.B freesp
> +shows a histogram of all free space extents in the filesystem.
> +The command takes the following options:
> +
> +.RS 1.0i
> +.PD 0
> +.TP 0.4i
> +.B \-a agno
> +Collect free space information from this allocation group.
> +This option can be specified multiple times to collect from multiple groups.
> +
> +.TP
> +.B \-b
> +This argument establishes that the histogram bin sizes are successive powers of two.
> +This is the default, and is mutually exclusive with the
> +.BR "-e" ", " "-h" ", and " "-m" " options."
> +
> +.TP
> +.B \-d
> +Print debugging information such as the raw free space extent information.
> +
> +.TP
> +.B \-e bsize
> +Set all histogram bin sizes to a specific value.
> +This option is mutually exclusive with the
> +.BR "-b" ", " "-h" ", and " "-m" " options."
> +
> +.TP
> +.B \-h bsize
> +Create a histogram bin with a lower bound of this value.
> +The upper bound of this bin will be one less than the lower bound of the
> +next highest histogram bin.
> +This option can be given multiple times to control the exact bin sizes.
> +This option is mutually exclusive with the
> +.BR "-b" ", " "-e" ", and " "-m" " options."
> +
> +.TP
> +.B \-m factor
> +Create each histogram bin with a size that is this many times the size
> +of the prvious bin created.
> +This option is mutually exclusive with the
> +.BR "-b" ", " "-e" ", and " "-h" " options."
> +
> +.TP
> +.B \-r
> +Query the realtime device for free space information.
> +
> +.TP
> +.B \-s
> +Display a summary of the free space information found.
> +.PD
> +.RE
> +.TP
> +.BR "help [ " command " ]"
> +Display a brief description of one or all commands.
> +.TP
> +.BI "prealloc [ \-u id ] [ \-g id ] [ -p id ] [ \-m minlen ] [ \-s ]"
> +Controls speculative preallocation.
> +If no
> +.BR "-u" ", " "-g" ", or " "-p"
> +options are given, this command acts on all files.
> +The command takes the following options:
> +
> +.RS 1.0i
> +.PD 0
> +.TP 0.4i
> +.B \-u uid
> +Clear all speculative preallocations for files with this user id.
> +This option can be given in combination with the
> +.B "-g" " and " "-p"
> +options.
> +
> +.TP
> +.B \-g gid
> +Clear all speculative preallocations for files with this group id.
> +This option can be given in combination with the
> +.B "-u" " and " "-p"
> +options.
> +
> +.TP
> +.B \-p pid
> +Clear all speculative preallocations for files with this project id.
> +This option can be given in combination with the
> +.B "-u" " and " "-g"
> +options.
> +
> +.TP
> +.B \-m minlen
> +Ignore all files smaller than this size.
> +Units can be supplied for this argument.
> +
> +.TP
> +.B \-s
> +Flush all dirty data and metadata to disk.
> +.PD
> +.RE
> +.TP
> +.B print
> +Display a list of all open files.
> +.TP
> +.B quit
> +Exit
> +.BR xfs_spaceman .
> +.TP
> +.BI "trim ( \-a agno | \-f | " "offset" " " "length" " ) [ -m minlen ]"
> +Instructs the underlying storage device to release all storage that may
> +be backing free space in the filesystem.
> +The command takes the following options:
> +
> +.RS 1.0i
> +.PD 0
> +.TP 0.4i
> +.B \-a agno
> +Trim free space extents in the given allocation group.
> +This option is mutually exclusive with the
> +.BR "-f" " option and the "
> +.IR "offset" "/" "length" " options."
> +
> +.TP
> +.B \-f
> +Trim all free space in the filesystem.
> +This option is mutually exclusive with the
> +.BR "-a" " option and the "
> +.IR "offset" "/" "length" " options."
> +
> +.TP
> +.IR "option" ", " "length"
> +Trim all free space within the physical range defined by the
> +.I offset
> +and
> +.I length
> +from this filesystem.
> +Units can be appended to these arguments.
> +This option is mutually exclusive with the
> +.BR "-a" " and " "-f" " options."
> +
> +.TP
> +.B \-m minlen
> +Do not trim free space extents shorter than this length.
> +Units can be appended to this argument.
> +.PD
> +.RE
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 12/12] xfs_spaceman: add group summary mode
  2017-06-15 20:37 ` [PATCH 12/12] xfs_spaceman: add group summary mode Darrick J. Wong
@ 2017-06-21 21:53   ` Eric Sandeen
  2017-06-21 21:58     ` Darrick J. Wong
  0 siblings, 1 reply; 30+ messages in thread
From: Eric Sandeen @ 2017-06-21 21:53 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/15/17 3:37 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add a -g switch to show only a per-group summary.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

with a small fix (see below)

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

> ---
>  man/man8/xfs_spaceman.8 |    6 +++++-
>  spaceman/freesp.c       |   27 ++++++++++++++++++++++++---
>  2 files changed, 29 insertions(+), 4 deletions(-)
> 
> 
> diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
> index f064360..0302196 100644
> --- a/man/man8/xfs_spaceman.8
> +++ b/man/man8/xfs_spaceman.8
> @@ -25,7 +25,7 @@ then the program exits.
>  
>  .SH COMMANDS
>  .TP
> -.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
> +.BI "freesp [ \-dgrs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
>  With no arguments,
>  .B freesp
>  shows a histogram of all free space extents in the filesystem.
> @@ -49,6 +49,10 @@ This is the default, and is mutually exclusive with the
>  Print debugging information such as the raw free space extent information.
>  
>  .TP
> +.B \-g
> +Print the free space block and extent counts for each AG.
> +
> +.TP
>  .B \-e bsize
>  Set all histogram bin sizes to a specific value.
>  This option is mutually exclusive with the
> diff --git a/spaceman/freesp.c b/spaceman/freesp.c
> index 99e50cf..79eea44 100644
> --- a/spaceman/freesp.c
> +++ b/spaceman/freesp.c
> @@ -43,6 +43,7 @@ static long long	multsize;
>  static int		histcount;
>  static int		seen1;
>  static int		summaryflag;
> +static int		gflag;
>  static bool		rtflag;
>  static long long	totblocks;
>  static long long	totexts;
> @@ -164,6 +165,8 @@ scan_ag(
>  	off64_t			bperag;
>  	off64_t			aglen;
>  	xfs_agblock_t		agbno;
> +	unsigned long long	freeblks = 0;
> +	unsigned long long	freeexts = 0;
>  	int			ret;
>  	int			i;
>  
> @@ -216,6 +219,8 @@ scan_ag(
>  			agbno = (extent->fmr_physical - (bperag * agno)) /
>  								blocksize;
>  			aglen = extent->fmr_length / blocksize;
> +			freeblks += aglen;
> +			freeexts++;
>  
>  			addtohist(agno, agbno, aglen);
>  		}
> @@ -225,6 +230,15 @@ scan_ag(
>  			break;
>  		fsmap_advance(fsmap);
>  	}
> +
> +	if (gflag) {
> +		if (agno == NULLAGNUMBER)
> +			printf(_("     rtdev %10llu %10llu\n"), freeexts,
> +					freeblks);
> +		else
> +			printf(_("%10u %10llu %10llu\n"), agno, freeexts,
> +					freeblks);
> +	}
>  }
>  static void
>  aglistadd(
> @@ -258,7 +272,7 @@ init(
>  	hist = NULL;
>  	rtflag = false;
>  
> -	while ((c = getopt(argc, argv, "a:bde:h:m:rs")) != EOF) {
> +	while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
>  		switch (c) {
>  		case 'a':
>  			aglistadd(optarg);
> @@ -280,6 +294,10 @@ init(
>  				return command_usage(&freesp_cmd);
>  			speced = 1;
>  			break;
> +		case 'g':
> +			histcount = 0;
> +			gflag++;
> +			break;

This global needs to be reset in init() or it sticks:

(I can fix it on the way in)

xfs_spaceman> freesp
   from      to extents  blocks    pct
      1       1  647503  647503   1.69
      2       3  334189  790976   2.07
      4       7  369499 2009667   5.26
      8      15  116258 1233152   3.23
     16      31   16648  370824   0.97
     32      63    8817  387963   1.01
     64     127    5474  487854   1.28
    128     255    3483  631960   1.65
    256     511    2613  956121   2.50
    512    1023    1758 1249204   3.27
   1024    2047     828 1151649   3.01
   2048    4095     357  972144   2.54
   4096    8191     122  687724   1.80
   8192   16383      38  432634   1.13
  16384   32767      23  566786   1.48
  32768   65535      15  765430   2.00
  65536  131071      14 1359754   3.56
 131072  262143       4  653264   1.71
 262144  524287       4 1775562   4.64
 524288 1048575       2 1390287   3.64
1048576 2097151       2 2446908   6.40
2097152 4194303       2 6234130  16.31
4194304 8388607       2 11023746  28.84
xfs_spaceman> freesp -g
        AG    extents     blocks
         0     280589    6629403
         1     289696   11847480
         2      14299    7806587
         3     923071   11941772
xfs_spaceman> freesp
        AG    extents     blocks
         0     280589    6629403
         1     289696   11847480
         2      14299    7806587
         3     923071   11941772


>  		case 'h':
>  			if (speced && !histcount)
>  				goto many_spec;
> @@ -331,13 +349,15 @@ freesp_f(
>  
>  	if (!init(argc, argv))
>  		return 0;
> +	if (gflag)
> +		printf(_("        AG    extents     blocks\n"));
>  	if (rtflag)
>  		scan_ag(NULLAGNUMBER);
>  	for (agno = 0; !rtflag && agno < file->geom.agcount; agno++)  {
>  		if (inaglist(agno))
>  			scan_ag(agno);
>  	}
> -	if (histcount)
> +	if (histcount && !gflag)
>  		printhist();
>  	if (summaryflag) {
>  		printf(_("total free extents %lld\n"), totexts);
> @@ -363,6 +383,7 @@ freesp_help(void)
>  " -b       -- binary histogram bin size\n"
>  " -d       -- debug output\n"
>  " -e bsize -- Use fixed histogram bin size of bsize\n"
> +" -g       -- Print only a per-AG summary.\n"
>  " -h hbsz  -- Use custom histogram bin size of h1.\n"
>  "             Multiple specifications are allowed.\n"
>  " -m bmult -- Use histogram bin size multiplier of bmult.\n"
> @@ -382,7 +403,7 @@ freesp_init(void)
>  	freesp_cmd.cfunc = freesp_f;
>  	freesp_cmd.argmin = 0;
>  	freesp_cmd.argmax = -1;
> -	freesp_cmd.args = "[-drs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
> +	freesp_cmd.args = "[-dgrs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
>  	freesp_cmd.flags = CMD_FLAG_ONESHOT;
>  	freesp_cmd.oneline = _("Examine filesystem free space");
>  	freesp_cmd.help = freesp_help;
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 12/12] xfs_spaceman: add group summary mode
  2017-06-21 21:53   ` Eric Sandeen
@ 2017-06-21 21:58     ` Darrick J. Wong
  0 siblings, 0 replies; 30+ messages in thread
From: Darrick J. Wong @ 2017-06-21 21:58 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 21, 2017 at 04:53:52PM -0500, Eric Sandeen wrote:
> On 6/15/17 3:37 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Add a -g switch to show only a per-group summary.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> 
> with a small fix (see below)
> 
> Reviewed-by: Eric Sandeen <sandeen@redhat.com>
> 
> > ---
> >  man/man8/xfs_spaceman.8 |    6 +++++-
> >  spaceman/freesp.c       |   27 ++++++++++++++++++++++++---
> >  2 files changed, 29 insertions(+), 4 deletions(-)
> > 
> > 
> > diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
> > index f064360..0302196 100644
> > --- a/man/man8/xfs_spaceman.8
> > +++ b/man/man8/xfs_spaceman.8
> > @@ -25,7 +25,7 @@ then the program exits.
> >  
> >  .SH COMMANDS
> >  .TP
> > -.BI "freesp [ \-drs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
> > +.BI "freesp [ \-dgrs ] [-a agno] [ \-b | \-e bsize | \-h bsize | \-m factor ]"
> >  With no arguments,
> >  .B freesp
> >  shows a histogram of all free space extents in the filesystem.
> > @@ -49,6 +49,10 @@ This is the default, and is mutually exclusive with the
> >  Print debugging information such as the raw free space extent information.
> >  
> >  .TP
> > +.B \-g
> > +Print the free space block and extent counts for each AG.
> > +
> > +.TP
> >  .B \-e bsize
> >  Set all histogram bin sizes to a specific value.
> >  This option is mutually exclusive with the
> > diff --git a/spaceman/freesp.c b/spaceman/freesp.c
> > index 99e50cf..79eea44 100644
> > --- a/spaceman/freesp.c
> > +++ b/spaceman/freesp.c
> > @@ -43,6 +43,7 @@ static long long	multsize;
> >  static int		histcount;
> >  static int		seen1;
> >  static int		summaryflag;
> > +static int		gflag;
> >  static bool		rtflag;
> >  static long long	totblocks;
> >  static long long	totexts;
> > @@ -164,6 +165,8 @@ scan_ag(
> >  	off64_t			bperag;
> >  	off64_t			aglen;
> >  	xfs_agblock_t		agbno;
> > +	unsigned long long	freeblks = 0;
> > +	unsigned long long	freeexts = 0;
> >  	int			ret;
> >  	int			i;
> >  
> > @@ -216,6 +219,8 @@ scan_ag(
> >  			agbno = (extent->fmr_physical - (bperag * agno)) /
> >  								blocksize;
> >  			aglen = extent->fmr_length / blocksize;
> > +			freeblks += aglen;
> > +			freeexts++;
> >  
> >  			addtohist(agno, agbno, aglen);
> >  		}
> > @@ -225,6 +230,15 @@ scan_ag(
> >  			break;
> >  		fsmap_advance(fsmap);
> >  	}
> > +
> > +	if (gflag) {
> > +		if (agno == NULLAGNUMBER)
> > +			printf(_("     rtdev %10llu %10llu\n"), freeexts,
> > +					freeblks);
> > +		else
> > +			printf(_("%10u %10llu %10llu\n"), agno, freeexts,
> > +					freeblks);
> > +	}
> >  }
> >  static void
> >  aglistadd(
> > @@ -258,7 +272,7 @@ init(
> >  	hist = NULL;
> >  	rtflag = false;
> >  
> > -	while ((c = getopt(argc, argv, "a:bde:h:m:rs")) != EOF) {
> > +	while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
> >  		switch (c) {
> >  		case 'a':
> >  			aglistadd(optarg);
> > @@ -280,6 +294,10 @@ init(
> >  				return command_usage(&freesp_cmd);
> >  			speced = 1;
> >  			break;
> > +		case 'g':
> > +			histcount = 0;
> > +			gflag++;
> > +			break;
> 
> This global needs to be reset in init() or it sticks:
> 
> (I can fix it on the way in)

Ok, sounds good.  Assuming it's a simple insertion of "gflag = 0" somewhere
before the getopt while loop,

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> 
> xfs_spaceman> freesp
>    from      to extents  blocks    pct
>       1       1  647503  647503   1.69
>       2       3  334189  790976   2.07
>       4       7  369499 2009667   5.26
>       8      15  116258 1233152   3.23
>      16      31   16648  370824   0.97
>      32      63    8817  387963   1.01
>      64     127    5474  487854   1.28
>     128     255    3483  631960   1.65
>     256     511    2613  956121   2.50
>     512    1023    1758 1249204   3.27
>    1024    2047     828 1151649   3.01
>    2048    4095     357  972144   2.54
>    4096    8191     122  687724   1.80
>    8192   16383      38  432634   1.13
>   16384   32767      23  566786   1.48
>   32768   65535      15  765430   2.00
>   65536  131071      14 1359754   3.56
>  131072  262143       4  653264   1.71
>  262144  524287       4 1775562   4.64
>  524288 1048575       2 1390287   3.64
> 1048576 2097151       2 2446908   6.40
> 2097152 4194303       2 6234130  16.31
> 4194304 8388607       2 11023746  28.84
> xfs_spaceman> freesp -g
>         AG    extents     blocks
>          0     280589    6629403
>          1     289696   11847480
>          2      14299    7806587
>          3     923071   11941772
> xfs_spaceman> freesp
>         AG    extents     blocks
>          0     280589    6629403
>          1     289696   11847480
>          2      14299    7806587
>          3     923071   11941772
> 
> 
> >  		case 'h':
> >  			if (speced && !histcount)
> >  				goto many_spec;
> > @@ -331,13 +349,15 @@ freesp_f(
> >  
> >  	if (!init(argc, argv))
> >  		return 0;
> > +	if (gflag)
> > +		printf(_("        AG    extents     blocks\n"));
> >  	if (rtflag)
> >  		scan_ag(NULLAGNUMBER);
> >  	for (agno = 0; !rtflag && agno < file->geom.agcount; agno++)  {
> >  		if (inaglist(agno))
> >  			scan_ag(agno);
> >  	}
> > -	if (histcount)
> > +	if (histcount && !gflag)
> >  		printhist();
> >  	if (summaryflag) {
> >  		printf(_("total free extents %lld\n"), totexts);
> > @@ -363,6 +383,7 @@ freesp_help(void)
> >  " -b       -- binary histogram bin size\n"
> >  " -d       -- debug output\n"
> >  " -e bsize -- Use fixed histogram bin size of bsize\n"
> > +" -g       -- Print only a per-AG summary.\n"
> >  " -h hbsz  -- Use custom histogram bin size of h1.\n"
> >  "             Multiple specifications are allowed.\n"
> >  " -m bmult -- Use histogram bin size multiplier of bmult.\n"
> > @@ -382,7 +403,7 @@ freesp_init(void)
> >  	freesp_cmd.cfunc = freesp_f;
> >  	freesp_cmd.argmin = 0;
> >  	freesp_cmd.argmax = -1;
> > -	freesp_cmd.args = "[-drs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
> > +	freesp_cmd.args = "[-dgrs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
> >  	freesp_cmd.flags = CMD_FLAG_ONESHOT;
> >  	freesp_cmd.oneline = _("Examine filesystem free space");
> >  	freesp_cmd.help = freesp_help;
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2017-06-21 21:58 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-15 20:35 [PATCH v9 00/12] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
2017-06-15 20:35 ` [PATCH 01/12] xfs_io: refactor numlen into a library function Darrick J. Wong
2017-06-21 20:40   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 02/12] libxcmd: add cvt{int, long} to convert strings to int and long Darrick J. Wong
2017-06-21 20:16   ` Eric Sandeen
2017-06-21 20:29     ` Darrick J. Wong
2017-06-21 20:33       ` Eric Sandeen
2017-06-21 20:38         ` Darrick J. Wong
2017-06-21 21:19   ` [PATCH v2 " Darrick J. Wong
2017-06-15 20:36 ` [PATCH 03/12] libxfs: use crc32c slice-by-8 variant by default Darrick J. Wong
2017-06-21 20:42   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 04/12] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
2017-06-21 20:43   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 05/12] xfs_io: support the new getfsmap ioctl Darrick J. Wong
2017-06-21 20:51   ` Eric Sandeen
2017-06-21 20:54     ` Darrick J. Wong
2017-06-15 20:36 ` [PATCH 06/12] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
2017-06-15 20:36 ` [PATCH 07/12] xfs_spaceman: space management tool Darrick J. Wong
2017-06-21 21:12   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 08/12] xfs_spaceman: add FITRIM support Darrick J. Wong
2017-06-21 21:21   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 09/12] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
2017-06-21 21:26   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 10/12] xfs_spaceman: Free space mapping command Darrick J. Wong
2017-06-21 21:32   ` Eric Sandeen
2017-06-15 20:36 ` [PATCH 11/12] xfs_spaceman: add a man page Darrick J. Wong
2017-06-21 21:45   ` Eric Sandeen
2017-06-15 20:37 ` [PATCH 12/12] xfs_spaceman: add group summary mode Darrick J. Wong
2017-06-21 21:53   ` Eric Sandeen
2017-06-21 21:58     ` Darrick J. Wong

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.