mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
Distributed Spare (dRAID) Feature
This patch adds a new top-level vdev type called dRAID, which stands
for Distributed parity RAID. This pool configuration allows all dRAID
vdevs to participate when rebuilding to a distributed hot spare device.
This can substantially reduce the total time required to restore full
parity to pool with a failed device.
A dRAID pool can be created using the new top-level `draid` type.
Like `raidz`, the desired redundancy is specified after the type:
`draid[1,2,3]`. No additional information is required to create the
pool and reasonable default values will be chosen based on the number
of child vdevs in the dRAID vdev.
zpool create <pool> draid[1,2,3] <vdevs...>
Unlike raidz, additional optional dRAID configuration values can be
provided as part of the draid type as colon separated values. This
allows administrators to fully specify a layout for either performance
or capacity reasons. The supported options include:
zpool create <pool> \
draid[<parity>][:<data>d][:<children>c][:<spares>s] \
<vdevs...>
- draid[parity] - Parity level (default 1)
- draid[:<data>d] - Data devices per group (default 8)
- draid[:<children>c] - Expected number of child vdevs
- draid[:<spares>s] - Distributed hot spares (default 0)
Abbreviated example `zpool status` output for a 68 disk dRAID pool
with two distributed spares using special allocation classes.
```
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
slag7 ONLINE 0 0 0
draid2:8d:68c:2s-0 ONLINE 0 0 0
L0 ONLINE 0 0 0
L1 ONLINE 0 0 0
...
U25 ONLINE 0 0 0
U26 ONLINE 0 0 0
spare-53 ONLINE 0 0 0
U27 ONLINE 0 0 0
draid2-0-0 ONLINE 0 0 0
U28 ONLINE 0 0 0
U29 ONLINE 0 0 0
...
U42 ONLINE 0 0 0
U43 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
L5 ONLINE 0 0 0
U5 ONLINE 0 0 0
mirror-2 ONLINE 0 0 0
L6 ONLINE 0 0 0
U6 ONLINE 0 0 0
spares
draid2-0-0 INUSE currently in use
draid2-0-1 AVAIL
```
When adding test coverage for the new dRAID vdev type the following
options were added to the ztest command. These options are leverages
by zloop.sh to test a wide range of dRAID configurations.
-K draid|raidz|random - kind of RAID to test
-D <value> - dRAID data drives per group
-S <value> - dRAID distributed hot spares
-R <value> - RAID parity (raidz or dRAID)
The zpool_create, zpool_import, redundancy, replacement and fault
test groups have all been updated provide test coverage for the
dRAID feature.
Co-authored-by: Isaac Huang <he.huang@intel.com>
Co-authored-by: Mark Maybee <mmaybee@cray.com>
Co-authored-by: Don Brady <don.brady@delphix.com>
Co-authored-by: Matthew Ahrens <mahrens@delphix.com>
Co-authored-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Mark Maybee <mmaybee@cray.com>
Reviewed-by: Matt Ahrens <matt@delphix.com>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #10102
This commit is contained in:
+39
-23
@@ -142,6 +142,7 @@
|
||||
#include <sys/zap.h>
|
||||
#include <sys/vdev.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
#include <sys/vdev_draid.h>
|
||||
#include <sys/uberblock_impl.h>
|
||||
#include <sys/metaslab.h>
|
||||
#include <sys/metaslab_impl.h>
|
||||
@@ -453,31 +454,13 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
|
||||
if (vd->vdev_fru != NULL)
|
||||
fnvlist_add_string(nv, ZPOOL_CONFIG_FRU, vd->vdev_fru);
|
||||
|
||||
if (vd->vdev_nparity != 0) {
|
||||
ASSERT(strcmp(vd->vdev_ops->vdev_op_type,
|
||||
VDEV_TYPE_RAIDZ) == 0);
|
||||
if (vd->vdev_ops->vdev_op_config_generate != NULL)
|
||||
vd->vdev_ops->vdev_op_config_generate(vd, nv);
|
||||
|
||||
/*
|
||||
* Make sure someone hasn't managed to sneak a fancy new vdev
|
||||
* into a crufty old storage pool.
|
||||
*/
|
||||
ASSERT(vd->vdev_nparity == 1 ||
|
||||
(vd->vdev_nparity <= 2 &&
|
||||
spa_version(spa) >= SPA_VERSION_RAIDZ2) ||
|
||||
(vd->vdev_nparity <= 3 &&
|
||||
spa_version(spa) >= SPA_VERSION_RAIDZ3));
|
||||
|
||||
/*
|
||||
* Note that we'll add the nparity tag even on storage pools
|
||||
* that only support a single parity device -- older software
|
||||
* will just ignore it.
|
||||
*/
|
||||
fnvlist_add_uint64(nv, ZPOOL_CONFIG_NPARITY, vd->vdev_nparity);
|
||||
}
|
||||
|
||||
if (vd->vdev_wholedisk != -1ULL)
|
||||
if (vd->vdev_wholedisk != -1ULL) {
|
||||
fnvlist_add_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
|
||||
vd->vdev_wholedisk);
|
||||
}
|
||||
|
||||
if (vd->vdev_not_present && !(flags & VDEV_CONFIG_MISSING))
|
||||
fnvlist_add_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, 1);
|
||||
@@ -785,6 +768,14 @@ vdev_label_read_config(vdev_t *vd, uint64_t txg)
|
||||
if (!vdev_readable(vd))
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* The label for a dRAID distributed spare is not stored on disk.
|
||||
* Instead it is generated when needed which allows us to bypass
|
||||
* the pipeline when reading the config from the label.
|
||||
*/
|
||||
if (vd->vdev_ops == &vdev_draid_spare_ops)
|
||||
return (vdev_draid_read_config_spare(vd));
|
||||
|
||||
vp_abd = abd_alloc_linear(sizeof (vdev_phys_t), B_TRUE);
|
||||
vp = abd_to_buf(vp_abd);
|
||||
|
||||
@@ -1497,7 +1488,8 @@ vdev_uberblock_load_impl(zio_t *zio, vdev_t *vd, int flags,
|
||||
for (int c = 0; c < vd->vdev_children; c++)
|
||||
vdev_uberblock_load_impl(zio, vd->vdev_child[c], flags, cbp);
|
||||
|
||||
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
|
||||
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd) &&
|
||||
vd->vdev_ops != &vdev_draid_spare_ops) {
|
||||
for (int l = 0; l < VDEV_LABELS; l++) {
|
||||
for (int n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
|
||||
vdev_label_read(zio, vd, l,
|
||||
@@ -1586,6 +1578,13 @@ vdev_copy_uberblocks(vdev_t *vd)
|
||||
SCL_STATE);
|
||||
ASSERT(vd->vdev_ops->vdev_op_leaf);
|
||||
|
||||
/*
|
||||
* No uberblocks are stored on distributed spares, they may be
|
||||
* safely skipped when expanding a leaf vdev.
|
||||
*/
|
||||
if (vd->vdev_ops == &vdev_draid_spare_ops)
|
||||
return;
|
||||
|
||||
spa_config_enter(vd->vdev_spa, locks, FTAG, RW_READER);
|
||||
|
||||
ub_abd = abd_alloc_linear(VDEV_UBERBLOCK_SIZE(vd), B_TRUE);
|
||||
@@ -1647,6 +1646,15 @@ vdev_uberblock_sync(zio_t *zio, uint64_t *good_writes,
|
||||
if (!vdev_writeable(vd))
|
||||
return;
|
||||
|
||||
/*
|
||||
* There's no need to write uberblocks to a distributed spare, they
|
||||
* are already stored on all the leaves of the parent dRAID. For
|
||||
* this same reason vdev_uberblock_load_impl() skips distributed
|
||||
* spares when reading uberblocks.
|
||||
*/
|
||||
if (vd->vdev_ops == &vdev_draid_spare_ops)
|
||||
return;
|
||||
|
||||
/* If the vdev was expanded, need to copy uberblock rings. */
|
||||
if (vd->vdev_state == VDEV_STATE_HEALTHY &&
|
||||
vd->vdev_copy_uberblocks == B_TRUE) {
|
||||
@@ -1763,6 +1771,14 @@ vdev_label_sync(zio_t *zio, uint64_t *good_writes,
|
||||
if (!vdev_writeable(vd))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The top-level config never needs to be written to a distributed
|
||||
* spare. When read vdev_dspare_label_read_config() will generate
|
||||
* the config for the vdev_label_read_config().
|
||||
*/
|
||||
if (vd->vdev_ops == &vdev_draid_spare_ops)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Generate a label describing the top-level config to which we belong.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user