Changeset View
Changeset View
Standalone View
Standalone View
sys/contrib/openzfs/module/zfs/vdev_label.c
Show First 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | |||||
#include <sys/vdev_impl.h> | #include <sys/vdev_impl.h> | ||||
#include <sys/uberblock_impl.h> | #include <sys/uberblock_impl.h> | ||||
#include <sys/metaslab.h> | #include <sys/metaslab.h> | ||||
#include <sys/metaslab_impl.h> | #include <sys/metaslab_impl.h> | ||||
#include <sys/zio.h> | #include <sys/zio.h> | ||||
#include <sys/dsl_scan.h> | #include <sys/dsl_scan.h> | ||||
#include <sys/abd.h> | #include <sys/abd.h> | ||||
#include <sys/fs/zfs.h> | #include <sys/fs/zfs.h> | ||||
#include <sys/byteorder.h> | |||||
#include <sys/zfs_bootenv.h> | |||||
/* | /* | ||||
* Basic routines to read and write from a vdev label. | * Basic routines to read and write from a vdev label. | ||||
* Used throughout the rest of this file. | * Used throughout the rest of this file. | ||||
*/ | */ | ||||
uint64_t | uint64_t | ||||
vdev_label_offset(uint64_t psize, int l, uint64_t offset) | vdev_label_offset(uint64_t psize, int l, uint64_t offset) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,068 Lines • ▼ Show 20 Lines | vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags) | ||||
for (int c = 0; c < vd->vdev_children; c++) | for (int c = 0; c < vd->vdev_children; c++) | ||||
vdev_label_read_bootenv_impl(zio, vd->vdev_child[c], flags); | vdev_label_read_bootenv_impl(zio, vd->vdev_child[c], flags); | ||||
/* | /* | ||||
* We just use the first label that has a correct checksum; the | * We just use the first label that has a correct checksum; the | ||||
* bootloader should have rewritten them all to be the same on boot, | * 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 | * and any changes we made since boot have been the same across all | ||||
* labels. | * labels. | ||||
* | |||||
* While grub supports writing to all four labels, other bootloaders | |||||
* don't, so we only use the first two labels to store boot | |||||
* information. | |||||
*/ | */ | ||||
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) { | if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) { | ||||
for (int l = 0; l < VDEV_LABELS / 2; l++) { | for (int l = 0; l < VDEV_LABELS; l++) { | ||||
vdev_label_read(zio, vd, l, | vdev_label_read(zio, vd, l, | ||||
abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE), | abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE), | ||||
offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE, | offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE, | ||||
vdev_label_read_bootenv_done, zio, flags); | vdev_label_read_bootenv_done, zio, flags); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
int | int | ||||
vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *command) | vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv) | ||||
{ | { | ||||
nvlist_t *config; | |||||
spa_t *spa = rvd->vdev_spa; | spa_t *spa = rvd->vdev_spa; | ||||
abd_t *abd = NULL; | abd_t *abd = NULL; | ||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | | int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | | ||||
ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD; | ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD; | ||||
ASSERT(command); | ASSERT(bootenv); | ||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ||||
zio_t *zio = zio_root(spa, NULL, &abd, flags); | zio_t *zio = zio_root(spa, NULL, &abd, flags); | ||||
vdev_label_read_bootenv_impl(zio, rvd, flags); | vdev_label_read_bootenv_impl(zio, rvd, flags); | ||||
int err = zio_wait(zio); | int err = zio_wait(zio); | ||||
if (abd != NULL) { | if (abd != NULL) { | ||||
char *buf; | |||||
vdev_boot_envblock_t *vbe = abd_to_buf(abd); | vdev_boot_envblock_t *vbe = abd_to_buf(abd); | ||||
if (vbe->vbe_version != VB_RAW) { | |||||
abd_free(abd); | vbe->vbe_version = ntohll(vbe->vbe_version); | ||||
return (SET_ERROR(ENOTSUP)); | switch (vbe->vbe_version) { | ||||
case VB_RAW: | |||||
/* | |||||
* if we have textual data in vbe_bootenv, create nvlist | |||||
* with key "envmap". | |||||
*/ | |||||
if (*vbe->vbe_bootenv == '\0') { | |||||
fnvlist_add_uint64(bootenv, BOOTENV_VERSION, | |||||
VB_NVLIST); | |||||
break; | |||||
} | } | ||||
fnvlist_add_uint64(bootenv, BOOTENV_VERSION, VB_RAW); | |||||
vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0'; | vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0'; | ||||
fnvlist_add_string(command, "envmap", vbe->vbe_bootenv); | fnvlist_add_string(bootenv, GRUB_ENVMAP, | ||||
/* abd was allocated in vdev_label_read_bootenv_impl() */ | vbe->vbe_bootenv); | ||||
break; | |||||
case VB_NVLIST: | |||||
err = nvlist_unpack(vbe->vbe_bootenv, | |||||
sizeof (vbe->vbe_bootenv), &config, 0); | |||||
if (err == 0) { | |||||
fnvlist_merge(bootenv, config); | |||||
nvlist_free(config); | |||||
break; | |||||
} | |||||
/* FALLTHROUGH */ | |||||
default: | |||||
/* Check for FreeBSD zfs bootonce command string */ | |||||
buf = abd_to_buf(abd); | |||||
if (*buf == '\0') { | |||||
fnvlist_add_uint64(bootenv, BOOTENV_VERSION, | |||||
VB_NVLIST); | |||||
break; | |||||
} | |||||
fnvlist_add_string(bootenv, FREEBSD_BOOTONCE, buf); | |||||
} | |||||
/* | |||||
* abd was allocated in vdev_label_read_bootenv_impl() | |||||
*/ | |||||
abd_free(abd); | abd_free(abd); | ||||
/* If we managed to read any successfully, return success. */ | /* | ||||
* If we managed to read any successfully, | |||||
* return success. | |||||
*/ | |||||
return (0); | return (0); | ||||
} | } | ||||
return (err); | return (err); | ||||
} | } | ||||
int | int | ||||
vdev_label_write_bootenv(vdev_t *vd, char *envmap) | vdev_label_write_bootenv(vdev_t *vd, nvlist_t *env) | ||||
{ | { | ||||
zio_t *zio; | zio_t *zio; | ||||
spa_t *spa = vd->vdev_spa; | spa_t *spa = vd->vdev_spa; | ||||
vdev_boot_envblock_t *bootenv; | 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 = ENXIO; | int error; | ||||
size_t nvsize; | |||||
char *nvbuf; | |||||
if (strlen(envmap) >= sizeof (bootenv->vbe_bootenv)) { | error = nvlist_size(env, &nvsize, NV_ENCODE_XDR); | ||||
if (error != 0) | |||||
return (SET_ERROR(error)); | |||||
if (nvsize >= sizeof (bootenv->vbe_bootenv)) { | |||||
return (SET_ERROR(E2BIG)); | return (SET_ERROR(E2BIG)); | ||||
} | } | ||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); | ||||
error = ENXIO; | |||||
for (int c = 0; c < vd->vdev_children; c++) { | for (int c = 0; c < vd->vdev_children; c++) { | ||||
int child_err = vdev_label_write_bootenv(vd->vdev_child[c], | int child_err; | ||||
envmap); | |||||
child_err = vdev_label_write_bootenv(vd->vdev_child[c], env); | |||||
/* | /* | ||||
* As long as any of the disks managed to write all of their | * As long as any of the disks managed to write all of their | ||||
* labels successfully, return success. | * labels successfully, return success. | ||||
*/ | */ | ||||
if (child_err == 0) | if (child_err == 0) | ||||
error = child_err; | error = child_err; | ||||
} | } | ||||
if (!vd->vdev_ops->vdev_op_leaf || vdev_is_dead(vd) || | if (!vd->vdev_ops->vdev_op_leaf || vdev_is_dead(vd) || | ||||
!vdev_writeable(vd)) { | !vdev_writeable(vd)) { | ||||
return (error); | return (error); | ||||
} | } | ||||
ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE); | ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE); | ||||
abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); | ||||
abd_zero(abd, VDEV_PAD_SIZE); | abd_zero(abd, VDEV_PAD_SIZE); | ||||
bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE); | bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE); | ||||
nvbuf = bootenv->vbe_bootenv; | |||||
nvsize = sizeof (bootenv->vbe_bootenv); | |||||
char *buf = bootenv->vbe_bootenv; | bootenv->vbe_version = fnvlist_lookup_uint64(env, BOOTENV_VERSION); | ||||
(void) strlcpy(buf, envmap, sizeof (bootenv->vbe_bootenv)); | switch (bootenv->vbe_version) { | ||||
bootenv->vbe_version = VB_RAW; | case VB_RAW: | ||||
if (nvlist_lookup_string(env, GRUB_ENVMAP, &nvbuf) == 0) { | |||||
(void) strlcpy(bootenv->vbe_bootenv, nvbuf, nvsize); | |||||
} | |||||
error = 0; | |||||
break; | |||||
case VB_NVLIST: | |||||
error = nvlist_pack(env, &nvbuf, &nvsize, NV_ENCODE_XDR, | |||||
KM_SLEEP); | |||||
break; | |||||
default: | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
if (error == 0) { | |||||
bootenv->vbe_version = htonll(bootenv->vbe_version); | |||||
abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE); | 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); | ||||
for (int l = 0; l < VDEV_LABELS / 2; l++) { | for (int l = 0; l < VDEV_LABELS; l++) { | ||||
vdev_label_write(zio, vd, l, abd, | vdev_label_write(zio, vd, l, abd, | ||||
offsetof(vdev_label_t, vl_be), | 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; | ||||
▲ Show 20 Lines • Show All 569 Lines • Show Last 20 Lines |