All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/3] getfsmapx ioctl
@ 2016-05-05 19:47 ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:47 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams, Darrick J. Wong
  Cc: linux-fsdevel, linux-api, xfs, linux-btrfs, linux-ext4

Hi,

Building on the discussion "Exposing Extent Information to Userspace"
at LSF, this patchset offers the userspace definition, implementation,
and manpages for a new FS_IOC_GETFSMAPX ioctl that enables userspace
to query the filesystem for a map of every extent in a given range of
physical block keyspace.

Note that prior to the existence of block sharing, I'd have said
"given range of physical blocks", but now that we can return multiple
owner:offset pairs for a given block, the block keyspace now has to
include extra fields to uniquely identify a reverse mapping record.

This ioctl behaves in a similar manner to XFS_IOC_GETBMAPX -- pass in
an array of struct getfsmapx with key and other control values in the
first two array elements, and the kernel passes back extent
information in the other array elements.  The particulars of how to do
this are documented in the manpage that goes along with this set (it
applies against man-pages.git) and example code in the other patches
is against xfsprogs.git#for-next.

Basically, set the lowest key for which you want records in the first
array element; the highest key in the second; and the kernel spits out
records in the rest of the elements.  That's similar to how GETBMAPX
does it, but different from FIEMAP.  I added a dummy 64-bit "device
id" per Josef's request, though I'm thinking that could be cut down to
a simple dev_t.  I also wonder if the kernel should rewrite the low
key with the last element returned so as to seed the next call, but
userspace can do that too.

The kernel-space implementation (for XFS) is buried inside the xfs
reverse mapping patchset which is treading water at github[1].  I
prefer not to patchbomb the whole kernel series until I've put the
mess through better testing, but this should be enough to get the
mailing list discussion started.

Questions?  Comments?  Bike sheds?

--D

[1] https://github.com/djwong/linux/tree/djwong-experimental

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

* [RFC 0/3] getfsmapx ioctl
@ 2016-05-05 19:47 ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:47 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams, Darrick J. Wong
  Cc: linux-fsdevel, linux-api-u79uwXL29TY76Z2rM5mHXA, xfs,
	linux-btrfs, linux-ext4

Hi,

Building on the discussion "Exposing Extent Information to Userspace"
at LSF, this patchset offers the userspace definition, implementation,
and manpages for a new FS_IOC_GETFSMAPX ioctl that enables userspace
to query the filesystem for a map of every extent in a given range of
physical block keyspace.

Note that prior to the existence of block sharing, I'd have said
"given range of physical blocks", but now that we can return multiple
owner:offset pairs for a given block, the block keyspace now has to
include extra fields to uniquely identify a reverse mapping record.

This ioctl behaves in a similar manner to XFS_IOC_GETBMAPX -- pass in
an array of struct getfsmapx with key and other control values in the
first two array elements, and the kernel passes back extent
information in the other array elements.  The particulars of how to do
this are documented in the manpage that goes along with this set (it
applies against man-pages.git) and example code in the other patches
is against xfsprogs.git#for-next.

Basically, set the lowest key for which you want records in the first
array element; the highest key in the second; and the kernel spits out
records in the rest of the elements.  That's similar to how GETBMAPX
does it, but different from FIEMAP.  I added a dummy 64-bit "device
id" per Josef's request, though I'm thinking that could be cut down to
a simple dev_t.  I also wonder if the kernel should rewrite the low
key with the last element returned so as to seed the next call, but
userspace can do that too.

The kernel-space implementation (for XFS) is buried inside the xfs
reverse mapping patchset which is treading water at github[1].  I
prefer not to patchbomb the whole kernel series until I've put the
mess through better testing, but this should be enough to get the
mailing list discussion started.

Questions?  Comments?  Bike sheds?

--D

[1] https://github.com/djwong/linux/tree/djwong-experimental

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

* [RFC 0/3] getfsmapx ioctl
@ 2016-05-05 19:47 ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:47 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams, Darrick J. Wong
  Cc: linux-fsdevel, linux-api, linux-ext4, linux-btrfs, xfs

Hi,

Building on the discussion "Exposing Extent Information to Userspace"
at LSF, this patchset offers the userspace definition, implementation,
and manpages for a new FS_IOC_GETFSMAPX ioctl that enables userspace
to query the filesystem for a map of every extent in a given range of
physical block keyspace.

Note that prior to the existence of block sharing, I'd have said
"given range of physical blocks", but now that we can return multiple
owner:offset pairs for a given block, the block keyspace now has to
include extra fields to uniquely identify a reverse mapping record.

This ioctl behaves in a similar manner to XFS_IOC_GETBMAPX -- pass in
an array of struct getfsmapx with key and other control values in the
first two array elements, and the kernel passes back extent
information in the other array elements.  The particulars of how to do
this are documented in the manpage that goes along with this set (it
applies against man-pages.git) and example code in the other patches
is against xfsprogs.git#for-next.

Basically, set the lowest key for which you want records in the first
array element; the highest key in the second; and the kernel spits out
records in the rest of the elements.  That's similar to how GETBMAPX
does it, but different from FIEMAP.  I added a dummy 64-bit "device
id" per Josef's request, though I'm thinking that could be cut down to
a simple dev_t.  I also wonder if the kernel should rewrite the low
key with the last element returned so as to seed the next call, but
userspace can do that too.

The kernel-space implementation (for XFS) is buried inside the xfs
reverse mapping patchset which is treading water at github[1].  I
prefer not to patchbomb the whole kernel series until I've put the
mess through better testing, but this should be enough to get the
mailing list discussion started.

Questions?  Comments?  Bike sheds?

--D

[1] https://github.com/djwong/linux/tree/djwong-experimental

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

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

* [PATCH 1/3] document the XFS_IOC_GETFSMAPX ioctl
  2016-05-05 19:47 ` Darrick J. Wong
@ 2016-05-05 19:50   ` Darrick J. Wong
  -1 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:50 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, xfs, linux-btrfs, linux-ext4

Document the new XFS_IOC_GETFSMAPX that returns the physical layout
of a (disk-based) filesystem.

(Yes, the leading 'X' needs to fall off...)

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

diff --git a/man2/ioctl_getfsmapx.2 b/man2/ioctl_getfsmapx.2
new file mode 100644
index 0000000..b79a8e5
--- /dev/null
+++ b/man2/ioctl_getfsmapx.2
@@ -0,0 +1,253 @@
+.\" Copyright (C) 2016 Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" 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
+.\" %%%LICENSE_END
+.TH IOCTL-XFS_IOC_GETFSMAPX 2 2016-05-05 "Linux" "Linux Programmer's Manual"
+.SH NAME
+ioctl_getfsmapx \- retrieve the physical layout of the filesystem
+.SH SYNOPSIS
+.br
+.B #include <sys/ioctl.h>
+.br
+.B #include <linux/fs.h>
+.sp
+.BI "int ioctl(int " fd ", XFS_IOC_GETFSMAPX, struct getfsmapx * " arg );
+.SH DESCRIPTION
+This
+.BR ioctl (2)
+retrieves physical extent mappings for a filesystem.  This information can
+be used to discover which files are mapped to a physical block, examine
+free space, or find known bad blocks, among other things.
+
+The sole argument to this ioctl should be an array of the following
+structure:
+.in +4n
+.nf
+
+struct getfsmapx {
+	__s64		fmv_device;	/* device id */
+	__s64		fmv_block;	/* starting block */
+	__s64		fmv_owner;	/* owner id */
+	__s64		fmv_offset;	/* file offset of segment */
+	__s64		fmv_length;	/* length of segment, blocks */
+	__s32		fmv_oflags;	/* mapping flags */
+	__s32		fmv_iflags;	/* control flags (1st structure) */
+	__s32		fmv_count;	/* # of entries in array incl. input */
+	__s32		fmv_entries;	/* # of entries filled in (output). */
+	__s64		fmv_unused1;	/* future use, must be zero */
+};
+
+.fi
+.in
+The array must contain at least two elements.  The first two array
+elements specify the lowest and highest reverse-mapping keys, respectively,
+for which userspace would like physical mapping information.  A reverse
+mapping key consists of the tuple (device, block, owner, offset).  The
+owner and offset fields are part of the key because some filesystems
+support sharing physical blocks between multiple files and therefore may
+return multiple mappings for a given physical block.
+
+.SS Fields of struct getfsmapx
+.PP
+The
+.I fmv_device
+field contains a 64-bit cookie to uniquely identify the underlying storage
+device if the filesystem supports multiple devices.  If not, the field
+should be
+.BR FMV_DEV_DEFAULT "."
+
+.PP
+The
+.I fmv_block
+field contains the 512-byte sector address of the extent.
+
+.PP
+The
+.I fmv_owner
+field contains the owner of the extent.  This is generally an inode
+number, though if
+.B FMV_OF_SPECIAL_OWNER
+is set in the
+.I fmv_oflags
+field, then the owner value is one of the following special values:
+.TP
+.B FMV_OWN_FREE
+Free space.
+.TP
+.B FMV_OWN_UNKNOWN
+This extent has an unknown owner.
+.TP
+.B FMV_OWN_FS
+Static filesystem metadata.
+.TP
+.B FMV_OWN_LOG
+The filesystem journal.
+.TP
+.B FMV_OWN_AG
+Allocation group metadata.
+.TP
+.B FMV_OWN_INOBT
+The inode index, if one is provided.
+.TP
+.B FMV_OWN_INODES
+Inodes.
+.TP
+.B FMV_OWN_REFC
+Reference counting indexes.
+.TP
+.B FMV_OWN_COW
+This extent is being used to stage a copy-on-write.
+.TP
+.B FMV_OWN_DEFECTIVE:
+This extent has been marked defective either by the filesystem or the
+underlying device.
+
+.PP
+The
+.I fmv_offset
+field contains the logical address of the reverse mapping record, in units
+of 512-byte blocks.  This field has no meaning if the
+.BR FMV_OF_SPECIAL_OWNER " or " FMV_OF_EXTENT_MAP
+flags are set in
+.IR fmv_oflags "."
+
+.PP
+The
+.I fmv_length
+field contains the length of the extent, in units of 512-byte blocks.
+This field must be zero in the second array element.
+
+.PP
+The
+.I fmv_oflags
+field is a bitmask of extent state flags.  The bits are:
+.TP
+.B FMV_OF_PREALLOC
+The extent is allocated but not yet written.
+.TP
+.B FMV_OF_ATTR_FORK
+This extent contains extended attribute data.
+.TP
+.B FMV_OF_EXTENT_MAP
+This extent contains extent map information for the owner.
+.TP
+.B FMV_OF_SHARED
+Parts of this extent may be shared.
+.TP
+.B FMV_OF_SPECIAL_OWNER
+The
+.I fmv_owner
+field contains a special value instead of an inode number.
+.TP
+.B FMV_OF_LAST
+This is the last record in the filesystem.
+
+.PP
+The
+.I fmv_iflags
+field is a bitmask passed to the kernel to alter the output.  There are no
+flags defined, so this value must be zero in the first two array elements.
+
+.PP
+The
+.I fmv_count
+field contains the number of elements in the array being passed to the
+kernel.  This count must include the two control elements at the start of
+the array.  The value must be specified in the first array element; in the
+second element this field must be zero.
+
+If this value is 2,
+.I fmv_entries
+will be set to the number of records that would have been returned had
+the array been large enough; no extent information will be returned.
+
+.PP
+The
+.I fmv_entries
+field contains the number of elements in the array that contain useful
+information if the ioctl returns a non-error value.  This value includes
+the two control elements at the start of the array and is only set in the
+first array element; in the second, this field must be zero.
+
+.PP
+The
+.I fmv_unused1
+field must be zero in the first two array elements.
+
+.SS Array Elements
+.PP
+The key fields (fmv_device, fmv_block, fmv_owner, fmv_offset) of the first
+element of the array specify the lowest extent record in the keyspace that
+the caller wants returned.  For example, if the key is set to
+(FMV_DEV_DEFAULT, 36, 0, 0), the filesystem will only return records for
+extents starting at or above sector 36 on disk.  For convenience, the
+.I fmv_length
+field will be added to the
+.IR fmv_block " and " fmv_offset
+fields as appropriate so that the (fmv_device, fmv_block, fmv_owner,
+fmv_offset, fmv_length) fields in the last array element can be copied
+into the first element to seed the next ioctl call.
+
+The key fields of the second element of the array specify the highest
+extent record in the keyspace that the caller wants returned.  Returning
+to our example above, if that example key were instead passed in via the
+second array element, the filesystem will not return records for extents
+going past sector 36 on disk.  For convenience, the four key fields can be
+set to ~0 (all ones) to signify "end of filesystem".
+
+If
+.I fmv_count
+in the first element of the array is 2, then
+.I fmv_entries
+in the first element of the array will be set to the number of extent
+records found in the filesystem.  Otherwise,
+.I fmv_entries
+will be set to the number of extents actually returned, and the subsequent
+array elements will be filled out with extent information.  In these
+subsequent array elements, the fields
+.IR fmv_iflags ", " fmv_count ", " fmv_entries ", and " fmv_unused1
+will be set to zero by the filesystem.
+
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EINVAL
+The array is not long enough, or a non-zero value was passed in one of the
+fields that must be zero.
+.TP
+.B EFAULT
+The pointer passed in was not mapped to a valid memory address.
+.TP
+.B EBADF
+.IR fd
+is not open for reading.
+.TP
+.B EPERM
+This query is not allowed.
+.TP
+.B EOPNOTSUPP
+The filesystem does not support this command.
+
+.SH CONFORMING TO
+This API is Linux-specific.  Not all filesystems support it.
+.fi
+.in
+.SH SEE ALSO
+.BR ioctl (2)

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

* [PATCH 1/3] document the XFS_IOC_GETFSMAPX ioctl
@ 2016-05-05 19:50   ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:50 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, linux-ext4, linux-btrfs, xfs

Document the new XFS_IOC_GETFSMAPX that returns the physical layout
of a (disk-based) filesystem.

(Yes, the leading 'X' needs to fall off...)

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

diff --git a/man2/ioctl_getfsmapx.2 b/man2/ioctl_getfsmapx.2
new file mode 100644
index 0000000..b79a8e5
--- /dev/null
+++ b/man2/ioctl_getfsmapx.2
@@ -0,0 +1,253 @@
+.\" Copyright (C) 2016 Oracle.  All rights reserved.
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" 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
+.\" %%%LICENSE_END
+.TH IOCTL-XFS_IOC_GETFSMAPX 2 2016-05-05 "Linux" "Linux Programmer's Manual"
+.SH NAME
+ioctl_getfsmapx \- retrieve the physical layout of the filesystem
+.SH SYNOPSIS
+.br
+.B #include <sys/ioctl.h>
+.br
+.B #include <linux/fs.h>
+.sp
+.BI "int ioctl(int " fd ", XFS_IOC_GETFSMAPX, struct getfsmapx * " arg );
+.SH DESCRIPTION
+This
+.BR ioctl (2)
+retrieves physical extent mappings for a filesystem.  This information can
+be used to discover which files are mapped to a physical block, examine
+free space, or find known bad blocks, among other things.
+
+The sole argument to this ioctl should be an array of the following
+structure:
+.in +4n
+.nf
+
+struct getfsmapx {
+	__s64		fmv_device;	/* device id */
+	__s64		fmv_block;	/* starting block */
+	__s64		fmv_owner;	/* owner id */
+	__s64		fmv_offset;	/* file offset of segment */
+	__s64		fmv_length;	/* length of segment, blocks */
+	__s32		fmv_oflags;	/* mapping flags */
+	__s32		fmv_iflags;	/* control flags (1st structure) */
+	__s32		fmv_count;	/* # of entries in array incl. input */
+	__s32		fmv_entries;	/* # of entries filled in (output). */
+	__s64		fmv_unused1;	/* future use, must be zero */
+};
+
+.fi
+.in
+The array must contain at least two elements.  The first two array
+elements specify the lowest and highest reverse-mapping keys, respectively,
+for which userspace would like physical mapping information.  A reverse
+mapping key consists of the tuple (device, block, owner, offset).  The
+owner and offset fields are part of the key because some filesystems
+support sharing physical blocks between multiple files and therefore may
+return multiple mappings for a given physical block.
+
+.SS Fields of struct getfsmapx
+.PP
+The
+.I fmv_device
+field contains a 64-bit cookie to uniquely identify the underlying storage
+device if the filesystem supports multiple devices.  If not, the field
+should be
+.BR FMV_DEV_DEFAULT "."
+
+.PP
+The
+.I fmv_block
+field contains the 512-byte sector address of the extent.
+
+.PP
+The
+.I fmv_owner
+field contains the owner of the extent.  This is generally an inode
+number, though if
+.B FMV_OF_SPECIAL_OWNER
+is set in the
+.I fmv_oflags
+field, then the owner value is one of the following special values:
+.TP
+.B FMV_OWN_FREE
+Free space.
+.TP
+.B FMV_OWN_UNKNOWN
+This extent has an unknown owner.
+.TP
+.B FMV_OWN_FS
+Static filesystem metadata.
+.TP
+.B FMV_OWN_LOG
+The filesystem journal.
+.TP
+.B FMV_OWN_AG
+Allocation group metadata.
+.TP
+.B FMV_OWN_INOBT
+The inode index, if one is provided.
+.TP
+.B FMV_OWN_INODES
+Inodes.
+.TP
+.B FMV_OWN_REFC
+Reference counting indexes.
+.TP
+.B FMV_OWN_COW
+This extent is being used to stage a copy-on-write.
+.TP
+.B FMV_OWN_DEFECTIVE:
+This extent has been marked defective either by the filesystem or the
+underlying device.
+
+.PP
+The
+.I fmv_offset
+field contains the logical address of the reverse mapping record, in units
+of 512-byte blocks.  This field has no meaning if the
+.BR FMV_OF_SPECIAL_OWNER " or " FMV_OF_EXTENT_MAP
+flags are set in
+.IR fmv_oflags "."
+
+.PP
+The
+.I fmv_length
+field contains the length of the extent, in units of 512-byte blocks.
+This field must be zero in the second array element.
+
+.PP
+The
+.I fmv_oflags
+field is a bitmask of extent state flags.  The bits are:
+.TP
+.B FMV_OF_PREALLOC
+The extent is allocated but not yet written.
+.TP
+.B FMV_OF_ATTR_FORK
+This extent contains extended attribute data.
+.TP
+.B FMV_OF_EXTENT_MAP
+This extent contains extent map information for the owner.
+.TP
+.B FMV_OF_SHARED
+Parts of this extent may be shared.
+.TP
+.B FMV_OF_SPECIAL_OWNER
+The
+.I fmv_owner
+field contains a special value instead of an inode number.
+.TP
+.B FMV_OF_LAST
+This is the last record in the filesystem.
+
+.PP
+The
+.I fmv_iflags
+field is a bitmask passed to the kernel to alter the output.  There are no
+flags defined, so this value must be zero in the first two array elements.
+
+.PP
+The
+.I fmv_count
+field contains the number of elements in the array being passed to the
+kernel.  This count must include the two control elements at the start of
+the array.  The value must be specified in the first array element; in the
+second element this field must be zero.
+
+If this value is 2,
+.I fmv_entries
+will be set to the number of records that would have been returned had
+the array been large enough; no extent information will be returned.
+
+.PP
+The
+.I fmv_entries
+field contains the number of elements in the array that contain useful
+information if the ioctl returns a non-error value.  This value includes
+the two control elements at the start of the array and is only set in the
+first array element; in the second, this field must be zero.
+
+.PP
+The
+.I fmv_unused1
+field must be zero in the first two array elements.
+
+.SS Array Elements
+.PP
+The key fields (fmv_device, fmv_block, fmv_owner, fmv_offset) of the first
+element of the array specify the lowest extent record in the keyspace that
+the caller wants returned.  For example, if the key is set to
+(FMV_DEV_DEFAULT, 36, 0, 0), the filesystem will only return records for
+extents starting at or above sector 36 on disk.  For convenience, the
+.I fmv_length
+field will be added to the
+.IR fmv_block " and " fmv_offset
+fields as appropriate so that the (fmv_device, fmv_block, fmv_owner,
+fmv_offset, fmv_length) fields in the last array element can be copied
+into the first element to seed the next ioctl call.
+
+The key fields of the second element of the array specify the highest
+extent record in the keyspace that the caller wants returned.  Returning
+to our example above, if that example key were instead passed in via the
+second array element, the filesystem will not return records for extents
+going past sector 36 on disk.  For convenience, the four key fields can be
+set to ~0 (all ones) to signify "end of filesystem".
+
+If
+.I fmv_count
+in the first element of the array is 2, then
+.I fmv_entries
+in the first element of the array will be set to the number of extent
+records found in the filesystem.  Otherwise,
+.I fmv_entries
+will be set to the number of extents actually returned, and the subsequent
+array elements will be filled out with extent information.  In these
+subsequent array elements, the fields
+.IR fmv_iflags ", " fmv_count ", " fmv_entries ", and " fmv_unused1
+will be set to zero by the filesystem.
+
+.SH RETURN VALUE
+On error, \-1 is returned, and
+.I errno
+is set to indicate the error.
+.PP
+.SH ERRORS
+Error codes can be one of, but are not limited to, the following:
+.TP
+.B EINVAL
+The array is not long enough, or a non-zero value was passed in one of the
+fields that must be zero.
+.TP
+.B EFAULT
+The pointer passed in was not mapped to a valid memory address.
+.TP
+.B EBADF
+.IR fd
+is not open for reading.
+.TP
+.B EPERM
+This query is not allowed.
+.TP
+.B EOPNOTSUPP
+The filesystem does not support this command.
+
+.SH CONFORMING TO
+This API is Linux-specific.  Not all filesystems support it.
+.fi
+.in
+.SH SEE ALSO
+.BR ioctl (2)

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

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

* [PATCH 2/3] xfs: introduce the XFS_IOC_GETFSMAPX ioctl
  2016-05-05 19:47 ` Darrick J. Wong
@ 2016-05-05 19:51   ` Darrick J. Wong
  -1 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:51 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, xfs, linux-btrfs, linux-ext4

Introduce a new ioctl that uses the reverse mapping btree to return
information about the physical layout of the filesystem.  This is
the xfsprogs side of things for userspace support.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libxfs/xfs_fs.h       |   65 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+), 14 deletions(-)

diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index d5ed090..6573fcc 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -117,6 +117,70 @@ struct getbmapx {
 #define BMV_OF_SHARED		0x8	/* segment shared with another file */
 
 /*
+ *	Structure for XFS_IOC_GETFSMAPX.
+ *
+ *	Similar to XFS_IOC_GETBMAPX, the first two elements in the array are
+ *	used to constrain the output.  The first element in the array should
+ *	represent the lowest disk address that the user wants to learn about.
+ *	The second element in the array should represent the highest disk
+ *	address to query.  Subsequent array elements will be filled out by the
+ *	command.
+ *
+ *	The fmv_iflags field is only used in the first structure.  The
+ *	fmv_oflags field is filled in for each returned structure after the
+ *	second structure.  The fmv_unused1 fields in the first two array
+ *	elements must be zero.
+ *
+ *	The fmv_count, fmv_entries, and fmv_iflags fields in the second array
+ *	element must be zero.
+ *
+ *	fmv_block, fmv_offset, and fmv_length are expressed in units of 512
+ *	byte sectors.
+ */
+#ifndef HAVE_GETFSMAPX
+struct getfsmapx {
+	__s64		fmv_device;	/* device id */
+	__s64		fmv_block;	/* starting block */
+	__s64		fmv_owner;	/* owner id */
+	__s64		fmv_offset;	/* file offset of segment */
+	__s64		fmv_length;	/* length of segment, blocks */
+	__s32		fmv_oflags;	/* mapping flags */
+	__s32		fmv_iflags;	/* control flags (1st structure) */
+	__s32		fmv_count;	/* # of entries in array incl. input */
+	__s32		fmv_entries;	/* # of entries filled in (output). */
+	__s64		fmv_unused1;	/* future use, must be zero */
+};
+#endif
+
+/*	fmv_device values - set by XFS_IOC_GETFSMAPX caller.	*/
+/* use this value if the filesystem doesn't support multiple devices. */
+#define FMV_DEV_DEFAULT	0
+
+/*	fmv_flags values - set by XFS_IOC_GETFSMAPX caller.	*/
+/* no flags defined yet */
+#define FMV_IF_VALID	0
+
+/*	fmv_flags values - returned for each non-header segment */
+#define FMV_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
+#define FMV_OF_ATTR_FORK	0x2	/* segment = attribute fork */
+#define FMV_OF_EXTENT_MAP	0x4	/* segment = extent map */
+#define FMV_OF_SHARED		0x8	/* segment = shared with another file */
+#define FMV_OF_SPECIAL_OWNER	0x10	/* owner is a special value */
+#define FMV_OF_LAST		0x20	/* segment is the last in the FS */
+
+/*	fmv_owner special values */
+#define	FMV_OWN_FREE		(-1ULL)	/* free space */
+#define FMV_OWN_UNKNOWN		(-2ULL)	/* unknown owner */
+#define FMV_OWN_FS		(-3ULL)	/* static fs metadata */
+#define FMV_OWN_LOG		(-4ULL)	/* journalling log */
+#define FMV_OWN_AG		(-5ULL)	/* per-AG metadata */
+#define FMV_OWN_INOBT		(-6ULL)	/* inode btree blocks */
+#define FMV_OWN_INODES		(-7ULL)	/* inodes */
+#define FMV_OWN_REFC		(-8ULL) /* refcount tree */
+#define FMV_OWN_COW		(-9ULL) /* cow allocations */
+#define FMV_OWN_DEFECTIVE	(-10ULL) /* bad blocks */
+
+/*
  * Structure for XFS_IOC_FSSETDM.
  * For use by backup and restore programs to set the XFS on-disk inode
  * fields di_dmevmask and di_dmstate.  These must be set to exactly and
@@ -523,6 +587,7 @@ typedef struct xfs_swapext
 #define XFS_IOC_GETBMAPX	_IOWR('X', 56, struct getbmap)
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
+#define XFS_IOC_GETFSMAPX	_IOWR('X', 59, struct getfsmapx)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s

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

* [PATCH 2/3] xfs: introduce the XFS_IOC_GETFSMAPX ioctl
@ 2016-05-05 19:51   ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:51 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, linux-ext4, linux-btrfs, xfs

Introduce a new ioctl that uses the reverse mapping btree to return
information about the physical layout of the filesystem.  This is
the xfsprogs side of things for userspace support.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 libxfs/xfs_fs.h       |   65 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+), 14 deletions(-)

diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index d5ed090..6573fcc 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -117,6 +117,70 @@ struct getbmapx {
 #define BMV_OF_SHARED		0x8	/* segment shared with another file */
 
 /*
+ *	Structure for XFS_IOC_GETFSMAPX.
+ *
+ *	Similar to XFS_IOC_GETBMAPX, the first two elements in the array are
+ *	used to constrain the output.  The first element in the array should
+ *	represent the lowest disk address that the user wants to learn about.
+ *	The second element in the array should represent the highest disk
+ *	address to query.  Subsequent array elements will be filled out by the
+ *	command.
+ *
+ *	The fmv_iflags field is only used in the first structure.  The
+ *	fmv_oflags field is filled in for each returned structure after the
+ *	second structure.  The fmv_unused1 fields in the first two array
+ *	elements must be zero.
+ *
+ *	The fmv_count, fmv_entries, and fmv_iflags fields in the second array
+ *	element must be zero.
+ *
+ *	fmv_block, fmv_offset, and fmv_length are expressed in units of 512
+ *	byte sectors.
+ */
+#ifndef HAVE_GETFSMAPX
+struct getfsmapx {
+	__s64		fmv_device;	/* device id */
+	__s64		fmv_block;	/* starting block */
+	__s64		fmv_owner;	/* owner id */
+	__s64		fmv_offset;	/* file offset of segment */
+	__s64		fmv_length;	/* length of segment, blocks */
+	__s32		fmv_oflags;	/* mapping flags */
+	__s32		fmv_iflags;	/* control flags (1st structure) */
+	__s32		fmv_count;	/* # of entries in array incl. input */
+	__s32		fmv_entries;	/* # of entries filled in (output). */
+	__s64		fmv_unused1;	/* future use, must be zero */
+};
+#endif
+
+/*	fmv_device values - set by XFS_IOC_GETFSMAPX caller.	*/
+/* use this value if the filesystem doesn't support multiple devices. */
+#define FMV_DEV_DEFAULT	0
+
+/*	fmv_flags values - set by XFS_IOC_GETFSMAPX caller.	*/
+/* no flags defined yet */
+#define FMV_IF_VALID	0
+
+/*	fmv_flags values - returned for each non-header segment */
+#define FMV_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
+#define FMV_OF_ATTR_FORK	0x2	/* segment = attribute fork */
+#define FMV_OF_EXTENT_MAP	0x4	/* segment = extent map */
+#define FMV_OF_SHARED		0x8	/* segment = shared with another file */
+#define FMV_OF_SPECIAL_OWNER	0x10	/* owner is a special value */
+#define FMV_OF_LAST		0x20	/* segment is the last in the FS */
+
+/*	fmv_owner special values */
+#define	FMV_OWN_FREE		(-1ULL)	/* free space */
+#define FMV_OWN_UNKNOWN		(-2ULL)	/* unknown owner */
+#define FMV_OWN_FS		(-3ULL)	/* static fs metadata */
+#define FMV_OWN_LOG		(-4ULL)	/* journalling log */
+#define FMV_OWN_AG		(-5ULL)	/* per-AG metadata */
+#define FMV_OWN_INOBT		(-6ULL)	/* inode btree blocks */
+#define FMV_OWN_INODES		(-7ULL)	/* inodes */
+#define FMV_OWN_REFC		(-8ULL) /* refcount tree */
+#define FMV_OWN_COW		(-9ULL) /* cow allocations */
+#define FMV_OWN_DEFECTIVE	(-10ULL) /* bad blocks */
+
+/*
  * Structure for XFS_IOC_FSSETDM.
  * For use by backup and restore programs to set the XFS on-disk inode
  * fields di_dmevmask and di_dmstate.  These must be set to exactly and
@@ -523,6 +587,7 @@ typedef struct xfs_swapext
 #define XFS_IOC_GETBMAPX	_IOWR('X', 56, struct getbmap)
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
+#define XFS_IOC_GETFSMAPX	_IOWR('X', 59, struct getfsmapx)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

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

* [PATCH 3/3] xfs_io: support the new getfsmap ioctl
  2016-05-05 19:47 ` Darrick J. Wong
@ 2016-05-05 19:52   ` Darrick J. Wong
  -1 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:52 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, xfs, linux-btrfs, linux-ext4

Add a new command, 'fsmap', to xfs_io so that we can query the filesystem
extent map on a live filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/Makefile       |    2 
 io/fsmap.c        |  485 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c         |    1 
 io/io.h           |    1 
 man/man8/xfs_io.8 |   47 +++++
 5 files changed, 535 insertions(+), 1 deletion(-)
 create mode 100644 io/fsmap.c

diff --git a/io/Makefile b/io/Makefile
index 0b53f41..6439e1d 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -11,7 +11,7 @@ HFILES = init.h io.h
 CFILES = init.c \
 	attr.c bmap.c file.c freeze.c fsync.c getrusage.c imap.c link.c \
 	mmap.c open.c parent.c pread.c prealloc.c pwrite.c seek.c shutdown.c \
-	sync.c truncate.c reflink.c
+	sync.c truncate.c reflink.c fsmap.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
diff --git a/io/fsmap.c b/io/fsmap.c
new file mode 100644
index 0000000..bf72555
--- /dev/null
+++ b/io/fsmap.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2016 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 "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "io.h"
+#include "input.h"
+
+static cmdinfo_t fsmap_cmd;
+
+static void
+fsmap_help(void)
+{
+	printf(_(
+"\n"
+" prints the block mapping for an XFS filesystem"
+"\n"
+" Example:\n"
+" 'fsmap -vp' - tabular format verbose map, including unwritten extents\n"
+"\n"
+" fsmap prints the map of disk blocks used by the whole filesystem.\n"
+" The map lists each extent used by the file, as well as regions in the\n"
+" filesystem that do not have any corresponding blocks (free space).\n"
+" By default, each line of the listing takes the following form:\n"
+"     extent: [startoffset..endoffset] owner startblock..endblock\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -n -- query n extents.\n"
+" -v -- Verbose information, specify ag info.  Show flags legend on 2nd -v\n"
+"\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);
+}
+
+static const char *
+special_owner(
+	__int64_t	owner)
+{
+	switch (owner) {
+	case FMV_OWN_FREE:
+		return _("free space");
+	case FMV_OWN_UNKNOWN:
+		return _("unknown");
+	case FMV_OWN_FS:
+		return _("static fs metadata");
+	case FMV_OWN_LOG:
+		return _("journalling log");
+	case FMV_OWN_AG:
+		return _("per-AG metadata");
+	case FMV_OWN_INOBT:
+		return _("inode btree");
+	case FMV_OWN_INODES:
+		return _("inodes");
+	case FMV_OWN_REFC:
+		return _("refcount btree");
+	case FMV_OWN_COW:
+		return _("cow reservation");
+	case FMV_OWN_DEFECTIVE:
+		return _("defective");
+	default:
+		return _("unknown");
+	}
+}
+
+static void
+dump_map(
+	unsigned long long	nr,
+	struct getfsmapx	*map)
+{
+	unsigned long long	i;
+	struct getfsmapx	*p;
+
+	for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+		printf("\t%llu: [%lld..%lld]: ", i + nr,
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			printf("%s", special_owner(p->fmv_owner));
+		else if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			printf(_("inode %lld extent map"),
+				(long long) p->fmv_owner);
+		else
+			printf(_("inode %lld %lld..%lld"),
+				(long long) p->fmv_owner,
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+		printf(_(" %lld blocks\n"),
+			(long long)p->fmv_length);
+	}
+}
+
+/*
+ * Verbose mode displays:
+ *   extent: [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_SHARED	01000000	/* shared extent */
+#define	FLG_ATTR_FORK	00100000	/* attribute fork */
+#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 getfsmapx	*map,
+	bool			*dumped_flags,
+	struct xfs_fsop_geom	*fsgeo)
+{
+	unsigned long long	i;
+	struct getfsmapx	*p;
+	int			agno;
+	off64_t 		agoff, bbperag;
+	int			foff_w, boff_w, aoff_w, tot_w, agno_w, own_w, nr_w;
+	char			rbuf[32], bbuf[32], abuf[32], obuf[32], nbuf[32];
+	int			sunit, swidth;
+	int			flg = 0;
+
+	foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
+	nr_w = 4;
+	tot_w = MINTOT_WIDTH;
+	bbperag = (off64_t)fsgeo->agblocks *
+		  (off64_t)fsgeo->blocksize / BBSIZE;
+	sunit = (fsgeo->sunit * fsgeo->blocksize) / BBSIZE;
+	swidth = (fsgeo->swidth * fsgeo->blocksize) / BBSIZE;
+
+	/*
+	 * Go through the extents and figure out the width
+	 * needed for all columns.
+	 */
+	for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+		if (p->fmv_oflags & FMV_OF_PREALLOC ||
+		    p->fmv_oflags & FMV_OF_ATTR_FORK ||
+		    p->fmv_oflags & FMV_OF_SHARED)
+			flg = 1;
+		if (sunit &&
+		    (p->fmv_block  % sunit != 0 ||
+		     ((p->fmv_block + p->fmv_length) % sunit) != 0 ||
+		     p->fmv_block % swidth != 0 ||
+		     ((p->fmv_block + p->fmv_length) % swidth) != 0))
+			flg = 1;
+		if (flg)
+			*dumped_flags = true;
+		snprintf(nbuf, sizeof(nbuf), "%llu", nr + i);
+		nr_w = max(nr_w, strlen(nbuf));
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		boff_w = max(boff_w, strlen(bbuf));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			own_w = max(own_w, strlen(special_owner(p->fmv_owner)));
+		else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmv_owner);
+			own_w = max(own_w, strlen(obuf));
+		}
+		if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			foff_w = max(foff_w, strlen(_("extent_map")));
+		else if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			;
+		else {
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+			foff_w = max(foff_w, strlen(rbuf));
+		}
+		agno = p->fmv_block / bbperag;
+		agoff = p->fmv_block - (agno * bbperag);
+		snprintf(abuf, sizeof(abuf),
+			"(%lld..%lld)",
+			(long long)agoff,
+			(long long)(agoff + p->fmv_length - 1));
+		aoff_w = max(aoff_w, strlen(abuf));
+		tot_w = max(tot_w,
+			numlen(p->fmv_length));
+	}
+	agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount));
+	if (nr == 0)
+		printf("%*s: %-*s %-*s %-*s %*s %-*s %*s%s\n",
+			nr_w, _("EXT"),
+			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 = map + 2; i < map->fmv_entries; i++, p++) {
+		flg = FLG_NULL;
+		if (p->fmv_oflags & FMV_OF_PREALLOC)
+			flg |= FLG_PRE;
+		if (p->fmv_oflags & FMV_OF_ATTR_FORK)
+			flg |= FLG_ATTR_FORK;
+		if (p->fmv_oflags & FMV_OF_SHARED)
+			flg |= FLG_SHARED;
+		/*
+		 * If striping enabled, determine if extent starts/ends
+		 * on a stripe unit boundary.
+		 */
+		if (sunit) {
+			if (p->fmv_block  % sunit != 0) {
+				flg |= FLG_BSU;
+			}
+			if (((p->fmv_block +
+			      p->fmv_length ) % sunit ) != 0) {
+				flg |= FLG_ESU;
+			}
+			if (p->fmv_block % swidth != 0) {
+				flg |= FLG_BSW;
+			}
+			if (((p->fmv_block +
+			      p->fmv_length ) % swidth ) != 0) {
+				flg |= FLG_ESW;
+			}
+		}
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER) {
+			snprintf(obuf, sizeof(obuf), "%s",
+				special_owner(p->fmv_owner));
+			snprintf(rbuf, sizeof(rbuf), " ");
+		} else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmv_owner);
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+		}
+		agno = p->fmv_block / bbperag;
+		agoff = p->fmv_block - (agno * bbperag);
+		snprintf(abuf, sizeof(abuf),
+			"(%lld..%lld)",
+			(long long)agoff,
+			(long long)(agoff + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			printf("%*llu: %-*s %-*s %-*s %*d %-*s %*lld\n",
+				nr_w, nr + i,
+				boff_w, bbuf,
+				own_w, obuf,
+				foff_w, _("extent map"),
+				agno_w, agno,
+				aoff_w, abuf,
+				tot_w, (long long)p->fmv_length);
+		else {
+			printf("%*llu: %-*s %-*s %-*s", nr_w, nr + i,
+				boff_w, bbuf, own_w, obuf, foff_w, rbuf);
+			printf(" %*d %-*s", agno_w, agno,
+				aoff_w, abuf);
+			printf(" %*lld", tot_w,
+				(long long)p->fmv_length);
+			if (flg == FLG_NULL) {
+				printf("\n");
+			} else {
+				printf(" %-*.*o\n", NFLG, NFLG, flg);
+			}
+		}
+	}
+}
+
+static void
+dump_verbose_key(void)
+{
+	printf(_(" FLAG Values:\n"));
+	printf(_("    %*.*o Shared extent\n"),
+		NFLG+1, NFLG+1, FLG_SHARED);
+	printf(_("    %*.*o Attribute fork\n"),
+		NFLG+1, NFLG+1, FLG_ATTR_FORK);
+	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 getfsmapx	*p;
+	struct getfsmapx	*nmap;
+	struct getfsmapx	*map;
+	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			fmv_iflags = 0;	/* flags for XFS_IOC_GETFSMAPX */
+	int			i = 0;
+	int			c;
+	unsigned long long	nr = 0;
+	size_t			fsblocksize, fssectsize;
+	bool			dumped_flags = false;
+
+	init_cvtnum(&fsblocksize, &fssectsize);
+
+	while ((c = getopt(argc, argv, "n:v")) != EOF) {
+		switch (c) {
+		case 'n':	/* number of extents specified */
+			nflag = atoi(optarg);
+			break;
+		case 'v':	/* Verbose output */
+			vflag++;
+			break;
+		default:
+			return command_usage(&fsmap_cmd);
+		}
+	}
+
+	if (argc > optind) {
+		start = cvtnum(fsblocksize, fssectsize, argv[optind]);
+		if (start < 0) {
+			fprintf(stderr,
+				_("Bad rmap start_fsb %s.\n"),
+				argv[optind]);
+			return 0;
+		}
+	}
+
+	if (argc > optind + 1) {
+		end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
+		if (end < 0) {
+			fprintf(stderr,
+				_("Bad rmap end_fsb %s.\n"),
+				argv[optind + 1]);
+			return 0;
+		}
+	}
+
+	if (vflag) {
+		c = xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &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 + 2 : 32;	/* initial guess - 32 */
+	map = malloc(map_size * sizeof(*map));
+	if (map == NULL) {
+		fprintf(stderr, _("%s: malloc of %lu bytes failed.\n"),
+			progname, map_size * sizeof(*map));
+		exitcode = 1;
+		return 0;
+	}
+
+	map->fmv_iflags = fmv_iflags;
+	map->fmv_device = FMV_DEV_DEFAULT;
+	map->fmv_block = start / 512;
+	map->fmv_owner = 0;
+	map->fmv_offset = 0;
+	map->fmv_length = 0;
+	(map + 1)->fmv_device = ULLONG_MAX;
+	(map + 1)->fmv_block = (unsigned long long)end / 512;
+	(map + 1)->fmv_owner = ULLONG_MAX;
+	(map + 1)->fmv_offset = ULLONG_MAX;
+
+	/* Count mappings */
+	if (!nflag) {
+		map->fmv_count = 2;
+		i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, map->fmv_iflags, file->name,
+				strerror(errno));
+			free(map);
+			exitcode = 1;
+			return 0;
+		}
+		if (map->fmv_entries > map_size * 2) {
+			unsigned long long nr;
+
+			nr = 5ULL * map->fmv_entries / 4 + 2;
+			nmap_size = nr > INT_MAX ? INT_MAX : nr;
+			nmap = realloc(map, nmap_size * sizeof(*map));
+			if (nmap == NULL) {
+				fprintf(stderr,
+					_("%s: cannot realloc %lu bytes\n"),
+					progname, map_size*sizeof(*map));
+			} else {
+				map = nmap;
+				map_size = nmap_size;
+			}
+		}
+	}
+
+	map->fmv_count = map_size;
+	do {
+		/* Get some extents */
+		i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, map->fmv_iflags, file->name,
+				strerror(errno));
+			free(map);
+			exitcode = 1;
+			return 0;
+		}
+
+		if (map->fmv_entries == 0)
+			break;
+
+		if (!vflag)
+			dump_map(nr, map);
+		else
+			dump_map_verbose(nr, map, &dumped_flags, &fsgeo);
+
+		p = map + 1 + map->fmv_entries;
+		if (p->fmv_oflags & FMV_OF_LAST)
+			break;
+
+		nr += map->fmv_entries;
+		map->fmv_device = p->fmv_device;
+		map->fmv_block = p->fmv_block;
+		map->fmv_owner = p->fmv_owner;
+		map->fmv_offset = p->fmv_offset;
+		map->fmv_oflags = p->fmv_oflags;
+		map->fmv_length = p->fmv_length;
+	} while(true);
+
+	if (dumped_flags)
+		dump_verbose_key();
+
+	free(map);
+	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;
+	fsmap_cmd.args = _("[-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 51f1f5c..4ae8274 100644
--- a/io/init.c
+++ b/io/init.c
@@ -60,6 +60,7 @@ init_commands(void)
 	file_init();
 	flink_init();
 	freeze_init();
+	fsmap_init();
 	fsync_init();
 	getrusage_init();
 	help_init();
diff --git a/io/io.h b/io/io.h
index 172b1f8..cef1763 100644
--- a/io/io.h
+++ b/io/io.h
@@ -97,6 +97,7 @@ extern void		bmap_init(void);
 extern void		file_init(void);
 extern void		flink_init(void);
 extern void		freeze_init(void);
+extern void		fsmap_init(void);
 extern void		fsync_init(void);
 extern void		getrusage_init(void);
 extern void		help_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 34f90c7..60c46ee 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -267,6 +267,53 @@ ioctl.  Options behave as described in the
 .BR xfs_bmap (8)
 manual page.
 .TP
+.BI "fsmap [ \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
+Prints the mapping of disk blocks used by an XFS 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 ": [" 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.
+.BI "The optional " start " and " end " arguments can be used to constrain
+the output to a particular range of disk blocks.
+.RE
+.RS 1.0i
+.PD 0
+.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] 9+ messages in thread

* [PATCH 3/3] xfs_io: support the new getfsmap ioctl
@ 2016-05-05 19:52   ` Darrick J. Wong
  0 siblings, 0 replies; 9+ messages in thread
From: Darrick J. Wong @ 2016-05-05 19:52 UTC (permalink / raw)
  To: Mark Fasheh, Josef Bacik, Dave Chinner, Theodore Ts'o,
	Ross Zwisler, Dan Williams
  Cc: linux-fsdevel, linux-api, linux-ext4, linux-btrfs, xfs

Add a new command, 'fsmap', to xfs_io so that we can query the filesystem
extent map on a live filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 io/Makefile       |    2 
 io/fsmap.c        |  485 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/init.c         |    1 
 io/io.h           |    1 
 man/man8/xfs_io.8 |   47 +++++
 5 files changed, 535 insertions(+), 1 deletion(-)
 create mode 100644 io/fsmap.c

diff --git a/io/Makefile b/io/Makefile
index 0b53f41..6439e1d 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -11,7 +11,7 @@ HFILES = init.h io.h
 CFILES = init.c \
 	attr.c bmap.c file.c freeze.c fsync.c getrusage.c imap.c link.c \
 	mmap.c open.c parent.c pread.c prealloc.c pwrite.c seek.c shutdown.c \
-	sync.c truncate.c reflink.c
+	sync.c truncate.c reflink.c fsmap.c
 
 LLDLIBS = $(LIBXCMD) $(LIBHANDLE)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBHANDLE)
diff --git a/io/fsmap.c b/io/fsmap.c
new file mode 100644
index 0000000..bf72555
--- /dev/null
+++ b/io/fsmap.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2016 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 "platform_defs.h"
+#include "command.h"
+#include "init.h"
+#include "io.h"
+#include "input.h"
+
+static cmdinfo_t fsmap_cmd;
+
+static void
+fsmap_help(void)
+{
+	printf(_(
+"\n"
+" prints the block mapping for an XFS filesystem"
+"\n"
+" Example:\n"
+" 'fsmap -vp' - tabular format verbose map, including unwritten extents\n"
+"\n"
+" fsmap prints the map of disk blocks used by the whole filesystem.\n"
+" The map lists each extent used by the file, as well as regions in the\n"
+" filesystem that do not have any corresponding blocks (free space).\n"
+" By default, each line of the listing takes the following form:\n"
+"     extent: [startoffset..endoffset] owner startblock..endblock\n"
+" All the file offsets and disk blocks are in units of 512-byte blocks.\n"
+" -n -- query n extents.\n"
+" -v -- Verbose information, specify ag info.  Show flags legend on 2nd -v\n"
+"\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);
+}
+
+static const char *
+special_owner(
+	__int64_t	owner)
+{
+	switch (owner) {
+	case FMV_OWN_FREE:
+		return _("free space");
+	case FMV_OWN_UNKNOWN:
+		return _("unknown");
+	case FMV_OWN_FS:
+		return _("static fs metadata");
+	case FMV_OWN_LOG:
+		return _("journalling log");
+	case FMV_OWN_AG:
+		return _("per-AG metadata");
+	case FMV_OWN_INOBT:
+		return _("inode btree");
+	case FMV_OWN_INODES:
+		return _("inodes");
+	case FMV_OWN_REFC:
+		return _("refcount btree");
+	case FMV_OWN_COW:
+		return _("cow reservation");
+	case FMV_OWN_DEFECTIVE:
+		return _("defective");
+	default:
+		return _("unknown");
+	}
+}
+
+static void
+dump_map(
+	unsigned long long	nr,
+	struct getfsmapx	*map)
+{
+	unsigned long long	i;
+	struct getfsmapx	*p;
+
+	for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+		printf("\t%llu: [%lld..%lld]: ", i + nr,
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			printf("%s", special_owner(p->fmv_owner));
+		else if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			printf(_("inode %lld extent map"),
+				(long long) p->fmv_owner);
+		else
+			printf(_("inode %lld %lld..%lld"),
+				(long long) p->fmv_owner,
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+		printf(_(" %lld blocks\n"),
+			(long long)p->fmv_length);
+	}
+}
+
+/*
+ * Verbose mode displays:
+ *   extent: [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_SHARED	01000000	/* shared extent */
+#define	FLG_ATTR_FORK	00100000	/* attribute fork */
+#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 getfsmapx	*map,
+	bool			*dumped_flags,
+	struct xfs_fsop_geom	*fsgeo)
+{
+	unsigned long long	i;
+	struct getfsmapx	*p;
+	int			agno;
+	off64_t 		agoff, bbperag;
+	int			foff_w, boff_w, aoff_w, tot_w, agno_w, own_w, nr_w;
+	char			rbuf[32], bbuf[32], abuf[32], obuf[32], nbuf[32];
+	int			sunit, swidth;
+	int			flg = 0;
+
+	foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
+	nr_w = 4;
+	tot_w = MINTOT_WIDTH;
+	bbperag = (off64_t)fsgeo->agblocks *
+		  (off64_t)fsgeo->blocksize / BBSIZE;
+	sunit = (fsgeo->sunit * fsgeo->blocksize) / BBSIZE;
+	swidth = (fsgeo->swidth * fsgeo->blocksize) / BBSIZE;
+
+	/*
+	 * Go through the extents and figure out the width
+	 * needed for all columns.
+	 */
+	for (i = 0, p = map + 2; i < map->fmv_entries; i++, p++) {
+		if (p->fmv_oflags & FMV_OF_PREALLOC ||
+		    p->fmv_oflags & FMV_OF_ATTR_FORK ||
+		    p->fmv_oflags & FMV_OF_SHARED)
+			flg = 1;
+		if (sunit &&
+		    (p->fmv_block  % sunit != 0 ||
+		     ((p->fmv_block + p->fmv_length) % sunit) != 0 ||
+		     p->fmv_block % swidth != 0 ||
+		     ((p->fmv_block + p->fmv_length) % swidth) != 0))
+			flg = 1;
+		if (flg)
+			*dumped_flags = true;
+		snprintf(nbuf, sizeof(nbuf), "%llu", nr + i);
+		nr_w = max(nr_w, strlen(nbuf));
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		boff_w = max(boff_w, strlen(bbuf));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			own_w = max(own_w, strlen(special_owner(p->fmv_owner)));
+		else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmv_owner);
+			own_w = max(own_w, strlen(obuf));
+		}
+		if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			foff_w = max(foff_w, strlen(_("extent_map")));
+		else if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER)
+			;
+		else {
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+			foff_w = max(foff_w, strlen(rbuf));
+		}
+		agno = p->fmv_block / bbperag;
+		agoff = p->fmv_block - (agno * bbperag);
+		snprintf(abuf, sizeof(abuf),
+			"(%lld..%lld)",
+			(long long)agoff,
+			(long long)(agoff + p->fmv_length - 1));
+		aoff_w = max(aoff_w, strlen(abuf));
+		tot_w = max(tot_w,
+			numlen(p->fmv_length));
+	}
+	agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount));
+	if (nr == 0)
+		printf("%*s: %-*s %-*s %-*s %*s %-*s %*s%s\n",
+			nr_w, _("EXT"),
+			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 = map + 2; i < map->fmv_entries; i++, p++) {
+		flg = FLG_NULL;
+		if (p->fmv_oflags & FMV_OF_PREALLOC)
+			flg |= FLG_PRE;
+		if (p->fmv_oflags & FMV_OF_ATTR_FORK)
+			flg |= FLG_ATTR_FORK;
+		if (p->fmv_oflags & FMV_OF_SHARED)
+			flg |= FLG_SHARED;
+		/*
+		 * If striping enabled, determine if extent starts/ends
+		 * on a stripe unit boundary.
+		 */
+		if (sunit) {
+			if (p->fmv_block  % sunit != 0) {
+				flg |= FLG_BSU;
+			}
+			if (((p->fmv_block +
+			      p->fmv_length ) % sunit ) != 0) {
+				flg |= FLG_ESU;
+			}
+			if (p->fmv_block % swidth != 0) {
+				flg |= FLG_BSW;
+			}
+			if (((p->fmv_block +
+			      p->fmv_length ) % swidth ) != 0) {
+				flg |= FLG_ESW;
+			}
+		}
+		snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
+			(long long) p->fmv_block,
+			(long long)(p->fmv_block + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_SPECIAL_OWNER) {
+			snprintf(obuf, sizeof(obuf), "%s",
+				special_owner(p->fmv_owner));
+			snprintf(rbuf, sizeof(rbuf), " ");
+		} else {
+			snprintf(obuf, sizeof(obuf), "%lld",
+				(long long)p->fmv_owner);
+			snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
+				(long long) p->fmv_offset,
+				(long long)(p->fmv_offset + p->fmv_length - 1));
+		}
+		agno = p->fmv_block / bbperag;
+		agoff = p->fmv_block - (agno * bbperag);
+		snprintf(abuf, sizeof(abuf),
+			"(%lld..%lld)",
+			(long long)agoff,
+			(long long)(agoff + p->fmv_length - 1));
+		if (p->fmv_oflags & FMV_OF_EXTENT_MAP)
+			printf("%*llu: %-*s %-*s %-*s %*d %-*s %*lld\n",
+				nr_w, nr + i,
+				boff_w, bbuf,
+				own_w, obuf,
+				foff_w, _("extent map"),
+				agno_w, agno,
+				aoff_w, abuf,
+				tot_w, (long long)p->fmv_length);
+		else {
+			printf("%*llu: %-*s %-*s %-*s", nr_w, nr + i,
+				boff_w, bbuf, own_w, obuf, foff_w, rbuf);
+			printf(" %*d %-*s", agno_w, agno,
+				aoff_w, abuf);
+			printf(" %*lld", tot_w,
+				(long long)p->fmv_length);
+			if (flg == FLG_NULL) {
+				printf("\n");
+			} else {
+				printf(" %-*.*o\n", NFLG, NFLG, flg);
+			}
+		}
+	}
+}
+
+static void
+dump_verbose_key(void)
+{
+	printf(_(" FLAG Values:\n"));
+	printf(_("    %*.*o Shared extent\n"),
+		NFLG+1, NFLG+1, FLG_SHARED);
+	printf(_("    %*.*o Attribute fork\n"),
+		NFLG+1, NFLG+1, FLG_ATTR_FORK);
+	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 getfsmapx	*p;
+	struct getfsmapx	*nmap;
+	struct getfsmapx	*map;
+	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			fmv_iflags = 0;	/* flags for XFS_IOC_GETFSMAPX */
+	int			i = 0;
+	int			c;
+	unsigned long long	nr = 0;
+	size_t			fsblocksize, fssectsize;
+	bool			dumped_flags = false;
+
+	init_cvtnum(&fsblocksize, &fssectsize);
+
+	while ((c = getopt(argc, argv, "n:v")) != EOF) {
+		switch (c) {
+		case 'n':	/* number of extents specified */
+			nflag = atoi(optarg);
+			break;
+		case 'v':	/* Verbose output */
+			vflag++;
+			break;
+		default:
+			return command_usage(&fsmap_cmd);
+		}
+	}
+
+	if (argc > optind) {
+		start = cvtnum(fsblocksize, fssectsize, argv[optind]);
+		if (start < 0) {
+			fprintf(stderr,
+				_("Bad rmap start_fsb %s.\n"),
+				argv[optind]);
+			return 0;
+		}
+	}
+
+	if (argc > optind + 1) {
+		end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
+		if (end < 0) {
+			fprintf(stderr,
+				_("Bad rmap end_fsb %s.\n"),
+				argv[optind + 1]);
+			return 0;
+		}
+	}
+
+	if (vflag) {
+		c = xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &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 + 2 : 32;	/* initial guess - 32 */
+	map = malloc(map_size * sizeof(*map));
+	if (map == NULL) {
+		fprintf(stderr, _("%s: malloc of %lu bytes failed.\n"),
+			progname, map_size * sizeof(*map));
+		exitcode = 1;
+		return 0;
+	}
+
+	map->fmv_iflags = fmv_iflags;
+	map->fmv_device = FMV_DEV_DEFAULT;
+	map->fmv_block = start / 512;
+	map->fmv_owner = 0;
+	map->fmv_offset = 0;
+	map->fmv_length = 0;
+	(map + 1)->fmv_device = ULLONG_MAX;
+	(map + 1)->fmv_block = (unsigned long long)end / 512;
+	(map + 1)->fmv_owner = ULLONG_MAX;
+	(map + 1)->fmv_offset = ULLONG_MAX;
+
+	/* Count mappings */
+	if (!nflag) {
+		map->fmv_count = 2;
+		i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, map->fmv_iflags, file->name,
+				strerror(errno));
+			free(map);
+			exitcode = 1;
+			return 0;
+		}
+		if (map->fmv_entries > map_size * 2) {
+			unsigned long long nr;
+
+			nr = 5ULL * map->fmv_entries / 4 + 2;
+			nmap_size = nr > INT_MAX ? INT_MAX : nr;
+			nmap = realloc(map, nmap_size * sizeof(*map));
+			if (nmap == NULL) {
+				fprintf(stderr,
+					_("%s: cannot realloc %lu bytes\n"),
+					progname, map_size*sizeof(*map));
+			} else {
+				map = nmap;
+				map_size = nmap_size;
+			}
+		}
+	}
+
+	map->fmv_count = map_size;
+	do {
+		/* Get some extents */
+		i = xfsctl(file->name, file->fd, XFS_IOC_GETFSMAPX, map);
+		if (i < 0) {
+			fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAPX)"
+				" iflags=0x%x [\"%s\"]: %s\n"),
+				progname, map->fmv_iflags, file->name,
+				strerror(errno));
+			free(map);
+			exitcode = 1;
+			return 0;
+		}
+
+		if (map->fmv_entries == 0)
+			break;
+
+		if (!vflag)
+			dump_map(nr, map);
+		else
+			dump_map_verbose(nr, map, &dumped_flags, &fsgeo);
+
+		p = map + 1 + map->fmv_entries;
+		if (p->fmv_oflags & FMV_OF_LAST)
+			break;
+
+		nr += map->fmv_entries;
+		map->fmv_device = p->fmv_device;
+		map->fmv_block = p->fmv_block;
+		map->fmv_owner = p->fmv_owner;
+		map->fmv_offset = p->fmv_offset;
+		map->fmv_oflags = p->fmv_oflags;
+		map->fmv_length = p->fmv_length;
+	} while(true);
+
+	if (dumped_flags)
+		dump_verbose_key();
+
+	free(map);
+	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;
+	fsmap_cmd.args = _("[-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 51f1f5c..4ae8274 100644
--- a/io/init.c
+++ b/io/init.c
@@ -60,6 +60,7 @@ init_commands(void)
 	file_init();
 	flink_init();
 	freeze_init();
+	fsmap_init();
 	fsync_init();
 	getrusage_init();
 	help_init();
diff --git a/io/io.h b/io/io.h
index 172b1f8..cef1763 100644
--- a/io/io.h
+++ b/io/io.h
@@ -97,6 +97,7 @@ extern void		bmap_init(void);
 extern void		file_init(void);
 extern void		flink_init(void);
 extern void		freeze_init(void);
+extern void		fsmap_init(void);
 extern void		fsync_init(void);
 extern void		getrusage_init(void);
 extern void		help_init(void);
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 34f90c7..60c46ee 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -267,6 +267,53 @@ ioctl.  Options behave as described in the
 .BR xfs_bmap (8)
 manual page.
 .TP
+.BI "fsmap [ \-v ] [ \-n " nx " ] [ " start " ] [ " end " ]
+Prints the mapping of disk blocks used by an XFS 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 ": [" 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.
+.BI "The optional " start " and " end " arguments can be used to constrain
+the output to a particular range of disk blocks.
+.RE
+.RS 1.0i
+.PD 0
+.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

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

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

end of thread, other threads:[~2016-05-05 19:55 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-05 19:47 [RFC 0/3] getfsmapx ioctl Darrick J. Wong
2016-05-05 19:47 ` Darrick J. Wong
2016-05-05 19:47 ` Darrick J. Wong
2016-05-05 19:50 ` [PATCH 1/3] document the XFS_IOC_GETFSMAPX ioctl Darrick J. Wong
2016-05-05 19:50   ` Darrick J. Wong
2016-05-05 19:51 ` [PATCH 2/3] xfs: introduce " Darrick J. Wong
2016-05-05 19:51   ` Darrick J. Wong
2016-05-05 19:52 ` [PATCH 3/3] xfs_io: support the new getfsmap ioctl Darrick J. Wong
2016-05-05 19:52   ` 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.