All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support
@ 2017-06-02 19:51 Darrick J. Wong
  2017-06-02 19:51 ` [PATCH 01/10] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
                   ` (9 more replies)
  0 siblings, 10 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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 v7 patchset; it should
apply directly to for-next.

The first 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 next two patches 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 fourth 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] 29+ messages in thread

* [PATCH 01/10] xfs: introduce the XFS_IOC_GETFSMAP ioctl
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-02 19:51 ` [PATCH 02/10] xfs_io: refactor numlen into a library function Darrick J. Wong
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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] 29+ messages in thread

* [PATCH 02/10] xfs_io: refactor numlen into a library function
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
  2017-06-02 19:51 ` [PATCH 01/10] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-13 21:09   ` Eric Sandeen
  2017-06-02 19:51 ` [PATCH 03/10] xfs_io: support the new getfsmap ioctl Darrick J. Wong
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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] 29+ messages in thread

* [PATCH 03/10] xfs_io: support the new getfsmap ioctl
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
  2017-06-02 19:51 ` [PATCH 01/10] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
  2017-06-02 19:51 ` [PATCH 02/10] xfs_io: refactor numlen into a library function Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-13 22:20   ` Eric Sandeen
  2017-06-02 19:51 ` [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 UTC (permalink / raw)
  To: sandeen, darrick.wong; +Cc: linux-xfs

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

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           |  549 ++++++++++++++++++++++++++++++++++++++++++++++++++
 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    |   66 ++++++
 11 files changed, 663 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..1f4de81
--- /dev/null
+++ b/io/fsmap.c
@@ -0,0 +1,549 @@
+/*
+ * 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"
+" Example:\n"
+" 'fsmap [-d|-l|-r] [-v] [-n nr] [startoff] [endoff]' - tabular format verbose map, including unwritten extents\n"
+"\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: [startoffset..endoffset] owner startblock..endblock\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.\n"
+" -l -- query only the log device.\n"
+" -r -- query only the realtime device.\n"
+" -n -- query n extents.\n"
+" -v -- Verbose information, specify ag info.  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];
+
+	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));
+		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 extent map"),
+				(long long) p->fmr_owner);
+		else
+			printf(_("inode %lld %lld..%lld"),
+				(long long)p->fmr_owner,
+				(long long)BTOBBT(p->fmr_offset),
+				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
+		printf(_(" %lld blocks\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			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, "dln:rv")) != EOF) {
+		switch (c) {
+		case 'd':	/* data device */
+			dflag = 1;
+			break;
+		case 'l':	/* log device */
+			lflag = 1;
+			break;
+		case 'n':	/* number of extents specified */
+			nflag = atoi(optarg);
+			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)
+		return command_usage(&fsmap_cmd);
+
+	if (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(&nr, head);
+		else
+			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
+
+		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] [-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..bd8af47 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -301,6 +301,72 @@ ioctl.  Options behave as described in the
 .BR xfs_bmap (8)
 manual page.
 .TP
+.BI "fsmap [ \-d | \-l | \-r ] [ \-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 \-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 the number of extents in the filesystem and uses
+that value to compute the group size.
+.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.
+.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] 29+ messages in thread

* [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (2 preceding siblings ...)
  2017-06-02 19:51 ` [PATCH 03/10] xfs_io: support the new getfsmap ioctl Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-13 22:34   ` Eric Sandeen
  2017-06-02 19:51 ` [PATCH 05/10] xfs_spaceman: space management tool Darrick J. Wong
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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>
---
 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 31239ca..2d8d9c8 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -144,5 +144,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] 29+ messages in thread

* [PATCH 05/10] xfs_spaceman: space management tool
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (3 preceding siblings ...)
  2017-06-02 19:51 ` [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-14 14:15   ` Eric Sandeen
  2017-06-02 19:51 ` [PATCH 06/10] xfs_spaceman: add FITRIM support Darrick J. Wong
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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   |  149 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/init.c   |  114 +++++++++++++++++++++++++++++++++++++++++
 spaceman/init.h   |   23 ++++++++
 spaceman/space.h  |   36 +++++++++++++
 6 files changed, 357 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..9356066
--- /dev/null
+++ b/spaceman/file.c
@@ -0,0 +1,149 @@
+/*
+ * 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 (%s,%s,%s%s%s)\n"),
+		braces? '[' : ' ', index, braces? ']' : ' ', file->name,
+		file->flags & O_SYNC ? _("sync") : _("non-sync"),
+		file->flags & O_DIRECT ? _("direct") : _("non-direct"),
+		file->flags & O_RDONLY ? _("read-only") : _("read-write"),
+		file->flags & O_APPEND ? _(",append-only") : "",
+		file->flags & O_NONBLOCK ? _(",non-block") : "");
+}
+
+int
+filelist_f(void)
+{
+	int		i;
+
+	for (i = 0; i < filecount; i++)
+		print_fileio(&filetable[i], i, &filetable[i] == file);
+	return 0;
+}
+
+static int
+print_f(
+	int		argc,
+	char		**argv)
+{
+	filelist_f();
+	return 0;
+}
+
+int
+openfile(
+	char		*path,
+	xfs_fsop_geom_t	*geom,
+	int		flags,
+	mode_t		mode)
+{
+	int		fd;
+
+	fd = open(path, flags, mode);
+	if (fd < 0) {
+		if ((errno == EISDIR) && (flags & O_RDWR)) {
+			/* make it as if we asked for O_RDONLY & try again */
+			flags &= ~O_RDWR;
+			flags |= O_RDONLY;
+			fd = open(path, flags, mode);
+			if (fd < 0) {
+				perror(path);
+				return -1;
+			}
+		} else {
+			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,
+	int		flags)
+{
+	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->flags = flags;
+	file->name = filename;
+	file->geom = *geometry;
+	return 0;
+}
+
+void
+file_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..5dbaef2
--- /dev/null
+++ b/spaceman/init.c
@@ -0,0 +1,114 @@
+/*
+ * 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)
+{
+	file_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, flags = 0;
+	mode_t		mode = 0600;
+	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, flags, mode)) < 0)
+		exit(1);
+	if (!platform_test_xfs_fd(c))
+		printf(_("Not an XFS filesystem!\n"));
+	if (addfile(argv[optind], c, &geometry, flags) < 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..165e4f5
--- /dev/null
+++ b/spaceman/init.h
@@ -0,0 +1,23 @@
+/*
+ * 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
+ */
+
+extern char	*progname;
+extern int	exitcode;
+
+#define min(a,b)	(((a)<(b))?(a):(b))
+#define max(a,b)	(((a)>(b))?(a):(b))
diff --git a/spaceman/space.h b/spaceman/space.h
new file mode 100644
index 0000000..6e1bc52
--- /dev/null
+++ b/spaceman/space.h
@@ -0,0 +1,36 @@
+/*
+ * 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
+ */
+
+typedef struct fileio {
+	int		fd;		/* open file descriptor */
+	int		flags;		/* flags describing file state */
+	char		*name;		/* file name at time of open */
+	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
+} 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 filelist_f(void);
+
+extern int	openfile(char *, xfs_fsop_geom_t *, int, mode_t);
+extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
+
+extern void	file_init(void);
+extern void	help_init(void);
+extern void	quit_init(void);


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

* [PATCH 06/10] xfs_spaceman: add FITRIM support
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (4 preceding siblings ...)
  2017-06-02 19:51 ` [PATCH 05/10] xfs_spaceman: space management tool Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-14 15:32   ` Eric Sandeen
  2017-06-02 19:51 ` [PATCH 07/10] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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   |  127 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 130 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 5dbaef2..0958377 100644
--- a/spaceman/init.c
+++ b/spaceman/init.c
@@ -40,6 +40,7 @@ init_commands(void)
 	file_init();
 	help_init();
 	quit_init();
+	trim_init();
 }
 
 static int
diff --git a/spaceman/space.h b/spaceman/space.h
index 6e1bc52..7b4f034 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -34,3 +34,4 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
 extern void	file_init(void);
 extern void	help_init(void);
 extern void	quit_init(void);
+extern void	trim_init(void);
diff --git a/spaceman/trim.c b/spaceman/trim.c
new file mode 100644
index 0000000..d58f6d1
--- /dev/null
+++ b/spaceman/trim.c
@@ -0,0 +1,127 @@
+/*
+ * 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 = atoi(optarg);
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'm':
+			minlen = cvtnum(file->geom.blocksize,
+					file->geom.sectsize, argv[optind]);
+			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"
+"Options: [-m minlen] [-f]|[-a agno]|[offset length]\n"
+"\n"
+" -m minlen     -- skip freespace extents smaller than minlen\n"
+" -f            -- trim all the freespace in the entire filesystem\n"
+" -a agno       -- trim all the freespace in the given AG agno\n"
+" offset length -- trim the freespace in the range {offset, length}\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] [-f]|[-a agno]|[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] 29+ messages in thread

* [PATCH 07/10] xfs_spaceman: add new speculative prealloc control
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (5 preceding siblings ...)
  2017-06-02 19:51 ` [PATCH 06/10] xfs_spaceman: add FITRIM support Darrick J. Wong
@ 2017-06-02 19:51 ` Darrick J. Wong
  2017-06-14 15:05   ` Eric Sandeen
  2017-06-02 19:52 ` [PATCH 08/10] xfs_spaceman: Free space mapping command Darrick J. Wong
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:51 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 |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/space.h    |    1 
 4 files changed, 114 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 0958377..93a8a1e 100644
--- a/spaceman/init.c
+++ b/spaceman/init.c
@@ -39,6 +39,7 @@ init_commands(void)
 {
 	file_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..71c0d80
--- /dev/null
+++ b/spaceman/prealloc.c
@@ -0,0 +1,111 @@
+/*
+ * 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 = atoi(optarg);
+			break;
+		case 'u':
+			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
+			eofb.eof_uid = atoi(optarg);
+			break;
+		case 'p':
+			eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
+			eofb.eof_prid = atoi(optarg);
+			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"
+"Options: [-s] [-ugp id] [-m minlen]\n"
+"\n"
+" -s        -- wait for removal to complete\n"
+" -u uid    -- remove prealloc on files matching user <uid>\n"
+" -g gid    -- remove prealloc on files matching group <gid>\n"
+" -p prid   -- remove prealloc on files matching project <prid>\n"
+" -m minlen -- only consider files larger than <minlen>\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] [-ugp 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 7b4f034..0ae3116 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -33,5 +33,6 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
 
 extern void	file_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] 29+ messages in thread

* [PATCH 08/10] xfs_spaceman: Free space mapping command
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (6 preceding siblings ...)
  2017-06-02 19:51 ` [PATCH 07/10] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
@ 2017-06-02 19:52 ` Darrick J. Wong
  2017-06-14 15:43   ` Eric Sandeen
  2017-06-14 16:04   ` Eric Sandeen
  2017-06-02 19:52 ` [PATCH 09/10] xfs_spaceman: add a man page Darrick J. Wong
  2017-06-02 19:52 ` [PATCH 10/10] xfs_spaceman: add group summary mode Darrick J. Wong
  9 siblings, 2 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:52 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   |  367 +++++++++++++++++++++++++++++++++++++++++++++++++++
 spaceman/init.c     |    8 +
 spaceman/prealloc.c |    1 
 spaceman/space.h    |   12 +-
 spaceman/trim.c     |    1 
 7 files changed, 409 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 9356066..46b8826 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;
@@ -69,8 +70,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;
 
 	fd = open(path, flags, mode);
@@ -95,6 +98,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;
 }
 
@@ -103,7 +116,8 @@ addfile(
 	char		*name,
 	int		fd,
 	xfs_fsop_geom_t	*geometry,
-	int		flags)
+	int		flags,
+	struct fs_path	*fs_path)
 {
 	char		*filename;
 
@@ -131,6 +145,7 @@ addfile(
 	file->flags = flags;
 	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..2290a5e
--- /dev/null
+++ b/spaceman/freesp.c
@@ -0,0 +1,367 @@
+/*
+ * 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"
+
+typedef struct histent
+{
+	int		low;
+	int		high;
+	long long	count;
+	long long	blocks;
+} histent_t;
+
+static int		agcount;
+static xfs_agnumber_t	*aglist;
+static int		dumpflag;
+static int		equalsize;
+static histent_t	*hist;
+static int		histcount;
+static int		multsize;
+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(
+	int	h)
+{
+	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)
+{
+	int		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(
+	int	maxlen)
+{
+	int	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("%7d %7d %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)
+{
+	aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
+	aglist[agcount] = (xfs_agnumber_t)atoi(a);
+	agcount++;
+}
+
+static int
+init(
+	int		argc,
+	char		**argv)
+{
+	int		c;
+	int		speced = 0;
+
+	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)
+				return 0;
+			multsize = 2;
+			speced = 1;
+			break;
+		case 'd':
+			dumpflag = 1;
+			break;
+		case 'e':
+			if (speced)
+				return 0;
+			equalsize = atoi(optarg);
+			speced = 1;
+			break;
+		case 'h':
+			if (speced && !histcount)
+				return 0;
+			addhistent(atoi(optarg));
+			speced = 1;
+			break;
+		case 'm':
+			if (speced)
+				return 0;
+			multsize = atoi(optarg);
+			speced = 1;
+			break;
+		case 'r':
+			rtflag = true;
+			break;
+		case 's':
+			summaryflag = 1;
+			break;
+		case '?':
+			return 0;
+		}
+	}
+	if (optind != argc)
+		return 0;
+	if (!speced)
+		multsize = 2;
+	histinit(file->geom.agblocks);
+	return 1;
+}
+
+/*
+ * 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"
+"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\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"));
+
+}
+
+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 = "[-bdsr] [-a agno] [-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 93a8a1e..f3c93c3 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;
@@ -38,6 +39,7 @@ static void
 init_commands(void)
 {
 	file_init();
+	freesp_init();
 	help_init();
 	prealloc_init();
 	quit_init();
@@ -71,12 +73,14 @@ init(
 	int		c, flags = 0;
 	mode_t		mode = 0600;
 	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':
@@ -93,11 +97,11 @@ init(
 	if (optind != argc - 1)
 		usage();
 
-	if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
+	if ((c = openfile(argv[optind], &geometry, flags, mode, &fsp)) < 0)
 		exit(1);
 	if (!platform_test_xfs_fd(c))
 		printf(_("Not an XFS filesystem!\n"));
-	if (addfile(argv[optind], c, &geometry, flags) < 0)
+	if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
 		exit(1);
 
 	init_commands();
diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
index 71c0d80..55b85ed 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 0ae3116..bf9a2df 100644
--- a/spaceman/space.h
+++ b/spaceman/space.h
@@ -21,6 +21,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 */
@@ -28,11 +29,18 @@ extern int		filecount;	/* number of open files */
 extern fileio_t		*file;		/* active file in file table */
 extern int filelist_f(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	file_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
diff --git a/spaceman/trim.c b/spaceman/trim.c
index d58f6d1..b5908cc 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] 29+ messages in thread

* [PATCH 09/10] xfs_spaceman: add a man page
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (7 preceding siblings ...)
  2017-06-02 19:52 ` [PATCH 08/10] xfs_spaceman: Free space mapping command Darrick J. Wong
@ 2017-06-02 19:52 ` Darrick J. Wong
  2017-06-14 16:06   ` Eric Sandeen
  2017-06-02 19:52 ` [PATCH 10/10] xfs_spaceman: add group summary mode Darrick J. Wong
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:52 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 |  102 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 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..c1d19c0
--- /dev/null
+++ b/man/man8/xfs_spaceman.8
@@ -0,0 +1,102 @@
+.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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
+With no arguments,
+.B freesp
+shows a histogram of all free space extents in the filesystem.
+The
+.B -b
+argument establishes that the histogram bins are successive powers of two.
+This is the default.
+The
+.BR -h " and " -m
+options can be used to specify a custom histogram bin size as well as a
+multiplication factor for subsequent bin sizes.
+The
+.BR -e
+option fixes the histogram size to a particular value.
+The
+.BR -a " and " -r
+options constrain the free space information report to a particular AG
+or the realtime device, respectively.  The
+.B -a
+option may be specified multiple times.
+A summary of free space information will be printed if the
+.B -s
+option is given.
+.TP
+.BR "help [ " command " ]"
+Display a brief description of one or all commands.
+.TP
+.BI "prealloc [ \-ugp id ] [ \-m minlen ] [ \-s ]"
+Controls speculative preallocation.  The
+.BR -u ","
+.BR -g ","
+and
+.B -p
+options will clear all speculative preallocations for a given user,
+group, or project ID, respectively.
+The
+.B -m
+option causes the operation to ignore any file with a size smaller than
+.BR minlen "."
+The
+.B -s
+option will flush all dirty data and metadata to disk.
+.TP
+.B print
+Display a list of all open files.
+.TP
+.B quit
+Exit
+.BR xfs_spaceman .
+.TP
+.BI "trim [ \-f ] [ \-a agno ] [ \-m minlen ] [" " offset length " ]
+Instructs the underlying storage device to release all storage that may
+be backing free space in the filesystem.
+The
+.B -f
+option trims all free space in the entire filesystem.
+The
+.B -a
+option trims only the free space in a given AG.
+The
+.B -m
+option only trims free space extents that are longer than
+.IR minlen "."
+The
+.IR offset " and " length
+arguments trim all free space between
+.I offset
+and
+.IR "offset+length" "."
+The
+.BR -a " and " -f
+options are mutually exclusive with each other as well as with the
+.IR offset " and " length
+arguments.


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

* [PATCH 10/10] xfs_spaceman: add group summary mode
  2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
                   ` (8 preceding siblings ...)
  2017-06-02 19:52 ` [PATCH 09/10] xfs_spaceman: add a man page Darrick J. Wong
@ 2017-06-02 19:52 ` Darrick J. Wong
  2017-06-14 16:23   ` Eric Sandeen
  9 siblings, 1 reply; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-02 19:52 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 |    8 +++++++-
 spaceman/freesp.c       |   29 +++++++++++++++++++++++++----
 2 files changed, 32 insertions(+), 5 deletions(-)


diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
index c1d19c0..a57a0c3 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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
+.BI "freesp [ \-srg ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
 With no arguments,
 .B freesp
 shows a histogram of all free space extents in the filesystem.
@@ -49,6 +49,12 @@ option may be specified multiple times.
 A summary of free space information will be printed if the
 .B -s
 option is given.
+The
+.B -g
+option prints a brief per-AG summary of the free space found in that AG.
+If
+.B -r
+is specified it will also report on free space in the realtime device.
 .TP
 .BR "help [ " command " ]"
 Display a brief description of one or all commands.
diff --git a/spaceman/freesp.c b/spaceman/freesp.c
index 2290a5e..351f0ce 100644
--- a/spaceman/freesp.c
+++ b/spaceman/freesp.c
@@ -42,6 +42,7 @@ static int		histcount;
 static int		multsize;
 static int		seen1;
 static int		summaryflag;
+static int		gflag;
 static bool		rtflag;
 static long long	totblocks;
 static long long	totexts;
@@ -159,6 +160,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;
 
@@ -211,6 +214,8 @@ scan_ag(
 			agbno = (extent->fmr_physical - (bperag * agno)) /
 								blocksize;
 			aglen = extent->fmr_length / blocksize;
+			freeblks += aglen;
+			freeexts++;
 
 			addtohist(agno, agbno, aglen);
 		}
@@ -220,6 +225,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(
@@ -244,7 +258,7 @@ init(
 	aglist = NULL;
 	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);
@@ -264,6 +278,10 @@ init(
 			equalsize = atoi(optarg);
 			speced = 1;
 			break;
+		case 'g':
+			histcount = 0;
+			gflag++;
+			break;
 		case 'h':
 			if (speced && !histcount)
 				return 0;
@@ -306,13 +324,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);
@@ -334,12 +354,13 @@ freesp_help(void)
 "\n"
 "Examine filesystem free space\n"
 "\n"
-"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"
+"Options: [-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\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"
+" -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"
@@ -357,7 +378,7 @@ freesp_init(void)
 	freesp_cmd.cfunc = freesp_f;
 	freesp_cmd.argmin = 0;
 	freesp_cmd.argmax = -1;
-	freesp_cmd.args = "[-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";
+	freesp_cmd.args = "[-bdgsr] [-a agno] [-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] 29+ messages in thread

* Re: [PATCH 02/10] xfs_io: refactor numlen into a library function
  2017-06-02 19:51 ` [PATCH 02/10] xfs_io: refactor numlen into a library function Darrick J. Wong
@ 2017-06-13 21:09   ` Eric Sandeen
  0 siblings, 0 replies; 29+ messages in thread
From: Eric Sandeen @ 2017-06-13 21:09 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/2/17 2:51 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] 29+ messages in thread

* Re: [PATCH 03/10] xfs_io: support the new getfsmap ioctl
  2017-06-02 19:51 ` [PATCH 03/10] xfs_io: support the new getfsmap ioctl Darrick J. Wong
@ 2017-06-13 22:20   ` Eric Sandeen
  2017-06-14  0:23     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-13 22:20 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/2/17 2:51 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Sorry Darrick, lots more here that I missed the first time around.

> ---
>  io/Makefile          |    7 +
>  io/copy_file_range.c |    2 
>  io/encrypt.c         |    1 
>  io/fsmap.c           |  549 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  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    |   66 ++++++
>  11 files changed, 663 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..1f4de81
> --- /dev/null
> +++ b/io/fsmap.c
> @@ -0,0 +1,549 @@
> +/*
> + * 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"
> +" Example:\n"
> +" 'fsmap [-d|-l|-r] [-v] [-n nr] [startoff] [endoff]' - tabular format verbose map, including unwritten extents\n"

Whoops, I missed this the first time, that's a restatement of all options,
not an example.

(Or I'd probably just drop it, I don't think an example is needed
or useful, but as you like it... I guess it's modeled on fiemap)

> +"\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: [startoffset..endoffset] owner startblock..endblock\n"

Hm, no:

	0: 8:17 [0..1175]: unknown 1176 blocks
	1: 8:17 [1176..1183]: free space 8 blocks

manpage gets it right:

extent: major:minor [startblock..endblock]: owner startoffset..endoffset length

(I might drop the last "blocks"... is it useful?  fiemap doesn't use it)


Sigh, sorry for missing stuff on the first round.

OK fiemap prints a header with -v ...

# io/xfs_io -c "fiemap -v" /mnt/test/junk
/mnt/test/junk:
 EXT: FILE-OFFSET      BLOCK-RANGE      TOTAL FLAGS
   0: [0..7]:          hole                 8
   1: [8..39]:         5936..5967          32   0x0

I think that'd be useful here, too ... oh, bug.  see below.  (s/nr == 0/*nr == 0/)

> +" 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.\n"

(this is the default, yes?)

> +" -l -- query only the log device.\n"

Hm, with an external log I get:

xfs_io> fsmap -l
	0: 7:7 [0..2097151]: journalling log 2097152 blocks

but an internal log does not show those blocks in the output, should it?

> +" -r -- query only the realtime device.\n"
> +" -n -- query n extents.\n"

-n doesn't seem to work as described:

# io/xfs_io -c "fsmap -n 2" /mnt/test
	0: 8:17 [0..1175]: unknown 1176 blocks
	1: 8:17 [1176..1183]: free space 8 blocks
	2: 8:17 [1184..1327]: unknown 144 blocks
	3: 8:17 [1328..1343]: free space 16 blocks

Oh, it's "at a time" - so help text needs to be fixed to reflect that it's 
the size of each query, not the total number queried.

> +" -v -- Verbose information, specify ag info.  Show flags legend on 2nd -v\n"

(and FILE-OFFSET of each owner?)

There are no flags printed AFAICT, and no different behavior with -vv...
but that seems broken somewhere.

Oh, it looks like if no flags are (will be?) printed we skip the column
and the legend.  That seems a little odd to me... is it intentional?

> +"\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");

(Aside: I wonder if there's any way to avoid spaces in the owner field,
so that awk can easily parse the result...?  Not sure how that'd look,
but it'd make it a lot more useful when presented with thousands of
records)

> +	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];
> +
> +	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));
> +		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 extent map"),
> +				(long long) p->fmr_owner);
> +		else
> +			printf(_("inode %lld %lld..%lld"),
> +				(long long)p->fmr_owner,
> +				(long long)BTOBBT(p->fmr_offset),
> +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> +		printf(_(" %lld blocks\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)

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			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, "dln:rv")) != EOF) {
> +		switch (c) {
> +		case 'd':	/* data device */
> +			dflag = 1;
> +			break;
> +		case 'l':	/* log device */
> +			lflag = 1;
> +			break;
> +		case 'n':	/* number of extents specified */
> +			nflag = atoi(optarg);
> +			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)
> +		return command_usage(&fsmap_cmd);
> +
> +	if (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);

Manpage, below:

"In the absence of -n, xfs_fsmap queries the system for the number of extents in the filesystem
and uses that value to compute the group size."

Looks hard-coded to 131072, no?

> +	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(&nr, head);
> +		else
> +			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
> +
> +		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] [-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..bd8af47 100644
> --- a/man/man8/xfs_io.8
> +++ b/man/man8/xfs_io.8
> @@ -301,6 +301,72 @@ ioctl.  Options behave as described in the
>  .BR xfs_bmap (8)
>  manual page.
>  .TP
> +.BI "fsmap [ \-d | \-l | \-r ] [ \-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 \-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 the number of extents in the filesystem and uses
> +that value to compute the group size.
> +.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.
> +.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] 29+ messages in thread

* Re: [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version
  2017-06-02 19:51 ` [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
@ 2017-06-13 22:34   ` Eric Sandeen
  0 siblings, 0 replies; 29+ messages in thread
From: Eric Sandeen @ 2017-06-13 22:34 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/2/17 2:51 PM, Darrick J. Wong wrote:
> 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 31239ca..2d8d9c8 100644
> --- a/libxfs/libxfs_api_defs.h
> +++ b/libxfs/libxfs_api_defs.h
> @@ -144,5 +144,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);
>  }
>  
>  /*
> 
> --
> 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] 29+ messages in thread

* Re: [PATCH 03/10] xfs_io: support the new getfsmap ioctl
  2017-06-13 22:20   ` Eric Sandeen
@ 2017-06-14  0:23     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14  0:23 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Tue, Jun 13, 2017 at 05:20:28PM -0500, Eric Sandeen wrote:
> On 6/2/17 2:51 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Sorry Darrick, lots more here that I missed the first time around.
> 
> > ---
> >  io/Makefile          |    7 +
> >  io/copy_file_range.c |    2 
> >  io/encrypt.c         |    1 
> >  io/fsmap.c           |  549 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  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    |   66 ++++++
> >  11 files changed, 663 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..1f4de81
> > --- /dev/null
> > +++ b/io/fsmap.c
> > @@ -0,0 +1,549 @@
> > +/*
> > + * 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"
> > +" Example:\n"
> > +" 'fsmap [-d|-l|-r] [-v] [-n nr] [startoff] [endoff]' - tabular format verbose map, including unwritten extents\n"
> 
> Whoops, I missed this the first time, that's a restatement of all options,
> not an example.
> 
> (Or I'd probably just drop it, I don't think an example is needed
> or useful, but as you like it... I guess it's modeled on fiemap)

Yeah, it's unnecessary and redundant.  I'll remove it.

> > +"\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: [startoffset..endoffset] owner startblock..endblock\n"
> 
> Hm, no:
> 
> 	0: 8:17 [0..1175]: unknown 1176 blocks
> 	1: 8:17 [1176..1183]: free space 8 blocks
> 
> manpage gets it right:
> 
> extent: major:minor [startblock..endblock]: owner startoffset..endoffset length
> 
> (I might drop the last "blocks"... is it useful?  fiemap doesn't use it)

Probably came from the bmap command, but yes, it's redundant since we
already state that the numbers are in units of blocks.

I'll update the help screen to list the correct fields.

> Sigh, sorry for missing stuff on the first round.
> 
> OK fiemap prints a header with -v ...
> 
> # io/xfs_io -c "fiemap -v" /mnt/test/junk
> /mnt/test/junk:
>  EXT: FILE-OFFSET      BLOCK-RANGE      TOTAL FLAGS
>    0: [0..7]:          hole                 8
>    1: [8..39]:         5936..5967          32   0x0
> 
> I think that'd be useful here, too ... oh, bug.  see below.  (s/nr == 0/*nr == 0/)

Got it.

> > +" 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.\n"
> 
> (this is the default, yes?)

Yes.

> > +" -l -- query only the log device.\n"
> 
> Hm, with an external log I get:
> 
> xfs_io> fsmap -l
> 	0: 7:7 [0..2097151]: journalling log 2097152 blocks
> 
> but an internal log does not show those blocks in the output, should it?

It will if you have rmapbt=1.  Maybe the kernel should report the owner
of an external log as 'unknown' if the caller doesn't have
CAP_SYS_ADMIN, similar to how we don't report specific owners for the
data device.

> > +" -r -- query only the realtime device.\n"
> > +" -n -- query n extents.\n"
> 
> -n doesn't seem to work as described:
> 
> # io/xfs_io -c "fsmap -n 2" /mnt/test
> 	0: 8:17 [0..1175]: unknown 1176 blocks
> 	1: 8:17 [1176..1183]: free space 8 blocks
> 	2: 8:17 [1184..1327]: unknown 144 blocks
> 	3: 8:17 [1328..1343]: free space 16 blocks
> 
> Oh, it's "at a time" - so help text needs to be fixed to reflect that it's 
> the size of each query, not the total number queried.

Yes.  Will fix the documentation.

> > +" -v -- Verbose information, specify ag info.  Show flags legend on 2nd -v\n"
> 
> (and FILE-OFFSET of each owner?)
> 
> There are no flags printed AFAICT, and no different behavior with -vv...
> but that seems broken somewhere.
> 
> Oh, it looks like if no flags are (will be?) printed we skip the column
> and the legend.  That seems a little odd to me... is it intentional?

bmap doesn't show the legend unless any of the flags are used, so fsmap
behaves similarly.

> > +"\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");
> 
> (Aside: I wonder if there's any way to avoid spaces in the owner field,
> so that awk can easily parse the result...?  Not sure how that'd look,
> but it'd make it a lot more useful when presented with thousands of
> records)

Well, a few options come to mind -- adding a machine-friendly format,
enclosing the owner names with spaces for easier parsing, or stuffing
in weird nonbreaking space sequences (yuck).  I'm partial to the second.

> > +	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];
> > +
> > +	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));
> > +		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 extent map"),
> > +				(long long) p->fmr_owner);
> > +		else
> > +			printf(_("inode %lld %lld..%lld"),
> > +				(long long)p->fmr_owner,
> > +				(long long)BTOBBT(p->fmr_offset),
> > +				(long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
> > +		printf(_(" %lld blocks\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)
> 
> if (*nr == 0)

Oops, fixed.

> > +		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			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, "dln:rv")) != EOF) {
> > +		switch (c) {
> > +		case 'd':	/* data device */
> > +			dflag = 1;
> > +			break;
> > +		case 'l':	/* log device */
> > +			lflag = 1;
> > +			break;
> > +		case 'n':	/* number of extents specified */
> > +			nflag = atoi(optarg);
> > +			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)
> > +		return command_usage(&fsmap_cmd);
> > +
> > +	if (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);
> 
> Manpage, below:
> 
> "In the absence of -n, xfs_fsmap queries the system for the number of extents in the filesystem
> and uses that value to compute the group size."
> 
> Looks hard-coded to 131072, no?

Yes.  It used to behave as specified, but it was very slow to rake every
rmap record in the system twice, so I changed it to 131072 and evidently
forgot to update the manpage.

--D

> > +	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(&nr, head);
> > +		else
> > +			dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
> > +
> > +		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] [-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..bd8af47 100644
> > --- a/man/man8/xfs_io.8
> > +++ b/man/man8/xfs_io.8
> > @@ -301,6 +301,72 @@ ioctl.  Options behave as described in the
> >  .BR xfs_bmap (8)
> >  manual page.
> >  .TP
> > +.BI "fsmap [ \-d | \-l | \-r ] [ \-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 \-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 the number of extents in the filesystem and uses
> > +that value to compute the group size.
> > +.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.
> > +.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] 29+ messages in thread

* Re: [PATCH 05/10] xfs_spaceman: space management tool
  2017-06-02 19:51 ` [PATCH 05/10] xfs_spaceman: space management tool Darrick J. Wong
@ 2017-06-14 14:15   ` Eric Sandeen
  2017-06-14 16:16     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 14:15 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs, Dave Chinner

On 6/2/17 2:51 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>
> ---
>  Makefile          |    3 +
>  spaceman/Makefile |   33 ++++++++++++
>  spaceman/file.c   |  149 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.c   |  114 +++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.h   |   23 ++++++++
>  spaceman/space.h  |   36 +++++++++++++
>  6 files changed, 357 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..9356066
> --- /dev/null
> +++ b/spaceman/file.c
> @@ -0,0 +1,149 @@
> +/*
> + * 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 (%s,%s,%s%s%s)\n"),
> +		braces? '[' : ' ', index, braces? ']' : ' ', file->name,
> +		file->flags & O_SYNC ? _("sync") : _("non-sync"),

Ok, my question last round about this didn't get answered:

[xfsprogs-fsmap]# spaceman/xfs_spaceman -c print mnt/
[000] mnt/           (non-sync,non-direct,read-write)

[xfsprogs-fsmap]# mount -o remount,ro mnt

[xfsprogs-fsmap]# spaceman/xfs_spaceman -c print mnt/
[000] mnt/           (non-sync,non-direct,read-write)

I don't see that these flags have any meaning; indeed, the flags argument
to openfile() and addfile() is initialized to 0 and never changed.  Should
it just be removed?

If you want to get things merged and clean stuff up with a later patch,
that's ok by me, or we could remove them now.  It's not really a functionality
problem so I'm not too bothered.

> +		file->flags & O_DIRECT ? _("direct") : _("non-direct"),
> +		file->flags & O_RDONLY ? _("read-only") : _("read-write"),
> +		file->flags & O_APPEND ? _(",append-only") : "",
> +		file->flags & O_NONBLOCK ? _(",non-block") : "");
> +}
> +
> +int
> +filelist_f(void)
> +{
> +	int		i;
> +
> +	for (i = 0; i < filecount; i++)
> +		print_fileio(&filetable[i], i, &filetable[i] == file);
> +	return 0;
> +}
> +
> +static int
> +print_f(
> +	int		argc,
> +	char		**argv)
> +{
> +	filelist_f();

No need for the separate filelist_f, just put the guts here, it has no other
caller.

Hm, actually ... for consistency with xfs_io should we have both "file" and "print?"

       file [ N ]
              Display a list of all open files and (optionally) switch to an alternate current open file.

       print  Display a list of all open files and memory mapped regions.  The current file and current mapping are distinguishable from  any others.

Again not overly concerned about this, just pointing it out - if it's modeled on xfs_io behavior, let's keep it as similar as possible.

(If this gets changed, don't forget the manpage)

> +	return 0;
> +}
> +
> +int
> +openfile(
> +	char		*path,
> +	xfs_fsop_geom_t	*geom,
> +	int		flags,

flags is always called with flags == 0, so all the flag testing below is pointless -
and probably doesn't relate to filesystem-wide operations anyway.

> +	mode_t		mode)
> +{
> +	int		fd;
> +
> +	fd = open(path, flags, mode);
> +	if (fd < 0) {
> +		if ((errno == EISDIR) && (flags & O_RDWR)) {
> +			/* make it as if we asked for O_RDONLY & try again */
> +			flags &= ~O_RDWR;
> +			flags |= O_RDONLY;
> +			fd = open(path, flags, mode);
> +			if (fd < 0) {
> +				perror(path);
> +				return -1;
> +			}
> +		} else {
> +			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,
> +	int		flags)
> +{
> +	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));

Are you keeping all the filetable stuff with the thought that maybe some 
day we'll allow operation on multiple filesystems?  Probably a good idea.

> +	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->flags = flags;
> +	file->name = filename;
> +	file->geom = *geometry;
> +	return 0;
> +}
> +
> +void
> +file_init(void)
> +{

file_init setting up print_cmd is confusing - unless you want both ala xfs_io,
I'd go consistently with either "print" or "file" but not a mishmash.

> +	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..5dbaef2
> --- /dev/null
> +++ b/spaceman/init.c
> @@ -0,0 +1,114 @@
> +/*
> + * 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)
> +{
> +	file_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, flags = 0;
> +	mode_t		mode = 0600;
> +	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, flags, mode)) < 0)
> +		exit(1);
> +	if (!platform_test_xfs_fd(c))
> +		printf(_("Not an XFS filesystem!\n"));
> +	if (addfile(argv[optind], c, &geometry, flags) < 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..165e4f5
> --- /dev/null
> +++ b/spaceman/init.h
> @@ -0,0 +1,23 @@
> +/*
> + * 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
> + */
> +
> +extern char	*progname;
> +extern int	exitcode;
> +
> +#define min(a,b)	(((a)<(b))?(a):(b))
> +#define max(a,b)	(((a)>(b))?(a):(b))

these aren't actually used, FWIW.

> diff --git a/spaceman/space.h b/spaceman/space.h
> new file mode 100644
> index 0000000..6e1bc52
> --- /dev/null
> +++ b/spaceman/space.h
> @@ -0,0 +1,36 @@
> +/*
> + * 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
> + */
> +
> +typedef struct fileio {
> +	int		fd;		/* open file descriptor */
> +	int		flags;		/* flags describing file state */

This is never used, really - always 0.

> +	char		*name;		/* file name at time of open */
> +	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> +} 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 filelist_f(void);
> +
> +extern int	openfile(char *, xfs_fsop_geom_t *, int, mode_t);
> +extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
> +
> +extern void	file_init(void);
> +extern void	help_init(void);
> +extern void	quit_init(void);
> 


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

* Re: [PATCH 07/10] xfs_spaceman: add new speculative prealloc control
  2017-06-02 19:51 ` [PATCH 07/10] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
@ 2017-06-14 15:05   ` Eric Sandeen
  2017-06-14 16:39     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 15:05 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/2/17 2:51 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>
> ---
>  spaceman/Makefile   |    2 -
>  spaceman/init.c     |    1 
>  spaceman/prealloc.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/space.h    |    1 
>  4 files changed, 114 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 0958377..93a8a1e 100644
> --- a/spaceman/init.c
> +++ b/spaceman/init.c
> @@ -39,6 +39,7 @@ init_commands(void)
>  {
>  	file_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..71c0d80
> --- /dev/null
> +++ b/spaceman/prealloc.c
> @@ -0,0 +1,111 @@
> +/*
> + * 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 = atoi(optarg);
> +			break;
> +		case 'u':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
> +			eofb.eof_uid = atoi(optarg);
> +			break;
> +		case 'p':
> +			eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
> +			eofb.eof_prid = atoi(optarg);
> +			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"
> +"Options: [-s] [-ugp id] [-m minlen]\n"

No need to restate Options: because that comes out already with help,
from the args in the cmd struct:

xfs_spaceman> help prealloc
prealloc [-s] [-ugp id] [-m minlen] -- Control speculative preallocation

Control speculative preallocation

Options: [-s] [-ugp id] [-m minlen]


> +"\n"
> +" -s        -- wait for removal to complete\n"
> +" -u uid    -- remove prealloc on files matching user <uid>\n"
> +" -g gid    -- remove prealloc on files matching group <gid>\n"
> +" -p prid   -- remove prealloc on files matching project <prid>\n"
> +" -m minlen -- only consider files larger than <minlen>\n"

units?  (bytes?)  Probably should mention that suffixes are accepted,
in the manpage.

> +"\n"));

I guess with no -u, -g, or -p it removes speculative prealloc for all ids?
That'd be good to add to the manpage.

> +
> +}
> +
> +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] [-ugp id] [-m minlen]";

This help is a little confusing:

xfs_spaceman> prealloc -ugp 0
prealloc [-s] [-ugp id] [-m minlen] -- Control speculative preallocation

xfs_spaceman> prealloc -u 0 -g 0 -p 0
xfs_spaceman> 

should probably be:

prealloc [-s] [-u id] [-g id] [-p id] [-m minlen] -- Control speculative preallocation


> +	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 7b4f034..0ae3116 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -33,5 +33,6 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
>  
>  extern void	file_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] 29+ messages in thread

* Re: [PATCH 06/10] xfs_spaceman: add FITRIM support
  2017-06-02 19:51 ` [PATCH 06/10] xfs_spaceman: add FITRIM support Darrick J. Wong
@ 2017-06-14 15:32   ` Eric Sandeen
  2017-06-14 16:32     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 15:32 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/2/17 2:51 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>
> ---
>  spaceman/Makefile |    2 -
>  spaceman/init.c   |    1 
>  spaceman/space.h  |    1 
>  spaceman/trim.c   |  127 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 130 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 5dbaef2..0958377 100644
> --- a/spaceman/init.c
> +++ b/spaceman/init.c
> @@ -40,6 +40,7 @@ init_commands(void)
>  	file_init();
>  	help_init();
>  	quit_init();
> +	trim_init();
>  }
>  
>  static int
> diff --git a/spaceman/space.h b/spaceman/space.h
> index 6e1bc52..7b4f034 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -34,3 +34,4 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
>  extern void	file_init(void);
>  extern void	help_init(void);
>  extern void	quit_init(void);
> +extern void	trim_init(void);
> diff --git a/spaceman/trim.c b/spaceman/trim.c
> new file mode 100644
> index 0000000..d58f6d1
> --- /dev/null
> +++ b/spaceman/trim.c
> @@ -0,0 +1,127 @@
> +/*
> + * 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 = atoi(optarg);

(do we want to detect errors with strtol?)  *shrug*

> +			break;
> +		case 'f':
> +			fflag = 1;
> +			break;
> +		case 'm':
> +			minlen = cvtnum(file->geom.blocksize,
> +					file->geom.sectsize, argv[optind]);

s/argtv[optind]/optarg/ (see below)

> +			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"
> +"Options: [-m minlen] [-f]|[-a agno]|[offset length]\n"

Don't restate the Options, that comes out already from
the cmd args:

xfs_spaceman> help trim
trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space

Discard filesystem free space

Options: [-m minlen] [-f]|[-a agno]|[offset length]


> +"\n"
> +" -m minlen     -- skip freespace extents smaller than minlen\n"
> +" -f            -- trim all the freespace in the entire filesystem\n"
> +" -a agno       -- trim all the freespace in the given AG agno\n"
> +" offset length -- trim the freespace in the range {offset, length}\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] [-f]|[-a agno]|[offset length]";

I found myself wondering if any of these were default, and testing shows
that nope, one is of the last 3 or'd options is required.  Is the above
the standard way to represent that?

/me checks http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html

Meh, not sure.  If it were in a SYNOPSIS it would be 3 separate lines
for each of the 3 modes (-f / -a / offset length)

is:

trim_cmd.args = "-f|-a agno|offset length [-m minlen] ";

any better?  Not sure there is a standard way to show mutually exclusive
but required options on a single line.

> +	trim_cmd.flags = CMD_FLAG_ONESHOT;
> +	trim_cmd.oneline = _("Discard filesystem free space");
> +	trim_cmd.help = trim_help;
> +
> +	add_command(&trim_cmd);
> +}
> +

A little testing....

xfs_spaceman> trim -m 16k
Segmentation fault

(sadface)

ahah:
                case 'm':
                        minlen = cvtnum(file->geom.blocksize,
-                                       file->geom.sectsize, argv[optind]);
+                                       file->geom.sectsize, optarg);


Ok, also, am I forgetting how getopt works?

xfs_spaceman> trim -f -m
trim: option requires an argument -- 'm'
trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space

xfs_spaceman> trim -m -f
trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space
xfs_spaceman> 

getopt is interpreting "-f" as the required argument for "-m?"
I didn't know it did that ....

xfs_spaceman> trim -m -derp -f
xfs_spaceman: ioctl(FITRIM) ["mnt/"]: Invalid argument

hm maybe we do want something better than atoi()...

-Eric

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

* Re: [PATCH 08/10] xfs_spaceman: Free space mapping command
  2017-06-02 19:52 ` [PATCH 08/10] xfs_spaceman: Free space mapping command Darrick J. Wong
@ 2017-06-14 15:43   ` Eric Sandeen
  2017-06-14 16:43     ` Darrick J. Wong
  2017-06-14 16:04   ` Eric Sandeen
  1 sibling, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 15:43 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner

On 6/2/17 2:52 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>
> ---
>  spaceman/Makefile   |    7 +
>  spaceman/file.c     |   19 ++-
>  spaceman/freesp.c   |  367 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  spaceman/init.c     |    8 +
>  spaceman/prealloc.c |    1 
>  spaceman/space.h    |   12 +-
>  spaceman/trim.c     |    1 
>  7 files changed, 409 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 9356066..46b8826 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;
> @@ -69,8 +70,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;
>  
>  	fd = open(path, flags, mode);
> @@ -95,6 +98,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;
>  }
>  
> @@ -103,7 +116,8 @@ addfile(
>  	char		*name,
>  	int		fd,
>  	xfs_fsop_geom_t	*geometry,
> -	int		flags)
> +	int		flags,
> +	struct fs_path	*fs_path)
>  {
>  	char		*filename;
>  
> @@ -131,6 +145,7 @@ addfile(
>  	file->flags = flags;
>  	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..2290a5e
> --- /dev/null
> +++ b/spaceman/freesp.c
> @@ -0,0 +1,367 @@
> +/*
> + * 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"
> +
> +typedef struct histent
> +{
> +	int		low;
> +	int		high;
> +	long long	count;
> +	long long	blocks;
> +} histent_t;
> +
> +static int		agcount;
> +static xfs_agnumber_t	*aglist;
> +static int		dumpflag;
> +static int		equalsize;
> +static histent_t	*hist;
> +static int		histcount;
> +static int		multsize;
> +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(
> +	int	h)
> +{
> +	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)
> +{
> +	int		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(
> +	int	maxlen)
> +{
> +	int	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("%7d %7d %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)
> +{
> +	aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
> +	aglist[agcount] = (xfs_agnumber_t)atoi(a);
> +	agcount++;
> +}
> +
> +static int
> +init(
> +	int		argc,
> +	char		**argv)
> +{
> +	int		c;
> +	int		speced = 0;
> +
> +	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)
> +				return 0;
> +			multsize = 2;
> +			speced = 1;
> +			break;
> +		case 'd':
> +			dumpflag = 1;
> +			break;
> +		case 'e':
> +			if (speced)
> +				return 0;
> +			equalsize = atoi(optarg);
> +			speced = 1;
> +			break;
> +		case 'h':
> +			if (speced && !histcount)
> +				return 0;
> +			addhistent(atoi(optarg));
> +			speced = 1;
> +			break;
> +		case 'm':
> +			if (speced)
> +				return 0;
> +			multsize = atoi(optarg);
> +			speced = 1;
> +			break;
> +		case 'r':
> +			rtflag = true;
> +			break;
> +		case 's':
> +			summaryflag = 1;
> +			break;
> +		case '?':
> +			return 0;
> +		}
> +	}
> +	if (optind != argc)
> +		return 0;
> +	if (!speced)
> +		multsize = 2;
> +	histinit(file->geom.agblocks);
> +	return 1;
> +}
> +
> +/*
> + * 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"
> +"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"

don't restate options ...

> +"\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"));
> +
> +}
> +
> +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 = "[-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";

[-a agno]... right?  Multiple allowed?

> +	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 93a8a1e..f3c93c3 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;
> @@ -38,6 +39,7 @@ static void
>  init_commands(void)
>  {
>  	file_init();
> +	freesp_init();
>  	help_init();
>  	prealloc_init();
>  	quit_init();
> @@ -71,12 +73,14 @@ init(
>  	int		c, flags = 0;
>  	mode_t		mode = 0600;
>  	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':
> @@ -93,11 +97,11 @@ init(
>  	if (optind != argc - 1)
>  		usage();
>  
> -	if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
> +	if ((c = openfile(argv[optind], &geometry, flags, mode, &fsp)) < 0)
>  		exit(1);
>  	if (!platform_test_xfs_fd(c))
>  		printf(_("Not an XFS filesystem!\n"));
> -	if (addfile(argv[optind], c, &geometry, flags) < 0)
> +	if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
>  		exit(1);
>  
>  	init_commands();
> diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
> index 71c0d80..55b85ed 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 0ae3116..bf9a2df 100644
> --- a/spaceman/space.h
> +++ b/spaceman/space.h
> @@ -21,6 +21,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 */
> @@ -28,11 +29,18 @@ extern int		filecount;	/* number of open files */
>  extern fileio_t		*file;		/* active file in file table */
>  extern int filelist_f(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	file_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
> diff --git a/spaceman/trim.c b/spaceman/trim.c
> index d58f6d1..b5908cc 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] 29+ messages in thread

* Re: [PATCH 08/10] xfs_spaceman: Free space mapping command
  2017-06-02 19:52 ` [PATCH 08/10] xfs_spaceman: Free space mapping command Darrick J. Wong
  2017-06-14 15:43   ` Eric Sandeen
@ 2017-06-14 16:04   ` Eric Sandeen
  2017-06-14 16:57     ` Darrick J. Wong
  1 sibling, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 16:04 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs, Dave Chinner


On 6/2/17 2:52 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>


> +static int
> +init(
> +	int		argc,
> +	char		**argv)
> +{
> +	int		c;
> +	int		speced = 0;
> +
> +	agcount = dumpflag = equalsize = multsize = optind = 0;
> +	histcount = seen1 = summaryflag = 0;

ew, setting histcount to 0 (already done, right?  it's global?)
and checking it here but never setting it is a little confusing.
Maybe the comment below would help.

> +	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)
> +				return 0;
> +			multsize = 2;
> +			speced = 1;
> +			break;
> +		case 'd':
> +			dumpflag = 1;
> +			break;
> +		case 'e':
> +			if (speced)
> +				return 0;
> +			equalsize = atoi(optarg);
> +			speced = 1;
> +			break;
> +		case 'h':
> +			if (speced && !histcount)
> +				return 0;

maybe:			/* increments histcount */

> +			addhistent(atoi(optarg));
> +			speced = 1;
> +			break;
> +		case 'm':
> +			if (speced)
> +				return 0;
> +			multsize = atoi(optarg);
> +			speced = 1;
> +			break;
> +		case 'r':
> +			rtflag = true;
> +			break;
> +		case 's':
> +			summaryflag = 1;
> +			break;
> +		case '?':
> +			return 0;
> +		}
> +	}
> +	if (optind != argc)
> +		return 0;
> +	if (!speced)
> +		multsize = 2;

Manpage indicates that -b, -e, and -h are mutually exclusive, but that's
not enforced here (?)

Specifying more than one seems to break the command:

xfs_spaceman> freesp -b
   from      to extents  blocks    pct
      2       3       1       3   0.00
  32768   65536       4  259537 100.00
xfs_spaceman> freesp -e 4096
   from      to extents  blocks    pct
      1    4096       1       3   0.00
  61441   65536       4  259537 100.00
xfs_spaceman> freesp -e 4096 -b
xfs_spaceman> 

Oh I see, if more than one is "speced" init returns 0 (silently).

I'd document what speced is for, and I guess print a warning about
the exclusive nature of the 3 arguments in each case if re-spec'd
before returning.

And note the exclusive nature somehow in the help.
(hm, looks like -m is in this exclusive group too?)

in gneneral need to sanitize behavior w/ built in help as well
as manpage...


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

* Re: [PATCH 09/10] xfs_spaceman: add a man page
  2017-06-02 19:52 ` [PATCH 09/10] xfs_spaceman: add a man page Darrick J. Wong
@ 2017-06-14 16:06   ` Eric Sandeen
  2017-06-14 20:49     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 16:06 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/2/17 2:52 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>
> ---
>  man/man8/xfs_spaceman.8 |  102 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 102 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..c1d19c0
> --- /dev/null
> +++ b/man/man8/xfs_spaceman.8
> @@ -0,0 +1,102 @@
> +.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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"

Hmm this differs from program help:

freesp [-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult] 

are -b, -e, and -h mutually exclusive or not?

xfs_spaceman> freesp -b
   from      to extents  blocks    pct
      2       3       1       3   0.00
  32768   65536       4  259537 100.00
xfs_spaceman> freesp -e 4096
   from      to extents  blocks    pct
      1    4096       1       3   0.00
  61441   65536       4  259537 100.00
xfs_spaceman> freesp -e 4096 -b
xfs_spaceman> 

Also, where are -g and -d ?


[-a agno]...

(multiple allowed)

> +With no arguments,
> +.B freesp
> +shows a histogram of all free space extents in the filesystem.
> +The
> +.B -b
> +argument establishes that the histogram bins are successive powers of two.
> +This is the default.
> +The
> +.BR -h " and " -m
> +options can be used to specify a custom histogram bin size as well as a
> +multiplication factor for subsequent bin sizes.
> +The
> +.BR -e
> +option fixes the histogram size to a particular value.
> +The
> +.BR -a " and " -r
> +options constrain the free space information report to a particular AG
> +or the realtime device, respectively.  The
> +.B -a
> +option may be specified multiple times.
> +A summary of free space information will be printed if the
> +.B -s
> +option is given.
> +.TP
> +.BR "help [ " command " ]"
> +Display a brief description of one or all commands.
> +.TP
> +.BI "prealloc [ \-ugp id ] [ \-m minlen ] [ \-s ]"
> +Controls speculative preallocation.  The
> +.BR -u ","
> +.BR -g ","
> +and
> +.B -p
> +options will clear all speculative preallocations for a given user,
> +group, or project ID, respectively.
> +The
> +.B -m
> +option causes the operation to ignore any file with a size smaller than
> +.BR minlen "."
> +The
> +.B -s
> +option will flush all dirty data and metadata to disk.
> +.TP
> +.B print
> +Display a list of all open files.
> +.TP
> +.B quit
> +Exit
> +.BR xfs_spaceman .
> +.TP
> +.BI "trim [ \-f ] [ \-a agno ] [ \-m minlen ] [" " offset length " ]

Hm, this looks like -f / -a / offset/length are all optional, and not
mutually exclusive.

If this were in a SYNOPSIS section, it'd require 3 synopses I think.

Perhaps here it should be:

trim -f [ -m minlen ] [ offset length ]
trim -a agno [ -m minlen ] [ offset length ]
trim offset length [ -m minlen ]

as 3 separate invocation methods?

> +Instructs the underlying storage device to release all storage that may
> +be backing free space in the filesystem.
> +The
> +.B -f
> +option trims all free space in the entire filesystem.
> +The
> +.B -a
> +option trims only the free space in a given AG.
> +The
> +.B -m
> +option only trims free space extents that are longer than
> +.IR minlen "."

units?  (suffixes?)

> +The
> +.IR offset " and " length
> +arguments trim all free space between
> +.I offset
> +and
> +.IR "offset+length" "."

Units?

> +The
> +.BR -a " and " -f
> +options are mutually exclusive with each other as well as with the
> +.IR offset " and " length
> +arguments.
> 
> --
> 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] 29+ messages in thread

* Re: [PATCH 05/10] xfs_spaceman: space management tool
  2017-06-14 14:15   ` Eric Sandeen
@ 2017-06-14 16:16     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14 16:16 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-xfs, Dave Chinner

On Wed, Jun 14, 2017 at 09:15:49AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:51 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>
> > ---
> >  Makefile          |    3 +
> >  spaceman/Makefile |   33 ++++++++++++
> >  spaceman/file.c   |  149 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  spaceman/init.c   |  114 +++++++++++++++++++++++++++++++++++++++++
> >  spaceman/init.h   |   23 ++++++++
> >  spaceman/space.h  |   36 +++++++++++++
> >  6 files changed, 357 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..9356066
> > --- /dev/null
> > +++ b/spaceman/file.c
> > @@ -0,0 +1,149 @@
> > +/*
> > + * 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 (%s,%s,%s%s%s)\n"),
> > +		braces? '[' : ' ', index, braces? ']' : ' ', file->name,
> > +		file->flags & O_SYNC ? _("sync") : _("non-sync"),
> 
> Ok, my question last round about this didn't get answered:
> 
> [xfsprogs-fsmap]# spaceman/xfs_spaceman -c print mnt/
> [000] mnt/           (non-sync,non-direct,read-write)
> 
> [xfsprogs-fsmap]# mount -o remount,ro mnt
> 
> [xfsprogs-fsmap]# spaceman/xfs_spaceman -c print mnt/
> [000] mnt/           (non-sync,non-direct,read-write)
> 
> I don't see that these flags have any meaning; indeed, the flags argument
> to openfile() and addfile() is initialized to 0 and never changed.  Should
> it just be removed?
> 
> If you want to get things merged and clean stuff up with a later patch,
> that's ok by me, or we could remove them now.  It's not really a functionality
> problem so I'm not too bothered.

I don't think any of these flags matter for spaceman, I'll just remove
the fd flags reporting.

> > +		file->flags & O_DIRECT ? _("direct") : _("non-direct"),
> > +		file->flags & O_RDONLY ? _("read-only") : _("read-write"),

FWIW I'm not sure how this /ever/ works, since "#define O_RDONLY 0".

> > +		file->flags & O_APPEND ? _(",append-only") : "",
> > +		file->flags & O_NONBLOCK ? _(",non-block") : "");
> > +}
> > +
> > +int
> > +filelist_f(void)
> > +{
> > +	int		i;
> > +
> > +	for (i = 0; i < filecount; i++)
> > +		print_fileio(&filetable[i], i, &filetable[i] == file);
> > +	return 0;
> > +}
> > +
> > +static int
> > +print_f(
> > +	int		argc,
> > +	char		**argv)
> > +{
> > +	filelist_f();
> 
> No need for the separate filelist_f, just put the guts here, it has no other
> caller.
> 
> Hm, actually ... for consistency with xfs_io should we have both "file" and "print?"
> 
>        file [ N ]
>               Display a list of all open files and (optionally) switch to an alternate current open file.
> 
>        print  Display a list of all open files and memory mapped regions.  The current file and current mapping are distinguishable from  any others.
> 
> Again not overly concerned about this, just pointing it out - if it's modeled on xfs_io behavior, let's keep it as similar as possible.
> 
> (If this gets changed, don't forget the manpage)

I'll just get rid of the weird print_f -> filelist_f indirection since
spaceman doesn't have a 'file' command anymore anyway.

> 
> > +	return 0;
> > +}
> > +
> > +int
> > +openfile(
> > +	char		*path,
> > +	xfs_fsop_geom_t	*geom,
> > +	int		flags,
> 
> flags is always called with flags == 0, so all the flag testing below is pointless -
> and probably doesn't relate to filesystem-wide operations anyway.

I'll get rid of mode too, since we don't use it either.

> 
> > +	mode_t		mode)
> > +{
> > +	int		fd;
> > +
> > +	fd = open(path, flags, mode);
> > +	if (fd < 0) {
> > +		if ((errno == EISDIR) && (flags & O_RDWR)) {
> > +			/* make it as if we asked for O_RDONLY & try again */
> > +			flags &= ~O_RDWR;
> > +			flags |= O_RDONLY;
> > +			fd = open(path, flags, mode);
> > +			if (fd < 0) {
> > +				perror(path);
> > +				return -1;
> > +			}
> > +		} else {
> > +			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,
> > +	int		flags)
> > +{
> > +	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));
> 
> Are you keeping all the filetable stuff with the thought that maybe some 
> day we'll allow operation on multiple filesystems?  Probably a good idea.

Yes.

> > +	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->flags = flags;
> > +	file->name = filename;
> > +	file->geom = *geometry;
> > +	return 0;
> > +}
> > +
> > +void
> > +file_init(void)
> > +{
> 
> file_init setting up print_cmd is confusing - unless you want both ala xfs_io,
> I'd go consistently with either "print" or "file" but not a mishmash.

Ok, print_init it is.
> 
> > +	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..5dbaef2
> > --- /dev/null
> > +++ b/spaceman/init.c
> > @@ -0,0 +1,114 @@
> > +/*
> > + * 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)
> > +{
> > +	file_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, flags = 0;
> > +	mode_t		mode = 0600;
> > +	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, flags, mode)) < 0)
> > +		exit(1);
> > +	if (!platform_test_xfs_fd(c))
> > +		printf(_("Not an XFS filesystem!\n"));
> > +	if (addfile(argv[optind], c, &geometry, flags) < 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..165e4f5
> > --- /dev/null
> > +++ b/spaceman/init.h
> > @@ -0,0 +1,23 @@
> > +/*
> > + * 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
> > + */
> > +
> > +extern char	*progname;
> > +extern int	exitcode;
> > +
> > +#define min(a,b)	(((a)<(b))?(a):(b))
> > +#define max(a,b)	(((a)>(b))?(a):(b))
> 
> these aren't actually used, FWIW.

Deleted.

Also realizing that these header files don't have ifndef guards; will
add those too.

> > diff --git a/spaceman/space.h b/spaceman/space.h
> > new file mode 100644
> > index 0000000..6e1bc52
> > --- /dev/null
> > +++ b/spaceman/space.h
> > @@ -0,0 +1,36 @@
> > +/*
> > + * 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
> > + */
> > +
> > +typedef struct fileio {
> > +	int		fd;		/* open file descriptor */
> > +	int		flags;		/* flags describing file state */
> 
> This is never used, really - always 0.

Deleted.

--D

> 
> > +	char		*name;		/* file name at time of open */
> > +	xfs_fsop_geom_t	geom;		/* XFS filesystem geometry */
> > +} 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 filelist_f(void);
> > +
> > +extern int	openfile(char *, xfs_fsop_geom_t *, int, mode_t);
> > +extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
> > +
> > +extern void	file_init(void);
> > +extern void	help_init(void);
> > +extern void	quit_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] 29+ messages in thread

* Re: [PATCH 10/10] xfs_spaceman: add group summary mode
  2017-06-02 19:52 ` [PATCH 10/10] xfs_spaceman: add group summary mode Darrick J. Wong
@ 2017-06-14 16:23   ` Eric Sandeen
  2017-06-14 17:22     ` Darrick J. Wong
  0 siblings, 1 reply; 29+ messages in thread
From: Eric Sandeen @ 2017-06-14 16:23 UTC (permalink / raw)
  To: Darrick J. Wong, sandeen; +Cc: linux-xfs

On 6/2/17 2:52 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>
> ---
>  man/man8/xfs_spaceman.8 |    8 +++++++-
>  spaceman/freesp.c       |   29 +++++++++++++++++++++++++----
>  2 files changed, 32 insertions(+), 5 deletions(-)
> 
> 
> diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
> index c1d19c0..a57a0c3 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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
> +.BI "freesp [ \-srg ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
>  With no arguments,
>  .B freesp
>  shows a histogram of all free space extents in the filesystem.
> @@ -49,6 +49,12 @@ option may be specified multiple times.
>  A summary of free space information will be printed if the
>  .B -s
>  option is given.
> +The
> +.B -g
> +option prints a brief per-AG summary of the free space found in that AG.
> +If
> +.B -r
> +is specified it will also report on free space in the realtime device.

If you're reworking patches, the -r switch doc should probably go in the manpage
patch, not here, but *shrug*

>  .TP
>  .BR "help [ " command " ]"
>  Display a brief description of one or all commands.
> diff --git a/spaceman/freesp.c b/spaceman/freesp.c
> index 2290a5e..351f0ce 100644
> --- a/spaceman/freesp.c
> +++ b/spaceman/freesp.c
> @@ -42,6 +42,7 @@ static int		histcount;
>  static int		multsize;
>  static int		seen1;
>  static int		summaryflag;
> +static int		gflag;
>  static bool		rtflag;
>  static long long	totblocks;
>  static long long	totexts;
> @@ -159,6 +160,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;
>  
> @@ -211,6 +214,8 @@ scan_ag(
>  			agbno = (extent->fmr_physical - (bperag * agno)) /
>  								blocksize;
>  			aglen = extent->fmr_length / blocksize;
> +			freeblks += aglen;
> +			freeexts++;
>  
>  			addtohist(agno, agbno, aglen);
>  		}
> @@ -220,6 +225,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);
> +	}

ok so -g can be used with -a?

xfs_spaceman> freesp -g
        AG    extents     blocks
         0          2      65519
         1          1      65527
         2          1      62967
         3          1      65527
xfs_spaceman> freesp -a 0
        AG    extents     blocks
         0          2      65519
xfs_spaceman> freesp -a 0 -g
        AG    extents     blocks
         0          2      65519

Ok, so I guess "-g" is the same as "-a" for each AG?  Documenting it that
way might help.

Oh, wait, something is wrong here - global vars, danger danger?

xfs_spaceman> freesp
   from      to extents  blocks    pct
      1       1       1       1   0.00
  32768   65536       4  254826 100.00
xfs_spaceman> freesp -g
        AG    extents     blocks
         0          1      63479
         1          2      62854
         2          1      62967
         3          1      65527
xfs_spaceman> freesp
        AG    extents     blocks
         0          1      63479
         1          2      62854
         2          1      62967
         3          1      65527

*blink*

yeah, I think you need to set gflag = 0 in init().

(and scratch my earlier comment about histcount being init to zero so why
do it in init, now I see why) ;)



>  }
>  static void
>  aglistadd(
> @@ -244,7 +258,7 @@ init(
>  	aglist = NULL;
>  	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);
> @@ -264,6 +278,10 @@ init(
>  			equalsize = atoi(optarg);
>  			speced = 1;
>  			break;
> +		case 'g':
> +			histcount = 0;
> +			gflag++;
> +			break;
>  		case 'h':
>  			if (speced && !histcount)
>  				return 0;
> @@ -306,13 +324,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);
> @@ -334,12 +354,13 @@ freesp_help(void)
>  "\n"
>  "Examine filesystem free space\n"
>  "\n"
> -"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"
> +"Options: [-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"

(drop the options re-specification in the long help)

>  "\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"
> +" -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"
> @@ -357,7 +378,7 @@ freesp_init(void)
>  	freesp_cmd.cfunc = freesp_f;
>  	freesp_cmd.argmin = 0;
>  	freesp_cmd.argmax = -1;
> -	freesp_cmd.args = "[-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";
> +	freesp_cmd.args = "[-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";

(make clear which ones are mutually exclusive)

>  	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] 29+ messages in thread

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

On Wed, Jun 14, 2017 at 10:32:53AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:51 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>
> > ---
> >  spaceman/Makefile |    2 -
> >  spaceman/init.c   |    1 
> >  spaceman/space.h  |    1 
> >  spaceman/trim.c   |  127 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 130 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 5dbaef2..0958377 100644
> > --- a/spaceman/init.c
> > +++ b/spaceman/init.c
> > @@ -40,6 +40,7 @@ init_commands(void)
> >  	file_init();
> >  	help_init();
> >  	quit_init();
> > +	trim_init();
> >  }
> >  
> >  static int
> > diff --git a/spaceman/space.h b/spaceman/space.h
> > index 6e1bc52..7b4f034 100644
> > --- a/spaceman/space.h
> > +++ b/spaceman/space.h
> > @@ -34,3 +34,4 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
> >  extern void	file_init(void);
> >  extern void	help_init(void);
> >  extern void	quit_init(void);
> > +extern void	trim_init(void);
> > diff --git a/spaceman/trim.c b/spaceman/trim.c
> > new file mode 100644
> > index 0000000..d58f6d1
> > --- /dev/null
> > +++ b/spaceman/trim.c
> > @@ -0,0 +1,127 @@
> > +/*
> > + * 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 = atoi(optarg);
> 
> (do we want to detect errors with strtol?)  *shrug*

strtoul, since it's an AG number.

Some of the xfsprogs code still uses atoi, maybe they ought to be converted?

$ grep atoi.*optarg */*.c -n
db/freesp.c:170:                        equalsize = atoi(optarg);
db/freesp.c:176:                        addhistent(atoi(optarg));
db/freesp.c:182:                        multsize = atoi(optarg);
fsr/xfs_fsr.c:273:                      howlong = atoi(optarg);
fsr/xfs_fsr.c:282:                      argv_blksz_dio = atoi(optarg);
fsr/xfs_fsr.c:285:                      npasses = atoi(optarg);
fsr/xfs_fsr.c:290:                              nfrags = atoi(optarg);
growfs/xfs_growfs.c:169:                        maxpct = atoi(optarg);
io/bmap.c:97:                   nflag = atoi(optarg);
io/fiemap.c:230:                        max_extents = atoi(optarg);
io/fsmap.c:379:                 nflag = atoi(optarg);
logprint/logprint.c:197:                                print_start = atoi(optarg);
quota/project.c:290:                    recurse_depth = atoi(optarg);
quota/report.c:225:                     lower = (uint)atoi(optarg);
quota/report.c:228:                     upper = (uint)atoi(optarg);
quota/report.c:717:                     lower = (uint)atoi(optarg);
quota/report.c:721:                     upper = (uint)atoi(optarg);
rtcp/xfs_rtcp.c:49:                     extsize = atoi(optarg);

Separate patch, though...

> 
> > +			break;
> > +		case 'f':
> > +			fflag = 1;
> > +			break;
> > +		case 'm':
> > +			minlen = cvtnum(file->geom.blocksize,
> > +					file->geom.sectsize, argv[optind]);
> 
> s/argtv[optind]/optarg/ (see below)

Fixed.

> 
> > +			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"
> > +"Options: [-m minlen] [-f]|[-a agno]|[offset length]\n"
> 
> Don't restate the Options, that comes out already from
> the cmd args:
> 
> xfs_spaceman> help trim
> trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space
> 
> Discard filesystem free space
> 
> Options: [-m minlen] [-f]|[-a agno]|[offset length]

Fixed.

> > +"\n"
> > +" -m minlen     -- skip freespace extents smaller than minlen\n"
> > +" -f            -- trim all the freespace in the entire filesystem\n"
> > +" -a agno       -- trim all the freespace in the given AG agno\n"
> > +" offset length -- trim the freespace in the range {offset, length}\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] [-f]|[-a agno]|[offset length]";
> 
> I found myself wondering if any of these were default, and testing shows
> that nope, one is of the last 3 or'd options is required.  Is the above
> the standard way to represent that?
> 
> /me checks http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
> 
> Meh, not sure.  If it were in a SYNOPSIS it would be 3 separate lines
> for each of the 3 modes (-f / -a / offset length)
> 
> is:
> 
> trim_cmd.args = "-f|-a agno|offset length [-m minlen] ";
> 
> any better?  Not sure there is a standard way to show mutually exclusive
> but required options on a single line.

ISTR seeing (in DOS land) people using:

(-a agno|-f|offset length)

to communicate that exactly one of the options has to be specified.
It's familiar at least to the extent that egrep uses the same syntax
to find any of the three options, and unfamiliar in that we're using
it here to mean XOR whereas egrep uses it for OR.

For now I'll amend the help text to state that exactly one of -a, -f,
or the offset/length pair are required.

> > +	trim_cmd.flags = CMD_FLAG_ONESHOT;
> > +	trim_cmd.oneline = _("Discard filesystem free space");
> > +	trim_cmd.help = trim_help;
> > +
> > +	add_command(&trim_cmd);
> > +}
> > +
> 
> A little testing....
> 
> xfs_spaceman> trim -m 16k
> Segmentation fault
> 
> (sadface)
> 
> ahah:
>                 case 'm':
>                         minlen = cvtnum(file->geom.blocksize,
> -                                       file->geom.sectsize, argv[optind]);
> +                                       file->geom.sectsize, optarg);

Fixed.

> Ok, also, am I forgetting how getopt works?
> 
> xfs_spaceman> trim -f -m
> trim: option requires an argument -- 'm'
> trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space
> 
> xfs_spaceman> trim -m -f
> trim [-m minlen] [-f]|[-a agno]|[offset length] -- Discard filesystem free space
> xfs_spaceman> 
> 
> getopt is interpreting "-f" as the required argument for "-m?"
> I didn't know it did that ....

Yep, it does that.  getopt("f:m") means that the next arg after -f is sucked
in as -f's argument even if it matches something else in the getopt string.
For all it knows, -m /is/ a valid input to -f.

> xfs_spaceman> trim -m -derp -f
> xfs_spaceman: ioctl(FITRIM) ["mnt/"]: Invalid argument
> 
> hm maybe we do want something better than atoi()...

strtoul, as mentioned above.

--D

> 
> -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] 29+ messages in thread

* Re: [PATCH 07/10] xfs_spaceman: add new speculative prealloc control
  2017-06-14 15:05   ` Eric Sandeen
@ 2017-06-14 16:39     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14 16:39 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs, Dave Chinner

On Wed, Jun 14, 2017 at 10:05:33AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:51 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>
> > ---
> >  spaceman/Makefile   |    2 -
> >  spaceman/init.c     |    1 
> >  spaceman/prealloc.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  spaceman/space.h    |    1 
> >  4 files changed, 114 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 0958377..93a8a1e 100644
> > --- a/spaceman/init.c
> > +++ b/spaceman/init.c
> > @@ -39,6 +39,7 @@ init_commands(void)
> >  {
> >  	file_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..71c0d80
> > --- /dev/null
> > +++ b/spaceman/prealloc.c
> > @@ -0,0 +1,111 @@
> > +/*
> > + * 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 = atoi(optarg);
> > +			break;
> > +		case 'u':
> > +			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
> > +			eofb.eof_uid = atoi(optarg);
> > +			break;
> > +		case 'p':
> > +			eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
> > +			eofb.eof_prid = atoi(optarg);
> > +			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"
> > +"Options: [-s] [-ugp id] [-m minlen]\n"
> 
> No need to restate Options: because that comes out already with help,
> from the args in the cmd struct:
> 
> xfs_spaceman> help prealloc
> prealloc [-s] [-ugp id] [-m minlen] -- Control speculative preallocation
> 
> Control speculative preallocation
> 
> Options: [-s] [-ugp id] [-m minlen]
> 
> 
> > +"\n"
> > +" -s        -- wait for removal to complete\n"
> > +" -u uid    -- remove prealloc on files matching user <uid>\n"
> > +" -g gid    -- remove prealloc on files matching group <gid>\n"
> > +" -p prid   -- remove prealloc on files matching project <prid>\n"
> > +" -m minlen -- only consider files larger than <minlen>\n"
> 
> units?  (bytes?)  Probably should mention that suffixes are accepted,
> in the manpage.
> 
> > +"\n"));
> 
> I guess with no -u, -g, or -p it removes speculative prealloc for all ids?
> That'd be good to add to the manpage.

Will do.

> > +
> > +}
> > +
> > +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] [-ugp id] [-m minlen]";
> 
> This help is a little confusing:
> 
> xfs_spaceman> prealloc -ugp 0
> prealloc [-s] [-ugp id] [-m minlen] -- Control speculative preallocation
> 
> xfs_spaceman> prealloc -u 0 -g 0 -p 0
> xfs_spaceman> 
> 
> should probably be:
> 
> prealloc [-s] [-u id] [-g id] [-p id] [-m minlen] -- Control speculative preallocation

Fixed.

--D

> > +	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 7b4f034..0ae3116 100644
> > --- a/spaceman/space.h
> > +++ b/spaceman/space.h
> > @@ -33,5 +33,6 @@ extern int	addfile(char *, int , xfs_fsop_geom_t *, int);
> >  
> >  extern void	file_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
> > 
> --
> 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] 29+ messages in thread

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

On Wed, Jun 14, 2017 at 10:43:06AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:52 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>
> > ---
> >  spaceman/Makefile   |    7 +
> >  spaceman/file.c     |   19 ++-
> >  spaceman/freesp.c   |  367 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  spaceman/init.c     |    8 +
> >  spaceman/prealloc.c |    1 
> >  spaceman/space.h    |   12 +-
> >  spaceman/trim.c     |    1 
> >  7 files changed, 409 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 9356066..46b8826 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;
> > @@ -69,8 +70,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;
> >  
> >  	fd = open(path, flags, mode);
> > @@ -95,6 +98,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;
> >  }
> >  
> > @@ -103,7 +116,8 @@ addfile(
> >  	char		*name,
> >  	int		fd,
> >  	xfs_fsop_geom_t	*geometry,
> > -	int		flags)
> > +	int		flags,
> > +	struct fs_path	*fs_path)
> >  {
> >  	char		*filename;
> >  
> > @@ -131,6 +145,7 @@ addfile(
> >  	file->flags = flags;
> >  	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..2290a5e
> > --- /dev/null
> > +++ b/spaceman/freesp.c
> > @@ -0,0 +1,367 @@
> > +/*
> > + * 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"
> > +
> > +typedef struct histent
> > +{
> > +	int		low;
> > +	int		high;
> > +	long long	count;
> > +	long long	blocks;
> > +} histent_t;
> > +
> > +static int		agcount;
> > +static xfs_agnumber_t	*aglist;
> > +static int		dumpflag;
> > +static int		equalsize;
> > +static histent_t	*hist;
> > +static int		histcount;
> > +static int		multsize;
> > +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(
> > +	int	h)
> > +{
> > +	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)
> > +{
> > +	int		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(
> > +	int	maxlen)
> > +{
> > +	int	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("%7d %7d %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)
> > +{
> > +	aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
> > +	aglist[agcount] = (xfs_agnumber_t)atoi(a);
> > +	agcount++;
> > +}
> > +
> > +static int
> > +init(
> > +	int		argc,
> > +	char		**argv)
> > +{
> > +	int		c;
> > +	int		speced = 0;
> > +
> > +	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)
> > +				return 0;
> > +			multsize = 2;
> > +			speced = 1;
> > +			break;
> > +		case 'd':
> > +			dumpflag = 1;
> > +			break;
> > +		case 'e':
> > +			if (speced)
> > +				return 0;
> > +			equalsize = atoi(optarg);
> > +			speced = 1;
> > +			break;
> > +		case 'h':
> > +			if (speced && !histcount)
> > +				return 0;
> > +			addhistent(atoi(optarg));
> > +			speced = 1;
> > +			break;
> > +		case 'm':
> > +			if (speced)
> > +				return 0;
> > +			multsize = atoi(optarg);
> > +			speced = 1;
> > +			break;
> > +		case 'r':
> > +			rtflag = true;
> > +			break;
> > +		case 's':
> > +			summaryflag = 1;
> > +			break;
> > +		case '?':
> > +			return 0;
> > +		}
> > +	}
> > +	if (optind != argc)
> > +		return 0;
> > +	if (!speced)
> > +		multsize = 2;
> > +	histinit(file->geom.agblocks);
> > +	return 1;
> > +}
> > +
> > +/*
> > + * 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"
> > +"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"
> 
> don't restate options ...

Fixed.

> > +"\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"));
> > +
> > +}
> > +
> > +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 = "[-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";
> 
> [-a agno]... right?  Multiple allowed?

Yes, and fixed.

--D

> > +	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 93a8a1e..f3c93c3 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;
> > @@ -38,6 +39,7 @@ static void
> >  init_commands(void)
> >  {
> >  	file_init();
> > +	freesp_init();
> >  	help_init();
> >  	prealloc_init();
> >  	quit_init();
> > @@ -71,12 +73,14 @@ init(
> >  	int		c, flags = 0;
> >  	mode_t		mode = 0600;
> >  	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':
> > @@ -93,11 +97,11 @@ init(
> >  	if (optind != argc - 1)
> >  		usage();
> >  
> > -	if ((c = openfile(argv[optind], &geometry, flags, mode)) < 0)
> > +	if ((c = openfile(argv[optind], &geometry, flags, mode, &fsp)) < 0)
> >  		exit(1);
> >  	if (!platform_test_xfs_fd(c))
> >  		printf(_("Not an XFS filesystem!\n"));
> > -	if (addfile(argv[optind], c, &geometry, flags) < 0)
> > +	if (addfile(argv[optind], c, &geometry, flags, &fsp) < 0)
> >  		exit(1);
> >  
> >  	init_commands();
> > diff --git a/spaceman/prealloc.c b/spaceman/prealloc.c
> > index 71c0d80..55b85ed 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 0ae3116..bf9a2df 100644
> > --- a/spaceman/space.h
> > +++ b/spaceman/space.h
> > @@ -21,6 +21,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 */
> > @@ -28,11 +29,18 @@ extern int		filecount;	/* number of open files */
> >  extern fileio_t		*file;		/* active file in file table */
> >  extern int filelist_f(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	file_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
> > diff --git a/spaceman/trim.c b/spaceman/trim.c
> > index d58f6d1..b5908cc 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
> > 
> --
> 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] 29+ messages in thread

* Re: [PATCH 08/10] xfs_spaceman: Free space mapping command
  2017-06-14 16:04   ` Eric Sandeen
@ 2017-06-14 16:57     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14 16:57 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs, Dave Chinner

On Wed, Jun 14, 2017 at 11:04:11AM -0500, Eric Sandeen wrote:
> 
> On 6/2/17 2:52 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>
> 
> 
> > +static int
> > +init(
> > +	int		argc,
> > +	char		**argv)
> > +{
> > +	int		c;
> > +	int		speced = 0;
> > +
> > +	agcount = dumpflag = equalsize = multsize = optind = 0;
> > +	histcount = seen1 = summaryflag = 0;
> 
> ew, setting histcount to 0 (already done, right?  it's global?)
> and checking it here but never setting it is a little confusing.

Yes, I'll get rid of the redundant initializers.

> Maybe the comment below would help.

Ok.

> > +	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)
> > +				return 0;
> > +			multsize = 2;
> > +			speced = 1;
> > +			break;
> > +		case 'd':
> > +			dumpflag = 1;
> > +			break;
> > +		case 'e':
> > +			if (speced)
> > +				return 0;
> > +			equalsize = atoi(optarg);
> > +			speced = 1;
> > +			break;
> > +		case 'h':
> > +			if (speced && !histcount)
> > +				return 0;
> 
> maybe:			/* increments histcount */

Fixed.

> > +			addhistent(atoi(optarg));
> > +			speced = 1;
> > +			break;
> > +		case 'm':
> > +			if (speced)
> > +				return 0;
> > +			multsize = atoi(optarg);
> > +			speced = 1;
> > +			break;
> > +		case 'r':
> > +			rtflag = true;
> > +			break;
> > +		case 's':
> > +			summaryflag = 1;
> > +			break;
> > +		case '?':
> > +			return 0;
> > +		}
> > +	}
> > +	if (optind != argc)
> > +		return 0;
> > +	if (!speced)
> > +		multsize = 2;
> 
> Manpage indicates that -b, -e, and -h are mutually exclusive, but that's
> not enforced here (?)
> 
> Specifying more than one seems to break the command:
> 
> xfs_spaceman> freesp -b
>    from      to extents  blocks    pct
>       2       3       1       3   0.00
>   32768   65536       4  259537 100.00
> xfs_spaceman> freesp -e 4096
>    from      to extents  blocks    pct
>       1    4096       1       3   0.00
>   61441   65536       4  259537 100.00
> xfs_spaceman> freesp -e 4096 -b
> xfs_spaceman> 
> 
> Oh I see, if more than one is "speced" init returns 0 (silently).
> 
> I'd document what speced is for, and I guess print a warning about
> the exclusive nature of the 3 arguments in each case if re-spec'd
> before returning.

I made a note of that in the help screen and redirected all the overspec
cases to print the help screen instead of silently returning.

> And note the exclusive nature somehow in the help.
> (hm, looks like -m is in this exclusive group too?)
> 
> in gneneral need to sanitize behavior w/ built in help as well
> as manpage...

Ok.

--D

> --
> 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] 29+ messages in thread

* Re: [PATCH 10/10] xfs_spaceman: add group summary mode
  2017-06-14 16:23   ` Eric Sandeen
@ 2017-06-14 17:22     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14 17:22 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 14, 2017 at 11:23:51AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:52 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>
> > ---
> >  man/man8/xfs_spaceman.8 |    8 +++++++-
> >  spaceman/freesp.c       |   29 +++++++++++++++++++++++++----
> >  2 files changed, 32 insertions(+), 5 deletions(-)
> > 
> > 
> > diff --git a/man/man8/xfs_spaceman.8 b/man/man8/xfs_spaceman.8
> > index c1d19c0..a57a0c3 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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
> > +.BI "freesp [ \-srg ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
> >  With no arguments,
> >  .B freesp
> >  shows a histogram of all free space extents in the filesystem.
> > @@ -49,6 +49,12 @@ option may be specified multiple times.
> >  A summary of free space information will be printed if the
> >  .B -s
> >  option is given.
> > +The
> > +.B -g
> > +option prints a brief per-AG summary of the free space found in that AG.
> > +If
> > +.B -r
> > +is specified it will also report on free space in the realtime device.
> 
> If you're reworking patches, the -r switch doc should probably go in the manpage
> patch, not here, but *shrug*

<shrug> I was mostly re

> 
> >  .TP
> >  .BR "help [ " command " ]"
> >  Display a brief description of one or all commands.
> > diff --git a/spaceman/freesp.c b/spaceman/freesp.c
> > index 2290a5e..351f0ce 100644
> > --- a/spaceman/freesp.c
> > +++ b/spaceman/freesp.c
> > @@ -42,6 +42,7 @@ static int		histcount;
> >  static int		multsize;
> >  static int		seen1;
> >  static int		summaryflag;
> > +static int		gflag;
> >  static bool		rtflag;
> >  static long long	totblocks;
> >  static long long	totexts;
> > @@ -159,6 +160,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;
> >  
> > @@ -211,6 +214,8 @@ scan_ag(
> >  			agbno = (extent->fmr_physical - (bperag * agno)) /
> >  								blocksize;
> >  			aglen = extent->fmr_length / blocksize;
> > +			freeblks += aglen;
> > +			freeexts++;
> >  
> >  			addtohist(agno, agbno, aglen);
> >  		}
> > @@ -220,6 +225,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);
> > +	}
> 
> ok so -g can be used with -a?
> 
> xfs_spaceman> freesp -g
>         AG    extents     blocks
>          0          2      65519
>          1          1      65527
>          2          1      62967
>          3          1      65527
> xfs_spaceman> freesp -a 0
>         AG    extents     blocks
>          0          2      65519
> xfs_spaceman> freesp -a 0 -g
>         AG    extents     blocks
>          0          2      65519
> 
> Ok, so I guess "-g" is the same as "-a" for each AG?  Documenting it that
> way might help.

Well, -g is "tell us about each AG individually" whereas the -a options
together mean "tell us about this specific subset of AGs".

We can make -g and -a mutually exclusive with each other.

> Oh, wait, something is wrong here - global vars, danger danger?
> 
> xfs_spaceman> freesp
>    from      to extents  blocks    pct
>       1       1       1       1   0.00
>   32768   65536       4  254826 100.00
> xfs_spaceman> freesp -g
>         AG    extents     blocks
>          0          1      63479
>          1          2      62854
>          2          1      62967
>          3          1      65527
> xfs_spaceman> freesp
>         AG    extents     blocks
>          0          1      63479
>          1          2      62854
>          2          1      62967
>          3          1      65527
> 
> *blink*
> 
> yeah, I think you need to set gflag = 0 in init().
> 
> (and scratch my earlier comment about histcount being init to zero so why
> do it in init, now I see why) ;)

Heh.  Yeah, I noticed that when I was fiddling with the patches.

> >  }
> >  static void
> >  aglistadd(
> > @@ -244,7 +258,7 @@ init(
> >  	aglist = NULL;
> >  	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);
> > @@ -264,6 +278,10 @@ init(
> >  			equalsize = atoi(optarg);
> >  			speced = 1;
> >  			break;
> > +		case 'g':
> > +			histcount = 0;
> > +			gflag++;
> > +			break;
> >  		case 'h':
> >  			if (speced && !histcount)
> >  				return 0;
> > @@ -306,13 +324,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);
> > @@ -334,12 +354,13 @@ freesp_help(void)
> >  "\n"
> >  "Examine filesystem free space\n"
> >  "\n"
> > -"Options: [-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"
> > +"Options: [-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]\n"
> 
> (drop the options re-specification in the long help)

Ok.

> >  "\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"
> > +" -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"
> > @@ -357,7 +378,7 @@ freesp_init(void)
> >  	freesp_cmd.cfunc = freesp_f;
> >  	freesp_cmd.argmin = 0;
> >  	freesp_cmd.argmax = -1;
> > -	freesp_cmd.args = "[-bdsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";
> > +	freesp_cmd.args = "[-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult]";
> 
> (make clear which ones are mutually exclusive)

Ok.

--D

> 
> >  	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] 29+ messages in thread

* Re: [PATCH 09/10] xfs_spaceman: add a man page
  2017-06-14 16:06   ` Eric Sandeen
@ 2017-06-14 20:49     ` Darrick J. Wong
  0 siblings, 0 replies; 29+ messages in thread
From: Darrick J. Wong @ 2017-06-14 20:49 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: sandeen, linux-xfs

On Wed, Jun 14, 2017 at 11:06:44AM -0500, Eric Sandeen wrote:
> On 6/2/17 2:52 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>
> > ---
> >  man/man8/xfs_spaceman.8 |  102 +++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 102 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..c1d19c0
> > --- /dev/null
> > +++ b/man/man8/xfs_spaceman.8
> > @@ -0,0 +1,102 @@
> > +.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 [ \-sr ] [ \-b | \-e bsize | \-h h1 [ \-m bmult ]] [-a agno]"
> 
> Hmm this differs from program help:
> 
> freesp [-bdgsr] [-a agno] [-e bsize] [-h h1]... [-m bmult] 
> 
> are -b, -e, and -h mutually exclusive or not?
> 
> xfs_spaceman> freesp -b
>    from      to extents  blocks    pct
>       2       3       1       3   0.00
>   32768   65536       4  259537 100.00
> xfs_spaceman> freesp -e 4096
>    from      to extents  blocks    pct
>       1    4096       1       3   0.00
>   61441   65536       4  259537 100.00
> xfs_spaceman> freesp -e 4096 -b
> xfs_spaceman> 
> 
> Also, where are -g and -d ?

-g is in the next patch.

-d needs to be added.
> 
> 
> [-a agno]...
> 
> (multiple allowed)

Oh heck this manpage is so hard to read I'm going to reflow the whole
section to be easier on the eyes:

freesp [ -drs ] [-a agno] [ -b | -e bsize | -h bsize | -m factor ]
      With  no  arguments,  freesp shows a histogram of all free space
      extents in the filesystem.   The  command  takes  the  following
      options:

	 -a agno
	     Collect  free  space  information  from  this  allocation
	     group.  This option can be specified  multiple  times  to
	     collect from multiple groups.

	 -b  This  argument  establishes  that the histogram bin sizes
	     are successive powers of two.  This is the  default,  and
	     is mutually exclusive with the -e, -h, and -m options.

	 -d  Print  debugging  information  such as the raw free space
	     extent information.

	 -e bsize
	     Set all histogram bin sizes to a  specific  value.   This
	     option  is  mutually  exclusive  with  the -b, -h, and -m
	     options.

	 -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
	     -b, -e, and -m options.

	 -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 -b, -e, and -h options.

	 -r  Query the realtime device for free space information.

	 -s  Display a summary of the free space information found.


> > +With no arguments,
> > +.B freesp
> > +shows a histogram of all free space extents in the filesystem.
> > +The
> > +.B -b
> > +argument establishes that the histogram bins are successive powers of two.
> > +This is the default.
> > +The
> > +.BR -h " and " -m
> > +options can be used to specify a custom histogram bin size as well as a
> > +multiplication factor for subsequent bin sizes.
> > +The
> > +.BR -e
> > +option fixes the histogram size to a particular value.
> > +The
> > +.BR -a " and " -r
> > +options constrain the free space information report to a particular AG
> > +or the realtime device, respectively.  The
> > +.B -a
> > +option may be specified multiple times.
> > +A summary of free space information will be printed if the
> > +.B -s
> > +option is given.
> > +.TP
> > +.BR "help [ " command " ]"
> > +Display a brief description of one or all commands.
> > +.TP
> > +.BI "prealloc [ \-ugp id ] [ \-m minlen ] [ \-s ]"
> > +Controls speculative preallocation.  The
> > +.BR -u ","
> > +.BR -g ","
> > +and
> > +.B -p
> > +options will clear all speculative preallocations for a given user,
> > +group, or project ID, respectively.
> > +The
> > +.B -m
> > +option causes the operation to ignore any file with a size smaller than
> > +.BR minlen "."
> > +The
> > +.B -s
> > +option will flush all dirty data and metadata to disk.
> > +.TP
> > +.B print
> > +Display a list of all open files.
> > +.TP
> > +.B quit
> > +Exit
> > +.BR xfs_spaceman .
> > +.TP
> > +.BI "trim [ \-f ] [ \-a agno ] [ \-m minlen ] [" " offset length " ]
> 
> Hm, this looks like -f / -a / offset/length are all optional, and not
> mutually exclusive.
> 
> If this were in a SYNOPSIS section, it'd require 3 synopses I think.
> 
> Perhaps here it should be:
> 
> trim -f [ -m minlen ] [ offset length ]
> trim -a agno [ -m minlen ] [ offset length ]
> trim offset length [ -m minlen ]
> 
> as 3 separate invocation methods?

Only problem is that if we add general options in the future we'll have
to add them to all three invocations.

trim ( -a agno | -f | offset length ) [ -m minlen ]

How about that instead?

> 
> > +Instructs the underlying storage device to release all storage that may
> > +be backing free space in the filesystem.
> > +The
> > +.B -f
> > +option trims all free space in the entire filesystem.
> > +The
> > +.B -a
> > +option trims only the free space in a given AG.
> > +The
> > +.B -m
> > +option only trims free space extents that are longer than
> > +.IR minlen "."
> 
> units?  (suffixes?)

I'll add that.

> > +The
> > +.IR offset " and " length
> > +arguments trim all free space between
> > +.I offset
> > +and
> > +.IR "offset+length" "."
> 
> Units?

and that.

--D

> 
> > +The
> > +.BR -a " and " -f
> > +options are mutually exclusive with each other as well as with the
> > +.IR offset " and " length
> > +arguments.
> > 
> > --
> > 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] 29+ messages in thread

end of thread, other threads:[~2017-06-14 20:49 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-02 19:51 [PATCH v8 00/10] xfsprogs 4.12: GETFSMAP support Darrick J. Wong
2017-06-02 19:51 ` [PATCH 01/10] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
2017-06-02 19:51 ` [PATCH 02/10] xfs_io: refactor numlen into a library function Darrick J. Wong
2017-06-13 21:09   ` Eric Sandeen
2017-06-02 19:51 ` [PATCH 03/10] xfs_io: support the new getfsmap ioctl Darrick J. Wong
2017-06-13 22:20   ` Eric Sandeen
2017-06-14  0:23     ` Darrick J. Wong
2017-06-02 19:51 ` [PATCH 04/10] xfs_repair: replace rmap_compare with libxfs version Darrick J. Wong
2017-06-13 22:34   ` Eric Sandeen
2017-06-02 19:51 ` [PATCH 05/10] xfs_spaceman: space management tool Darrick J. Wong
2017-06-14 14:15   ` Eric Sandeen
2017-06-14 16:16     ` Darrick J. Wong
2017-06-02 19:51 ` [PATCH 06/10] xfs_spaceman: add FITRIM support Darrick J. Wong
2017-06-14 15:32   ` Eric Sandeen
2017-06-14 16:32     ` Darrick J. Wong
2017-06-02 19:51 ` [PATCH 07/10] xfs_spaceman: add new speculative prealloc control Darrick J. Wong
2017-06-14 15:05   ` Eric Sandeen
2017-06-14 16:39     ` Darrick J. Wong
2017-06-02 19:52 ` [PATCH 08/10] xfs_spaceman: Free space mapping command Darrick J. Wong
2017-06-14 15:43   ` Eric Sandeen
2017-06-14 16:43     ` Darrick J. Wong
2017-06-14 16:04   ` Eric Sandeen
2017-06-14 16:57     ` Darrick J. Wong
2017-06-02 19:52 ` [PATCH 09/10] xfs_spaceman: add a man page Darrick J. Wong
2017-06-14 16:06   ` Eric Sandeen
2017-06-14 20:49     ` Darrick J. Wong
2017-06-02 19:52 ` [PATCH 10/10] xfs_spaceman: add group summary mode Darrick J. Wong
2017-06-14 16:23   ` Eric Sandeen
2017-06-14 17:22     ` 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.