diff options
author | Ryan Moeller <[email protected]> | 2020-08-21 15:53:17 -0400 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-21 12:53:17 -0700 |
commit | 6fe3498ca3406ffb10f729ab4f9cf63f10c74ede (patch) | |
tree | 59f4761a8918487531a124c72a937511dc7620d8 /include | |
parent | 6706552ea6bdc74122b530ba5e8a6956f5160428 (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 'include')
-rw-r--r-- | include/libzfs.h | 1 | ||||
-rw-r--r-- | include/os/freebsd/spl/sys/mod_os.h | 6 | ||||
-rw-r--r-- | include/sys/fs/zfs.h | 10 | ||||
-rw-r--r-- | include/sys/vdev.h | 1 | ||||
-rw-r--r-- | include/sys/vdev_impl.h | 29 |
5 files changed, 46 insertions, 1 deletions
diff --git a/include/libzfs.h b/include/libzfs.h index b405ad1e1..4e6336180 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -390,6 +390,7 @@ typedef enum { ZPOOL_STATUS_REMOVED_DEV, /* removed device */ ZPOOL_STATUS_REBUILDING, /* device being rebuilt */ ZPOOL_STATUS_REBUILD_SCRUB, /* recommend scrubbing the pool */ + ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */ /* * Finally, the following indicates a healthy pool. diff --git a/include/os/freebsd/spl/sys/mod_os.h b/include/os/freebsd/spl/sys/mod_os.h index 9a3b29e1e..ec1da1a46 100644 --- a/include/os/freebsd/spl/sys/mod_os.h +++ b/include/os/freebsd/spl/sys/mod_os.h @@ -78,6 +78,12 @@ #define param_set_slop_shift_args(var) \ CTLTYPE_INT, &var, 0, param_set_slop_shift, "I" +#define param_set_min_auto_ashift_args(var) \ + CTLTYPE_U64, &var, 0, param_set_min_auto_ashift, "QU" + +#define param_set_max_auto_ashift_args(var) \ + CTLTYPE_U64, &var, 0, param_set_max_auto_ashift, "QU" + #include <sys/kernel.h> #define module_init(fn) \ static void \ diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 001893b71..d3acd674a 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -873,6 +873,7 @@ typedef enum vdev_aux { VDEV_AUX_EXTERNAL_PERSIST, /* persistent forced fault */ VDEV_AUX_ACTIVE, /* vdev active on a different host */ VDEV_AUX_CHILDREN_OFFLINE, /* all children are offline */ + VDEV_AUX_ASHIFT_TOO_BIG, /* vdev's min block size is too large */ } vdev_aux_t; /* @@ -1068,8 +1069,17 @@ typedef struct vdev_stat { uint64_t vs_trim_state; /* vdev_trim_state_t */ uint64_t vs_trim_action_time; /* time_t */ uint64_t vs_rebuild_processed; /* bytes rebuilt */ + uint64_t vs_configured_ashift; /* TLV vdev_ashift */ + uint64_t vs_logical_ashift; /* vdev_logical_ashift */ + uint64_t vs_physical_ashift; /* vdev_physical_ashift */ } vdev_stat_t; +/* BEGIN CSTYLED */ +#define VDEV_STAT_VALID(field, uint64_t_field_count) \ + ((uint64_t_field_count * sizeof (uint64_t)) >= \ + (offsetof(vdev_stat_t, field) + sizeof (((vdev_stat_t *)NULL)->field))) +/* END CSTYLED */ + /* * Extended stats * diff --git a/include/sys/vdev.h b/include/sys/vdev.h index a7e880636..797065fdd 100644 --- a/include/sys/vdev.h +++ b/include/sys/vdev.h @@ -94,6 +94,7 @@ extern void vdev_rele(vdev_t *); extern int vdev_metaslab_init(vdev_t *vd, uint64_t txg); extern void vdev_metaslab_fini(vdev_t *vd); extern void vdev_metaslab_set_size(vdev_t *); +extern void vdev_ashift_optimize(vdev_t *); extern void vdev_expand(vdev_t *vd, uint64_t txg); extern void vdev_split(vdev_t *vd); extern void vdev_deadman(vdev_t *vd, char *tag); diff --git a/include/sys/vdev_impl.h b/include/sys/vdev_impl.h index b9298c62d..90d607746 100644 --- a/include/sys/vdev_impl.h +++ b/include/sys/vdev_impl.h @@ -69,7 +69,7 @@ extern uint32_t zfs_vdev_async_write_max_active; * Virtual device operations */ typedef int vdev_open_func_t(vdev_t *vd, uint64_t *size, uint64_t *max_size, - uint64_t *ashift); + uint64_t *ashift, uint64_t *pshift); typedef void vdev_close_func_t(vdev_t *vd); typedef uint64_t vdev_asize_func_t(vdev_t *vd, uint64_t psize); typedef void vdev_io_start_func_t(zio_t *zio); @@ -216,6 +216,25 @@ struct vdev { uint64_t vdev_min_asize; /* min acceptable asize */ uint64_t vdev_max_asize; /* max acceptable asize */ uint64_t vdev_ashift; /* block alignment shift */ + + /* + * Logical block alignment shift + * + * The smallest sized/aligned I/O supported by the device. + */ + uint64_t vdev_logical_ashift; + /* + * Physical block alignment shift + * + * The device supports logical I/Os with vdev_logical_ashift + * size/alignment, but optimum performance will be achieved by + * aligning/sizing requests to vdev_physical_ashift. Smaller + * requests may be inflated or incur device level read-modify-write + * operations. + * + * May be 0 to indicate no preference (i.e. use vdev_logical_ashift). + */ + uint64_t vdev_physical_ashift; uint64_t vdev_state; /* see VDEV_STATE_* #defines */ uint64_t vdev_prevstate; /* used when reopening a vdev */ vdev_ops_t *vdev_ops; /* vdev operations */ @@ -586,6 +605,14 @@ extern int vdev_obsolete_counts_are_precise(vdev_t *vd, boolean_t *are_precise); */ int vdev_checkpoint_sm_object(vdev_t *vd, uint64_t *sm_obj); +/* + * Vdev ashift optimization tunables + */ +extern uint64_t zfs_vdev_min_auto_ashift; +extern uint64_t zfs_vdev_max_auto_ashift; +int param_set_min_auto_ashift(ZFS_MODULE_PARAM_ARGS); +int param_set_max_auto_ashift(ZFS_MODULE_PARAM_ARGS); + #ifdef __cplusplus } #endif |