diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-09-17 14:23:19 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-09-17 14:23:19 +0400 |
commit | de28f63015e215a128e3e64ae99db9f44099d23d (patch) | |
tree | 6155a91e1993a150ae9f8af53bfeb18c0ab4dde4 /usr/src/lib/libpool/common/pool_kernel.c | |
download | libpool-de28f63015e215a128e3e64ae99db9f44099d23d.tar.gz |
Initial illumos sourcesillumos
Diffstat (limited to 'usr/src/lib/libpool/common/pool_kernel.c')
-rw-r--r-- | usr/src/lib/libpool/common/pool_kernel.c | 3591 |
1 files changed, 3591 insertions, 0 deletions
diff --git a/usr/src/lib/libpool/common/pool_kernel.c b/usr/src/lib/libpool/common/pool_kernel.c new file mode 100644 index 0000000..2e1375e --- /dev/null +++ b/usr/src/lib/libpool/common/pool_kernel.c @@ -0,0 +1,3591 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2012 Milan Jurik. All rights reserved. + */ + +#include <assert.h> +#include <errno.h> +#include <exacct.h> +#include <fcntl.h> +#include <libnvpair.h> +#include <limits.h> +#include <poll.h> +#include <pool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stropts.h> +#include <thread.h> +#include <time.h> +#include <unistd.h> + +#include <libxml/tree.h> + +#include <sys/mman.h> +#include <sys/pool.h> +#include <sys/pool_impl.h> +#include <sys/priocntl.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "dict.h" + +#include "pool_internal.h" +#include "pool_impl.h" +#include "pool_kernel_impl.h" + +/* + * libpool kernel Manipulation Routines + * + * pool_kernel.c implements the kernel manipulation routines used by the + * libpool kernel datastore. The functions are grouped into the following + * logical areas + * + */ + +/* + * Device snapshot transfer buffer size + */ +#define KERNEL_SNAPSHOT_BUF_SZ 65535 + +/* + * Kernel result set's initial size. 8 is probably large enough for + * most queries. Queries requiring more space are accomodated using + * realloc on a per result set basis. + */ +#define KERNEL_RS_INITIAL_SZ 8 + +/* + * Property manipulation macros + */ +#define KERNEL_PROP_RDONLY 0x1 + +/* + * Information required to evaluate qualifying elements for a query + */ +struct query_obj { + const pool_conf_t *conf; + const pool_elem_t *src; + const char *src_attr; + pool_elem_class_t classes; + pool_value_t **props; + pool_knl_result_set_t *rs; +}; + +/* + * Identifies a pool element with a processor set id + */ +typedef struct pool_set_xref { + pool_knl_pool_t *psx_pool; + uint_t psx_pset_id; + struct pool_set_xref *psx_next; +} pool_set_xref_t; + +/* + * Controls exacct snapshot load into libpool data structure + */ +typedef struct pool_snap_load { + int *psl_changed; + pool_set_xref_t *psl_xref; + pool_elem_t *psl_system; + pool_knl_resource_t *psl_pset; +} pool_snap_load_t; + +/* + * Information about an XML document which is being constructed + */ +struct knl_to_xml { + xmlDocPtr ktx_doc; + xmlNodePtr ktx_node; +}; + +/* + * Undo structure processing. The following structures are all used to + * allow changes to the libpool snapshot and kernel following an + * unsuccessful commit. + */ +typedef struct pool_create_undo { + pool_create_t pcu_ioctl; + pool_elem_t *pcu_elem; +} pool_create_undo_t; + +typedef struct pool_destroy_undo { + pool_destroy_t pdu_ioctl; + pool_elem_t *pdu_elem; +} pool_destroy_undo_t; + +typedef struct pool_assoc_undo { + pool_assoc_t pau_ioctl; + pool_elem_t *pau_assoc; + pool_elem_t *pau_oldres; + pool_elem_t *pau_newres; +} pool_assoc_undo_t; + +typedef struct pool_dissoc_undo { + pool_dissoc_t pdu_ioctl; + pool_elem_t *pdu_dissoc; + pool_elem_t *pdu_oldres; + pool_elem_t *pdu_newres; +} pool_dissoc_undo_t; + +typedef struct pool_xtransfer_undo { + pool_xtransfer_t pxu_ioctl; + pool_elem_t *pxu_src; + pool_elem_t *pxu_tgt; + pool_component_t **pxu_rl; +} pool_xtransfer_undo_t; + +typedef struct pool_propput_undo { + pool_propput_t ppu_ioctl; + pool_elem_t *ppu_elem; + nvlist_t *ppu_alist; + nvlist_t *ppu_blist; + uchar_t ppu_doioctl; +} pool_propput_undo_t; + +typedef struct pool_proprm_undo { + pool_proprm_t pru_ioctl; + pool_elem_t *pru_elem; + pool_value_t pru_oldval; +} pool_proprm_undo_t; + +extern const char *dtd_location; + +extern const char *element_class_tags[]; +extern const char pool_info_location[]; + +/* + * These functions are defined in pool_xml.c and represent the minimum + * XML support required to allow a pool kernel configuration to be + * exported as an XML document. + */ +extern int pool_xml_set_attr(xmlNodePtr, xmlChar *, const pool_value_t *); +extern int pool_xml_set_prop(xmlNodePtr, xmlChar *, const pool_value_t *); +extern void xml_init(void); +extern xmlNodePtr node_create(xmlNodePtr, const xmlChar *); +extern void pool_error_func(void *, const char *, ...); +/* + * Utilities + */ +static int load_group(pool_conf_t *, pool_knl_elem_t *, ea_object_t *, + pool_snap_load_t *); +static void pool_knl_elem_free(pool_knl_elem_t *, int); +static int pool_knl_put_xml_property(pool_elem_t *, xmlNodePtr, const char *, + const pool_value_t *); +static int pool_knl_snap_load_push(pool_snap_load_t *, pool_knl_pool_t *); +static int pool_knl_snap_load_update(pool_snap_load_t *, int, uint_t); +static int pool_knl_snap_load_remove(pool_snap_load_t *, int, uint_t); +static nvpair_t *pool_knl_find_nvpair(nvlist_t *, const char *); +static int pool_knl_nvlist_add_value(nvlist_t *, const char *, + const pool_value_t *); +static int pool_knl_recover(pool_conf_t *); +static uint64_t hash_id(const pool_elem_t *); +static int blocking_open(const char *, int); + +/* + * Connections + */ +static void pool_knl_connection_free(pool_knl_connection_t *); + +/* + * Configuration + */ +static int pool_knl_close(pool_conf_t *); +static int pool_knl_validate(const pool_conf_t *, pool_valid_level_t); +static int pool_knl_commit(pool_conf_t *); +static int pool_knl_export(const pool_conf_t *, const char *, + pool_export_format_t); +static int pool_knl_rollback(pool_conf_t *); +static pool_result_set_t *pool_knl_exec_query(const pool_conf_t *, + const pool_elem_t *, const char *, pool_elem_class_t, pool_value_t **); +static int pool_knl_remove(pool_conf_t *); +static char *pool_knl_get_binding(pool_conf_t *, pid_t); +static int pool_knl_set_binding(pool_conf_t *, const char *, idtype_t, id_t); +static char *pool_knl_get_resource_binding(pool_conf_t *, + pool_resource_elem_class_t, pid_t); +static int pool_knl_res_transfer(pool_resource_t *, pool_resource_t *, + uint64_t); +static int pool_knl_res_xtransfer(pool_resource_t *, pool_resource_t *, + pool_component_t **); + +/* + * Result Sets + */ +static pool_knl_result_set_t *pool_knl_result_set_alloc(const pool_conf_t *); +static int pool_knl_result_set_append(pool_knl_result_set_t *, + pool_knl_elem_t *); +static int pool_knl_result_set_realloc(pool_knl_result_set_t *); +static void pool_knl_result_set_free(pool_knl_result_set_t *); +static pool_elem_t *pool_knl_rs_next(pool_result_set_t *); +static pool_elem_t *pool_knl_rs_prev(pool_result_set_t *); +static pool_elem_t *pool_knl_rs_first(pool_result_set_t *); +static pool_elem_t *pool_knl_rs_last(pool_result_set_t *); +static int pool_knl_rs_set_index(pool_result_set_t *, int); +static int pool_knl_rs_get_index(pool_result_set_t *); +static int pool_knl_rs_count(pool_result_set_t *); +static int pool_knl_rs_close(pool_result_set_t *); + +/* + * Element (and sub-type) + */ +static pool_knl_elem_t *pool_knl_elem_wrap(pool_conf_t *, pool_elem_class_t, + pool_resource_elem_class_t, pool_component_elem_class_t); +static pool_elem_t *pool_knl_elem_create(pool_conf_t *, pool_elem_class_t, + pool_resource_elem_class_t, pool_component_elem_class_t); +static int pool_knl_elem_remove(pool_elem_t *); +static int pool_knl_set_container(pool_elem_t *, pool_elem_t *); +static pool_elem_t *pool_knl_get_container(const pool_elem_t *); +/* + * Pool element specific + */ +static int pool_knl_pool_associate(pool_t *, const pool_resource_t *); +static int pool_knl_pool_dissociate(pool_t *, const pool_resource_t *); + +/* + * Resource elements specific + */ +static int pool_knl_resource_is_system(const pool_resource_t *); +static int pool_knl_resource_can_associate(const pool_resource_t *); + +/* Properties */ +static pool_value_class_t pool_knl_get_property(const pool_elem_t *, + const char *, pool_value_t *); +static pool_value_class_t pool_knl_get_dynamic_property(const pool_elem_t *, + const char *, pool_value_t *); +static int pool_knl_put_property(pool_elem_t *, const char *, + const pool_value_t *); +static int pool_knl_rm_property(pool_elem_t *, const char *); +static pool_value_t **pool_knl_get_properties(const pool_elem_t *, uint_t *); + +/* + * Logging + */ +static int log_item_commit(log_item_t *); +static int log_item_undo(log_item_t *); +static int log_item_release(log_item_t *); + +/* + * Utilities + */ + +/* + * load_group() updates the library configuration with the kernel + * snapshot supplied in ep. The function is designed to be called + * recursively. This function depends implicitly on the ordering of + * the data provided in ep. Changes to the ordering of data in ep must + * be matched by changes to this function. + */ +int +load_group(pool_conf_t *conf, pool_knl_elem_t *elem, ea_object_t *ep, + pool_snap_load_t *psl) +{ + ea_object_t *eo; + pool_knl_elem_t *old_elem; + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + int ret = PO_SUCCESS; + + if ((ep->eo_catalog & EXD_DATA_MASK) == EXD_GROUP_SYSTEM) { + if ((elem = pool_knl_elem_wrap(conf, PEC_SYSTEM, PREC_INVALID, + PCEC_INVALID)) == NULL) + return (PO_FAIL); + if (nvlist_alloc(&elem->pke_properties, NV_UNIQUE_NAME_TYPE, + 0) != 0) { + pool_knl_elem_free(elem, PO_FALSE); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Check to see if we already have an element + * for this data. If we have, free the newly + * created elem and continue with the old one + */ + if ((old_elem = dict_get(prov->pkc_elements, elem)) != NULL) { + nvlist_free(old_elem->pke_properties); + old_elem->pke_properties = elem->pke_properties; + pool_knl_elem_free(elem, PO_FALSE); + elem = old_elem; + } else { + if (dict_put(prov->pkc_elements, elem, elem) != NULL) { + pool_knl_elem_free(elem, PO_TRUE); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + psl->psl_system = (pool_elem_t *)elem; + } + + for (eo = ep->eo_group.eg_objs; eo != NULL; eo = eo->eo_next) { + int data; + pool_knl_elem_t *prop_elem = NULL; + + data = (eo->eo_catalog & EXD_DATA_MASK); + + switch (data) { + case EXD_SYSTEM_TSTAMP: + case EXD_POOL_TSTAMP: + case EXD_PSET_TSTAMP: + case EXD_CPU_TSTAMP: + if (eo->eo_item.ei_uint64 > prov->pkc_lotime) { + if (eo->eo_item.ei_uint64 > prov->pkc_ltime) + prov->pkc_ltime = eo->eo_item.ei_uint64; + if (psl->psl_changed) { + switch (data) { + case EXD_SYSTEM_TSTAMP: + *psl->psl_changed |= POU_SYSTEM; + break; + case EXD_POOL_TSTAMP: + *psl->psl_changed |= POU_POOL; + break; + case EXD_PSET_TSTAMP: + *psl->psl_changed |= POU_PSET; + break; + case EXD_CPU_TSTAMP: + *psl->psl_changed |= POU_CPU; + break; + } + } + } + break; + case EXD_SYSTEM_PROP: + case EXD_POOL_PROP: + case EXD_PSET_PROP: + case EXD_CPU_PROP: + if (data == EXD_PSET_PROP) { + prop_elem = elem; + elem = (pool_knl_elem_t *)psl->psl_pset; + } + nvlist_free(elem->pke_properties); + if (nvlist_unpack(eo->eo_item.ei_raw, + eo->eo_item.ei_size, &elem->pke_properties, 0) != + 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + elem->pke_ltime = prov->pkc_ltime; + if (data == EXD_PSET_PROP) { + elem = prop_elem; + } + break; + case EXD_POOL_POOLID: + if (nvlist_alloc(&elem->pke_properties, + NV_UNIQUE_NAME_TYPE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (nvlist_add_int64(elem->pke_properties, + "pool.sys_id", + (int64_t)eo->eo_item.ei_uint32) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if ((old_elem = dict_get(prov->pkc_elements, elem)) != + NULL) { + nvlist_free(old_elem->pke_properties); + old_elem->pke_properties = elem->pke_properties; + pool_knl_elem_free(elem, PO_FALSE); + elem = old_elem; + } else { + if (dict_put(prov->pkc_elements, elem, elem) != + NULL) { + pool_knl_elem_free(elem, PO_TRUE); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + if (pool_knl_snap_load_push(psl, + (pool_knl_pool_t *)elem) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + ((pool_knl_pool_t *)elem)->pkp_assoc[PREC_PSET] = NULL; + break; + case EXD_POOL_PSETID: + if (pool_knl_snap_load_update(psl, EXD_POOL_PSETID, + eo->eo_item.ei_uint32) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case EXD_PSET_PSETID: + if (nvlist_alloc(&elem->pke_properties, + NV_UNIQUE_NAME_TYPE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (nvlist_add_int64(elem->pke_properties, + "pset.sys_id", + (int64_t)eo->eo_item.ei_uint32) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if ((old_elem = dict_get(prov->pkc_elements, elem)) != + NULL) { + nvlist_free(old_elem->pke_properties); + old_elem->pke_properties = elem->pke_properties; + pool_knl_elem_free(elem, PO_FALSE); + elem = old_elem; + } else { + if (dict_put(prov->pkc_elements, elem, elem) != + NULL) { + pool_knl_elem_free(elem, PO_TRUE); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + psl->psl_pset = (pool_knl_resource_t *)elem; + if (pool_knl_snap_load_remove(psl, data, + eo->eo_item.ei_uint32) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case EXD_CPU_CPUID: + if (nvlist_alloc(&elem->pke_properties, + NV_UNIQUE_NAME_TYPE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (nvlist_add_int64(elem->pke_properties, + "cpu.sys_id", + (int64_t)eo->eo_item.ei_uint32) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if ((old_elem = dict_get(prov->pkc_elements, elem)) != + NULL) { + nvlist_free(old_elem->pke_properties); + old_elem->pke_properties = elem->pke_properties; + old_elem->pke_parent = elem->pke_parent; + pool_knl_elem_free(elem, PO_FALSE); + elem = old_elem; + } else { + if (dict_put(prov->pkc_elements, elem, elem) != + NULL) { + pool_knl_elem_free(elem, PO_TRUE); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + break; + case EXD_GROUP_POOL: + if ((elem = pool_knl_elem_wrap(conf, PEC_POOL, + PREC_INVALID, PCEC_INVALID)) == NULL) + return (PO_FAIL); + if (pool_set_container(psl->psl_system, + (pool_elem_t *)elem) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case EXD_GROUP_PSET: + if ((elem = pool_knl_elem_wrap(conf, PEC_RES_COMP, + PREC_PSET, PCEC_INVALID)) == NULL) + return (PO_FAIL); + if (pool_set_container(psl->psl_system, + (pool_elem_t *)elem) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case EXD_GROUP_CPU: + if ((elem = pool_knl_elem_wrap(conf, PEC_COMP, + PREC_INVALID, PCEC_CPU)) == NULL) + return (PO_FAIL); + if (pool_set_container((pool_elem_t *)psl->psl_pset, + (pool_elem_t *)elem) != PO_SUCCESS) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + default: + break; + } + + + if (eo->eo_type == EO_GROUP) { + if ((ret = load_group(conf, elem, eo, psl)) == PO_FAIL) + break; + } + } + return (ret); +} + +/* + * Push a snapshot entry onto the list of pools in the snapshot. + */ +int +pool_knl_snap_load_push(pool_snap_load_t *psl, pool_knl_pool_t *pkp) +{ + pool_set_xref_t *psx; + + if ((psx = malloc(sizeof (pool_set_xref_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(psx, 0, sizeof (pool_set_xref_t)); + psx->psx_pool = pkp; + /* + * Push onto the list of pools + */ + psx->psx_next = psl->psl_xref; + psl->psl_xref = psx; + + return (PO_SUCCESS); +} + +/* + * Update the current cross-reference for the supplied type of + * resource. + */ +int +pool_knl_snap_load_update(pool_snap_load_t *psl, int type, uint_t id) +{ + switch (type) { + case EXD_POOL_PSETID: + psl->psl_xref->psx_pset_id = id; + break; + default: + return (PO_FAIL); + } + + return (PO_SUCCESS); +} + +/* + * Remove a resource entry with the supplied type and id from the + * snapshot list when it is no longer required. + */ +int +pool_knl_snap_load_remove(pool_snap_load_t *psl, int type, uint_t id) +{ + pool_set_xref_t *current, *prev, *next; + + for (prev = NULL, current = psl->psl_xref; current != NULL; + current = next) { + switch (type) { + case EXD_PSET_PSETID: + if (current->psx_pset_id == id) + current->psx_pool->pkp_assoc[PREC_PSET] = + psl->psl_pset; + break; + default: + return (PO_FAIL); + } + next = current->psx_next; + if (current->psx_pool->pkp_assoc[PREC_PSET] != NULL) { + if (prev != NULL) { + prev->psx_next = current->psx_next; + } else { + psl->psl_xref = current->psx_next; + } + free(current); + } else + prev = current; + } + + return (PO_SUCCESS); +} + +/* + * Return the nvpair with the supplied name from the supplied list. + * + * NULL is returned if the name cannot be found in the list. + */ +nvpair_t * +pool_knl_find_nvpair(nvlist_t *l, const char *name) +{ + nvpair_t *pair; + + for (pair = nvlist_next_nvpair(l, NULL); pair != NULL; + pair = nvlist_next_nvpair(l, pair)) { + if (strcmp(nvpair_name(pair), name) == 0) + break; + } + return (pair); +} + +/* + * Close the configuration. There are a few steps to closing a configuration: + * - Close the pseudo device + * - Free the data provider + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_close(pool_conf_t *conf) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + + if (close(prov->pkc_fd) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Rollback any pending changes before freeing the prov. This + * ensures there are no memory leaks from pending transactions. + * However, don't rollback when we've done a temporary pool since the + * pool/resources haven't really been committed in this case. + * They will all be freed in pool_knl_connection_free and we don't + * want to double free them. + */ + if (!(conf->pc_prov->pc_oflags & PO_TEMP)) + (void) pool_knl_rollback(conf); + pool_knl_connection_free(prov); + return (PO_SUCCESS); +} + +/* + * Remove elements in this map (previously identified as "dead") from + * the configuration map (prov->pkc_elements). + */ + +/* ARGSUSED1 */ +static void +remove_dead_elems(const void *key, void **value, void *cl) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)key; + pool_conf_t *conf = TO_CONF(TO_ELEM(pke)); + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + + assert(dict_remove(prov->pkc_elements, pke) != NULL); +#ifdef DEBUG + dprintf("remove_dead_elems:\n"); + pool_elem_dprintf(TO_ELEM(pke)); +#endif /* DEBUG */ + pool_knl_elem_free(pke, PO_TRUE); +} + +/* + * Find elements which were not updated the last time that + * load_group() was called. Add those elements into a separate map + * (passed in cl) which will be later used to remove these elements + * from the configuration map. + */ +/* ARGSUSED1 */ +static void +find_dead_elems(const void *key, void **value, void *cl) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)key; + pool_conf_t *conf = TO_CONF(TO_ELEM(pke)); + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + dict_hdl_t *dead_map = (dict_hdl_t *)cl; + + if (pke->pke_ltime < prov->pkc_ltime) + (void) dict_put(dead_map, pke, pke); +} + +/* + * Update the snapshot held by the library. This function acts as the + * controller for the snapshot update procedure. Then snapshot is + * actually updated in multiple phases by the load_group() function + * (which updates existing elements and creates new elements as + * required) and then by find_dead_elems and remove_dead_elems + * (respectively responsible for identifying elements which are to be + * removed and then removing them). + * + * Returns PO_SUCCESS + */ +int +pool_knl_update(pool_conf_t *conf, int *changed) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + pool_query_t query = {0}; + ea_object_t *ep; + dict_hdl_t *dead_map; + pool_snap_load_t psl = { NULL }; + + /* + * Ensure the library snapshot is consistent, if there are any + * outstanding transactions return failure. + */ + if (log_size(prov->pkc_log) != 0) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + /* + * Query the kernel for a snapshot of the configuration state. Use + * load_group to allocate the user-land representation of the + * data returned in the snapshot. + */ + /* LINTED E_CONSTANT_CONDITION */ + while (1) { + if (ioctl(prov->pkc_fd, POOL_QUERY, &query) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if ((query.pq_io_buf = calloc(1, + (query.pq_io_bufsize < KERNEL_SNAPSHOT_BUF_SZ) ? + query.pq_io_bufsize * 2 : query.pq_io_bufsize)) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (ioctl(prov->pkc_fd, POOL_QUERY, &query) < 0) { + free(query.pq_io_buf); + if (errno != ENOMEM) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + query.pq_io_bufsize = 0; + query.pq_io_buf = NULL; + } else + break; + } + if (ea_unpack_object(&ep, EUP_NOALLOC, query.pq_io_buf, + query.pq_io_bufsize) != EO_GROUP) { + free(query.pq_io_buf); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + /* + * Update the library snapshot + */ + psl.psl_changed = changed; + prov->pkc_lotime = prov->pkc_ltime; + if (load_group(conf, NULL, ep, &psl) != PO_SUCCESS) { + free(query.pq_io_buf); + ea_free_object(ep, EUP_NOALLOC); + return (PO_FAIL); + } + + free(query.pq_io_buf); + ea_free_object(ep, EUP_NOALLOC); + /* + * Now search the dictionary for items that must be removed because + * they were neither created nor updated. + */ + if ((dead_map = dict_new((int (*)(const void *, const void *)) + pool_elem_compare, (uint64_t (*)(const void *))hash_id)) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + dict_map(prov->pkc_elements, find_dead_elems, dead_map); + + if (dict_length(dead_map) > 0) { + dict_map(dead_map, remove_dead_elems, NULL); + } + dict_free(&dead_map); + + return (PO_SUCCESS); +} + +/* + * Rely on the kernel to always keep a kernel configuration valid. + * Returns PO_SUCCESS + */ +/* ARGSUSED */ +int +pool_knl_validate(const pool_conf_t *conf, pool_valid_level_t level) +{ + return ((conf->pc_state == POF_INVALID) ? PO_FAIL : PO_SUCCESS); +} + +/* + * Process all the outstanding transactions in the log. If the processing + * fails, then attempt to rollback and "undo" the changes. + */ +int +pool_knl_commit(pool_conf_t *conf) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + int lock = 1; + + /* + * Lock the kernel state for the commit + */ + if (ioctl(prov->pkc_fd, POOL_COMMIT, lock) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + lock = 0; + /* + * If the state is LS_FAIL, then try to recover before + * performing the commit. + */ + if (prov->pkc_log->l_state == LS_FAIL) { + if (pool_knl_recover(conf) == PO_FAIL) { + /* + * Unlock the kernel state for the + * commit. Assert that this * can't fail, + * since if it ever does fail the library is + * unusable. + */ + assert(ioctl(prov->pkc_fd, POOL_COMMIT, lock) >= 0); + } + } + /* + * Commit the log + */ + if (log_walk(prov->pkc_log, log_item_commit) != PO_SUCCESS) { + (void) pool_knl_recover(conf); + /* + * Unlock the kernel state for the commit. Assert that + * this can't fail, since if it ever does fail the + * library is unusable. + */ + assert(ioctl(prov->pkc_fd, POOL_COMMIT, lock) >= 0); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Unlock the kernel state for the commit. Assert that this + * can't fail, since if it ever does fail the library is + * unusable. + */ + assert(ioctl(prov->pkc_fd, POOL_COMMIT, lock) >= 0); + /* + * Release the log resources + */ + (void) log_walk(prov->pkc_log, log_item_release); + log_empty(prov->pkc_log); + return (PO_SUCCESS); +} + +/* + * prop_build_cb() is designed to be called from + * pool_walk_properties(). The property value is used to put an XML + * property on the supplied ktx_node. This is an essential part of the + * mechanism used to export a kernel configuration in libpool XML + * form. + */ +/* ARGSUSED */ +static int +prop_build_cb(pool_conf_t *UNUSED, pool_elem_t *pe, const char *name, + pool_value_t *pval, void *user) +{ + struct knl_to_xml *info = (struct knl_to_xml *)user; + + return (pool_knl_put_xml_property((pool_elem_t *)pe, info->ktx_node, + name, pval)); +} + +/* + * Duplicate some of the functionality from pool_xml_put_property() + * (see pool_xml.c) to allow a kernel configuration to add XML nodes + * to an XML tree which represents the kernel configuration. This is + * an essential part of the mechanism used to export a kernel + * configuration in libpool XML form. + */ +int +pool_knl_put_xml_property(pool_elem_t *pe, xmlNodePtr node, const char *name, + const pool_value_t *val) +{ + + /* + * "type" is a special attribute which is not visible ever outside of + * libpool. Use the specific type accessor function. + */ + if (strcmp(name, c_type) == 0) { + return (pool_xml_set_attr(node, BAD_CAST name, + val)); + } + if (is_ns_property(pe, name) != NULL) { /* in ns */ + if (pool_xml_set_attr(node, + BAD_CAST property_name_minus_ns(pe, name), val) == PO_FAIL) + return (pool_xml_set_prop(node, BAD_CAST name, + val)); + } else + return (pool_xml_set_prop(node, BAD_CAST name, val)); + return (PO_SUCCESS); +} + +/* + * Export the kernel configuration as an XML file. The configuration + * is used to build an XML document in memory. This document is then + * saved to the supplied location. + */ +int +pool_knl_export(const pool_conf_t *conf, const char *location, + pool_export_format_t fmt) +{ + xmlNodePtr node_comment; + xmlNodePtr system; + int ret; + pool_t **ps; + pool_resource_t **rs; + uint_t nelem; + int i; + struct knl_to_xml info; + char_buf_t *cb = NULL; + xmlValidCtxtPtr cvp; + + xml_init(); + + + switch (fmt) { + case POX_NATIVE: + info.ktx_doc = xmlNewDoc(BAD_CAST "1.0"); + (void) xmlCreateIntSubset(info.ktx_doc, BAD_CAST "system", + BAD_CAST "-//Sun Microsystems Inc//DTD Resource " + "Management All//EN", + BAD_CAST dtd_location); + + if ((cvp = xmlNewValidCtxt()) == NULL) { + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + /* + * Call xmlValidateDocument() to force the parsing of + * the DTD. Ignore errors and warning messages as we + * know the document isn't valid. + */ + (void) xmlValidateDocument(cvp, info.ktx_doc); + xmlFreeValidCtxt(cvp); + if ((info.ktx_node = node_create(NULL, BAD_CAST "system")) == + NULL) { + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + + system = info.ktx_node; + info.ktx_doc->_private = (void *)conf; + + (void) xmlDocSetRootElement(info.ktx_doc, info.ktx_node); + (void) xmlSetProp(info.ktx_node, BAD_CAST c_ref_id, + BAD_CAST "dummy"); + if ((node_comment = xmlNewDocComment(info.ktx_doc, + BAD_CAST "\nConfiguration for pools facility. Do NOT" + " edit this file by hand - use poolcfg(1)" + " or libpool(3POOL) instead.\n")) == NULL) { + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + if (xmlAddPrevSibling(info.ktx_node, node_comment) == NULL) { + xmlFree(node_comment); + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + if (pool_walk_any_properties((pool_conf_t *)conf, + pool_conf_to_elem(conf), &info, prop_build_cb, 1) == + PO_FAIL) { + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) { + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + /* + * Now add pool details + */ + if ((ps = pool_query_pools(conf, &nelem, NULL)) != NULL) { + for (i = 0; i < nelem; i++) { + pool_elem_t *elem = TO_ELEM(ps[i]); + uint_t nreselem; + const char *sep = ""; + int j; + + if (elem_is_tmp(elem)) + continue; + + if ((info.ktx_node = node_create(system, + BAD_CAST element_class_tags + [pool_elem_class(elem)])) == NULL) { + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + if (pool_walk_any_properties( + (pool_conf_t *)conf, + elem, &info, prop_build_cb, 1) == PO_FAIL) { + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + /* + * TODO: pset specific res manipulation + */ + if ((rs = pool_query_pool_resources(conf, ps[i], + &nreselem, NULL)) == NULL) { + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + if (set_char_buf(cb, "") == PO_FAIL) { + free(rs); + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + for (j = 0; j < nreselem; j++) { + pool_elem_t *reselem = TO_ELEM(rs[j]); + if (append_char_buf(cb, "%s%s_%d", sep, + pool_elem_class_string(reselem), + (int)elem_get_sysid(reselem)) == + PO_FAIL) { + free(rs); + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + sep = " "; + } + free(rs); + (void) xmlSetProp(info.ktx_node, BAD_CAST "res", + BAD_CAST cb->cb_buf); + if (set_char_buf(cb, "%s_%d", + pool_elem_class_string(elem), + (int)elem_get_sysid(elem)) == PO_FAIL) { + free(ps); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + (void) xmlSetProp(info.ktx_node, + BAD_CAST c_ref_id, + BAD_CAST cb->cb_buf); + } + free(ps); + } + /* + * Now add resource details (including components) + */ + if ((rs = pool_query_resources(conf, &nelem, NULL)) != NULL) { + for (i = 0; i < nelem; i++) { + pool_elem_t *elem = TO_ELEM(rs[i]); + pool_component_t **cs = NULL; + uint_t ncompelem; + int j; + + if (elem_is_tmp(elem)) + continue; + + if ((info.ktx_node = node_create(system, + BAD_CAST element_class_tags + [pool_elem_class(elem)])) == NULL) { + free(rs); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + if (pool_walk_any_properties( + (pool_conf_t *)conf, + elem, &info, prop_build_cb, 1) == PO_FAIL) { + free(rs); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + if (set_char_buf(cb, "%s_%d", + pool_elem_class_string(elem), + (int)elem_get_sysid(elem)) == PO_FAIL) { + free(rs); + free_char_buf(cb); + xmlFreeDoc(info.ktx_doc); + return (PO_FAIL); + } + (void) xmlSetProp(info.ktx_node, + BAD_CAST c_ref_id, + BAD_CAST cb->cb_buf); + if ((cs = pool_query_resource_components(conf, + rs[i], &ncompelem, NULL)) != NULL) { + xmlNodePtr resource = info.ktx_node; + + for (j = 0; j < ncompelem; j++) { + pool_elem_t *compelem = + TO_ELEM(cs[j]); + if ((info.ktx_node = + node_create(resource, + BAD_CAST element_class_tags + [pool_elem_class( + compelem)])) == NULL) { + pool_seterror( + POE_DATASTORE); + free(rs); + free(cs); + free_char_buf(cb); + xmlFreeDoc(info. + ktx_doc); + return (PO_FAIL); + } + if (pool_walk_any_properties( + (pool_conf_t *)conf, + compelem, &info, + prop_build_cb, 1) == + PO_FAIL) { + free(rs); + free(cs); + free_char_buf(cb); + xmlFreeDoc(info. + ktx_doc); + return (PO_FAIL); + } + if (set_char_buf(cb, "%s_%d", + pool_elem_class_string( + compelem), + (int)elem_get_sysid( + compelem)) == PO_FAIL) { + free(rs); + free(cs); + free_char_buf(cb); + xmlFreeDoc(info. + ktx_doc); + return (PO_FAIL); + } + (void) xmlSetProp(info.ktx_node, + BAD_CAST c_ref_id, + BAD_CAST cb->cb_buf); + } + free(cs); + } + } + free(rs); + } + free_char_buf(cb); + /* + * Set up the message handlers prior to calling + * xmlValidateDocument() + */ + if ((cvp = xmlNewValidCtxt()) == NULL) { + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + cvp->error = pool_error_func; + cvp->warning = pool_error_func; + if (xmlValidateDocument(cvp, info.ktx_doc) == 0) { + xmlFreeValidCtxt(cvp); + xmlFreeDoc(info.ktx_doc); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + xmlFreeValidCtxt(cvp); + ret = xmlSaveFormatFile(location, info.ktx_doc, 1); + xmlFreeDoc(info.ktx_doc); + if (ret == -1) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + return (PO_SUCCESS); + default: + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } +} + +/* + * Rollback the changes to the kernel + */ +int +pool_knl_recover(pool_conf_t *conf) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + + prov->pkc_log->l_state = LS_RECOVER; + if (log_reverse_walk(prov->pkc_log, log_item_undo) != PO_SUCCESS) { + dprintf("Library configuration consistency error\n"); + prov->pkc_log->l_state = LS_FAIL; + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + prov->pkc_log->l_state = LS_DO; + return (PO_SUCCESS); +} + +/* + * Rollback the changes to the configuration + */ +int +pool_knl_rollback(pool_conf_t *conf) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + + prov->pkc_log->l_state = LS_UNDO; + if (log_reverse_walk(prov->pkc_log, log_item_undo) != PO_SUCCESS) { + dprintf("Kernel configuration consistency error\n"); + (void) log_walk(prov->pkc_log, log_item_release); + log_empty(prov->pkc_log); + prov->pkc_log->l_state = LS_FAIL; + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + (void) log_walk(prov->pkc_log, log_item_release); + log_empty(prov->pkc_log); + prov->pkc_log->l_state = LS_DO; + return (PO_SUCCESS); +} + +/* + * Callback used to build the result set for a query. Each invocation will + * supply a candidate element for inclusion. The element is filtered by: + * - class + * - properties + * If the element "matches" the target, then it is added to the result + * set, otherwise it is ignored. + */ +/* ARGSUSED1 */ +static void +build_result_set(const void *key, void **value, void *cl) +{ + struct query_obj *qo = (struct query_obj *)cl; + pool_knl_elem_t *pke = (pool_knl_elem_t *)key; + + /* + * Check to see if it's the right class of element + */ + if (qo->classes & (1 << pool_elem_class((pool_elem_t *)key))) { + int i; + /* + * Now check to see if the src element is correct. If no src + * element is supplied, ignore this check + */ + if (qo->src) { + pool_knl_elem_t *parent; + + for (parent = pke; parent != NULL; + parent = parent->pke_parent) { + if (parent == (pool_knl_elem_t *)qo->src) + break; + } + if (parent == NULL) + return; + } + /* + * Now check for property matches (if there are any specified) + */ + if (qo->props) { + int matched = PO_TRUE; + for (i = 0; qo->props[i] != NULL; i++) { + pool_value_t val = POOL_VALUE_INITIALIZER; + + if (pool_get_property(TO_CONF(TO_ELEM(pke)), + (pool_elem_t *)pke, + pool_value_get_name(qo->props[i]), &val) == + POC_INVAL) { + matched = PO_FALSE; + break; + } else { + if (pool_value_equal(qo->props[i], + &val) != PO_TRUE) { + matched = PO_FALSE; + break; + } + } + } + if (matched == PO_TRUE) + (void) pool_knl_result_set_append(qo->rs, + (pool_knl_elem_t *)key); + } else { + (void) pool_knl_result_set_append(qo->rs, + (pool_knl_elem_t *)key); + } + } +} + +/* + * Execute the supplied query and return a result set which contains + * all qualifying elements. + */ +pool_result_set_t * +pool_knl_exec_query(const pool_conf_t *conf, const pool_elem_t *src, + const char *src_attr, pool_elem_class_t classes, pool_value_t **props) +{ + pool_knl_result_set_t *rs; + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + struct query_obj qo; + int matched = PO_TRUE; + + /* + * Have a buffer at this point, that we can use + */ + if ((rs = pool_knl_result_set_alloc(conf)) == NULL) { + return (NULL); + } + qo.conf = conf; + qo.src = src; + qo.src_attr = src_attr; + qo.classes = classes; + qo.props = props; + qo.rs = rs; + if (src_attr != NULL) { + pool_knl_pool_t *pkp = (pool_knl_pool_t *)src; + + /* + * Note: This logic is resource specific and must be + * extended for additional resource types. + */ + /* + * Check for property matches (if there are any specified) + */ + if (props) { + int i; + + for (i = 0; props[i] != NULL; i++) { + pool_value_t val = POOL_VALUE_INITIALIZER; + + if (pool_get_property(conf, + (pool_elem_t *)pkp->pkp_assoc[PREC_PSET], + pool_value_get_name(props[i]), &val) == + POC_INVAL) { + matched = PO_FALSE; + break; + } else { + if (pool_value_equal(props[i], + &val) != PO_TRUE) { + matched = PO_FALSE; + break; + } + } + } + } + + if (matched == PO_TRUE) + (void) pool_knl_result_set_append(rs, + (pool_knl_elem_t *)pkp->pkp_assoc[PREC_PSET]); + } else + dict_map(prov->pkc_elements, build_result_set, &qo); + + if (rs->pkr_count == 0) + pool_seterror(POE_INVALID_SEARCH); + return ((pool_result_set_t *)rs); +} + +/* + * Callback function intended to be used from pool_walk_pools(). If + * the supplied pool is not the default pool attempt to destroy it. + */ +/*ARGSUSED*/ +static int +destroy_pool_cb(pool_conf_t *conf, pool_t *pool, void *unused) +{ + if (elem_is_default(TO_ELEM(pool)) != PO_TRUE) + return (pool_destroy(conf, pool)); + /* + * Return PO_SUCCESS even though we don't delete the default + * pool so that the walk continues + */ + return (PO_SUCCESS); +} + +/* + * Remove the configuration details. This means remove all elements + * apart from the system elements. + */ +int +pool_knl_remove(pool_conf_t *conf) +{ + uint_t i, nelem; + pool_resource_t **resources; + + conf->pc_state = POF_DESTROY; + if ((resources = pool_query_resources(conf, &nelem, NULL)) != NULL) { + for (i = 0; i < nelem; i++) { + if (resource_is_system(resources[i]) == PO_FALSE) + if (pool_resource_destroy(conf, resources[i]) != + PO_SUCCESS) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + } + free(resources); + } + (void) pool_walk_pools(conf, conf, destroy_pool_cb); + if (pool_conf_commit(conf, PO_FALSE) != PO_SUCCESS) + return (PO_FAIL); + + if (pool_conf_close(conf) != PO_SUCCESS) + return (PO_FAIL); + + return (PO_SUCCESS); +} + +/* + * Determine the name of the pool to which the supplied pid is + * bound. If it cannot be determined return NULL. + */ +char * +pool_knl_get_binding(pool_conf_t *conf, pid_t pid) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + const char *sval; + char *name = NULL; + pool_bindq_t bindq; + pool_value_t *props[] = { NULL, NULL }; + uint_t nelem = 0; + pool_t **pools; + pool_value_t val = POOL_VALUE_INITIALIZER; + + props[0] = &val; + + bindq.pb_o_id_type = P_PID; + bindq.pb_o_id = pid; + if (ioctl(prov->pkc_fd, POOL_BINDQ, &bindq) < 0) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + + if (pool_value_set_name(props[0], "pool.sys_id") != PO_SUCCESS) { + return (NULL); + } + pool_value_set_int64(props[0], bindq.pb_i_id); + if ((pools = pool_query_pools(conf, &nelem, props)) == NULL) { + pool_seterror(POE_BADPARAM); + return (NULL); + } + + if (nelem != 1) { + free(pools); + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if (pool_get_ns_property(TO_ELEM(pools[0]), c_name, props[0]) + == POC_INVAL) { + free(pools); + return (NULL); + } + if (pool_value_get_string(props[0], &sval) != PO_SUCCESS) { + free(pools); + return (NULL); + } + if ((name = strdup(sval)) == NULL) { + free(pools); + pool_seterror(POE_SYSTEM); + return (NULL); + } + return (name); +} + +/* + * Bind idtype id to the pool name. + */ +int +pool_knl_set_binding(pool_conf_t *conf, const char *pool_name, idtype_t idtype, + id_t id) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + pool_bind_t bind; + pool_t *pool; + int ret; + + if ((pool = pool_get_pool(conf, pool_name)) == NULL) + return (PO_FAIL); + + bind.pb_o_id_type = idtype; + bind.pb_o_id = id; + bind.pb_o_pool_id = elem_get_sysid(TO_ELEM(pool)); + + while ((ret = ioctl(prov->pkc_fd, POOL_BIND, &bind)) < 0 && + errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * pool_knl_get_resource_binding() returns the binding for a pid to + * the supplied type of resource. If a binding cannot be determined, + * NULL is returned. + */ +char * +pool_knl_get_resource_binding(pool_conf_t *conf, + pool_resource_elem_class_t type, pid_t pid) +{ + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + const char *sval; + char *name = NULL; + pool_bindq_t bindq; + pool_value_t *props[] = { NULL, NULL }; + uint_t nelem = 0; + pool_t **pools; + pool_resource_t **resources; + pool_value_t val = POOL_VALUE_INITIALIZER; + + props[0] = &val; + bindq.pb_o_id_type = P_PID; + bindq.pb_o_id = pid; + if (ioctl(prov->pkc_fd, POOL_BINDQ, &bindq) < 0) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + + if (pool_value_set_name(props[0], "pool.sys_id") != PO_SUCCESS) { + return (NULL); + } + pool_value_set_int64(props[0], bindq.pb_i_id); + if ((pools = pool_query_pools(conf, &nelem, props)) == NULL) { + pool_seterror(POE_BADPARAM); + return (NULL); + } + + if (nelem != 1) { + free(pools); + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + + if (pool_value_set_string(props[0], pool_resource_type_string(type)) != + PO_SUCCESS || + pool_value_set_name(props[0], c_type) != PO_SUCCESS) { + free(pools); + return (NULL); + } + + if ((resources = pool_query_pool_resources(conf, pools[0], &nelem, + NULL)) == NULL) { + free(pools); + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + free(pools); + if (nelem != 1) { + free(resources); + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if (pool_get_ns_property(TO_ELEM(resources[0]), c_name, props[0]) == + POC_INVAL) { + free(resources); + return (NULL); + } + free(resources); + if (pool_value_get_string(props[0], &sval) != PO_SUCCESS) { + return (NULL); + } + if ((name = strdup(sval)) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + return (name); +} + +/* + * Allocate the required library data structure and initialise it. + */ +pool_knl_elem_t * +pool_knl_elem_wrap(pool_conf_t *conf, pool_elem_class_t class, + pool_resource_elem_class_t res_class, + pool_component_elem_class_t comp_class) +{ + pool_knl_elem_t *elem; + pool_elem_t *pe; + + switch (class) { + case PEC_SYSTEM: + if ((elem = malloc(sizeof (pool_knl_system_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_knl_system_t)); + break; + case PEC_POOL: + if ((elem = malloc(sizeof (pool_knl_pool_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_knl_pool_t)); + break; + case PEC_RES_COMP: + case PEC_RES_AGG: + if ((elem = malloc(sizeof (pool_knl_resource_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_knl_resource_t)); + break; + case PEC_COMP: + if ((elem = malloc(sizeof (pool_knl_component_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_knl_component_t)); + break; + default: + pool_seterror(POE_BADPARAM); + return (NULL); + } + pe = TO_ELEM(elem); + pe->pe_conf = conf; + pe->pe_class = class; + pe->pe_resource_class = res_class; + pe->pe_component_class = comp_class; + /* Set up the function pointers for element manipulation */ + pe->pe_get_prop = pool_knl_get_property; + pe->pe_put_prop = pool_knl_put_property; + pe->pe_rm_prop = pool_knl_rm_property; + pe->pe_get_props = pool_knl_get_properties; + pe->pe_remove = pool_knl_elem_remove; + pe->pe_get_container = pool_knl_get_container; + pe->pe_set_container = pool_knl_set_container; + /* + * Specific initialisation for different types of element + */ + if (class == PEC_POOL) { + pool_knl_pool_t *pp = (pool_knl_pool_t *)elem; + pp->pp_associate = pool_knl_pool_associate; + pp->pp_dissociate = pool_knl_pool_dissociate; + pp->pkp_assoc[PREC_PSET] = (pool_knl_resource_t *) + resource_by_sysid(conf, PS_NONE, "pset"); + } + if (class == PEC_RES_COMP || class == PEC_RES_AGG) { + pool_knl_resource_t *pr = (pool_knl_resource_t *)elem; + pr->pr_is_system = pool_knl_resource_is_system; + pr->pr_can_associate = pool_knl_resource_can_associate; + } +#if DEBUG + if (dict_put(((pool_knl_connection_t *)conf->pc_prov)->pkc_leaks, + elem, elem) != NULL) + assert(!"leak map put failed"); + dprintf("allocated %p\n", elem); +#endif /* DEBUG */ + return (elem); +} + +/* + * Allocate a new pool_knl_elem_t in the supplied configuration of the + * specified class. + * Returns element pointer/NULL + */ +pool_elem_t * +pool_knl_elem_create(pool_conf_t *conf, pool_elem_class_t class, + pool_resource_elem_class_t res_class, + pool_component_elem_class_t comp_class) +{ + pool_knl_elem_t *elem; + pool_create_undo_t *create; + pool_knl_connection_t *prov = (pool_knl_connection_t *)conf->pc_prov; + static int id = -3; + char_buf_t *cb; + + if ((elem = pool_knl_elem_wrap(conf, class, res_class, comp_class)) == + NULL) + return (NULL); + + /* + * Allocate an nvlist to hold properties + */ + if (nvlist_alloc(&elem->pke_properties, NV_UNIQUE_NAME_TYPE, 0) != 0) { + pool_knl_elem_free(elem, PO_FALSE); + pool_seterror(POE_SYSTEM); + return (NULL); + } + /* + * Allocate a temporary ID and name until the element is + * created for real + */ + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) { + pool_knl_elem_free(elem, PO_TRUE); + return (NULL); + } + if (set_char_buf(cb, "%s.sys_id", + pool_elem_class_string((pool_elem_t *)elem)) != PO_SUCCESS) { + pool_knl_elem_free(elem, PO_TRUE); + free_char_buf(cb); + return (NULL); + } + (void) nvlist_add_int64(elem->pke_properties, cb->cb_buf, id--); + if (set_char_buf(cb, "%s.name", + pool_elem_class_string((pool_elem_t *)elem)) != PO_SUCCESS) { + pool_knl_elem_free(elem, PO_TRUE); + free_char_buf(cb); + return (NULL); + } + (void) nvlist_add_string(elem->pke_properties, cb->cb_buf, ""); + /* + * If it's a resource class, it will need an initial size + */ + if (class == PEC_RES_COMP || class == PEC_RES_AGG) { + if (set_char_buf(cb, "%s.size", + pool_elem_class_string((pool_elem_t *)elem)) != + PO_SUCCESS) { + pool_knl_elem_free(elem, PO_TRUE); + free_char_buf(cb); + return (NULL); + } + (void) nvlist_add_uint64(elem->pke_properties, cb->cb_buf, 0); + } + free_char_buf(cb); + + /* + * Register the newly created element + */ + if (dict_put(prov->pkc_elements, elem, elem) != NULL) { + pool_knl_elem_free(elem, PO_TRUE); + pool_seterror(POE_SYSTEM); + return (NULL); + } + + if (prov->pkc_log->l_state != LS_DO) + return ((pool_elem_t *)elem); + + /* + * The remaining logic is setting up the arguments for the + * POOL_CREATE ioctl and appending the details into the log. + */ + if ((create = malloc(sizeof (pool_create_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + create->pcu_ioctl.pc_o_type = class; + switch (class) { + case PEC_SYSTEM: + pool_seterror(POE_BADPARAM); + free(create); + return (NULL); + case PEC_POOL: /* NO-OP */ + break; + case PEC_RES_COMP: + case PEC_RES_AGG: + create->pcu_ioctl.pc_o_sub_type = res_class; + break; + case PEC_COMP: + create->pcu_ioctl.pc_o_sub_type = comp_class; + break; + default: + pool_seterror(POE_BADPARAM); + free(create); + return (NULL); + } + + create->pcu_elem = (pool_elem_t *)elem; + + if (log_append(prov->pkc_log, POOL_CREATE, (void *)create) != + PO_SUCCESS) { + free(create); + return (NULL); + } + return ((pool_elem_t *)elem); +} + +/* + * Remove the details of the element from our userland copy and destroy + * the element (if appropriate) in the kernel. + */ +int +pool_knl_elem_remove(pool_elem_t *pe) +{ + pool_knl_connection_t *prov; + pool_destroy_undo_t *destroy; + + prov = (pool_knl_connection_t *)(TO_CONF(pe))->pc_prov; + + if (dict_remove(prov->pkc_elements, pe) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (prov->pkc_log->l_state != LS_DO) { + return (PO_SUCCESS); + } + + /* + * The remaining logic is setting up the arguments for the + * POOL_DESTROY ioctl and appending the details into the log. + */ + if ((destroy = malloc(sizeof (pool_destroy_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + destroy->pdu_ioctl.pd_o_type = pool_elem_class(pe); + + if (destroy->pdu_ioctl.pd_o_type == PEC_RES_COMP || + destroy->pdu_ioctl.pd_o_type == PEC_RES_AGG) + destroy->pdu_ioctl.pd_o_sub_type = pool_resource_elem_class(pe); + + if (destroy->pdu_ioctl.pd_o_type == PEC_COMP) + destroy->pdu_ioctl.pd_o_sub_type = + pool_component_elem_class(pe); + + destroy->pdu_elem = pe; + + if (log_append(prov->pkc_log, POOL_DESTROY, (void *)destroy) != + PO_SUCCESS) { + free(destroy); + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * Set the parent of the supplied child to the supplied parent + */ +int +pool_knl_set_container(pool_elem_t *pp, pool_elem_t *pc) +{ + pool_knl_elem_t *pkp = (pool_knl_elem_t *)pp; + pool_knl_elem_t *pkc = (pool_knl_elem_t *)pc; + + pkc->pke_parent = pkp; + return (PO_SUCCESS); +} + +/* + * TODO: Needed for msets and ssets. + */ +/* ARGSUSED */ +int +pool_knl_res_transfer(pool_resource_t *src, pool_resource_t *tgt, + uint64_t size) { + return (PO_FAIL); +} + +/* + * Transfer resource components from one resource set to another. + */ +int +pool_knl_res_xtransfer(pool_resource_t *src, pool_resource_t *tgt, + pool_component_t **rl) { + pool_elem_t *src_e = TO_ELEM(src); + pool_elem_t *tgt_e = TO_ELEM(tgt); + pool_xtransfer_undo_t *xtransfer; + size_t size; + pool_knl_connection_t *prov = + (pool_knl_connection_t *)TO_CONF(src_e)->pc_prov; + + if (prov->pkc_log->l_state != LS_DO) { + /* + * Walk the Result Set and move the resource components + */ + for (size = 0; rl[size] != NULL; size++) { + if (pool_set_container(TO_ELEM(tgt), + TO_ELEM(rl[size])) == PO_FAIL) { + return (PO_FAIL); + } + } + return (PO_SUCCESS); + } + + /* + * The remaining logic is setting up the arguments for the + * POOL_XTRANSFER ioctl and appending the details into the log. + */ + if ((xtransfer = malloc(sizeof (pool_xtransfer_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + if (pool_elem_class(src_e) == PEC_RES_COMP) { + xtransfer->pxu_ioctl.px_o_id_type = + pool_resource_elem_class(src_e); + } else { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + + + for (xtransfer->pxu_ioctl.px_o_complist_size = 0; + rl[xtransfer->pxu_ioctl.px_o_complist_size] != NULL; + xtransfer->pxu_ioctl.px_o_complist_size++) + /* calculate the size using the terminating NULL */; + if ((xtransfer->pxu_ioctl.px_o_comp_list = + calloc(xtransfer->pxu_ioctl.px_o_complist_size, + sizeof (id_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if ((xtransfer->pxu_rl = calloc( + xtransfer->pxu_ioctl.px_o_complist_size + 1, + sizeof (pool_component_t *))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memcpy(xtransfer->pxu_rl, rl, + xtransfer->pxu_ioctl.px_o_complist_size * + sizeof (pool_component_t *)); + xtransfer->pxu_src = src_e; + xtransfer->pxu_tgt = tgt_e; + + if (log_append(prov->pkc_log, POOL_XTRANSFER, (void *)xtransfer) != + PO_SUCCESS) { + free(xtransfer); + return (PO_FAIL); + } + for (size = 0; rl[size] != NULL; size++) { + if (pool_set_container(TO_ELEM(tgt), TO_ELEM(rl[size])) == + PO_FAIL) { + return (PO_FAIL); + } + } + return (PO_SUCCESS); +} + +/* + * Return the parent of an element. + */ +pool_elem_t * +pool_knl_get_container(const pool_elem_t *pe) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)pe; + + return ((pool_elem_t *)pke->pke_parent); +} + +/* + * Note: This function is resource specific, needs extending for other + * resource types + */ +int +pool_knl_resource_is_system(const pool_resource_t *pr) +{ + switch (pool_resource_elem_class(TO_ELEM(pr))) { + case PREC_PSET: + return (PSID_IS_SYSSET( + elem_get_sysid(TO_ELEM(pr)))); + default: + return (PO_FALSE); + } +} + +/* + * Note: This function is resource specific, needs extending for other + * resource types + */ +int +pool_knl_resource_can_associate(const pool_resource_t *pr) +{ + switch (pool_resource_elem_class(TO_ELEM(pr))) { + case PREC_PSET: + return (PO_TRUE); + default: + return (PO_FALSE); + } +} + +/* + * pool_knl_pool_associate() associates the supplied resource to the + * supplied pool. + * + * Returns: PO_SUCCESS/PO_FAIL + */ +int +pool_knl_pool_associate(pool_t *pool, const pool_resource_t *resource) +{ + pool_knl_connection_t *prov; + pool_knl_pool_t *pkp = (pool_knl_pool_t *)pool; + pool_resource_elem_class_t res_class = + pool_resource_elem_class(TO_ELEM(resource)); + pool_assoc_undo_t *assoc; + pool_knl_resource_t *orig_res = pkp->pkp_assoc[res_class]; + + /* + * Are we allowed to associate with this target? + */ + if (pool_knl_resource_can_associate(resource) == PO_FALSE) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + prov = (pool_knl_connection_t *)(TO_CONF(TO_ELEM(pool)))->pc_prov; + + if (prov->pkc_log->l_state != LS_DO) { + pkp->pkp_assoc[res_class] = (pool_knl_resource_t *)resource; + return (PO_SUCCESS); + } + + /* + * The remaining logic is setting up the arguments for the + * POOL_ASSOC ioctl and appending the details into the log. + */ + if ((assoc = malloc(sizeof (pool_assoc_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + assoc->pau_assoc = TO_ELEM(pool); + assoc->pau_oldres = (pool_elem_t *)orig_res; + assoc->pau_newres = TO_ELEM(resource); + + assoc->pau_ioctl.pa_o_id_type = res_class; + + if (log_append(prov->pkc_log, POOL_ASSOC, (void *)assoc) != + PO_SUCCESS) { + free(assoc); + pkp->pkp_assoc[res_class] = orig_res; + return (PO_FAIL); + } + pkp->pkp_assoc[res_class] = (pool_knl_resource_t *)resource; + return (PO_SUCCESS); +} + +/* + * pool_knl_pool_dissociate() dissociates the supplied resource from + * the supplied pool. + * + * Returns: PO_SUCCESS/PO_FAIL + */ +int +pool_knl_pool_dissociate(pool_t *pool, const pool_resource_t *resource) +{ + pool_knl_connection_t *prov; + pool_dissoc_undo_t *dissoc; + pool_knl_pool_t *pkp = (pool_knl_pool_t *)pool; + pool_resource_t *default_res = (pool_resource_t *)get_default_resource( + resource); + pool_resource_elem_class_t res_class = + pool_resource_elem_class(TO_ELEM(resource)); + + prov = (pool_knl_connection_t *)(TO_CONF(TO_ELEM(pool)))->pc_prov; + + if (prov->pkc_log->l_state != LS_DO) { + pkp->pkp_assoc[res_class] = (pool_knl_resource_t *)default_res; + return (PO_SUCCESS); + } + /* + * The remaining logic is setting up the arguments for the + * POOL_DISSOC ioctl and appending the details into the log. + */ + if ((dissoc = malloc(sizeof (pool_dissoc_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + dissoc->pdu_dissoc = TO_ELEM(pool); + dissoc->pdu_oldres = TO_ELEM(resource); + dissoc->pdu_newres = TO_ELEM(default_res); + + dissoc->pdu_ioctl.pd_o_id_type = res_class; + + if (log_append(prov->pkc_log, POOL_DISSOC, (void *)dissoc) != + PO_SUCCESS) { + free(dissoc); + pkp->pkp_assoc[res_class] = (pool_knl_resource_t *)resource; + return (PO_FAIL); + } + + /* + * Update our local copy + */ + pkp->pkp_assoc[res_class] = (pool_knl_resource_t *)default_res; + return (PO_SUCCESS); +} + +/* + * Allocate a data provider for the supplied configuration and optionally + * discover resources. + * The data provider is the cross over point from the "abstract" configuration + * functions into the data representation specific manipulation routines. + * This function sets up all the required pointers to create a kernel aware + * data provider. + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_connection_alloc(pool_conf_t *conf, int oflags) +{ + pool_knl_connection_t *prov; + + if ((prov = malloc(sizeof (pool_knl_connection_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(prov, 0, sizeof (pool_knl_connection_t)); + /* + * Initialise data members + */ + prov->pc_name = strdup("kernel"); + prov->pc_store_type = KERNEL_DATA_STORE; + prov->pc_oflags = oflags; + /* + * Initialise function pointers + */ + prov->pc_close = pool_knl_close; + prov->pc_validate = pool_knl_validate; + prov->pc_commit = pool_knl_commit; + prov->pc_export = pool_knl_export; + prov->pc_rollback = pool_knl_rollback; + prov->pc_exec_query = pool_knl_exec_query; + prov->pc_elem_create = pool_knl_elem_create; + prov->pc_remove = pool_knl_remove; + prov->pc_res_xfer = pool_knl_res_transfer; + prov->pc_res_xxfer = pool_knl_res_xtransfer; + prov->pc_get_binding = pool_knl_get_binding; + prov->pc_set_binding = pool_knl_set_binding; + prov->pc_get_resource_binding = pool_knl_get_resource_binding; + /* + * Associate the provider to it's configuration + */ + conf->pc_prov = (pool_connection_t *)prov; + /* + * End of common initialisation + */ + /* + * Attempt to open the pseudo device, if the configuration is opened + * readonly then try to open an info device, otherwise try to open + * the writeable device. + */ + if (oflags & PO_RDWR) { + if ((prov->pkc_fd = blocking_open(pool_dynamic_location(), + O_RDWR)) < 0) { + free(prov); + conf->pc_prov = NULL; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } else { + if ((prov->pkc_fd = open(pool_info_location, O_RDWR)) < 0) { + free(prov); + conf->pc_prov = NULL; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + /* + * Allocate the element dictionary + */ + if ((prov->pkc_elements = dict_new((int (*)(const void *, const void *)) + pool_elem_compare, (uint64_t (*)(const void *))hash_id)) == NULL) { + (void) close(prov->pkc_fd); + free(prov); + conf->pc_prov = NULL; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } +#if DEBUG + if ((prov->pkc_leaks = dict_new(NULL, NULL)) == NULL) { + dict_free(&prov->pkc_elements); + (void) close(prov->pkc_fd); + free(prov); + conf->pc_prov = NULL; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } +#endif /* DEBUG */ + /* + * Allocate the transaction log + */ + if ((prov->pkc_log = log_alloc(conf)) == NULL) { +#if DEBUG + dict_free(&prov->pkc_leaks); +#endif /* DEBUG */ + dict_free(&prov->pkc_elements); + (void) close(prov->pkc_fd); + free(prov); + conf->pc_prov = NULL; + return (PO_FAIL); + } + /* + * At this point the configuration provider has been initialized, + * mark the configuration as valid so that the various routines + * which rely on a valid configuration will work correctly. + */ + conf->pc_state = POF_VALID; + /* + * Update the library snapshot from the kernel + */ + if (pool_knl_update(conf, NULL) != PO_SUCCESS) { +#if DEBUG + dict_free(&prov->pkc_leaks); +#endif /* DEBUG */ + dict_free(&prov->pkc_elements); + (void) close(prov->pkc_fd); + free(prov); + conf->pc_prov = NULL; + conf->pc_state = POF_INVALID; + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +#if DEBUG +static void +pool_knl_elem_printf_cb(const void *key, void **value, void *cl) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)key; + dict_hdl_t *map = (dict_hdl_t *)cl; + + dprintf("leak elem:%p\n", pke); + if (pke->pke_properties != NULL) { + nvlist_print(stdout, pke->pke_properties); + } else + dprintf("no properties\n"); + assert(dict_get(map, pke) == NULL); +} +#endif /* DEBUG */ +/* + * pool_knl_elem_free() releases the resources associated with the + * supplied element. + */ +static void +pool_knl_elem_free(pool_knl_elem_t *pke, int freeprop) +{ +#if DEBUG + pool_conf_t *conf = TO_CONF(TO_ELEM(pke)); + if (dict_remove(((pool_knl_connection_t *)conf->pc_prov)->pkc_leaks, + pke) == NULL) + dprintf("%p, wasn't in the leak map\n", pke); + if (freeprop == PO_TRUE) { + pool_elem_dprintf(TO_ELEM(pke)); + } + dprintf("released %p\n", pke); +#endif /* DEBUG */ + if (freeprop == PO_TRUE) { + nvlist_free(pke->pke_properties); + } + free(pke); +} + +/* + * pool_knl_elem_free_cb() is designed to be used with + * dict_map(). When a connection is freed, this function is used to + * free all element resources. + */ +/* ARGSUSED1 */ +static void +pool_knl_elem_free_cb(const void *key, void **value, void *cl) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)key; + +#ifdef DEBUG + dprintf("pool_knl_elem_free_cb:\n"); + dprintf("about to release %p ", pke); + pool_elem_dprintf(TO_ELEM(pke)); +#endif /* DEBUG */ + pool_knl_elem_free(pke, PO_TRUE); +} + +/* + * Free the resources for a kernel data provider. + */ +void +pool_knl_connection_free(pool_knl_connection_t *prov) +{ + if (prov->pkc_log != NULL) { + (void) log_walk(prov->pkc_log, log_item_release); + log_free(prov->pkc_log); + } + if (prov->pkc_elements != NULL) { + dict_map(prov->pkc_elements, pool_knl_elem_free_cb, NULL); +#if DEBUG + dprintf("dict length is %llu\n", dict_length(prov->pkc_leaks)); + dict_map(prov->pkc_leaks, pool_knl_elem_printf_cb, + prov->pkc_elements); + assert(dict_length(prov->pkc_leaks) == 0); + dict_free(&prov->pkc_leaks); +#endif /* DEBUG */ + dict_free(&prov->pkc_elements); + } + free((void *)prov->pc_name); + free(prov); +} + +/* + * Return the specified property value. + * + * POC_INVAL is returned if an error is detected and the error code is updated + * to indicate the cause of the error. + */ +pool_value_class_t +pool_knl_get_property(const pool_elem_t *pe, const char *name, + pool_value_t *val) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)pe; + nvpair_t *pair; + const pool_prop_t *prop; + + if ((prop = provider_get_prop(pe, name)) != NULL) + if (prop_is_stored(prop) == PO_FALSE) + return (pool_knl_get_dynamic_property(pe, name, val)); + + if ((pair = pool_knl_find_nvpair(pke->pke_properties, name)) == NULL) { + pool_seterror(POE_BADPARAM); + return (POC_INVAL); + } + + if (pool_value_from_nvpair(val, pair) == PO_FAIL) { + return (POC_INVAL); + } + + return (pool_value_get_type(val)); +} + +/* + * Return the specified property value. + * + * If a property is designated as dynamic, then this function will + * always try to return the latest value of the property from the + * kernel. + * + * POC_INVAL is returned if an error is detected and the error code is updated + * to indicate the cause of the error. + */ +pool_value_class_t +pool_knl_get_dynamic_property(const pool_elem_t *pe, const char *name, + pool_value_t *val) +{ + pool_knl_connection_t *prov; + pool_propget_t propget = { 0 }; + nvlist_t *proplist; + nvpair_t *pair; + + propget.pp_o_id_type = pool_elem_class(pe); + if (pool_elem_class(pe) == PEC_RES_COMP || + pool_elem_class(pe) == PEC_RES_AGG) + propget.pp_o_id_subtype = pool_resource_elem_class(pe); + if (pool_elem_class(pe) == PEC_COMP) + propget.pp_o_id_subtype = + (pool_resource_elem_class_t)pool_component_elem_class(pe); + + propget.pp_o_id = elem_get_sysid(pe); + propget.pp_o_prop_name_size = strlen(name); + propget.pp_o_prop_name = (char *)name; + propget.pp_i_bufsize = KERNEL_SNAPSHOT_BUF_SZ; + propget.pp_i_buf = malloc(KERNEL_SNAPSHOT_BUF_SZ); + bzero(propget.pp_i_buf, KERNEL_SNAPSHOT_BUF_SZ); + + prov = (pool_knl_connection_t *)(TO_CONF(pe))->pc_prov; + if (ioctl(prov->pkc_fd, POOL_PROPGET, &propget) < 0) { + free(propget.pp_i_buf); + pool_seterror(POE_SYSTEM); + return (POC_INVAL); + } + if (nvlist_unpack(propget.pp_i_buf, propget.pp_i_bufsize, + &proplist, 0) != 0) { + free(propget.pp_i_buf); + pool_seterror(POE_SYSTEM); + return (POC_INVAL); + } + free(propget.pp_i_buf); + + if ((pair = nvlist_next_nvpair(proplist, NULL)) == NULL) { + nvlist_free(proplist); + pool_seterror(POE_SYSTEM); + return (POC_INVAL); + } + + if (pool_value_from_nvpair(val, pair) == PO_FAIL) { + nvlist_free(proplist); + return (POC_INVAL); + } + nvlist_free(proplist); + return (pool_value_get_type(val)); +} + +/* + * Update the specified property value. + * + * PO_FAIL is returned if an error is detected and the error code is updated + * to indicate the cause of the error. + */ +int +pool_knl_put_property(pool_elem_t *pe, const char *name, + const pool_value_t *val) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)pe; + pool_knl_connection_t *prov = + (pool_knl_connection_t *)(TO_CONF(pe))->pc_prov; + nvpair_t *bp, *ap; + pool_propput_undo_t *propput; + nvlist_t *bl = NULL; + const pool_prop_t *prop; + + if ((bp = pool_knl_find_nvpair(pke->pke_properties, name)) != NULL) { + if (nvlist_alloc(&bl, NV_UNIQUE_NAME_TYPE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (nvlist_add_nvpair(bl, bp) != 0) { + nvlist_free(bl); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + if (pool_knl_nvlist_add_value(pke->pke_properties, name, val) != + PO_SUCCESS) + return (PO_FAIL); + + if (prov->pkc_log->l_state != LS_DO) { + if (bl) + nvlist_free(bl); + return (PO_SUCCESS); + } + /* + * The remaining logic is setting up the arguments for the + * POOL_PROPPUT ioctl and appending the details into the log. + */ + if ((propput = malloc(sizeof (pool_propput_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(propput, 0, sizeof (pool_propput_undo_t)); + propput->ppu_blist = bl; + + ap = pool_knl_find_nvpair(pke->pke_properties, name); + + if (nvlist_alloc(&propput->ppu_alist, NV_UNIQUE_NAME_TYPE, 0) != 0) { + nvlist_free(propput->ppu_blist); + free(propput); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (nvlist_add_nvpair(propput->ppu_alist, ap) != 0) { + nvlist_free(propput->ppu_blist); + nvlist_free(propput->ppu_alist); + free(propput); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + if (nvlist_pack(propput->ppu_alist, + (char **)&propput->ppu_ioctl.pp_o_buf, + &propput->ppu_ioctl.pp_o_bufsize, NV_ENCODE_NATIVE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + nvlist_free(propput->ppu_alist); + propput->ppu_ioctl.pp_o_id_type = pool_elem_class(pe); + if (pool_elem_class(pe) == PEC_RES_COMP || + pool_elem_class(pe) == PEC_RES_AGG) + propput->ppu_ioctl.pp_o_id_sub_type = + pool_resource_elem_class(pe); + if (pool_elem_class(pe) == PEC_COMP) + propput->ppu_ioctl.pp_o_id_sub_type = + (pool_resource_elem_class_t)pool_component_elem_class(pe); + + propput->ppu_elem = pe; + if ((prop = provider_get_prop(propput->ppu_elem, name)) != NULL) { + if (prop_is_readonly(prop) == PO_TRUE) + propput->ppu_doioctl |= KERNEL_PROP_RDONLY; + } + + if (log_append(prov->pkc_log, POOL_PROPPUT, (void *)propput) != + PO_SUCCESS) { + nvlist_free(propput->ppu_blist); + free(propput); + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * Remove the specified property value. + * + * PO_FAIL is returned if an error is detected and the error code is + * updated to indicate the cause of the error. + */ +int +pool_knl_rm_property(pool_elem_t *pe, const char *name) +{ + pool_knl_elem_t *pke = (pool_knl_elem_t *)pe; + pool_knl_connection_t *prov = + (pool_knl_connection_t *)(TO_CONF(pe))->pc_prov; + pool_proprm_undo_t *proprm; + + if (pool_knl_find_nvpair(pke->pke_properties, name) == NULL) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + + if ((proprm = malloc(sizeof (pool_proprm_undo_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(proprm, 0, sizeof (pool_proprm_undo_t)); + proprm->pru_oldval.pv_class = POC_INVAL; + (void) pool_get_property(TO_CONF(pe), pe, name, &proprm->pru_oldval); + + if (prov->pkc_log->l_state != LS_DO) { + free(proprm); + (void) nvlist_remove_all(pke->pke_properties, (char *)name); + return (PO_SUCCESS); + } + /* + * The remaining logic is setting up the arguments for the + * POOL_PROPRM ioctl and appending the details into the log. + */ + + proprm->pru_ioctl.pp_o_id_type = pool_elem_class(pe); + if (pool_elem_class(pe) == PEC_RES_COMP || + pool_elem_class(pe) == PEC_RES_AGG) + proprm->pru_ioctl.pp_o_id_sub_type = + pool_resource_elem_class(pe); + + if (pool_elem_class(pe) == PEC_COMP) + proprm->pru_ioctl.pp_o_id_sub_type = + (pool_resource_elem_class_t)pool_component_elem_class(pe); + + proprm->pru_ioctl.pp_o_prop_name_size = strlen(name); + proprm->pru_ioctl.pp_o_prop_name = + (char *)pool_value_get_name(&proprm->pru_oldval); + proprm->pru_elem = pe; + + if (log_append(prov->pkc_log, POOL_PROPRM, (void *)proprm) != + PO_SUCCESS) { + free(proprm); + return (PO_FAIL); + } + + (void) nvlist_remove_all(pke->pke_properties, (char *)name); + return (PO_SUCCESS); +} + +/* + * Return a NULL terminated array of pool_value_t which represents all + * of the properties stored for an element + * + * Return NULL on failure. It is the caller's responsibility to free + * the returned array of values. + */ +pool_value_t ** +pool_knl_get_properties(const pool_elem_t *pe, uint_t *nprops) +{ + nvpair_t *pair; + pool_value_t **result; + pool_knl_elem_t *pke = (pool_knl_elem_t *)pe; + int i = 0; + + *nprops = 0; + + for (pair = nvlist_next_nvpair(pke->pke_properties, NULL); pair != NULL; + pair = nvlist_next_nvpair(pke->pke_properties, pair)) + (*nprops)++; + if ((result = calloc(*nprops + 1, sizeof (pool_value_t *))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + for (pair = nvlist_next_nvpair(pke->pke_properties, NULL); pair != NULL; + pair = nvlist_next_nvpair(pke->pke_properties, pair), i++) { + result[i] = pool_value_alloc(); + if (pool_value_from_nvpair(result[i], pair) == PO_FAIL) { + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + return (NULL); + } + } + return (result); +} + +/* + * Append an entry to a result set. Reallocate the array used to store + * results if it's full. + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_result_set_append(pool_knl_result_set_t *rs, pool_knl_elem_t *pke) +{ + if (rs->pkr_count == rs->pkr_size) + if (pool_knl_result_set_realloc(rs) != PO_SUCCESS) + return (PO_FAIL); + + rs->pkr_list[rs->pkr_count++] = pke; + + return (PO_SUCCESS); +} + +/* + * Resize the array used to store results. A simple doubling strategy + * is used. + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_result_set_realloc(pool_knl_result_set_t *rs) +{ + pool_knl_elem_t **old_list = rs->pkr_list; + int new_size = rs->pkr_size * 2; + + if ((rs->pkr_list = realloc(rs->pkr_list, + new_size * sizeof (pool_knl_elem_t *))) == NULL) { + rs->pkr_list = old_list; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + rs->pkr_size = new_size; + + return (PO_SUCCESS); +} + +/* + * Allocate a result set. The Result Set stores the result of a query. + * Returns pool_knl_result_set_t pointer/NULL + */ +pool_knl_result_set_t * +pool_knl_result_set_alloc(const pool_conf_t *conf) +{ + pool_knl_result_set_t *rs; + + if ((rs = malloc(sizeof (pool_knl_result_set_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(rs, 0, sizeof (pool_knl_result_set_t)); + rs->pkr_size = KERNEL_RS_INITIAL_SZ; + if (pool_knl_result_set_realloc(rs) == PO_FAIL) { + free(rs); + pool_seterror(POE_SYSTEM); + return (NULL); + } + rs->prs_conf = conf; + rs->prs_index = -1; + rs->prs_active = PO_TRUE; + /* Fix up the result set accessor functions to the knl specfic ones */ + rs->prs_next = pool_knl_rs_next; + rs->prs_prev = pool_knl_rs_prev; + rs->prs_first = pool_knl_rs_first; + rs->prs_last = pool_knl_rs_last; + rs->prs_get_index = pool_knl_rs_get_index; + rs->prs_set_index = pool_knl_rs_set_index; + rs->prs_close = pool_knl_rs_close; + rs->prs_count = pool_knl_rs_count; + return (rs); +} + +/* + * Free a result set. Ensure that the resources are all released at + * this point. + */ +void +pool_knl_result_set_free(pool_knl_result_set_t *rs) +{ + free(rs->pkr_list); + free(rs); +} +/* + * Return the next element in a result set. + * Returns pool_elem_t pointer/NULL + */ +pool_elem_t * +pool_knl_rs_next(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + if (kset->prs_index == kset->pkr_count - 1) + return (NULL); + return ((pool_elem_t *)kset->pkr_list[++kset->prs_index]); +} + +/* + * Return the previous element in a result set. + * Returns pool_elem_t pointer/NULL + */ +pool_elem_t * +pool_knl_rs_prev(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + if (kset->prs_index < 0) + return (NULL); + return ((pool_elem_t *)kset->pkr_list[kset->prs_index--]); +} + +/* + * Sets the current index in a result set. + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_rs_set_index(pool_result_set_t *set, int index) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + if (index < 0 || index >= kset->pkr_count) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + kset->prs_index = index; + return (PO_SUCCESS); +} + +/* + * Return the current index in a result set. + * Returns current index + */ +int +pool_knl_rs_get_index(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + return (kset->prs_index); +} + +/* + * Return the first element in a result set. + * Returns pool_elem_t pointer/NULL + */ +pool_elem_t * +pool_knl_rs_first(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + return ((pool_elem_t *)kset->pkr_list[0]); +} + +/* + * Return the last element in a result set. + * Returns pool_elem_t pointer/NULL + */ +pool_elem_t * +pool_knl_rs_last(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + return ((pool_elem_t *)kset->pkr_list[kset->pkr_count - 1]); +} + +/* + * Return the number of results in a result set. + * Returns result count + */ +int +pool_knl_rs_count(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + return (kset->pkr_count); +} + + +/* + * Close a result set. Free the resources + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_knl_rs_close(pool_result_set_t *set) +{ + pool_knl_result_set_t *kset = (pool_knl_result_set_t *)set; + + pool_knl_result_set_free(kset); + return (PO_SUCCESS); +} + +/* + * Commit an individual transaction log item(). This processing is + * essential to the pool_conf_commit() logic. When pool_conf_commit() + * is invoked, the pending transaction log for the configuration is + * walked and all pending changes to the kernel are invoked. If a + * change succeeds it is marked in the log as successful and + * processing continues, if it fails then failure is returned and the + * log will be "rolled back" to undo changes to the library snapshot + * and the kernel. + */ +int +log_item_commit(log_item_t *li) +{ + pool_knl_connection_t *prov = + (pool_knl_connection_t *)li->li_log->l_conf->pc_prov; + pool_create_undo_t *create; + pool_destroy_undo_t *destroy; + pool_assoc_undo_t *assoc; + pool_dissoc_undo_t *dissoc; + pool_propput_undo_t *propput; + pool_proprm_undo_t *proprm; + pool_xtransfer_undo_t *xtransfer; + char_buf_t *cb; + size_t size; + pool_elem_t *pair; + pool_value_t val = POOL_VALUE_INITIALIZER; + int ret; + + switch (li->li_op) { + case POOL_CREATE: + create = (pool_create_undo_t *)li->li_details; + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) + return (PO_FAIL); + if (set_char_buf(cb, "%s.sys_id", + pool_elem_class_string(create->pcu_elem)) != PO_SUCCESS) { + free_char_buf(cb); + return (PO_FAIL); + } +#ifdef DEBUG + dprintf("log_item_commit: POOL_CREATE, remove from dict\n"); + pool_elem_dprintf(create->pcu_elem); +#endif /* DEBUG */ + /* + * May not need to remove the element if it was + * already destroyed before commit. Just cast the + * return to void. + */ + (void) dict_remove(prov->pkc_elements, + (pool_knl_elem_t *)create->pcu_elem); + + if (ioctl(prov->pkc_fd, POOL_CREATE, &create->pcu_ioctl) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Now that we have created our element in the kernel, + * it has a valid allocated system id. Remove the + * element from the element dictionary, using the + * current key, and then re-insert under the new key. + */ +#ifdef DEBUG + pool_elem_dprintf(create->pcu_elem); +#endif /* DEBUG */ + assert(nvlist_add_int64( + ((pool_knl_elem_t *)create->pcu_elem)->pke_properties, + cb->cb_buf, create->pcu_ioctl.pc_i_id) == 0); + free_char_buf(cb); + assert(dict_put(prov->pkc_elements, create->pcu_elem, + create->pcu_elem) == NULL); + /* + * If the element has a pair in the static + * configuration, update it with the sys_id + */ + if ((pair = pool_get_pair(create->pcu_elem)) != NULL) { + pool_value_set_int64(&val, create->pcu_ioctl.pc_i_id); + assert(pool_put_any_ns_property(pair, c_sys_prop, &val) + == PO_SUCCESS); + } + li->li_state = LS_UNDO; + break; + case POOL_DESTROY: + destroy = (pool_destroy_undo_t *)li->li_details; + + destroy->pdu_ioctl.pd_o_id = elem_get_sysid(destroy->pdu_elem); + + /* + * It may be that this element was created in the last + * transaction. In which case POOL_CREATE, above, will + * have re-inserted the element in the dictionary. Try + * to remove it just in case this has occurred. + */ + (void) dict_remove(prov->pkc_elements, + (pool_knl_elem_t *)destroy->pdu_elem); + while ((ret = ioctl(prov->pkc_fd, POOL_DESTROY, + &destroy->pdu_ioctl)) < 0 && errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } +#ifdef DEBUG + dprintf("log_item_commit: POOL_DESTROY\n"); + pool_elem_dprintf(destroy->pdu_elem); +#endif /* DEBUG */ + li->li_state = LS_UNDO; + break; + case POOL_ASSOC: + assoc = (pool_assoc_undo_t *)li->li_details; + + assoc->pau_ioctl.pa_o_pool_id = + elem_get_sysid(assoc->pau_assoc); + assoc->pau_ioctl.pa_o_res_id = + elem_get_sysid(assoc->pau_newres); + while ((ret = ioctl(prov->pkc_fd, POOL_ASSOC, + &assoc->pau_ioctl)) < 0 && errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_UNDO; + break; + case POOL_DISSOC: + dissoc = (pool_dissoc_undo_t *)li->li_details; + + dissoc->pdu_ioctl.pd_o_pool_id = + elem_get_sysid(dissoc->pdu_dissoc); + + while ((ret = ioctl(prov->pkc_fd, POOL_DISSOC, + &dissoc->pdu_ioctl)) < 0 && errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_UNDO; + break; + case POOL_TRANSFER: + li->li_state = LS_UNDO; + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + case POOL_XTRANSFER: + xtransfer = (pool_xtransfer_undo_t *)li->li_details; + + xtransfer->pxu_ioctl.px_o_src_id = + elem_get_sysid(xtransfer->pxu_src); + xtransfer->pxu_ioctl.px_o_tgt_id = + elem_get_sysid(xtransfer->pxu_tgt); + for (size = 0; xtransfer->pxu_rl[size] != NULL; size ++) { + xtransfer->pxu_ioctl.px_o_comp_list[size] = + elem_get_sysid(TO_ELEM(xtransfer->pxu_rl[size])); +#ifdef DEBUG + dprintf("log_item_commit: POOL_XTRANSFER\n"); + pool_elem_dprintf(TO_ELEM(xtransfer->pxu_rl[size])); +#endif /* DEBUG */ + } + + /* + * Don't actually transfer resources if the configuration + * is in POF_DESTROY state. This is to prevent problems + * relating to transferring off-line CPUs. Instead rely + * on the POOL_DESTROY ioctl to transfer the CPUS. + */ + if (li->li_log->l_conf->pc_state != POF_DESTROY && + ioctl(prov->pkc_fd, POOL_XTRANSFER, + &xtransfer->pxu_ioctl) < 0) { +#ifdef DEBUG + dprintf("log_item_commit: POOL_XTRANSFER, ioctl " + "failed\n"); +#endif /* DEBUG */ + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_UNDO; + break; + case POOL_PROPPUT: + propput = (pool_propput_undo_t *)li->li_details; + + if (pool_elem_class(propput->ppu_elem) != PEC_SYSTEM) { + propput->ppu_ioctl.pp_o_id = + elem_get_sysid(propput->ppu_elem); + } + /* + * Some properties, e.g. pset.size, are read-only in the + * kernel and attempting to change them will fail and cause + * problems. Although this property is read-only through the + * public interface, the library needs to modify it's value. + */ + if ((propput->ppu_doioctl & KERNEL_PROP_RDONLY) == 0) { + if (ioctl(prov->pkc_fd, POOL_PROPPUT, + &propput->ppu_ioctl) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + li->li_state = LS_UNDO; + break; + case POOL_PROPRM: + proprm = (pool_proprm_undo_t *)li->li_details; + + if (pool_elem_class(proprm->pru_elem) != PEC_SYSTEM) { + proprm->pru_ioctl.pp_o_id = + elem_get_sysid(proprm->pru_elem); + } + if (ioctl(prov->pkc_fd, POOL_PROPRM, &proprm->pru_ioctl) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_UNDO; + break; + default: + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * Undo an individual transaction log item(). This processing is + * essential to the pool_conf_commit() and pool_conf_rollback() + * logic. Changes to the libpool snapshot and the kernel are carried + * out separately. The library snapshot is updated synchronously, + * however the kernel update is delayed until the user calls + * pool_conf_commit(). + * + * When undoing transactions, library changes will be undone unless + * this invocation is as a result of a commit failure, in which case + * the log state will be LS_RECOVER. Kernel changes will only be + * undone if they are marked as having been done, in which case the + * log item state will be LS_UNDO. + */ +int +log_item_undo(log_item_t *li) +{ + pool_knl_connection_t *prov = + (pool_knl_connection_t *)li->li_log->l_conf->pc_prov; + pool_create_undo_t *create; + pool_destroy_undo_t *destroy; + pool_assoc_undo_t *assoc; + pool_dissoc_undo_t *dissoc; + pool_propput_undo_t *propput; + pool_proprm_undo_t *proprm; + pool_xtransfer_undo_t *xtransfer; + char_buf_t *cb; + size_t size; + pool_destroy_t u_destroy; + pool_create_t u_create; + pool_assoc_t u_assoc; + pool_xtransfer_t u_xtransfer; + pool_propput_t u_propput; + pool_proprm_t u_proprm; + pool_conf_t *conf = li->li_log->l_conf; + nvpair_t *pair; + nvlist_t *tmplist; + int ret; + + if (li->li_log->l_state != LS_RECOVER) { + switch (li->li_op) { + case POOL_CREATE: + create = (pool_create_undo_t *)li->li_details; + + (void) dict_remove(prov->pkc_elements, create->pcu_elem); +#ifdef DEBUG + dprintf("log_item_undo: POOL_CREATE\n"); + assert(create->pcu_elem != NULL); + dprintf("log_item_undo: POOL_CREATE %p\n", create->pcu_elem); + pool_elem_dprintf(create->pcu_elem); +#endif /* DEBUG */ + pool_knl_elem_free((pool_knl_elem_t *)create->pcu_elem, + PO_TRUE); + break; + case POOL_DESTROY: + destroy = (pool_destroy_undo_t *)li->li_details; + + assert(dict_put(prov->pkc_elements, destroy->pdu_elem, + destroy->pdu_elem) == NULL); + break; + case POOL_ASSOC: + assoc = (pool_assoc_undo_t *)li->li_details; + + if (assoc->pau_oldres != NULL) + ((pool_knl_pool_t *)assoc->pau_assoc)->pkp_assoc + [pool_resource_elem_class(assoc->pau_oldres)] = + (pool_knl_resource_t *)assoc->pau_oldres; + break; + case POOL_DISSOC: + dissoc = (pool_dissoc_undo_t *)li->li_details; + + if (dissoc->pdu_oldres != NULL) + ((pool_knl_pool_t *)dissoc->pdu_dissoc)->pkp_assoc + [pool_resource_elem_class(dissoc->pdu_oldres)] = + (pool_knl_resource_t *)dissoc->pdu_oldres; + break; + case POOL_TRANSFER: + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + case POOL_XTRANSFER: + xtransfer = (pool_xtransfer_undo_t *)li->li_details; + + for (size = 0; xtransfer->pxu_rl[size] != NULL; size++) { + pool_value_t val = POOL_VALUE_INITIALIZER; + uint64_t src_size; + uint64_t tgt_size; + + if (pool_set_container(xtransfer->pxu_src, + TO_ELEM(xtransfer->pxu_rl[size])) == PO_FAIL) { + return (PO_FAIL); + } + /* + * Maintain the library view of the size + */ + if (resource_get_size(pool_elem_res(xtransfer->pxu_src), + &src_size) != PO_SUCCESS || + resource_get_size(pool_elem_res(xtransfer->pxu_tgt), + &tgt_size) != PO_SUCCESS) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + src_size++; + tgt_size--; + pool_value_set_uint64(&val, src_size); + (void) pool_put_any_ns_property(xtransfer->pxu_src, + c_size_prop, &val); + pool_value_set_uint64(&val, tgt_size); + (void) pool_put_any_ns_property(xtransfer->pxu_tgt, + c_size_prop, &val); + } + break; + case POOL_PROPPUT: + propput = (pool_propput_undo_t *)li->li_details; + + if ((propput->ppu_doioctl & KERNEL_PROP_RDONLY) == 0) { + if (propput->ppu_blist != NULL) { + if (nvlist_merge( + ((pool_knl_elem_t *)propput->ppu_elem)-> + pke_properties, propput->ppu_blist, 0) + != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } else { + if (nvlist_unpack(propput->ppu_ioctl.pp_o_buf, + propput->ppu_ioctl.pp_o_bufsize, + &propput->ppu_alist, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + pair = nvlist_next_nvpair(propput->ppu_alist, + NULL); + (void) nvlist_remove_all(((pool_knl_elem_t *) + propput->ppu_elem)->pke_properties, + nvpair_name(pair)); + nvlist_free(propput->ppu_alist); + } + } + break; + case POOL_PROPRM: + proprm = (pool_proprm_undo_t *)li->li_details; + + if (pool_value_get_type(&proprm->pru_oldval) != POC_INVAL) { + if (pool_put_property(conf, proprm->pru_elem, + proprm->pru_ioctl.pp_o_prop_name, + &proprm->pru_oldval) != PO_SUCCESS) { + return (PO_FAIL); + } + } + break; + default: + return (PO_FAIL); + } + } + /* + * Only try to undo the state of the kernel if we modified it. + */ + if (li->li_state == LS_DO) { + return (PO_SUCCESS); + } + + switch (li->li_op) { + case POOL_CREATE: + create = (pool_create_undo_t *)li->li_details; + + u_destroy.pd_o_type = create->pcu_ioctl.pc_o_type; + u_destroy.pd_o_sub_type = create->pcu_ioctl.pc_o_sub_type; + u_destroy.pd_o_id = create->pcu_ioctl.pc_i_id; + + while ((ret = ioctl(prov->pkc_fd, POOL_DESTROY, + &u_destroy)) < 0 && errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_DO; + break; + case POOL_DESTROY: + destroy = (pool_destroy_undo_t *)li->li_details; + + u_create.pc_o_type = destroy->pdu_ioctl.pd_o_type; + u_create.pc_o_sub_type = destroy->pdu_ioctl.pd_o_sub_type; + + if (ioctl(prov->pkc_fd, POOL_CREATE, &u_create) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) { + return (PO_FAIL); + } + if (set_char_buf(cb, "%s.sys_id", + pool_elem_class_string(destroy->pdu_elem)) != PO_SUCCESS) { + free_char_buf(cb); + return (PO_FAIL); + } + (void) nvlist_add_int64( + ((pool_knl_elem_t *)destroy->pdu_elem)->pke_properties, + cb->cb_buf, u_create.pc_i_id); + free_char_buf(cb); + if (dict_put(prov->pkc_elements, destroy->pdu_elem, + destroy->pdu_elem) != NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Now we need to reset all the properties and + * associations in the kernel for this newly created + * replacement. + */ + u_propput.pp_o_id_type = destroy->pdu_ioctl.pd_o_type; + u_propput.pp_o_id_sub_type = destroy->pdu_ioctl.pd_o_sub_type; + u_propput.pp_o_id = u_create.pc_i_id; + u_propput.pp_o_buf = NULL; + /* + * Remove the read-only properties before attempting + * to restore the state of the newly created property + */ + (void) nvlist_dup(((pool_knl_elem_t *)destroy->pdu_elem)-> + pke_properties, &tmplist, 0); + for (pair = nvlist_next_nvpair(tmplist, NULL); pair != NULL; + pair = nvlist_next_nvpair(tmplist, pair)) { + const pool_prop_t *prop; + char *name = nvpair_name(pair); + if ((prop = provider_get_prop(destroy->pdu_elem, + name)) != NULL) + if (prop_is_readonly(prop) == PO_TRUE) + (void) nvlist_remove_all(tmplist, name); + } + if (nvlist_pack(tmplist, (char **)&u_propput.pp_o_buf, + &u_propput.pp_o_bufsize, NV_ENCODE_NATIVE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + nvlist_free(tmplist); + if (ioctl(prov->pkc_fd, POOL_PROPPUT, &u_propput) < 0) { + free(u_propput.pp_o_buf); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + free(u_propput.pp_o_buf); + /* + * Now reset the associations for all the resource + * types if the thing which we are recreating is a + * pool + * + * TODO: This is resource specific and must be + * extended for additional resource types. + */ + if (destroy->pdu_ioctl.pd_o_type == PEC_POOL) { + u_assoc.pa_o_pool_id = u_create.pc_i_id; + u_assoc.pa_o_res_id = + elem_get_sysid( + TO_ELEM(((pool_knl_pool_t *)destroy->pdu_elem)-> + pkp_assoc[PREC_PSET])); + u_assoc.pa_o_id_type = PREC_PSET; + + if (ioctl(prov->pkc_fd, POOL_ASSOC, &u_assoc) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + li->li_state = LS_DO; + break; + case POOL_ASSOC: + assoc = (pool_assoc_undo_t *)li->li_details; + + u_assoc.pa_o_pool_id = elem_get_sysid(assoc->pau_assoc); + u_assoc.pa_o_res_id = elem_get_sysid(assoc->pau_oldres); + u_assoc.pa_o_id_type = assoc->pau_ioctl.pa_o_id_type; + + while ((ret = ioctl(prov->pkc_fd, POOL_ASSOC, &u_assoc)) < 0 && + errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_DO; + break; + case POOL_DISSOC: + dissoc = (pool_dissoc_undo_t *)li->li_details; + + u_assoc.pa_o_pool_id = elem_get_sysid(dissoc->pdu_dissoc); + u_assoc.pa_o_res_id = elem_get_sysid(dissoc->pdu_oldres); + u_assoc.pa_o_id_type = dissoc->pdu_ioctl.pd_o_id_type; + + while ((ret = ioctl(prov->pkc_fd, POOL_ASSOC, &u_assoc)) < 0 && + errno == EAGAIN) + ; + if (ret < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_DO; + break; + case POOL_TRANSFER: + li->li_state = LS_DO; + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + case POOL_XTRANSFER: + xtransfer = (pool_xtransfer_undo_t *)li->li_details; + + (void) memcpy(&u_xtransfer, &xtransfer->pxu_ioctl, + sizeof (pool_xtransfer_t)); + u_xtransfer.px_o_src_id = elem_get_sysid(xtransfer->pxu_tgt); + u_xtransfer.px_o_tgt_id = elem_get_sysid(xtransfer->pxu_src); + + if (ioctl(prov->pkc_fd, POOL_XTRANSFER, &u_xtransfer) < 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + li->li_state = LS_DO; + break; + case POOL_PROPPUT: + propput = (pool_propput_undo_t *)li->li_details; + + if ((propput->ppu_doioctl & KERNEL_PROP_RDONLY) == 0) { + if (propput->ppu_blist) { + (void) memcpy(&u_propput, &propput->ppu_ioctl, + sizeof (pool_propput_t)); + u_propput.pp_o_id = + elem_get_sysid(propput->ppu_elem); + u_propput.pp_o_buf = NULL; + if (nvlist_pack(propput->ppu_blist, + (char **)&u_propput.pp_o_buf, + &u_propput.pp_o_bufsize, + NV_ENCODE_NATIVE, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (ioctl(prov->pkc_fd, POOL_PROPPUT, + &u_propput) < 0) { + free(u_propput.pp_o_buf); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + free(u_propput.pp_o_buf); + } else { + if (nvlist_unpack(propput-> + ppu_ioctl.pp_o_buf, + propput->ppu_ioctl.pp_o_bufsize, + &propput->ppu_alist, 0) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + u_proprm.pp_o_id_type = + propput->ppu_ioctl.pp_o_id_type; + u_proprm.pp_o_id_sub_type = + propput->ppu_ioctl.pp_o_id_sub_type; + u_proprm.pp_o_id = + elem_get_sysid(propput->ppu_elem); + pair = nvlist_next_nvpair(propput->ppu_alist, + NULL); + u_proprm.pp_o_prop_name = nvpair_name(pair); + u_proprm.pp_o_prop_name_size = + strlen(u_proprm.pp_o_prop_name); + + if (provider_get_prop(propput->ppu_elem, + u_proprm.pp_o_prop_name) == NULL) { + if (ioctl(prov->pkc_fd, POOL_PROPRM, + &u_proprm) < 0) { + nvlist_free(propput->ppu_alist); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + } + nvlist_free(propput->ppu_alist); + } + } + li->li_state = LS_DO; + break; + case POOL_PROPRM: + proprm = (pool_proprm_undo_t *)li->li_details; + + u_propput.pp_o_id_type = proprm->pru_ioctl.pp_o_id_type; + u_propput.pp_o_id_sub_type = + proprm->pru_ioctl.pp_o_id_sub_type; + u_propput.pp_o_id = elem_get_sysid(proprm->pru_elem); + u_propput.pp_o_buf = NULL; + /* + * Only try to remove the appropriate property + */ + if (nvlist_alloc(&tmplist, NV_UNIQUE_NAME_TYPE, 0) != + 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + if (pool_knl_nvlist_add_value(tmplist, + pool_value_get_name(&proprm->pru_oldval), + &proprm->pru_oldval) != PO_SUCCESS) + return (PO_FAIL); + + if (nvlist_pack(tmplist, + (char **)&u_propput.pp_o_buf, &u_propput.pp_o_bufsize, + NV_ENCODE_NATIVE, 0) != 0) { + nvlist_free(tmplist); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + nvlist_free(tmplist); + if (ioctl(prov->pkc_fd, POOL_PROPPUT, &u_propput) < 0) { + free(u_propput.pp_o_buf); + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + free(u_propput.pp_o_buf); + li->li_state = LS_DO; + break; + default: + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * A log item stores state about the transaction it represents. This + * function releases the resources associated with the transaction and + * used to store the transaction state. + */ +int +log_item_release(log_item_t *li) +{ + pool_create_undo_t *create; + pool_destroy_undo_t *destroy; + pool_assoc_undo_t *assoc; + pool_dissoc_undo_t *dissoc; + pool_propput_undo_t *propput; + pool_proprm_undo_t *proprm; + pool_xtransfer_undo_t *xtransfer; + + switch (li->li_op) { + case POOL_CREATE: + create = (pool_create_undo_t *)li->li_details; + + free(create); + break; + case POOL_DESTROY: + destroy = (pool_destroy_undo_t *)li->li_details; + +#ifdef DEBUG + dprintf("log_item_release: POOL_DESTROY\n"); +#endif /* DEBUG */ + + if (li->li_state == LS_UNDO) { +#ifdef DEBUG + pool_elem_dprintf(destroy->pdu_elem); +#endif /* DEBUG */ + pool_knl_elem_free((pool_knl_elem_t *)destroy-> + pdu_elem, PO_TRUE); + } + free(destroy); + break; + case POOL_ASSOC: + assoc = (pool_assoc_undo_t *)li->li_details; + + free(assoc); + break; + case POOL_DISSOC: + dissoc = (pool_dissoc_undo_t *)li->li_details; + + free(dissoc); + break; + case POOL_TRANSFER: + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + case POOL_XTRANSFER: + xtransfer = (pool_xtransfer_undo_t *)li->li_details; + + free(xtransfer->pxu_rl); + free(xtransfer->pxu_ioctl.px_o_comp_list); + free(xtransfer); + break; + case POOL_PROPPUT: + propput = (pool_propput_undo_t *)li->li_details; + + if (propput->ppu_blist) + nvlist_free(propput->ppu_blist); + free(propput->ppu_ioctl.pp_o_buf); + free(propput); + break; + case POOL_PROPRM: + proprm = (pool_proprm_undo_t *)li->li_details; + + free(proprm); + break; + default: + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * pool_knl_nvlist_add_value() adds a pool_value_t to an nvlist. + */ +int +pool_knl_nvlist_add_value(nvlist_t *list, const char *name, + const pool_value_t *pv) +{ + uint64_t uval; + int64_t ival; + double dval; + uchar_t dval_b[sizeof (double)]; + uchar_t bval; + const char *sval; + pool_value_class_t type; + char *nv_name; + + if ((type = pool_value_get_type(pv)) == POC_INVAL) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + nv_name = (char *)name; + + switch (type) { + case POC_UINT: + if (pool_value_get_uint64(pv, &uval) == POC_INVAL) { + return (PO_FAIL); + } + if (nvlist_add_uint64(list, nv_name, uval) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case POC_INT: + if (pool_value_get_int64(pv, &ival) == POC_INVAL) { + return (PO_FAIL); + } + if (nvlist_add_int64(list, nv_name, ival) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case POC_DOUBLE: + if (pool_value_get_double(pv, &dval) == POC_INVAL) { + return (PO_FAIL); + } + /* + * Since there is no support for doubles in the + * kernel, store the double value in a byte array. + */ + (void) memcpy(dval_b, &dval, sizeof (double)); + if (nvlist_add_byte_array(list, nv_name, dval_b, + sizeof (double)) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case POC_BOOL: + if (pool_value_get_bool(pv, &bval) == POC_INVAL) { + return (PO_FAIL); + } + if (nvlist_add_byte(list, nv_name, bval) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + case POC_STRING: + if (pool_value_get_string(pv, &sval) == POC_INVAL) { + return (PO_FAIL); + } + if (nvlist_add_string(list, nv_name, (char *)sval) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + break; + default: + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * hash_id() hashes all elements in a pool configuration using the + * "sys_id" property. Not all elements have a "sys_id" property, + * however elem_get_sysid() caters for this by always returning a + * constant value for those elements. This isn't anticipated to lead + * to a performance degradation in the hash, since those elements + * which are likely to be most prevalent in a configuration do have + * "sys_id" as a property. + */ +uint64_t +hash_id(const pool_elem_t *pe) +{ + id_t id; + + id = elem_get_sysid(pe); + return (hash_buf(&id, sizeof (id))); +} + +/* + * blocking_open() guarantees access to the pool device, if open() + * is failing with EBUSY. + */ +int +blocking_open(const char *path, int oflag) +{ + int fd; + + while ((fd = open(path, oflag)) == -1 && errno == EBUSY) + (void) poll(NULL, 0, 1 * MILLISEC); + + return (fd); +} |