diff options
Diffstat (limited to 'usr/src/lib')
29 files changed, 1420 insertions, 269 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index d6d16ece16..2418a2ee02 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -251,6 +251,7 @@ SUBDIRS += \ libxnet \ libzdoor \ libzfs \ + libzfsbootenv \ libzfs_core \ libzfs_jni \ libzonecfg \ @@ -614,7 +615,8 @@ krb5: gss_mechs/mech_krb5 libtecla libldap5 libads: libnsl libadt_jni: libbsm libadutils: libldap5 libresolv2 -libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl +libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl \ + libzfsbootenv libbsm: libinetutil libscf libsecdb libtsol libbunyan: libumem libcfgadm: libdevinfo @@ -719,6 +721,7 @@ libvscan: libscf libsecdb libzdoor: libc libzonecfg libcontract libzfs: libdevid libgen libuutil libavl libefi libidmap \ libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libzutil +libzfsbootenv: libzfs libnvpair libzfs_core: libnvpair libzfs_jni: libdiskmgt libzfs libzutil libzonecfg: libuuid libsysevent libsec libbrand libpool libscf libproc \ diff --git a/usr/src/lib/libbe/Makefile.com b/usr/src/lib/libbe/Makefile.com index 969de425f3..ede66a0afc 100644 --- a/usr/src/lib/libbe/Makefile.com +++ b/usr/src/lib/libbe/Makefile.com @@ -52,7 +52,7 @@ INCS += -I$(SRCDIR) -I$(SRC)/cmd/boot/common -I$(SRC)/common/ficl CSTD= $(CSTD_GNU99) LDLIBS += -lficl-sys -lzfs -linstzones -luuid -lnvpair -lc -lgen -LDLIBS += -ldevinfo -lefi +LDLIBS += -ldevinfo -lefi -lzfsbootenv CPPFLAGS += $(INCS) CLOBBERFILES += $(LIBRARY) diff --git a/usr/src/lib/libbe/common/be_activate.c b/usr/src/lib/libbe/common/be_activate.c index 1c2454991d..6133f2ca59 100644 --- a/usr/src/lib/libbe/common/be_activate.c +++ b/usr/src/lib/libbe/common/be_activate.c @@ -47,6 +47,7 @@ #include <libbe.h> #include <libbe_priv.h> +#include <libzfsbootenv.h> char *mnttab = MNTTAB; @@ -92,6 +93,8 @@ be_activate(nvlist_t *be_attrs) { int ret = BE_SUCCESS; char *be_name = NULL; + be_nextboot_state_t nextboot; + boolean_t next_boot; /* Initialize libzfs handle */ if (!be_zfs_init()) @@ -114,7 +117,17 @@ be_activate(nvlist_t *be_attrs) return (BE_ERR_INVAL); } - ret = _be_activate(be_name); + if (nvlist_lookup_boolean_value(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT, + &next_boot) == 0) { + if (next_boot) + nextboot = BE_NEXTBOOT_SET; + else + nextboot = BE_NEXTBOOT_UNSET; + } else { + nextboot = BE_NEXTBOOT_IGNORE; + } + + ret = _be_activate(be_name, nextboot); be_zfs_fini(); @@ -206,6 +219,7 @@ be_installboot(nvlist_t *be_attrs) * Description: This does the actual work described in be_activate. * Parameters: * be_name - pointer to the name of BE to activate. + * nextboot - flag to ignore, set or unset nextboot * * Return: * BE_SUCCESS - Success @@ -214,7 +228,7 @@ be_installboot(nvlist_t *be_attrs) * Public */ int -_be_activate(char *be_name) +_be_activate(char *be_name, be_nextboot_state_t nextboot) { be_transaction_data_t cb = { 0 }; zfs_handle_t *zhp = NULL; @@ -233,6 +247,9 @@ _be_activate(char *be_name) if (be_name == NULL) return (BE_ERR_INVAL); + if (nextboot == BE_NEXTBOOT_SET && getzoneid() != GLOBAL_ZONEID) + return (BE_ERR_INVAL); + /* Set obe_name to be_name in the cb structure */ cb.obe_name = be_name; @@ -288,11 +305,31 @@ _be_activate(char *be_name) } if (getzoneid() == GLOBAL_ZONEID) { - if ((ret = set_bootfs(be_nodes->be_rpool, - root_ds)) != BE_SUCCESS) { - be_print_err(gettext("be_activate: failed to set " - "bootfs pool property for %s\n"), root_ds); - goto done; + switch (nextboot) { + case BE_NEXTBOOT_SET: + if ((ret = lzbe_set_boot_device(be_nodes->be_rpool, + lzbe_add, root_ds)) != 0) { + be_print_err(gettext("be_activate: failed to " + "set nextboot for %s\n"), root_ds); + goto done; + } + break; + case BE_NEXTBOOT_UNSET: + if ((ret = lzbe_set_boot_device(be_nodes->be_rpool, + lzbe_add, "")) != 0) { + be_print_err(gettext("be_activate: failed to " + "clear nextboot for %s\n"), root_ds); + goto done; + } + break; + default: + if ((ret = set_bootfs(be_nodes->be_rpool, + root_ds)) != BE_SUCCESS) { + be_print_err(gettext("be_activate: failed to " + "set bootfs pool property for %s\n"), + root_ds); + goto done; + } } } @@ -415,7 +452,8 @@ be_activate_current_be(void) return (ret); } - if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) { + ret = _be_activate(bt.obe_name, BE_NEXTBOOT_IGNORE); + if (ret != BE_SUCCESS) { be_print_err(gettext("be_activate_current_be: failed to " "activate %s\n"), bt.obe_name); return (ret); diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c index 926b7260da..3e0833ea83 100644 --- a/usr/src/lib/libbe/common/be_list.c +++ b/usr/src/lib/libbe/common/be_list.c @@ -47,6 +47,7 @@ #include <libbe.h> #include <libbe_priv.h> +#include <libzfsbootenv.h> /* * Callback data used for zfs_iter calls. @@ -986,13 +987,8 @@ be_qsort_compare_datasets(const void *x, const void *y) * Private */ static int -be_get_node_data( - zfs_handle_t *zhp, - be_node_list_t *be_node, - char *be_name, - const char *rpool, - char *current_be, - char *be_ds) +be_get_node_data(zfs_handle_t *zhp, be_node_list_t *be_node, char *be_name, + const char *rpool, char *current_be, char *be_ds) { char prop_buf[MAXPATHLEN]; nvlist_t *userprops = NULL; @@ -1041,6 +1037,8 @@ be_get_node_data( be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED); if (getzoneid() == GLOBAL_ZONEID) { + char *nextboot; + if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { be_print_err(gettext("be_get_node_data: failed to open " "pool (%s): %s\n"), rpool, @@ -1048,6 +1046,16 @@ be_get_node_data( return (zfs_err_to_be_err(g_zfs)); } + /* Set nextboot info */ + be_node->be_active_next = B_FALSE; + if (lzbe_get_boot_device(rpool, &nextboot) == 0) { + if (nextboot != NULL) { + if (strcmp(nextboot, be_ds) == 0) + be_node->be_active_next = B_TRUE; + free(nextboot); + } + } + (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN, NULL, B_FALSE); if (be_has_grub() && (be_default_grub_bootfs(rpool, diff --git a/usr/src/lib/libbe/common/libbe.h b/usr/src/lib/libbe/common/libbe.h index b670d86ce5..774fe1ef9f 100644 --- a/usr/src/lib/libbe/common/libbe.h +++ b/usr/src/lib/libbe/common/libbe.h @@ -67,6 +67,7 @@ extern "C" { #define BE_ATTR_ACTIVE "active" #define BE_ATTR_ACTIVE_ON_BOOT "active_boot" +#define BE_ATTR_ACTIVE_NEXTBOOT "active_nextboot" #define BE_ATTR_GLOBAL_ACTIVE "global_active" #define BE_ATTR_SPACE "space_used" #define BE_ATTR_DATASET "dataset" @@ -174,6 +175,7 @@ typedef struct be_snapshot_list { typedef struct be_node_list { boolean_t be_mounted; /* is BE currently mounted */ + boolean_t be_active_next; /* is this BE active on next boot */ boolean_t be_active_on_boot; /* is this BE active on boot */ boolean_t be_active; /* is this BE active currently */ boolean_t be_global_active; /* is zone's BE associated with */ diff --git a/usr/src/lib/libbe/common/libbe_priv.h b/usr/src/lib/libbe/common/libbe_priv.h index f2960c4f17..ace201577f 100644 --- a/usr/src/lib/libbe/common/libbe_priv.h +++ b/usr/src/lib/libbe/common/libbe_priv.h @@ -142,6 +142,12 @@ struct be_defaults { char be_deflt_bename_starts_with[ZFS_MAX_DATASET_NAME_LEN]; }; +typedef enum be_nextboot_state { + BE_NEXTBOOT_IGNORE = -1, + BE_NEXTBOOT_SET, + BE_NEXTBOOT_UNSET +} be_nextboot_state_t; + /* Library globals */ extern libzfs_handle_t *g_zfs; extern boolean_t do_print; @@ -201,7 +207,7 @@ int zfs_err_to_be_err(libzfs_handle_t *); int errno_to_be_err(int); /* be_activate.c */ -int _be_activate(char *); +int _be_activate(char *, be_nextboot_state_t); int be_activate_current_be(void); boolean_t be_is_active_on_boot(char *); diff --git a/usr/src/lib/libc/port/threads/pthr_attr.c b/usr/src/lib/libc/port/threads/pthr_attr.c index 7cfc970120..e5b3116eaf 100644 --- a/usr/src/lib/libc/port/threads/pthr_attr.c +++ b/usr/src/lib/libc/port/threads/pthr_attr.c @@ -529,16 +529,16 @@ pthread_attr_getname_np(pthread_attr_t *attr, char *buf, size_t len) /* * This function is a common BSD extension to pthread which is used to obtain * the attributes of a thread that might have changed after its creation, for - * example, it's stack address. + * example, its stack address. * * Note, there is no setattr analogue, nor do we desire to add one at this time. * Similarly there is no native threads API analogue (nor should we add one for * C11). * * The astute reader may note that there is a GNU version of this called - * pthread_getattr_np(). The two functions are similar, but subtley different in - * a rather important way. While the pthread_attr_get_np() expects to be given - * a pthread_attr_t that has had pthread_attr_init() called on in, + * pthread_getattr_np(). The two functions are similar, but subtly different in + * a rather important way. While pthread_attr_get_np() expects to be given + * a pthread_attr_t that has had pthread_attr_init() called on it, * pthread_getattr_np() does not. However, on GNU systems, where the function * originates, the pthread_attr_t is not opaque and thus it is entirely safe to * both call pthread_attr_init() and then call pthread_getattr_np() on the same @@ -556,7 +556,7 @@ pthread_attr_get_np(pthread_t tid, pthread_attr_t *attr) /* * To ensure that information about the target thread does not change or - * disappear while we're trying to interrogate it, we grab the uwlp + * disappear while we're trying to interrogate it, we grab the ulwp * lock. */ if (self->ul_lwpid == tid) { diff --git a/usr/src/lib/libc/port/threads/pthread.c b/usr/src/lib/libc/port/threads/pthread.c index dc0123361d..68c0fcbfa5 100644 --- a/usr/src/lib/libc/port/threads/pthread.c +++ b/usr/src/lib/libc/port/threads/pthread.c @@ -131,8 +131,8 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, flag, &tid, ap->guardsize, ap->name); if (error == 0) { /* - * Record the original inheritence value for - * pthread_getattr_np(). We should always be able to find the + * Record the original inheritance value for + * pthread_attr_get_np(). We should always be able to find the * thread. */ (void) _thr_setinherit(tid, ap->inherit); diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c index 9441aa6ed7..07f344ebaa 100644 --- a/usr/src/lib/libctf/common/ctf_convert.c +++ b/usr/src/lib/libctf/common/ctf_convert.c @@ -139,7 +139,7 @@ ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, for (i = 0; i < NCONVERTS; i++) { fp = NULL; - err = ctf_converters[i](fd, elf, nthrs, flags, + err = ctf_converters[i](fd, elf, bsize, nthrs, flags, &fp, errbuf, errlen); if (err != ECTF_CONVNODEBUG) @@ -169,8 +169,8 @@ ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, } ctf_file_t * -ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp, - char *errbuf, size_t errlen) +ctf_fdconvert(int fd, const char *label, uint_t bsize, uint_t nthrs, + uint_t flags, int *errp, char *errbuf, size_t errlen) { int err; Elf *elf; @@ -185,7 +185,8 @@ ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp, return (NULL); } - fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen); + fp = ctf_elfconvert(fd, elf, label, bsize, nthrs, flags, errp, errbuf, + errlen); (void) elf_end(elf); return (fp); diff --git a/usr/src/lib/libctf/common/ctf_dwarf.c b/usr/src/lib/libctf/common/ctf_dwarf.c index 388cb20489..d91888d6c1 100644 --- a/usr/src/lib/libctf/common/ctf_dwarf.c +++ b/usr/src/lib/libctf/common/ctf_dwarf.c @@ -30,6 +30,7 @@ /* * Copyright 2020 Joyent, Inc. * Copyright 2020 Robert Mustacchi + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ /* @@ -206,6 +207,8 @@ #include <dwarf.h> #include <libgen.h> #include <workq.h> +#include <thread.h> +#include <macros.h> #include <errno.h> #define DWARF_VERSION_TWO 2 @@ -254,12 +257,14 @@ typedef struct ctf_dwbitf { */ typedef struct ctf_die { Elf *cu_elf; /* shared libelf handle */ + int cu_fd; /* shared file descriptor */ char *cu_name; /* basename of the DIE */ ctf_merge_t *cu_cmh; /* merge handle */ ctf_list_t cu_vars; /* List of variables */ ctf_list_t cu_funcs; /* List of functions */ ctf_list_t cu_bitfields; /* Bit field members */ Dwarf_Debug cu_dwarf; /* libdwarf handle */ + mutex_t *cu_dwlock; /* libdwarf lock */ Dwarf_Die cu_cu; /* libdwarf compilation unit */ Dwarf_Off cu_cuoff; /* cu's offset */ Dwarf_Off cu_maxoff; /* maximum offset */ @@ -277,6 +282,7 @@ typedef struct ctf_die { ctf_id_t cu_longtid; /* id for a 'long' */ } ctf_cu_t; +static int ctf_dwarf_init_die(ctf_cu_t *); static int ctf_dwarf_offset(ctf_cu_t *, Dwarf_Die, Dwarf_Off *); static int ctf_dwarf_convert_die(ctf_cu_t *, Dwarf_Die); static int ctf_dwarf_convert_type(ctf_cu_t *, Dwarf_Die, ctf_id_t *, int); @@ -286,6 +292,13 @@ static int ctf_dwarf_function_count(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *, static int ctf_dwarf_convert_fargs(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *, ctf_id_t *); +#define DWARF_LOCK(cup) \ + if ((cup)->cu_dwlock != NULL) \ + mutex_enter((cup)->cu_dwlock) +#define DWARF_UNLOCK(cup) \ + if ((cup)->cu_dwlock != NULL) \ + mutex_exit((cup)->cu_dwlock) + /* * This is a generic way to set a CTF Conversion backend error depending on what * we were doing. Unless it was one of a specific set of errors that don't @@ -305,7 +318,8 @@ ctf_dwarf_error(ctf_cu_t *cup, ctf_file_t *cfp, int err, const char *fmt, ...) if (err == ENOMEM) return (err); - ret = snprintf(cup->cu_errbuf, rem, "die %s: ", cup->cu_name); + ret = snprintf(cup->cu_errbuf, rem, "die %s: ", + cup->cu_name != NULL ? cup->cu_name : "NULL"); if (ret < 0) goto err; off += ret; @@ -429,7 +443,10 @@ ctf_dwarf_attribute(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, int ret; Dwarf_Error derr; - if ((ret = dwarf_attr(die, name, attrp, &derr)) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_attr(die, name, attrp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); if (ret == DW_DLV_NO_ENTRY) { *attrp = NULL; @@ -441,6 +458,14 @@ ctf_dwarf_attribute(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, return (ECTF_CONVBKERR); } +static void +ctf_dwarf_dealloc(ctf_cu_t *cup, Dwarf_Ptr ptr, Dwarf_Unsigned type) +{ + DWARF_LOCK(cup); + dwarf_dealloc(cup->cu_dwarf, ptr, type); + DWARF_UNLOCK(cup); +} + static int ctf_dwarf_ref(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp) { @@ -451,8 +476,11 @@ ctf_dwarf_ref(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp) if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formref(attr, refp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formref(attr, refp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -474,8 +502,10 @@ ctf_dwarf_refdie(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, return (ret); off += cup->cu_cuoff; - if ((ret = dwarf_offdie(cup->cu_dwarf, off, diep, &derr)) != - DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_offdie(cup->cu_dwarf, off, diep, &derr); + DWARF_UNLOCK(cup); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get die from offset %" DW_PR_DUu ": %s\n", off, dwarf_errmsg(derr)); @@ -496,8 +526,11 @@ ctf_dwarf_signed(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formsdata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formsdata(attr, valp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -518,8 +551,11 @@ ctf_dwarf_unsigned(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formudata(attr, valp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -540,8 +576,11 @@ ctf_dwarf_boolean(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formflag(attr, val, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formflag(attr, val, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -564,12 +603,15 @@ ctf_dwarf_string(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, char **strp) if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formstring(attr, &s, &derr) == DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_formstring(attr, &s, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { if ((*strp = ctf_strdup(s)) == NULL) ret = ENOMEM; else ret = 0; - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ret); } @@ -598,17 +640,22 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) enum Dwarf_Form_Class class; if ((ret = ctf_dwarf_attribute(cup, die, DW_AT_data_member_location, - &attr)) != 0) + &attr)) != 0) { return (ret); + } - if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_whatform(attr, &form, &derr); + DWARF_UNLOCK(cup); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get dwarf attribute for for member location: %s", dwarf_errmsg(derr)); - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ECTF_CONVBKERR); } + DWARF_LOCK(cup); class = dwarf_get_form_class(cup->cu_vers, DW_AT_data_member_location, cup->cu_addrsz, form); if (class == DW_FORM_CLASS_CONSTANT) { @@ -621,12 +668,14 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) * match, fall through to the normal path. */ if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } if (dwarf_formsdata(attr, &sign, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); if (sign < 0) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "encountered negative member data " @@ -638,26 +687,28 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) } if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) { + DWARF_UNLOCK(cup); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to obtain location list for member offset: %s", dwarf_errmsg(derr)); - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ECTF_CONVBKERR); } - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to parse location structure for member"); - dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC); + ctf_dwarf_dealloc(cup, loc->ld_s, DW_DLA_LOC_BLOCK); + ctf_dwarf_dealloc(cup, loc, DW_DLA_LOCDESC); return (ECTF_CONVBKERR); } *valp = loc->ld_s->lr_number; - dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC); + ctf_dwarf_dealloc(cup, loc->ld_s, DW_DLA_LOC_BLOCK); + ctf_dwarf_dealloc(cup, loc, DW_DLA_LOCDESC); return (0); } @@ -666,8 +717,12 @@ static int ctf_dwarf_offset(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Off *offsetp) { Dwarf_Error derr; + int ret; - if (dwarf_dieoffset(die, offsetp, &derr) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_dieoffset(die, offsetp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, @@ -678,12 +733,14 @@ ctf_dwarf_offset(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Off *offsetp) /* simpler variant for debugging output */ static Dwarf_Off -ctf_die_offset(Dwarf_Die die) +ctf_die_offset(ctf_cu_t *cup, Dwarf_Die die) { Dwarf_Off off = -1; Dwarf_Error derr; + DWARF_LOCK(cup); (void) dwarf_dieoffset(die, &off, &derr); + DWARF_UNLOCK(cup); return (off); } @@ -691,8 +748,12 @@ static int ctf_dwarf_tag(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half *tagp) { Dwarf_Error derr; + int ret; - if (dwarf_tag(die, tagp, &derr) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_tag(die, tagp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, @@ -708,7 +769,9 @@ ctf_dwarf_sib(ctf_cu_t *cup, Dwarf_Die base, Dwarf_Die *sibp) int ret; *sibp = NULL; + DWARF_LOCK(cup); ret = dwarf_siblingof(cup->cu_dwarf, base, sibp, &derr); + DWARF_UNLOCK(cup); if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) return (0); @@ -725,7 +788,9 @@ ctf_dwarf_child(ctf_cu_t *cup, Dwarf_Die base, Dwarf_Die *childp) int ret; *childp = NULL; + DWARF_LOCK(cup); ret = dwarf_child(base, childp, &derr); + DWARF_UNLOCK(cup); if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) return (0); @@ -1442,14 +1507,15 @@ ctf_dwarf_create_sou(ctf_cu_t *cup, Dwarf_Die die, ctf_id_t *idp, decl = 0; } - if (decl != 0) { + if (decl == B_TRUE) { base = ctf_add_forward(cup->cu_ctfp, isroot, name, kind); } else if (kind == CTF_K_STRUCT) { base = ctf_add_struct(cup->cu_ctfp, isroot, name); } else { base = ctf_add_union(cup->cu_ctfp, isroot, name); } - ctf_dprintf("added sou %s (%d) (%d)\n", name, kind, base); + ctf_dprintf("added sou %s (%d) (%ld) forward=%d\n", + name, kind, base, decl == B_TRUE); if (name != NULL) ctf_free(name, strlen(name) + 1); if (base == CTF_ERR) @@ -1545,7 +1611,9 @@ ctf_dwarf_array_upper_bound(ctf_cu_t *cup, Dwarf_Die range, ctf_arinfo_t *ar) } } - if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_whatform(attr, &form, &derr); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get DW_AT_upper_bound attribute form: %s\n", dwarf_errmsg(derr)); @@ -1606,7 +1674,8 @@ ctf_dwarf_array_upper_bound(ctf_cu_t *cup, Dwarf_Die range, ctf_arinfo_t *ar) ret = ECTF_CONVBKERR; done: - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ret); } @@ -2219,11 +2288,13 @@ ctf_dwarf_convert_function(ctf_cu_t *cup, Dwarf_Die die) } ctf_dprintf("beginning work on function %s (die %llx)\n", - name, ctf_die_offset(die)); + name, ctf_die_offset(cup, die)); if ((ret = ctf_dwarf_boolean(cup, die, DW_AT_declaration, &b)) != 0) { - if (ret != ENOENT) + if (ret != ENOENT) { + ctf_free(name, strlen(name) + 1); return (ret); + } } else if (b != 0) { /* * GCC7 at least creates empty DW_AT_declarations for functions @@ -2233,7 +2304,8 @@ ctf_dwarf_convert_function(ctf_cu_t *cup, Dwarf_Die die) * DW_TAG_subprogram that is more complete. */ ctf_dprintf("ignoring declaration of function %s (die %llx)\n", - name, ctf_die_offset(die)); + name, ctf_die_offset(cup, die)); + ctf_free(name, strlen(name) + 1); return (0); } @@ -2347,7 +2419,7 @@ ctf_dwarf_convert_variable(ctf_cu_t *cup, Dwarf_Die die) if ((ret = ctf_dwarf_offset(cup, tdie, &offset)) != 0) return (ret); ctf_dprintf("die 0x%llx DW_AT_specification -> die 0x%llx\n", - ctf_die_offset(die), ctf_die_offset(tdie)); + ctf_die_offset(cup, die), ctf_die_offset(cup, tdie)); die = tdie; } else if (ret != ENOENT) { return (ret); @@ -2853,53 +2925,58 @@ ctf_dwarf_conv_weaks(ctf_cu_t *cup) return (ctf_symtab_iter(cup->cu_ctfp, ctf_dwarf_conv_weaks_cb, cup)); } -/* ARGSUSED */ static int ctf_dwarf_convert_one(void *arg, void *unused) { int ret; ctf_file_t *dedup; ctf_cu_t *cup = arg; + const char *name = cup->cu_name != NULL ? cup->cu_name : "NULL"; - ctf_dprintf("converting die: %s\n", cup->cu_name); - ctf_dprintf("max offset: %x\n", cup->cu_maxoff); VERIFY(cup != NULL); + if ((ret = ctf_dwarf_init_die(cup)) != 0) + return (ret); + + ctf_dprintf("converting die: %s - max offset: %x\n", name, + cup->cu_maxoff); + ret = ctf_dwarf_convert_die(cup, cup->cu_cu); - ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } ret = ctf_dwarf_fixup_die(cup, B_FALSE); - ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_fixup_die (%s, FALSE) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } ret = ctf_dwarf_fixup_die(cup, B_TRUE); - ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_fixup_die (%s, TRUE) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } - if ((ret = ctf_dwarf_conv_funcvars(cup)) != 0) { + ctf_dprintf("ctf_dwarf_conv_funcvars (%s) returned %d\n", + name, ret); return (ctf_dwarf_error(cup, NULL, ret, "failed to convert strong functions and variables")); } @@ -2911,6 +2988,8 @@ ctf_dwarf_convert_one(void *arg, void *unused) if (cup->cu_doweaks == B_TRUE) { if ((ret = ctf_dwarf_conv_weaks(cup)) != 0) { + ctf_dprintf("ctf_dwarf_conv_weaks (%s) returned %d\n", + name, ret); return (ctf_dwarf_error(cup, NULL, ret, "failed to convert weak functions and variables")); } @@ -2921,30 +3000,26 @@ ctf_dwarf_convert_one(void *arg, void *unused) } } - ctf_phase_dump(cup->cu_ctfp, "pre-dwarf-dedup", cup->cu_name); + ctf_phase_dump(cup->cu_ctfp, "pre-dwarf-dedup", name); ctf_dprintf("adding inputs for dedup\n"); if ((ret = ctf_merge_add(cup->cu_cmh, cup->cu_ctfp)) != 0) { return (ctf_dwarf_error(cup, NULL, ret, "failed to add inputs for merge")); } - ctf_dprintf("starting dedup of %s\n", cup->cu_name); + ctf_dprintf("starting dedup of %s\n", name); if ((ret = ctf_merge_dedup(cup->cu_cmh, &dedup)) != 0) { return (ctf_dwarf_error(cup, NULL, ret, "failed to deduplicate die")); } + ctf_close(cup->cu_ctfp); cup->cu_ctfp = dedup; - ctf_phase_dump(cup->cu_ctfp, "post-dwarf-dedup", cup->cu_name); + ctf_phase_dump(cup->cu_ctfp, "post-dwarf-dedup", name); return (0); } -/* - * Note, we expect that if we're returning a ctf_file_t from one of the dies, - * say in the single node case, it's been saved and the entry here has been set - * to NULL, which ctf_close happily ignores. - */ static void ctf_dwarf_free_die(ctf_cu_t *cup) { @@ -2953,13 +3028,18 @@ ctf_dwarf_free_die(ctf_cu_t *cup) ctf_dwbitf_t *cdb, *ndb; ctf_dwmap_t *map; void *cookie; - Dwarf_Error derr; ctf_dprintf("Beginning to free die: %p\n", cup); + + VERIFY3P(cup->cu_elf, !=, NULL); cup->cu_elf = NULL; + ctf_dprintf("Trying to free name: %p\n", cup->cu_name); - if (cup->cu_name != NULL) + if (cup->cu_name != NULL) { ctf_free(cup->cu_name, strlen(cup->cu_name) + 1); + cup->cu_name = NULL; + } + ctf_dprintf("Trying to free merge handle: %p\n", cup->cu_cmh); if (cup->cu_cmh != NULL) { ctf_merge_fini(cup->cu_cmh); @@ -2990,35 +3070,25 @@ ctf_dwarf_free_die(ctf_cu_t *cup) ctf_free(cdb, sizeof (ctf_dwbitf_t)); } - ctf_dprintf("Trying to clean up dwarf_t: %p\n", cup->cu_dwarf); - if (cup->cu_dwarf != NULL) - (void) dwarf_finish(cup->cu_dwarf, &derr); - cup->cu_dwarf = NULL; - ctf_close(cup->cu_ctfp); + if (cup->cu_ctfp != NULL) { + ctf_close(cup->cu_ctfp); + cup->cu_ctfp = NULL; + } cookie = NULL; - while ((map = avl_destroy_nodes(&cup->cu_map, &cookie)) != NULL) { + while ((map = avl_destroy_nodes(&cup->cu_map, &cookie)) != NULL) ctf_free(map, sizeof (ctf_dwmap_t)); - } avl_destroy(&cup->cu_map); cup->cu_errbuf = NULL; -} - -static void -ctf_dwarf_free_dies(ctf_cu_t *cdies, int ndies) -{ - int i; - ctf_dprintf("Beginning to free dies\n"); - for (i = 0; i < ndies; i++) { - ctf_dwarf_free_die(&cdies[i]); + if (cup->cu_cu != NULL) { + ctf_dwarf_dealloc(cup, cup->cu_cu, DW_DLA_DIE); + cup->cu_cu = NULL; } - - ctf_free(cdies, sizeof (ctf_cu_t) * ndies); } static int -ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies, +ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, uint_t *ndies, char *errbuf, size_t errlen) { int ret; @@ -3049,88 +3119,146 @@ ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies, return (0); } +/* + * Fill out just enough of each ctf_cu_t for the conversion process to + * be able to finish the rest in a (potentially) multithreaded context. + */ static int -ctf_dwarf_init_die(int fd, Elf *elf, ctf_cu_t *cup, int ndie, char *errbuf, - size_t errlen) +ctf_dwarf_preinit_dies(int fd, Elf *elf, Dwarf_Debug dw, + mutex_t *dwlock, Dwarf_Error *derr, uint_t ndies, ctf_cu_t *cdies, + uint_t flags, char *errbuf, size_t errlen) { - int ret; Dwarf_Unsigned hdrlen, abboff, nexthdr; Dwarf_Half addrsz, vers; Dwarf_Unsigned offset = 0; - Dwarf_Error derr; + uint_t added = 0; + int ret, i = 0; - while ((ret = dwarf_next_cu_header(cup->cu_dwarf, &hdrlen, &vers, - &abboff, &addrsz, &nexthdr, &derr)) != DW_DLV_NO_ENTRY) { + while ((ret = dwarf_next_cu_header(dw, &hdrlen, &vers, &abboff, + &addrsz, &nexthdr, derr)) != DW_DLV_NO_ENTRY) { + Dwarf_Die cu; + ctf_cu_t *cup; char *name; - Dwarf_Die cu, child; - /* Based on the counting above, we should be good to go */ - VERIFY(ret == DW_DLV_OK); - if (ndie > 0) { - ndie--; - offset = nexthdr; - continue; + VERIFY3U(i, <, ndies); + + cup = &cdies[i++]; + + cup->cu_fd = fd; + cup->cu_elf = elf; + cup->cu_dwarf = dw; + cup->cu_errbuf = errbuf; + cup->cu_errlen = errlen; + cup->cu_dwarf = dw; + if (ndies > 1) { + /* + * Only need to lock calls into libdwarf if there are + * multiple CUs. + */ + cup->cu_dwlock = dwlock; + cup->cu_doweaks = B_FALSE; + } else { + cup->cu_doweaks = B_TRUE; } - /* - * Compilers are apparently inconsistent. Some emit no DWARF for - * empty files and others emit empty compilation unit. - */ cup->cu_voidtid = CTF_ERR; cup->cu_longtid = CTF_ERR; - cup->cu_elf = elf; + cup->cu_cuoff = offset; cup->cu_maxoff = nexthdr - 1; cup->cu_vers = vers; cup->cu_addrsz = addrsz; - cup->cu_ctfp = ctf_fdcreate(fd, &ret); - if (cup->cu_ctfp == NULL) - return (ret); - - avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), - offsetof(ctf_dwmap_t, cdm_avl)); - cup->cu_errbuf = errbuf; - cup->cu_errlen = errlen; - bzero(&cup->cu_vars, sizeof (ctf_list_t)); - bzero(&cup->cu_funcs, sizeof (ctf_list_t)); - bzero(&cup->cu_bitfields, sizeof (ctf_list_t)); - - if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf, - errlen)) != 0) - return (ret); - if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) + if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) { + ctf_dprintf("cu %d - no cu %d\n", i, ret); return (ret); - - if (cu == NULL) { - (void) snprintf(errbuf, errlen, - "file does not contain DWARF data"); - return (ECTF_CONVNODEBUG); } - if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) - return (ret); - - if (child == NULL) { - (void) snprintf(errbuf, errlen, + if (cu == NULL) { + ctf_dprintf("cu %d - no cu data\n", i); + (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "file does not contain DWARF data"); return (ECTF_CONVNODEBUG); } - cup->cu_cuoff = offset; - cup->cu_cu = child; - - if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) - return (ret); - if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) { size_t len = strlen(name) + 1; char *b = basename(name); + cup->cu_name = strdup(b); ctf_free(name, len); + if (cup->cu_name == NULL) + return (ENOMEM); } - break; + + ret = ctf_dwarf_child(cup, cu, &cup->cu_cu); + dwarf_dealloc(cup->cu_dwarf, cu, DW_DLA_DIE); + if (ret != 0) { + ctf_dprintf("cu %d - no child '%s' %d\n", + i, cup->cu_name != NULL ? cup->cu_name : "NULL", + ret); + return (ret); + } + + if (cup->cu_cu == NULL) { + size_t len; + + ctf_dprintf("cu %d - no child data '%s' %d\n", + i, cup->cu_name != NULL ? cup->cu_name : "NULL", + ret); + if (cup->cu_name != NULL && + (len = strlen(cup->cu_name)) > 2 && + strncmp(".c", &cup->cu_name[len - 2], 2) == 0) { + /* + * Missing DEBUG data for a .c file, return an + * error unless this is permitted. + */ + if (!(flags & CTF_ALLOW_MISSING_DEBUG)) { + (void) snprintf( + cup->cu_errbuf, cup->cu_errlen, + "file %s is missing debug info", + cup->cu_name); + return (ECTF_CONVNODEBUG); + } + } + } else { + added++; + } + + ctf_dprintf("Pre-initialised cu %d - '%s'\n", i, + cup->cu_name != NULL ? cup->cu_name : "NULL"); + + offset = nexthdr; } + /* + * If none of the CUs had debug data, return an error. + */ + if (added == 0) + return (ECTF_CONVNODEBUG); + + return (0); +} + +static int +ctf_dwarf_init_die(ctf_cu_t *cup) +{ + int ret; + + cup->cu_ctfp = ctf_fdcreate(cup->cu_fd, &ret); + if (cup->cu_ctfp == NULL) + return (ret); + + avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), + offsetof(ctf_dwmap_t, cdm_avl)); + + if ((ret = ctf_dwarf_die_elfenc(cup->cu_elf, cup, + cup->cu_errbuf, cup->cu_errlen)) != 0) { + return (ret); + } + + if ((cup->cu_cmh = ctf_merge_init(cup->cu_fd, &ret)) == NULL) + return (ret); + return (0); } @@ -3165,8 +3293,10 @@ c_source_has_debug(const char *file, ctf_cu_t *cus, size_t nr_cus) return (B_TRUE); for (size_t i = 0; i < nr_cus; i++) { - if (strcmp(basename, cus[i].cu_name) == 0) + if (cus[i].cu_name != NULL && + strcmp(basename, cus[i].cu_name) == 0) { return (B_TRUE); + } } return (B_FALSE); @@ -3239,7 +3369,7 @@ ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf, if (!c_source_has_debug(file, cus, nr_cus)) { (void) snprintf(errmsg, errlen, - "file %s is missing debug info\n", file); + "file %s is missing debug info", file); return (ECTF_CONVNODEBUG); } } @@ -3247,15 +3377,126 @@ ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf, return (0); } +static int +ctf_dwarf_convert_batch(uint_t start, uint_t end, int fd, uint_t nthrs, + workq_t *wqp, ctf_cu_t *cdies, ctf_file_t **fpp) +{ + ctf_file_t *fpprev = NULL; + uint_t i, added; + ctf_cu_t *cup; + int ret, err; + + ctf_dprintf("Processing CU batch %u - %u\n", start, end - 1); + + added = 0; + for (i = start; i < end; i++) { + cup = &cdies[i]; + if (cup->cu_cu == NULL) + continue; + ctf_dprintf("adding cu %s: %p, %x %x\n", + cup->cu_name != NULL ? cup->cu_name : "NULL", + cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); + if (workq_add(wqp, cup) == -1) { + err = errno; + goto out; + } + added++; + } + + /* + * No debug data found in this batch, move on to the next. + * NB: ctf_dwarf_preinit_dies() has already checked that there is at + * least one CU with debug data present. + */ + if (added == 0) { + err = 0; + goto out; + } + + ctf_dprintf("Running conversion phase\n"); + + /* Run the conversions */ + ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err); + if (ret == WORKQ_ERROR) { + err = errno; + goto out; + } else if (ret == WORKQ_UERROR) { + ctf_dprintf("internal convert failed: %s\n", + ctf_errmsg(err)); + goto out; + } + + ctf_dprintf("starting merge phase\n"); + + ctf_merge_t *cmp = ctf_merge_init(fd, &err); + if (cmp == NULL) + goto out; + + if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { + ctf_merge_fini(cmp); + goto out; + } + + /* + * If we have the result of a previous merge then add it as an input to + * the next one. + */ + if (*fpp != NULL) { + ctf_dprintf("adding previous merge CU\n"); + fpprev = *fpp; + *fpp = NULL; + if ((err = ctf_merge_add(cmp, fpprev)) != 0) { + ctf_merge_fini(cmp); + goto out; + } + } + + ctf_dprintf("adding CUs to merge\n"); + for (i = start; i < end; i++) { + cup = &cdies[i]; + if (cup->cu_cu == NULL) + continue; + if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { + ctf_merge_fini(cmp); + *fpp = NULL; + goto out; + } + } + + ctf_dprintf("performing merge\n"); + err = ctf_merge_merge(cmp, fpp); + if (err != 0) { + ctf_dprintf("failed merge!\n"); + *fpp = NULL; + ctf_merge_fini(cmp); + goto out; + } + + ctf_merge_fini(cmp); + + ctf_dprintf("freeing CUs\n"); + for (i = start; i < end; i++) { + cup = &cdies[i]; + ctf_dprintf("freeing cu %d\n", i); + ctf_dwarf_free_die(cup); + } + +out: + ctf_close(fpprev); + return (err); +} + int -ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags, +ctf_dwarf_convert(int fd, Elf *elf, uint_t bsize, uint_t nthrs, uint_t flags, ctf_file_t **fpp, char *errbuf, size_t errlen) { - int err, ret, ndies, i; + int err, ret; + uint_t ndies, i; Dwarf_Debug dw; Dwarf_Error derr; ctf_cu_t *cdies = NULL, *cup; workq_t *wqp = NULL; + mutex_t dwlock = ERRORCHECKMUTEX; *fpp = NULL; @@ -3288,116 +3529,74 @@ ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags, if (ndies == 0) { (void) snprintf(errbuf, errlen, "file does not contain DWARF data\n"); + (void) dwarf_finish(dw, &derr); return (ECTF_CONVNODEBUG); } - (void) dwarf_finish(dw, &derr); cdies = ctf_alloc(sizeof (ctf_cu_t) * ndies); if (cdies == NULL) { + (void) dwarf_finish(dw, &derr); return (ENOMEM); } bzero(cdies, sizeof (ctf_cu_t) * ndies); - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, - &cup->cu_dwarf, &derr); - if (ret != 0) { - ctf_free(cdies, sizeof (ctf_cu_t) * ndies); - (void) snprintf(errbuf, errlen, - "failed to initialize DWARF: %s\n", - dwarf_errmsg(derr)); - return (ECTF_CONVBKERR); - } - - err = ctf_dwarf_init_die(fd, elf, cup, i, errbuf, errlen); - if (err != 0) - goto out; - - cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE; + if ((err = ctf_dwarf_preinit_dies(fd, elf, dw, &dwlock, &derr, + ndies, cdies, flags, errbuf, errlen)) != 0) { + goto out; } if (!(flags & CTF_ALLOW_MISSING_DEBUG) && (err = ctf_dwarf_check_missing(cdies, ndies, - elf, errbuf, errlen)) != 0) - goto out; - - /* - * If we only have one compilation unit, there's no reason to use - * multiple threads, even if the user requested them. After all, they - * just gave us an upper bound. - */ - if (ndies == 1) - nthrs = 1; - - if (workq_init(&wqp, nthrs) == -1) { - err = errno; + elf, errbuf, errlen)) != 0) { goto out; } - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - ctf_dprintf("adding cu %s: %p, %x %x\n", cup->cu_name, - cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); - if (workq_add(wqp, cup) == -1) { - err = errno; + /* Only one cu, no merge required */ + if (ndies == 1) { + cup = cdies; + + if ((err = ctf_dwarf_convert_one(cup, NULL)) != 0) goto out; - } + + *fpp = cup->cu_ctfp; + cup->cu_ctfp = NULL; + ctf_dwarf_free_die(cup); + goto success; } - ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err); - if (ret == WORKQ_ERROR) { + /* + * There's no need to have either more threads or a batch size larger + * than the total number of dies, even if the user requested them. + */ + nthrs = min(ndies, nthrs); + bsize = min(ndies, bsize); + + if (workq_init(&wqp, nthrs) == -1) { err = errno; goto out; - } else if (ret == WORKQ_UERROR) { - ctf_dprintf("internal convert failed: %s\n", - ctf_errmsg(err)); - goto out; } - ctf_dprintf("Determining next phase: have %d CUs\n", ndies); - if (ndies != 1) { - ctf_merge_t *cmp; - - cmp = ctf_merge_init(fd, &err); - if (cmp == NULL) - goto out; - - ctf_dprintf("setting threads\n"); - if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { - ctf_merge_fini(cmp); - goto out; - } - - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { - ctf_merge_fini(cmp); - goto out; - } - } - - ctf_dprintf("performing merge\n"); - err = ctf_merge_merge(cmp, fpp); + /* + * In order to avoid exhausting memory limits when converting files + * with a large number of dies, we process them in batches. + */ + for (i = 0; i < ndies; i += bsize) { + err = ctf_dwarf_convert_batch(i, min(i + bsize, ndies), + fd, nthrs, wqp, cdies, fpp); if (err != 0) { - ctf_dprintf("failed merge!\n"); *fpp = NULL; - ctf_merge_fini(cmp); goto out; } - ctf_merge_fini(cmp); - err = 0; - ctf_dprintf("successfully converted!\n"); - } else { - err = 0; - *fpp = cdies->cu_ctfp; - cdies->cu_ctfp = NULL; - ctf_dprintf("successfully converted!\n"); } +success: + err = 0; + ctf_dprintf("successfully converted!\n"); + out: + (void) dwarf_finish(dw, &derr); workq_fini(wqp); - ctf_dwarf_free_dies(cdies, ndies); + ctf_free(cdies, sizeof (ctf_cu_t) * ndies); return (err); } diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c index a18a2f46ef..a5888de8c0 100644 --- a/usr/src/lib/libctf/common/ctf_merge.c +++ b/usr/src/lib/libctf/common/ctf_merge.c @@ -129,10 +129,11 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, { ctf_merge_types_t *cmp = arg; ctf_merge_tinfo_t *cmt = cmp->cm_tmap; + uint_t kind; if (same == B_TRUE) { if (ctf_type_kind(ifp, iid) == CTF_K_FORWARD && - ctf_type_kind(ofp, oid) != CTF_K_FORWARD) { + (kind = ctf_type_kind(ofp, oid)) != CTF_K_FORWARD) { VERIFY(cmt[oid].cmt_map == 0); /* @@ -153,8 +154,8 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, cmt[oid].cmt_map = iid; cmt[oid].cmt_forward = B_TRUE; - ctf_dprintf("merge diff forward mapped %d->%d\n", oid, - iid); + ctf_dprintf("merge diff forward mapped %ld->%ld (%u)\n", + oid, iid, kind); return; } @@ -421,7 +422,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id) } static int -ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id) +ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id, uint_t kind) { int ret, flags; const ctf_type_t *tp; @@ -434,14 +435,7 @@ ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id) else flags = CTF_ADD_NONROOT; - /* - * ctf_add_forward tries to check to see if a given forward already - * exists in one of its hash tables. If we're here then we know that we - * have a forward in a container that isn't present in another. - * Therefore, we choose a token hash table to satisfy the API choice - * here. - */ - ret = ctf_add_forward(cmp->cm_out, flags, name, CTF_K_STRUCT); + ret = ctf_add_forward(cmp->cm_out, flags, name, kind); if (ret == CTF_ERR) return (CTF_ERR); @@ -494,19 +488,32 @@ ctf_merge_add_sou(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) else suid = ctf_add_union(cmp->cm_out, flags, name); + ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, suid); + if (suid == CTF_ERR) return (suid); - /* - * If this is a forward reference then its mapping should already - * exist. - */ if (forward == B_FALSE) { VERIFY(cmp->cm_tmap[id].cmt_map == 0); cmp->cm_tmap[id].cmt_map = suid; - ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, - suid); } else { + /* + * If this is a forward reference then its mapping should + * already exist. + */ + if (cmp->cm_tmap[id].cmt_map != suid) { + ctf_dprintf( + "mismatch sou \"%s\" as (%d) %d->%d (exp %d)\n", + name, kind, id, suid, cmp->cm_tmap[id].cmt_map); + ctf_hash_dump("src structs", + &cmp->cm_src->ctf_structs, cmp->cm_src); + ctf_hash_dump("src unions", + &cmp->cm_src->ctf_unions, cmp->cm_src); + ctf_hash_dump("out structs", + &cmp->cm_out->ctf_structs, cmp->cm_out); + ctf_hash_dump("out unions", + &cmp->cm_out->ctf_unions, cmp->cm_out); + } VERIFY(cmp->cm_tmap[id].cmt_map == suid); } cmp->cm_tmap[id].cmt_fixup = B_TRUE; @@ -551,9 +558,27 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id) case CTF_K_FUNCTION: ret = ctf_merge_add_func(cmp, id); break; - case CTF_K_FORWARD: - ret = ctf_merge_add_forward(cmp, id); + case CTF_K_FORWARD: { + const ctf_type_t *tp; + uint_t kind; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + + /* + * For forward declarations, ctt_type is the CTF_K_* + * kind for the tag. Older versions of the CTF tools may + * not have filled this in so if ctt_type is unknown or + * invalid, treat it as a struct. This mirrors the logic in + * ctf_bufopen(). + */ + + kind = tp->ctt_type; + if (kind == CTF_K_UNKNOWN || kind >= CTF_K_MAX) + kind = CTF_K_STRUCT; + + ret = ctf_merge_add_forward(cmp, id, kind); break; + } case CTF_K_STRUCT: case CTF_K_UNION: ret = ctf_merge_add_sou(cmp, id, B_FALSE); @@ -690,6 +715,7 @@ ctf_merge_common(ctf_merge_types_t *cmp) /* Pass 1 */ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { if (cmp->cm_tmap[i].cmt_forward == B_TRUE) { + ctf_dprintf("Forward %d\n", i); ret = ctf_merge_add_sou(cmp, i, B_TRUE); if (ret != 0) { return (ret); @@ -1566,11 +1592,11 @@ ctf_dedup_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, while (cmt[oid].cmt_missing == B_FALSE) oid = cmt[oid].cmt_map; cmt[iid].cmt_map = oid; - ctf_dprintf("%d->%d \n", iid, oid); + ctf_dprintf("dedup %d->%d \n", iid, oid); } else { VERIFY(cmt[iid].cmt_map == 0); cmt[iid].cmt_missing = B_TRUE; - ctf_dprintf("%d is missing\n", iid); + ctf_dprintf("dedup %d is missing\n", iid); } } diff --git a/usr/src/lib/libctf/common/libctf_impl.h b/usr/src/lib/libctf/common/libctf_impl.h index 5c88b9454d..0921a3ec1a 100644 --- a/usr/src/lib/libctf/common/libctf_impl.h +++ b/usr/src/lib/libctf/common/libctf_impl.h @@ -29,9 +29,9 @@ extern "C" { #endif -typedef int (*ctf_convert_f)(int, Elf *, uint_t, uint_t, +typedef int (*ctf_convert_f)(int, Elf *, uint_t, uint_t, uint_t, ctf_file_t **, char *, size_t); -extern int ctf_dwarf_convert(int, Elf *, uint_t, uint_t, +extern int ctf_dwarf_convert(int, Elf *, uint_t, uint_t, uint_t, ctf_file_t **, char *, size_t); /* diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index bdc24ac6ef..f69cb861c4 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -823,6 +823,8 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **, * Label manipulation. */ extern int zpool_clear_label(int); +extern int zpool_set_bootenv(zpool_handle_t *, const nvlist_t *); +extern int zpool_get_bootenv(zpool_handle_t *, nvlist_t **); /* is this zvol valid for use as a dump device? */ extern int zvol_check_dump_config(char *); diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index c3603884b6..e56628e2be 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright 2016 Nexenta Systems, Inc. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> @@ -397,7 +397,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, * Assuming bootfs is a valid dataset name. */ static boolean_t -bootfs_name_valid(const char *pool, char *bootfs) +bootfs_name_valid(const char *pool, const char *bootfs) { int len = strlen(pool); if (bootfs[0] == '\0') @@ -4414,6 +4414,38 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, free(mntpnt); } +int +zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap) +{ + int error = lzc_set_bootenv(zhp->zpool_name, envmap); + if (error != 0) { + (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, + dgettext(TEXT_DOMAIN, + "error setting bootenv in pool '%s'"), zhp->zpool_name); + } + + return (error); +} + +int +zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp) +{ + nvlist_t *nvl; + int error; + + nvl = NULL; + error = lzc_get_bootenv(zhp->zpool_name, &nvl); + if (error != 0) { + (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, + dgettext(TEXT_DOMAIN, + "error getting bootenv in pool '%s'"), zhp->zpool_name); + } else { + *nvlp = nvl; + } + + return (error); +} + /* * Read the EFI label from the config, if a label does not exist then * pass back the error to the caller. If the caller has passed a non-NULL diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 5ad0f9d4eb..0174e1a3f3 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -205,6 +205,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_find_vdev; zpool_find_vdev_by_physpath; zpool_fru_set; + zpool_get_bootenv; zpool_get_config; zpool_get_errlog; zpool_get_features; @@ -244,6 +245,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_reguid; zpool_reopen; zpool_scan; + zpool_set_bootenv; zpool_set_prop; zpool_skip_pool; zpool_state_to_name; diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.c b/usr/src/lib/libzfs_core/common/libzfs_core.c index 31ffddd93f..471a5e5515 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.c +++ b/usr/src/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. @@ -1372,3 +1372,21 @@ lzc_change_key(const char *fsname, uint64_t crypt_cmd, nvlist_t *props, nvlist_free(ioc_args); return (error); } + +/* + * Set the bootenv contents for the given pool. + */ +int +lzc_set_bootenv(const char *pool, const nvlist_t *env) +{ + return (lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, (nvlist_t *)env, NULL)); +} + +/* + * Get the contents of the bootenv of the given pool. + */ +int +lzc_get_bootenv(const char *pool, nvlist_t **outnvl) +{ + return (lzc_ioctl(ZFS_IOC_GET_BOOTENV, pool, NULL, outnvl)); +} diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.h b/usr/src/lib/libzfs_core/common/libzfs_core.h index 9b7721e7eb..4b408cbb63 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.h +++ b/usr/src/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2016 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. * Copyright (c) 2017 Datto Inc. @@ -118,6 +118,8 @@ int lzc_channel_program_nosync(const char *, const char *, uint64_t, int lzc_pool_checkpoint(const char *); int lzc_pool_checkpoint_discard(const char *); +int lzc_set_bootenv(const char *, const nvlist_t *); +int lzc_get_bootenv(const char *, nvlist_t **); #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs_core/common/mapfile-vers b/usr/src/lib/libzfs_core/common/mapfile-vers index 98516b66cc..bc4bc3631d 100644 --- a/usr/src/lib/libzfs_core/common/mapfile-vers +++ b/usr/src/lib/libzfs_core/common/mapfile-vers @@ -38,6 +38,13 @@ $mapfile_version 2 +SYMBOL_VERSION ILLUMOS_0.8 { + global: + + lzc_get_bootenv; + lzc_set_bootenv; +} ILLUMOS_0.7; + SYMBOL_VERSION ILLUMOS_0.7 { global: diff --git a/usr/src/lib/libzfsbootenv/Makefile b/usr/src/lib/libzfsbootenv/Makefile new file mode 100644 index 0000000000..82acb6ba44 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/Makefile @@ -0,0 +1,53 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.lib + +HDRS= libzfsbootenv.h + +HDRDIR= common + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install + +POFILE = libzfsbootenv.po +MSGFILES = `$(GREP) -l gettext $(HDRDIR)/*.[ch]` + +.KEEP_STATE: + +all install: install_h $(SUBDIRS) + +clean clobber: $(SUBDIRS) + +$(POFILE): pofile_MSGFILES + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libzfsbootenv/Makefile.com b/usr/src/lib/libzfsbootenv/Makefile.com new file mode 100644 index 0000000000..7b40b3839a --- /dev/null +++ b/usr/src/lib/libzfsbootenv/Makefile.com @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +LIBRARY= libzfsbootenv.a +VERS= .1 + +OBJECTS= \ + lzbe_device.o \ + lzbe_pair.o \ + lzbe_util.o + +include ../../Makefile.lib + +LIBS= $(DYNLIB) + +SRCDIR= ../common + +CSTD= $(CSTD_GNU99) + +LDLIBS += -lzfs -lnvpair -lc +CPPFLAGS += -I$(SRC)/uts/common/fs/zfs + + +CLOBBERFILES += $(LIBRARY) + +.KEEP_STATE: + +all: $(LIBS) + +include ../../Makefile.targ diff --git a/usr/src/lib/libzfsbootenv/amd64/Makefile b/usr/src/lib/libzfsbootenv/amd64/Makefile new file mode 100644 index 0000000000..dd76c2b252 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h b/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h new file mode 100644 index 0000000000..9d0422fa56 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h @@ -0,0 +1,41 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#ifndef _LIBZFSBOOTENV_H +#define _LIBZFSBOOTENV_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum lzbe_flags { + lzbe_add, /* add data to existing nvlist */ + lzbe_replace /* replace current nvlist */ +} lzbe_flags_t; + +extern int lzbe_nvlist_get(const char *, const char *, void **); +extern int lzbe_nvlist_set(const char *, const char *, void *); +extern void lzbe_nvlist_free(void *); +extern int lzbe_add_pair(void *, const char *, const char *, void *, size_t); +extern int lzbe_remove_pair(void *, const char *); +extern int lzbe_set_boot_device(const char *, lzbe_flags_t, const char *); +extern int lzbe_get_boot_device(const char *, char **); +extern int lzbe_bootenv_print(const char *, const char *, FILE *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBZFSBOOTENV_H */ diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_device.c b/usr/src/lib/libzfsbootenv/common/lzbe_device.c new file mode 100644 index 0000000000..670efd8b06 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_device.c @@ -0,0 +1,164 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> +#include <sys/zfs_bootenv.h> +#include <sys/vdev_impl.h> + +/* + * Store device name to zpool label bootenv area. + * This call will set bootenv version to VB_NVLIST, if bootenv currently + * does contain other version, then old data will be replaced. + */ +int +lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *descriptor; + uint64_t version; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) + return (rv); + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + switch (flag) { + case lzbe_add: + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + /* + * We got the nvlist, check for version. + * if version is missing or is not VB_NVLIST, + * create new list. + */ + rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, + &version); + if (rv == 0 && version == VB_NVLIST) + break; + + /* Drop this nvlist */ + fnvlist_free(nv); + } + /* FALLTHROUGH */ + case lzbe_replace: + nv = fnvlist_alloc(); + break; + default: + return (rv); + } + + /* version is mandatory */ + fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST); + + /* + * If device name is empty, remove boot device configuration. + */ + if ((device == NULL || *device == '\0')) { + if (nvlist_exists(nv, OS_BOOTONCE)) + fnvlist_remove(nv, OS_BOOTONCE); + } else { + /* + * Use device name directly if it does start with + * prefix "zfs:". Otherwise, add prefix and sufix. + */ + if (strncmp(device, "zfs:", 4) == 0) { + fnvlist_add_string(nv, OS_BOOTONCE, device); + } else { + descriptor = NULL; + if (asprintf(&descriptor, "zfs:%s:", device) > 0) + fnvlist_add_string(nv, OS_BOOTONCE, descriptor); + else + rv = ENOMEM; + free(descriptor); + } + } + + rv = zpool_set_bootenv(zphdl, nv); + if (rv != 0) + fprintf(stderr, "%s\n", libzfs_error_description(hdl)); + + fnvlist_free(nv); + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +/* + * Return boot device name from bootenv, if set. + */ +int +lzbe_get_boot_device(const char *pool, char **device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *val; + int rv = -1; + + if (pool == NULL || *pool == '\0' || device == NULL) + return (rv); + + if ((hdl = libzfs_init()) == NULL) + return (rv); + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val); + if (rv == 0) { + /* + * zfs device descriptor is in form of "zfs:dataset:", + * we only do need dataset name. + */ + if (strncmp(val, "zfs:", 4) == 0) { + val += 4; + val = strdup(val); + if (val != NULL) { + size_t len = strlen(val); + + if (val[len - 1] == ':') + val[len - 1] = '\0'; + *device = val; + } else { + rv = ENOMEM; + } + } else { + rv = EINVAL; + } + } + nvlist_free(nv); + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_pair.c b/usr/src/lib/libzfsbootenv/common/lzbe_pair.c new file mode 100644 index 0000000000..3a4f4ee7b7 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_pair.c @@ -0,0 +1,348 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> +#include <sys/zfs_bootenv.h> +#include <sys/vdev_impl.h> + +/* + * Get or create nvlist. If key is not NULL, get nvlist from bootenv, + * otherwise return bootenv. + */ +int +lzbe_nvlist_get(const char *pool, const char *key, void **ptr) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) { + return (rv); + } + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + nvlist_t *nvl, *dup; + + if (key != NULL) { + rv = nvlist_lookup_nvlist(nv, key, &nvl); + if (rv == 0) { + rv = nvlist_dup(nvl, &dup, 0); + nvlist_free(nv); + if (rv == 0) + nv = dup; + else + nv = NULL; + } else { + nvlist_free(nv); + rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0); + } + } + *ptr = nv; + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +int +lzbe_nvlist_set(const char *pool, const char *key, void *ptr) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + uint64_t version; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) { + return (rv); + } + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + if (key != NULL) { + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + /* + * We got the nvlist, check for version. + * if version is missing or is not VB_NVLIST, + * create new list. + */ + rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, + &version); + if (rv != 0 || version != VB_NVLIST) { + /* Drop this nvlist */ + fnvlist_free(nv); + /* Create and prepare new nvlist */ + nv = fnvlist_alloc(); + fnvlist_add_uint64(nv, BOOTENV_VERSION, + VB_NVLIST); + } + rv = nvlist_add_nvlist(nv, key, ptr); + if (rv == 0) + rv = zpool_set_bootenv(zphdl, nv); + nvlist_free(nv); + } + } else { + rv = zpool_set_bootenv(zphdl, ptr); + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +/* + * free nvlist we got via lzbe_nvlist_get() + */ +void +lzbe_nvlist_free(void *ptr) +{ + nvlist_free(ptr); +} + +static const char *typenames[] = { + "DATA_TYPE_UNKNOWN", + "DATA_TYPE_BOOLEAN", + "DATA_TYPE_BYTE", + "DATA_TYPE_INT16", + "DATA_TYPE_UINT16", + "DATA_TYPE_INT32", + "DATA_TYPE_UINT32", + "DATA_TYPE_INT64", + "DATA_TYPE_UINT64", + "DATA_TYPE_STRING", + "DATA_TYPE_BYTE_ARRAY", + "DATA_TYPE_INT16_ARRAY", + "DATA_TYPE_UINT16_ARRAY", + "DATA_TYPE_INT32_ARRAY", + "DATA_TYPE_UINT32_ARRAY", + "DATA_TYPE_INT64_ARRAY", + "DATA_TYPE_UINT64_ARRAY", + "DATA_TYPE_STRING_ARRAY", + "DATA_TYPE_HRTIME", + "DATA_TYPE_NVLIST", + "DATA_TYPE_NVLIST_ARRAY", + "DATA_TYPE_BOOLEAN_VALUE", + "DATA_TYPE_INT8", + "DATA_TYPE_UINT8", + "DATA_TYPE_BOOLEAN_ARRAY", + "DATA_TYPE_INT8_ARRAY", + "DATA_TYPE_UINT8_ARRAY" +}; + +static int +nvpair_type_from_name(const char *name) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(typenames); i++) { + if (strcmp(name, typenames[i]) == 0) + return (i); + } + return (0); +} + +/* + * Add pair defined by key, type and value into nvlist. + */ +int +lzbe_add_pair(void *ptr, const char *key, const char *type, void *value, + size_t size) +{ + nvlist_t *nv = ptr; + data_type_t dt; + int rv = 0; + + if (ptr == NULL || key == NULL || value == NULL) + return (rv); + + if (type == NULL) + type = "DATA_TYPE_STRING"; + dt = nvpair_type_from_name(type); + if (dt == DATA_TYPE_UNKNOWN) + return (EINVAL); + + switch (dt) { + case DATA_TYPE_BYTE: + if (size != sizeof (uint8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_byte(nv, key, *(uint8_t *)value); + break; + + case DATA_TYPE_INT16: + if (size != sizeof (int16_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int16(nv, key, *(int16_t *)value); + break; + + case DATA_TYPE_UINT16: + if (size != sizeof (uint16_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint16(nv, key, *(uint16_t *)value); + break; + + case DATA_TYPE_INT32: + if (size != sizeof (int32_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int32(nv, key, *(int32_t *)value); + break; + + case DATA_TYPE_UINT32: + if (size != sizeof (uint32_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint32(nv, key, *(uint32_t *)value); + break; + + case DATA_TYPE_INT64: + if (size != sizeof (int64_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int64(nv, key, *(int64_t *)value); + break; + + case DATA_TYPE_UINT64: + if (size != sizeof (uint64_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint64(nv, key, *(uint64_t *)value); + break; + + case DATA_TYPE_STRING: + rv = nvlist_add_string(nv, key, value); + break; + + case DATA_TYPE_BYTE_ARRAY: + rv = nvlist_add_byte_array(nv, key, value, size); + break; + + case DATA_TYPE_INT16_ARRAY: + rv = nvlist_add_int16_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT16_ARRAY: + rv = nvlist_add_uint16_array(nv, key, value, size); + break; + + case DATA_TYPE_INT32_ARRAY: + rv = nvlist_add_int32_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT32_ARRAY: + rv = nvlist_add_uint32_array(nv, key, value, size); + break; + + case DATA_TYPE_INT64_ARRAY: + rv = nvlist_add_int64_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT64_ARRAY: + rv = nvlist_add_uint64_array(nv, key, value, size); + break; + + case DATA_TYPE_STRING_ARRAY: + rv = nvlist_add_string_array(nv, key, value, size); + break; + + case DATA_TYPE_NVLIST: + rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value); + break; + + case DATA_TYPE_NVLIST_ARRAY: + rv = nvlist_add_nvlist_array(nv, key, value, size); + break; + + case DATA_TYPE_BOOLEAN_VALUE: + if (size != sizeof (boolean_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_boolean_value(nv, key, *(boolean_t *)value); + break; + + case DATA_TYPE_INT8: + if (size != sizeof (int8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int8(nv, key, *(int8_t *)value); + break; + + case DATA_TYPE_UINT8: + if (size != sizeof (uint8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint8(nv, key, *(uint8_t *)value); + break; + + case DATA_TYPE_BOOLEAN_ARRAY: + rv = nvlist_add_boolean_array(nv, key, value, size); + break; + + case DATA_TYPE_INT8_ARRAY: + rv = nvlist_add_int8_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT8_ARRAY: + rv = nvlist_add_uint8_array(nv, key, value, size); + break; + + default: + return (ENOTSUP); + } + + return (rv); +} + +int +lzbe_remove_pair(void *ptr, const char *key) +{ + + return (nvlist_remove_all(ptr, key)); +} diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_util.c b/usr/src/lib/libzfsbootenv/common/lzbe_util.c new file mode 100644 index 0000000000..35e9854958 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_util.c @@ -0,0 +1,39 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> + +/* + * Output bootenv information. + */ +int +lzbe_bootenv_print(const char *pool, const char *nvlist, FILE *of) +{ + nvlist_t *nv; + int rv = -1; + + if (pool == NULL || *pool == '\0' || of == NULL) + return (rv); + + rv = lzbe_nvlist_get(pool, nvlist, (void **)&nv); + if (rv == 0) { + nvlist_print(of, nv); + nvlist_free(nv); + } + + return (rv); +} diff --git a/usr/src/lib/libzfsbootenv/common/mapfile-vers b/usr/src/lib/libzfsbootenv/common/mapfile-vers new file mode 100644 index 0000000000..62b394c997 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/mapfile-vers @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOSprivate { + global: + lzbe_add_pair; + lzbe_bootenv_print; + lzbe_get_boot_device; + lzbe_nvlist_free; + lzbe_nvlist_get; + lzbe_nvlist_set; + lzbe_remove_pair; + lzbe_set_boot_device; + local: + *; +}; diff --git a/usr/src/lib/libzfsbootenv/i386/Makefile b/usr/src/lib/libzfsbootenv/i386/Makefile new file mode 100644 index 0000000000..ba9853a5a5 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libzfsbootenv/sparc/Makefile b/usr/src/lib/libzfsbootenv/sparc/Makefile new file mode 100644 index 0000000000..ba9853a5a5 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libzfsbootenv/sparcv9/Makefile b/usr/src/lib/libzfsbootenv/sparcv9/Makefile new file mode 100644 index 0000000000..dd76c2b252 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) |