Changeset View
Changeset View
Standalone View
Standalone View
sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
Show All 15 Lines | |||||
* fields enclosed by brackets "[]" replaced with your own identifying | * fields enclosed by brackets "[]" replaced with your own identifying | ||||
* information: Portions Copyright [yyyy] [name of copyright owner] | * information: Portions Copyright [yyyy] [name of copyright owner] | ||||
* | * | ||||
* CDDL HEADER END | * CDDL HEADER END | ||||
*/ | */ | ||||
/* | /* | ||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | ||||
* Copyright (c) 2012, 2018 by Delphix. All rights reserved. | * Copyright (c) 2012, 2020 by Delphix. All rights reserved. | ||||
* Copyright (c) 2017, Intel Corporation. | * Copyright (c) 2017, Intel Corporation. | ||||
* Copyright 2019 Joyent, Inc. | * Copyright 2019 Joyent, Inc. | ||||
*/ | */ | ||||
/* | /* | ||||
* Virtual Device Labels | * Virtual Device Labels | ||||
* --------------------- | * --------------------- | ||||
* | * | ||||
▲ Show 20 Lines • Show All 743 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) | vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) | ||||
{ | { | ||||
spa_t *spa = vd->vdev_spa; | spa_t *spa = vd->vdev_spa; | ||||
nvlist_t *label; | nvlist_t *label; | ||||
vdev_phys_t *vp; | vdev_phys_t *vp; | ||||
abd_t *vp_abd; | abd_t *vp_abd; | ||||
abd_t *pad2; | abd_t *bootenv; | ||||
uberblock_t *ub; | uberblock_t *ub; | ||||
abd_t *ub_abd; | abd_t *ub_abd; | ||||
zio_t *zio; | zio_t *zio; | ||||
char *buf; | char *buf; | ||||
size_t buflen; | size_t buflen; | ||||
int error; | int error; | ||||
uint64_t spare_guid, l2cache_guid; | uint64_t spare_guid, l2cache_guid; | ||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; | int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; | ||||
▲ Show 20 Lines • Show All 158 Lines • ▼ Show 20 Lines | vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) | ||||
*/ | */ | ||||
ub_abd = abd_alloc_linear(VDEV_UBERBLOCK_RING, B_TRUE); | ub_abd = abd_alloc_linear(VDEV_UBERBLOCK_RING, B_TRUE); | ||||
abd_zero(ub_abd, VDEV_UBERBLOCK_RING); | abd_zero(ub_abd, VDEV_UBERBLOCK_RING); | ||||
abd_copy_from_buf(ub_abd, &spa->spa_uberblock, sizeof (uberblock_t)); | abd_copy_from_buf(ub_abd, &spa->spa_uberblock, sizeof (uberblock_t)); | ||||
ub = abd_to_buf(ub_abd); | ub = abd_to_buf(ub_abd); | ||||
ub->ub_txg = 0; | ub->ub_txg = 0; | ||||
/* Initialize the 2nd padding area. */ | /* Initialize the 2nd padding area. */ | ||||
pad2 = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | bootenv = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | ||||
abd_zero(pad2, VDEV_PAD_SIZE); | abd_zero(bootenv, VDEV_PAD_SIZE); | ||||
/* | /* | ||||
* Write everything in parallel. | * Write everything in parallel. | ||||
*/ | */ | ||||
retry: | retry: | ||||
zio = zio_root(spa, NULL, NULL, flags); | zio = zio_root(spa, NULL, NULL, flags); | ||||
for (int l = 0; l < VDEV_LABELS; l++) { | for (int l = 0; l < VDEV_LABELS; l++) { | ||||
vdev_label_write(zio, vd, l, vp_abd, | vdev_label_write(zio, vd, l, vp_abd, | ||||
offsetof(vdev_label_t, vl_vdev_phys), | offsetof(vdev_label_t, vl_vdev_phys), | ||||
sizeof (vdev_phys_t), NULL, NULL, flags); | sizeof (vdev_phys_t), NULL, NULL, flags); | ||||
/* | /* | ||||
* Skip the 1st padding area. | * Skip the 1st padding area. | ||||
* Zero out the 2nd padding area where it might have | * Zero out the 2nd padding area where it might have | ||||
* left over data from previous filesystem format. | * left over data from previous filesystem format. | ||||
*/ | */ | ||||
vdev_label_write(zio, vd, l, pad2, | vdev_label_write(zio, vd, l, bootenv, | ||||
offsetof(vdev_label_t, vl_pad2), | offsetof(vdev_label_t, vl_be), | ||||
VDEV_PAD_SIZE, NULL, NULL, flags); | VDEV_PAD_SIZE, NULL, NULL, flags); | ||||
vdev_label_write(zio, vd, l, ub_abd, | vdev_label_write(zio, vd, l, ub_abd, | ||||
offsetof(vdev_label_t, vl_uberblock), | offsetof(vdev_label_t, vl_uberblock), | ||||
VDEV_UBERBLOCK_RING, NULL, NULL, flags); | VDEV_UBERBLOCK_RING, NULL, NULL, flags); | ||||
} | } | ||||
error = zio_wait(zio); | error = zio_wait(zio); | ||||
if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { | if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { | ||||
flags |= ZIO_FLAG_TRYHARD; | flags |= ZIO_FLAG_TRYHARD; | ||||
goto retry; | goto retry; | ||||
} | } | ||||
nvlist_free(label); | nvlist_free(label); | ||||
abd_free(pad2); | abd_free(bootenv); | ||||
abd_free(ub_abd); | abd_free(ub_abd); | ||||
abd_free(vp_abd); | abd_free(vp_abd); | ||||
/* | /* | ||||
* If this vdev hasn't been previously identified as a spare, then we | * If this vdev hasn't been previously identified as a spare, then we | ||||
* mark it as such only if a) we are labeling it as a spare, or b) it | * mark it as such only if a) we are labeling it as a spare, or b) it | ||||
* exists as a spare elsewhere in the system. Do the same for | * exists as a spare elsewhere in the system. Do the same for | ||||
* level 2 ARC devices. | * level 2 ARC devices. | ||||
*/ | */ | ||||
if (error == 0 && !vd->vdev_isspare && | if (error == 0 && !vd->vdev_isspare && | ||||
(reason == VDEV_LABEL_SPARE || | (reason == VDEV_LABEL_SPARE || | ||||
spa_spare_exists(vd->vdev_guid, NULL, NULL))) | spa_spare_exists(vd->vdev_guid, NULL, NULL))) | ||||
spa_spare_add(vd); | spa_spare_add(vd); | ||||
if (error == 0 && !vd->vdev_isl2cache && | if (error == 0 && !vd->vdev_isl2cache && | ||||
(reason == VDEV_LABEL_L2CACHE || | (reason == VDEV_LABEL_L2CACHE || | ||||
spa_l2cache_exists(vd->vdev_guid, NULL))) | spa_l2cache_exists(vd->vdev_guid, NULL))) | ||||
spa_l2cache_add(vd); | spa_l2cache_add(vd); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | |||||
* Done callback for vdev_label_read_bootenv_impl. If this is the first | |||||
* callback to finish, store our abd in the callback pointer. Otherwise, we | |||||
* just free our abd and return. | |||||
*/ | |||||
static void | |||||
vdev_label_read_bootenv_done(zio_t *zio) | |||||
{ | |||||
zio_t *rio = zio->io_private; | |||||
abd_t **cbp = rio->io_private; | |||||
ASSERT3U(zio->io_size, ==, VDEV_PAD_SIZE); | |||||
if (zio->io_error == 0) { | |||||
mutex_enter(&rio->io_lock); | |||||
if (*cbp == NULL) { | |||||
/* Will free this buffer in vdev_label_read_bootenv. */ | |||||
*cbp = zio->io_abd; | |||||
} else { | |||||
abd_free(zio->io_abd); | |||||
} | |||||
mutex_exit(&rio->io_lock); | |||||
} else { | |||||
abd_free(zio->io_abd); | |||||
} | |||||
} | |||||
static void | |||||
vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags) | |||||
{ | |||||
for (int c = 0; c < vd->vdev_children; c++) | |||||
vdev_label_read_bootenv_impl(zio, vd->vdev_child[c], flags); | |||||
/* | |||||
* We just use the first label that has a correct checksum; the | |||||
* bootloader should have rewritten them all to be the same on boot, | |||||
* and any changes we made since boot have been the same across all | |||||
* labels. | |||||
*/ | |||||
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) { | |||||
for (int l = 0; l < VDEV_LABELS; l++) { | |||||
vdev_label_read(zio, vd, l, | |||||
abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE), | |||||
offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE, | |||||
vdev_label_read_bootenv_done, zio, flags); | |||||
} | |||||
} | |||||
} | |||||
int | int | ||||
vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size) | vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv) | ||||
{ | { | ||||
spa_t *spa = vd->vdev_spa; | nvlist_t *config; | ||||
spa_t *spa = rvd->vdev_spa; | |||||
abd_t *abd = NULL; | |||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | | |||||
ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD; | |||||
ASSERT(bootenv); | |||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | |||||
zio_t *zio = zio_root(spa, NULL, &abd, flags); | |||||
vdev_label_read_bootenv_impl(zio, rvd, flags); | |||||
int err = zio_wait(zio); | |||||
if (abd != NULL) { | |||||
vdev_boot_envblock_t *vbe = abd_to_buf(abd); | |||||
err = nvlist_unpack(vbe->vbe_nvlist, | |||||
sizeof (vbe->vbe_nvlist), &config, 0); | |||||
if (err != 0) { | |||||
char *buf; | |||||
vbe->vbe_nvlist[sizeof (vbe->vbe_nvlist) - 1] = '\0'; | |||||
/* We have unstructured data, treat it as string. */ | |||||
buf = abd_to_buf(abd); | |||||
/* | |||||
* We can have zeroed block (no data); | |||||
* or FreeBSD zfs bootnext command string; | |||||
* or uint64_t 0 and envmap string. | |||||
*/ | |||||
if (*(uint64_t *)buf == 0) { | |||||
fnvlist_add_string(bootenv, "envmap", buf + 8); | |||||
} else { | |||||
fnvlist_add_string(bootenv, "command", buf); | |||||
} | |||||
} else { | |||||
fnvlist_merge(bootenv, config); | |||||
nvlist_free(config); | |||||
} | |||||
/* | |||||
* abd was allocated in vdev_label_read_bootenv_impl() | |||||
*/ | |||||
abd_free(abd); | |||||
/* | |||||
* If we managed to read any successfully, | |||||
* return success. | |||||
*/ | |||||
return (0); | |||||
} | |||||
return (err); | |||||
} | |||||
int | |||||
vdev_label_write_bootenv(vdev_t *vd, nvlist_t *env) | |||||
{ | |||||
zio_t *zio; | zio_t *zio; | ||||
abd_t *pad2; | spa_t *spa = vd->vdev_spa; | ||||
vdev_boot_envblock_t *bootenv; | |||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; | int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; | ||||
int error; | int error; | ||||
size_t nvsize; | |||||
char *nvbuf; | |||||
if (size > VDEV_PAD_SIZE) | error = nvlist_size(env, &nvsize, NV_ENCODE_XDR); | ||||
return (EINVAL); | if (error != 0) | ||||
return (SET_ERROR(error)); | |||||
if (!vd->vdev_ops->vdev_op_leaf) | if (nvsize >= sizeof (bootenv->vbe_nvlist)) { | ||||
return (ENODEV); | return (SET_ERROR(E2BIG)); | ||||
if (vdev_is_dead(vd)) | } | ||||
return (ENXIO); | |||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ||||
pad2 = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | error = ENXIO; | ||||
abd_zero(pad2, VDEV_PAD_SIZE); | for (int c = 0; c < vd->vdev_children; c++) { | ||||
abd_copy_from_buf(pad2, buf, size); | int child_err; | ||||
child_err = vdev_label_write_bootenv(vd->vdev_child[c], env); | |||||
/* | |||||
* As long as any of the disks managed to write all of their | |||||
* labels successfully, return success. | |||||
*/ | |||||
if (child_err == 0) | |||||
error = child_err; | |||||
} | |||||
if (!vd->vdev_ops->vdev_op_leaf || vdev_is_dead(vd) || | |||||
!vdev_writeable(vd)) { | |||||
return (error); | |||||
} | |||||
ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE); | |||||
abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | |||||
abd_zero(abd, VDEV_PAD_SIZE); | |||||
bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE); | |||||
nvbuf = bootenv->vbe_nvlist; | |||||
nvsize = sizeof (bootenv->vbe_nvlist); | |||||
error = nvlist_pack(env, &nvbuf, &nvsize, NV_ENCODE_XDR, KM_SLEEP); | |||||
if (error == 0) { | |||||
abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE); | |||||
} else { | |||||
abd_free(abd); | |||||
return (SET_ERROR(error)); | |||||
} | |||||
retry: | retry: | ||||
zio = zio_root(spa, NULL, NULL, flags); | zio = zio_root(spa, NULL, NULL, flags); | ||||
vdev_label_write(zio, vd, 0, pad2, | for (int l = 0; l < VDEV_LABELS; l++) { | ||||
offsetof(vdev_label_t, vl_pad2), | vdev_label_write(zio, vd, l, abd, | ||||
offsetof(vdev_label_t, vl_be), | |||||
VDEV_PAD_SIZE, NULL, NULL, flags); | VDEV_PAD_SIZE, NULL, NULL, flags); | ||||
} | |||||
error = zio_wait(zio); | error = zio_wait(zio); | ||||
if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { | if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { | ||||
flags |= ZIO_FLAG_TRYHARD; | flags |= ZIO_FLAG_TRYHARD; | ||||
goto retry; | goto retry; | ||||
} | } | ||||
abd_free(pad2); | abd_free(abd); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* ========================================================================== | * ========================================================================== | ||||
* uberblock load/sync | * uberblock load/sync | ||||
* ========================================================================== | * ========================================================================== | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 498 Lines • Show Last 20 Lines |