summaryrefslogtreecommitdiffstats
path: root/module/os/freebsd
diff options
context:
space:
mode:
authorRyan Moeller <[email protected]>2020-08-21 15:53:17 -0400
committerGitHub <[email protected]>2020-08-21 12:53:17 -0700
commit6fe3498ca3406ffb10f729ab4f9cf63f10c74ede (patch)
tree59f4761a8918487531a124c72a937511dc7620d8 /module/os/freebsd
parent6706552ea6bdc74122b530ba5e8a6956f5160428 (diff)
Import vdev ashift optimization from FreeBSD
Many modern devices use physical allocation units that are much larger than the minimum logical allocation size accessible by external commands. Two prevalent examples of this are 512e disk drives (512b logical sector, 4K physical sector) and flash devices (512b logical sector, 4K or larger allocation block size, and 128k or larger erase block size). Operations that modify less than the physical sector size result in a costly read-modify-write or garbage collection sequence on these devices. Simply exporting the true physical sector of the device to ZFS would yield optimal performance, but has two serious drawbacks: 1. Existing pools created with devices that have different logical and physical block sizes, but were configured to use the logical block size (e.g. because the OS version used for pool construction reported the logical block size instead of the physical block size) will suddenly find that the vdev allocation size has increased. This can be easily tolerated for active members of the array, but ZFS would prevent replacement of a vdev with another identical device because it now appears that the smaller allocation size required by the pool is not supported by the new device. 2. The device's physical block size may be too large to be supported by ZFS. The optimal allocation size for the vdev may be quite large. For example, a RAID controller may export a vdev that requires read-modify-write cycles unless accessed using 64k aligned/sized requests. ZFS currently has an 8k minimum block size limit. Reporting both the logical and physical allocation sizes for vdevs solves these problems. A device may be used so long as the logical block size is compatible with the configuration. By comparing the logical and physical block sizes, new configurations can be optimized and administrators can be notified of any existing pools that are sub-optimal. Reviewed-by: Ryan Moeller <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Co-authored-by: Matthew Macy <[email protected]> Signed-off-by: Matt Macy <[email protected]> Closes #10619
Diffstat (limited to 'module/os/freebsd')
-rw-r--r--module/os/freebsd/zfs/sysctl_os.c54
-rw-r--r--module/os/freebsd/zfs/vdev_file.c5
-rw-r--r--module/os/freebsd/zfs/vdev_geom.c11
3 files changed, 35 insertions, 35 deletions
diff --git a/module/os/freebsd/zfs/sysctl_os.c b/module/os/freebsd/zfs/sysctl_os.c
index d76b49de9..200bbf43d 100644
--- a/module/os/freebsd/zfs/sysctl_os.c
+++ b/module/os/freebsd/zfs/sysctl_os.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/vdev.h>
+#include <sys/vdev_impl.h>
#include <sys/dmu.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_dataset.h>
@@ -518,56 +519,53 @@ SYSCTL_INT(_vfs_zfs, OID_AUTO, space_map_ibs, CTLFLAG_RWTUN,
/* vdev.c */
-#ifdef notyet
-extern uint64_t zfs_max_auto_ashift;
-extern uint64_t zfs_min_auto_ashift;
-
-static int
-sysctl_vfs_zfs_max_auto_ashift(SYSCTL_HANDLER_ARGS)
+int
+param_set_min_auto_ashift(SYSCTL_HANDLER_ARGS)
{
uint64_t val;
int err;
- val = zfs_max_auto_ashift;
+ val = zfs_vdev_min_auto_ashift;
err = sysctl_handle_64(oidp, &val, 0, req);
if (err != 0 || req->newptr == NULL)
- return (err);
+ return (SET_ERROR(err));
- if (val > ASHIFT_MAX || val < zfs_min_auto_ashift)
- return (EINVAL);
+ if (val < ASHIFT_MIN || val > zfs_vdev_max_auto_ashift)
+ return (SET_ERROR(EINVAL));
- zfs_max_auto_ashift = val;
+ zfs_vdev_min_auto_ashift = val;
return (0);
}
-SYSCTL_PROC(_vfs_zfs, OID_AUTO, max_auto_ashift,
- CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_RW, 0, sizeof (uint64_t),
- sysctl_vfs_zfs_max_auto_ashift, "QU",
- "Max ashift used when optimising for logical -> physical sectors size on "
- "new top-level vdevs.");
-static int
-sysctl_vfs_zfs_min_auto_ashift(SYSCTL_HANDLER_ARGS)
+
+int
+param_set_max_auto_ashift(SYSCTL_HANDLER_ARGS)
{
uint64_t val;
int err;
- val = zfs_min_auto_ashift;
+ val = zfs_vdev_max_auto_ashift;
err = sysctl_handle_64(oidp, &val, 0, req);
if (err != 0 || req->newptr == NULL)
- return (err);
+ return (SET_ERROR(err));
- if (val < ASHIFT_MIN || val > zfs_max_auto_ashift)
- return (EINVAL);
+ if (val > ASHIFT_MAX || val < zfs_vdev_min_auto_ashift)
+ return (SET_ERROR(EINVAL));
- zfs_min_auto_ashift = val;
+ zfs_vdev_max_auto_ashift = val;
return (0);
}
-SYSCTL_PROC(_vfs_zfs, OID_AUTO, min_auto_ashift,
- CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_RW, 0, sizeof (uint64_t),
- sysctl_vfs_zfs_min_auto_ashift, "QU",
- "Min ashift used when creating new top-level vdevs.");
-#endif
+
+SYSCTL_PROC(_vfs_zfs, OID_AUTO, min_auto_ashift, CTLTYPE_U64 | CTLFLAG_RWTUN,
+ &zfs_vdev_min_auto_ashift, sizeof (zfs_vdev_min_auto_ashift),
+ param_set_min_auto_ashift, "QU",
+ "Min ashift used when creating new top-level vdev. (LEGACY)");
+SYSCTL_PROC(_vfs_zfs, OID_AUTO, max_auto_ashift, CTLTYPE_U64 | CTLFLAG_RWTUN,
+ &zfs_vdev_max_auto_ashift, sizeof (zfs_vdev_max_auto_ashift),
+ param_set_max_auto_ashift, "QU",
+ "Max ashift used when optimizing for logical -> physical sector size on "
+ "new top-level vdevs. (LEGACY)");
/*
* Since the DTL space map of a vdev is not expected to have a lot of
diff --git a/module/os/freebsd/zfs/vdev_file.c b/module/os/freebsd/zfs/vdev_file.c
index cca6bffd9..4d27751c8 100644
--- a/module/os/freebsd/zfs/vdev_file.c
+++ b/module/os/freebsd/zfs/vdev_file.c
@@ -83,7 +83,7 @@ vdev_file_open_mode(spa_mode_t spa_mode)
static int
vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
- uint64_t *ashift)
+ uint64_t *logical_ashift, uint64_t *physical_ashift)
{
vdev_file_t *vf;
zfs_file_t *fp;
@@ -167,7 +167,8 @@ skip_open:
}
*max_psize = *psize = zfa.zfa_size;
- *ashift = SPA_MINBLOCKSHIFT;
+ *logical_ashift = SPA_MINBLOCKSHIFT;
+ *physical_ashift = SPA_MINBLOCKSHIFT;
return (0);
}
diff --git a/module/os/freebsd/zfs/vdev_geom.c b/module/os/freebsd/zfs/vdev_geom.c
index 8462755f8..bf06f6919 100644
--- a/module/os/freebsd/zfs/vdev_geom.c
+++ b/module/os/freebsd/zfs/vdev_geom.c
@@ -802,7 +802,7 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid)
static int
vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
- uint64_t *logical_ashift)
+ uint64_t *logical_ashift, uint64_t *physical_ashift)
{
struct g_provider *pp;
struct g_consumer *cp;
@@ -949,11 +949,12 @@ skip_open:
* transfer size.
*/
*logical_ashift = highbit(MAX(pp->sectorsize, SPA_MINBLOCKSIZE)) - 1;
-#ifdef notyet
- if (pp->stripesize > (1 << *logical_ashift) && ISP2(pp->stripesize) &&
- pp->stripesize <= (1 << ASHIFT_MAX) && pp->stripeoffset == 0)
+ *physical_ashift = 0;
+ if (pp->stripesize && pp->stripesize > (1 << *logical_ashift) &&
+ ISP2(pp->stripesize) && pp->stripesize <= (1 << ASHIFT_MAX) &&
+ pp->stripeoffset == 0)
*physical_ashift = highbit(pp->stripesize) - 1;
-#endif
+
/*
* Clear the nowritecache settings, so that on a vdev_reopen()
* we will try again.