diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/lvm/metassist | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/lvm/metassist')
65 files changed, 40252 insertions, 0 deletions
diff --git a/usr/src/cmd/lvm/metassist/Makefile b/usr/src/cmd/lvm/metassist/Makefile new file mode 100644 index 0000000000..418bdeb5b9 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/Makefile @@ -0,0 +1,87 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = . + +PROG = metassist + +# Subdirectories with message catalogs +POSUBDIRS = \ + common \ + xml \ + layout \ + controller + +# For message catalog +POFILES = $(POSUBDIRS:%=%/%p.po) + +# Subdirectories that must be made +SUBDIRS = \ + scripts \ + sysfiles \ + $(POSUBDIRS) + +SUBOBJS = \ + common/volume_defaults.o \ + common/volume_devconfig.o \ + common/volume_dlist.o \ + common/volume_error.o \ + common/volume_nvpair.o \ + common/volume_output.o \ + common/volume_request.o \ + common/volume_string.o \ + xml/xml_convert.o \ + layout/layout.o \ + layout/layout_concat.o \ + layout/layout_device_cache.o \ + layout/layout_device_util.o \ + layout/layout_discovery.o \ + layout/layout_dlist_util.o \ + layout/layout_hsp.o \ + layout/layout_messages.o \ + layout/layout_mirror.o \ + layout/layout_request.o \ + layout/layout_slice.o \ + layout/layout_stripe.o \ + layout/layout_svm_util.o \ + layout/layout_validate.o \ + controller/getopt_ext.o \ + controller/metassist.o + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +LDLIBS += -ldiskmgt -lmeta -lnvpair -lxml2 -lxslt -lm + +POFILE = metassistp.po + +include $(METASSIST_TOPLEVEL)/Makefile.targ + +# Build master .po file from subdirs' .po files +$(POFILE): $(POSUBDIRS) .WAIT $(POFILES) + $(BUILDPO.pofiles) diff --git a/usr/src/cmd/lvm/metassist/Makefile.env b/usr/src/cmd/lvm/metassist/Makefile.env new file mode 100644 index 0000000000..598e2eba8b --- /dev/null +++ b/usr/src/cmd/lvm/metassist/Makefile.env @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Define DEBUG when running nightly DEBUG build +$(NOT_RELEASE_BUILD) CPPFLAGS += -DDEBUG diff --git a/usr/src/cmd/lvm/metassist/Makefile.targ b/usr/src/cmd/lvm/metassist/Makefile.targ new file mode 100644 index 0000000000..41c91e587c --- /dev/null +++ b/usr/src/cmd/lvm/metassist/Makefile.targ @@ -0,0 +1,156 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Wrapper script that will fail if there is any output, for commands +# that don't exit with a non-zero exit code when they find errors +ERRIFOUTPUT_SCRIPT = $(METASSIST_TOPLEVEL)/scripts/errifoutput + +# Suffixes for files that flag a particular check +SUFFIX_LINT = .lint +SUFFIX_CSTYLE = .cstyle +SUFFIX_HDRCHK = .hdrchk +SUFFIX_KEYWORDS = .keywords + +# Get list of (non-deleted) SCCS files +tmp1 :sh= echo SCCS/s.* +tmp2 = ${tmp1:SCCS/s.%=%} +SCCSFILES = ${tmp2:.del-%=} + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +catalog := TARGET = catalog +check := TARGET = check +cstyle := TARGET = cstyle +hdrchk := TARGET = hdrchk +keywords := TARGET = keywords +lint := TARGET = lint + +.KEEP_STATE: + +all: ${SUBDIRS} ${OBJS} .WAIT ${PROG} + +# Create the binary +$(PROG): $(OBJS) $(SUBOBJS) + $(LINK.c) $(OBJS) $(SUBOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +# Make objects in subdirectories +$(SUBOBJS) $(POFILES): + @cd $(@D); ${MAKE} $(@F) + +ROOTXML= $(ROOTSHLIB)/xml + +# Rule/definitions for DTDs +ROOTXMLDTD= $(ROOTXML)/dtd +ROOTXMLDTDFILES= $(DTDFILES:%=$(ROOTXMLDTD)/%) +$(ROOTXMLDTDFILES) := OWNER = root +$(ROOTXMLDTDFILES) := GROUP = sys +$(ROOTXMLDTDFILES) := FILEMODE = 0444 + +$(ROOTXMLDTD)/%: % + $(INS.file) + +# Rule/definitions for XSL style sheets +ROOTXMLSTYLE= $(ROOTXML)/style +ROOTXMLSTYLEFILES= $(STYLEFILES:%=$(ROOTXMLSTYLE)/%) +$(ROOTXMLSTYLEFILES) := OWNER = root +$(ROOTXMLSTYLEFILES) := GROUP = sys +$(ROOTXMLSTYLEFILES) := FILEMODE = 0444 + +$(ROOTXMLSTYLE)/%: % + $(INS.file) + +# Rule/definitions for /etc/default +ROOTETCDEFAULTFILES= $(DEFAULTFILES:%=$(ROOTETCDEFAULT)/%) +$(ROOTETCDEFAULTFILES) := OWNER = root +$(ROOTETCDEFAULTFILES) := GROUP = sys +$(ROOTETCDEFAULTFILES) := FILEMODE = 0644 + +$(ROOTETCDEFAULT)/%: % + $(INS.file) + +# Install recursively +install: all .WAIT \ + ${SUBDIRS} \ + $(ROOTUSRSBINPROG) \ + $(ROOTETCDEFAULTFILES) \ + $(ROOTXMLDTDFILES) \ + $(ROOTXMLSTYLEFILES) + +# Pattern-matching rule for lint +%$(SUFFIX_LINT): % + ${LINT.c} -I. ${INCLUDES} -y -c $< && touch $@ + +# Run lint on all source files +lint: ${SUBDIRS} $(SRCS:%=%$(SUFFIX_LINT)) + +# Pattern-matching rule for cstyle +# cstyle doesn't exit with a non-zero exit code when it finds errors, +# so run it through a wrapper script that fails if there is any output +%$(SUFFIX_CSTYLE): % + $(ERRIFOUTPUT_SCRIPT) ${CSTYLE} -p -P $< && touch $@ + +# Run cstyle on all source files and headers +cstyle: $(ERRIFOUTPUT_SCRIPT) ${SUBDIRS} $(SRCS:%=%.cstyle) $(HDRS:%=%$(SUFFIX_CSTYLE)) + +# Pattern-matching rule for hdrchk +# hdrchk doesn't exit with a non-zero exit code when it finds errors, +# so run it through a wrapper script that fails if there is any output +%$(SUFFIX_HDRCHK): % + $(ERRIFOUTPUT_SCRIPT) ${HDRCHK} $< && touch $@ + +# Run hdrchk on all headers +hdrchk: $(ERRIFOUTPUT_SCRIPT) ${SUBDIRS} $(HDRS:%=%$(SUFFIX_HDRCHK)) + +# Pattern-matching rule for keywords +%$(SUFFIX_KEYWORDS): % + keywords $< && touch $@ + +# Run keywords on all SCCS files +keywords: ${SUBDIRS} ${SCCSFILES:%=%$(SUFFIX_KEYWORDS)} + +check: cstyle lint hdrchk keywords + +clobber: ${SUBDIRS} + +clean: ${SUBDIRS} + -${RM} *.o *.ln *.i *~ core a.out $(CLEANFILES) \ + *$(SUFFIX_LINT) *$(SUFFIX_CSTYLE) *$(SUFFIX_HDRCHK) *$(SUFFIX_KEYWORDS) + +catalog: $(POFILE) + +${SUBDIRS}: FRC + @cd $@; pwd; ${MAKE} ${TARGET} + +FRC: + +# Included for message catalog handling +include $(SRC)/Makefile.msg.targ +include $(METASSIST_TOPLEVEL)/../../Makefile.targ diff --git a/usr/src/cmd/lvm/metassist/common/Makefile b/usr/src/cmd/lvm/metassist/common/Makefile new file mode 100644 index 0000000000..d3c5882528 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/Makefile @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +SRCS= \ + volume_defaults.c \ + volume_devconfig.c \ + volume_dlist.c \ + volume_error.c \ + volume_nvpair.c \ + volume_output.c \ + volume_request.c \ + volume_string.c + +OBJS = $(SRCS:%.c=%.o) +HDRS = $(SRCS:%.c=%.h) +MSGFILES = $(SRCS:%.c=%.i) + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +INCLUDES += -I../../../../lib/lvm/libmeta/common/hdrs +CFLAGS += $(INCLUDES) + +POFILE = commonp.po + +include $(METASSIST_TOPLEVEL)/Makefile.targ + +# Build .po file from message files +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) diff --git a/usr/src/cmd/lvm/metassist/common/volume_defaults.c b/usr/src/cmd/lvm/metassist/common/volume_defaults.c new file mode 100644 index 0000000000..cb6909f77f --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_defaults.c @@ -0,0 +1,1875 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <libintl.h> +#include "volume_defaults.h" +#include "volume_error.h" + +/* + * Methods which manipulate a defaults_t struct + */ + +static int defaults_get_singleton_component( + defaults_t *defaults, char *disksetname, + component_type_t type, devconfig_t **component, boolean_t create); + +/* + * Constructor: Create a defaults_t struct populated with default + * values. This defaults_t must be freed. + * + * @param defaults + * RETURN: a pointer to a new defaults_t + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +new_defaults( + defaults_t **defaults) +{ + devconfig_t *diskset; + int error = 0; + + *defaults = (defaults_t *)calloc(1, sizeof (defaults_t)); + if (*defaults == NULL) { + volume_set_error(gettext("new_defaults calloc() failed")); + return (-1); + } + + /* + * Create initial "global" (disk set-independent) defaults, as + * a devconfig_t of type disk set with NULL name + */ + if ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) { + free_defaults(*defaults); + return (error); + } + + /* Append global defaults disk set to disksets */ + defaults_set_disksets( + *defaults, dlist_append(dlist_new_item(diskset), + defaults_get_disksets(*defaults), AT_TAIL)); + + /* Set defaults */ + if ((error = defaults_set_mirror_nsubs( + *defaults, NULL, DEFAULT_MIRROR_NSUBS)) != 0 || + + (error = defaults_set_mirror_read( + *defaults, NULL, DEFAULT_MIRROR_READ)) != 0 || + + (error = defaults_set_mirror_write( + *defaults, NULL, DEFAULT_MIRROR_WRITE)) != 0 || + + (error = defaults_set_mirror_pass( + *defaults, NULL, DEFAULT_MIRROR_PASS)) != 0 || + + (error = defaults_set_mirror_usehsp( + *defaults, NULL, DEFAULT_MIRROR_USEHSP)) != 0 || + + (error = defaults_set_concat_usehsp( + *defaults, NULL, DEFAULT_CONCAT_USEHSP)) != 0 || + + (error = defaults_set_stripe_interlace( + *defaults, NULL, DEFAULT_STRIPE_INTERLACE)) != 0 || + + (error = defaults_set_stripe_mincomp( + *defaults, NULL, DEFAULT_STRIPE_MINCOMP)) != 0 || + + (error = defaults_set_stripe_maxcomp( + *defaults, NULL, DEFAULT_STRIPE_MAXCOMP)) != 0 || + + (error = defaults_set_stripe_usehsp( + *defaults, NULL, DEFAULT_STRIPE_USEHSP)) != 0 || + + (error = defaults_set_volume_redundancy_level( + *defaults, NULL, DEFAULT_VOLUME_REDUND_LEVEL)) != 0 || + + (error = defaults_set_volume_npaths( + *defaults, NULL, DEFAULT_VOLUME_NPATHS)) != 0 || + + (error = defaults_set_volume_usehsp( + *defaults, NULL, DEFAULT_VOLUME_USEHSP)) != 0) { + + free_defaults(*defaults); + return (error); + } + + return (0); +} + +/* + * Free memory (recursively) allocated to a defaults_t struct + * + * @param arg + * pointer to the defaults_t struct to free + */ +void +free_defaults( + void *arg) +{ + defaults_t *defaults = (defaults_t *)arg; + + if (defaults == NULL) { + return; + } + + /* Free the disksets */ + if (defaults->disksets != NULL) { + dlist_free_items(defaults->disksets, free_devconfig); + } + + /* Free the devconfig itself */ + free(defaults); +} + +/* + * Set list of diskset specific defaults + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param disksets + * a dlist_t representing the defaults for specific + * named disk sets + */ +void +defaults_set_disksets( + defaults_t *defaults, + dlist_t *disksets) +{ + defaults->disksets = disksets; +} + +/* + * Get list of diskset specific defaults + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @return a dlist_t representing the defaults for specific + * named disk sets + */ +dlist_t * +defaults_get_disksets( + defaults_t *defaults) +{ + return (defaults->disksets); +} + +/* + * Get a disk set with the given name from the given defaults_t + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param name + * the name of the disk set whose defaults to retrieve, + * or NULL to get the defaults for all disk sets + * + * @param diskset + * RETURN: defaults for the given named disk set, or + * defaults for all disk sets if name is NULL + * + * @return ENOENT + * if the named disk set does not exist + * + * @return 0 + * if the named disk set exists + */ +int +defaults_get_diskset_by_name( + defaults_t *defaults, + char *name, + devconfig_t **diskset) +{ + dlist_t *list; + *diskset = NULL; + + /* Get list of disk sets */ + list = defaults_get_disksets(defaults); + if (list != NULL) { + + /* For each disk set-specific defaults... */ + for (; list != NULL; list = list->next) { + + char *dname = NULL; + devconfig_t *d = (devconfig_t *)list->obj; + + /* Get the name if this disk set */ + devconfig_get_name(d, &dname); + + /* Do the names match? */ + if ( + /* Global defaults disk set */ + (name == NULL && dname == NULL) || + + /* Named disk set */ + (name != NULL && dname != NULL && + strcmp(name, dname) == 0)) { + + *diskset = d; + break; + } + } + } + + /* Diskset doesn't exist */ + if (*diskset == NULL) { + return (ENOENT); + } + + return (0); +} + +/* + * Get the first component of the given type from the given disk set. + * If not found, create the component if requested. + * + * @return ENOENT + * if the given disk set does not exist, or it exists, + * but the requested component does not exist under it + * and its creation was not requested + * + * @return 0 + * if the requested component exists or was created + * + * @return non-zero + * if the requested component does not exist and could + * not be created + */ +static int +defaults_get_singleton_component( + defaults_t *defaults, + char *disksetname, + component_type_t type, + devconfig_t **component, + boolean_t create) +{ + int error; + devconfig_t *diskset; + + /* Get the disk set referred to */ + if ((error = defaults_get_diskset_by_name( + defaults, disksetname, &diskset)) != 0) { + + volume_set_error( + gettext("could not get defaults for disk set %s"), + disksetname == NULL ? gettext("<NULL>") : disksetname); + + return (error); + } + + /* + * Get the singleton component under this disk set, create if + * requested + */ + return (devconfig_get_component(diskset, type, component, create)); +} + +/* + * Set name of the the default HSP to use + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param name + * the name of the default HSP to use + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_hsp_name( + defaults_t *defaults, + char *diskset, + char *name) +{ + devconfig_t *hsp = NULL; + int error = 0; + + /* Get/create singleton HSP element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_HSP, &hsp, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the name attribute */ + return (devconfig_set_hsp_name(hsp, name)); +} + +/* + * Get the name of the default HSP to use + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param name + * RETURN: the name of the default HSP to use + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_hsp_name( + defaults_t *defaults, + char *diskset, + char **name) +{ + char *disksets[2]; + devconfig_t *hsp; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton HSP element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_HSP, &hsp, FALSE); + + switch (error) { + /* HSP found for this disk set */ + case 0: + /* Get the nsubs attribute */ + if ((error = devconfig_get_name(hsp, name)) == 0) { + /* nsubs attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* HSP not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or HSP couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default number of submirrors for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default number of submirrors + * for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_mirror_nsubs( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *mirror = NULL; + int error = 0; + + /* Get/create singleton mirror element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the nsubs attribute */ + return (devconfig_set_mirror_nsubs(mirror, val)); +} + +/* + * Get the default number of submirrors for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default number of submirrors for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_mirror_nsubs( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *mirror; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton mirror element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE); + + switch (error) { + /* mirror found for this disk set */ + case 0: + /* Get the nsubs attribute */ + if ((error = devconfig_get_mirror_nsubs( + mirror, val)) == 0) { + /* nsubs attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* mirror not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or mirror couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default read strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default read strategy for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_mirror_read( + defaults_t *defaults, + char *diskset, + mirror_read_strategy_t val) +{ + devconfig_t *mirror = NULL; + int error = 0; + + /* Get/create singleton mirror element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the read attribute */ + return (devconfig_set_mirror_read(mirror, val)); +} + +/* + * Get the default read strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default read strategy for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_mirror_read( + defaults_t *defaults, + char *diskset, + mirror_read_strategy_t *val) +{ + char *disksets[2]; + devconfig_t *mirror; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton mirror element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE); + + switch (error) { + /* mirror found for this disk set */ + case 0: + /* Get the read attribute */ + if ((error = devconfig_get_mirror_read(mirror, val)) == 0) { + /* read attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* mirror not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or mirror couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default write strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default write strategy for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_mirror_write( + defaults_t *defaults, + char *diskset, + mirror_write_strategy_t val) +{ + devconfig_t *mirror = NULL; + int error = 0; + + /* Get/create singleton mirror element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the write attribute */ + return (devconfig_set_mirror_write(mirror, val)); +} + +/* + * Get the default write strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default write strategy for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_mirror_write( + defaults_t *defaults, + char *diskset, + mirror_write_strategy_t *val) +{ + char *disksets[2]; + devconfig_t *mirror; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton mirror element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE); + + switch (error) { + /* mirror found for this disk set */ + case 0: + /* Get the write attribute */ + if ((error = devconfig_get_mirror_write( + mirror, val)) == 0) { + /* write attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* mirror not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or mirror couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default resync pass for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default resync pass for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_mirror_pass( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *mirror = NULL; + int error = 0; + + /* Get/create singleton mirror element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the pass attribute */ + return (devconfig_set_mirror_pass(mirror, val)); +} + +/* + * Get the default resync pass for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default resync pass for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_mirror_pass( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *mirror; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton mirror element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE); + + switch (error) { + /* mirror found for this disk set */ + case 0: + /* Get the pass attribute */ + if ((error = devconfig_get_mirror_pass(mirror, val)) == 0) { + /* pass attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* mirror not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or mirror couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default HSP creation flag for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_mirror_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t val) +{ + devconfig_t *mirror = NULL; + int error = 0; + + /* Get/create singleton mirror element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the usehsp attribute */ + return (devconfig_set_volume_usehsp(mirror, val)); +} + +/* + * Get the default HSP creation flag for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_mirror_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t *val) +{ + char *disksets[2]; + devconfig_t *mirror; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton mirror element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE); + + switch (error) { + /* mirror found for this disk set */ + case 0: + /* Get the usehsp attribute */ + if ((error = devconfig_get_volume_usehsp( + mirror, val)) == 0) { + /* usehsp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* mirror not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or mirror couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default HSP creation flag for concatenated volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * concatenated volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_concat_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t val) +{ + devconfig_t *concat = NULL; + int error = 0; + + /* Get/create singleton concat element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_CONCAT, &concat, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the usehsp attribute */ + return (devconfig_set_volume_usehsp(concat, val)); +} + +/* + * Get the default HSP creation flag for concatenated volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for concatenated + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_concat_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t *val) +{ + char *disksets[2]; + devconfig_t *concat; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton concat element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_CONCAT, &concat, FALSE); + + switch (error) { + /* concat found for this disk set */ + case 0: + /* Get the usehsp attribute */ + if ((error = devconfig_get_volume_usehsp( + concat, val)) == 0) { + /* usehsp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* concat not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or concat couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default minimum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default minimum number of + * components for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_stripe_mincomp( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *stripe = NULL; + int error = 0; + + /* Get/create singleton stripe element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the mincomp attribute */ + return (devconfig_set_stripe_mincomp(stripe, val)); +} + +/* + * Get the default minimum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default minimum number of components for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_stripe_mincomp( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *stripe; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton stripe element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE); + + switch (error) { + /* stripe found for this disk set */ + case 0: + /* Get the mincomp attribute */ + if ((error = devconfig_get_stripe_mincomp( + stripe, val)) == 0) { + /* mincomp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* stripe not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or stripe couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default maximum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default maximum number of + * components for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_stripe_maxcomp( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *stripe = NULL; + int error = 0; + + /* Get/create singleton stripe element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the maxcomp attribute */ + return (devconfig_set_stripe_maxcomp(stripe, val)); +} + +/* + * Get the default maximum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default maximum number of components for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_stripe_maxcomp( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *stripe; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton stripe element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE); + + switch (error) { + /* stripe found for this disk set */ + case 0: + /* Get the maxcomp attribute */ + if ((error = devconfig_get_stripe_maxcomp( + stripe, val)) == 0) { + /* maxcomp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* stripe not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or stripe couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default interlace for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default interlace for striped + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_stripe_interlace( + defaults_t *defaults, + char *diskset, + uint64_t val) +{ + devconfig_t *stripe = NULL; + int error = 0; + + /* Get/create singleton stripe element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the interlace attribute */ + return (devconfig_set_stripe_interlace(stripe, val)); +} + +/* + * Get the default interlace for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default interlace for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_stripe_interlace( + defaults_t *defaults, + char *diskset, + uint64_t *val) +{ + char *disksets[2]; + devconfig_t *stripe; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton stripe element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE); + + switch (error) { + /* stripe found for this disk set */ + case 0: + /* Get the interlace attribute */ + if ((error = devconfig_get_stripe_interlace( + stripe, val)) == 0) { + /* interlace attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* stripe not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or stripe couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default HSP creation flag for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_stripe_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t val) +{ + devconfig_t *stripe = NULL; + int error = 0; + + /* Get/create singleton stripe element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the usehsp attribute */ + return (devconfig_set_volume_usehsp(stripe, val)); +} + +/* + * Get the default HSP creation flag for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for striped + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_stripe_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t *val) +{ + char *disksets[2]; + devconfig_t *stripe; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton stripe element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE); + + switch (error) { + /* stripe found for this disk set */ + case 0: + /* Get the usehsp attribute */ + if ((error = devconfig_get_volume_usehsp( + stripe, val)) == 0) { + /* usehsp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* stripe not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or stripe couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default redundancy level for generic volumes. + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * If 0, a stripe will be created by default. If > 0, a + * mirror with this number of submirrors will be created + * by default. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_volume_redundancy_level( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *volume = NULL; + int error = 0; + + /* Get/create singleton volume element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the redundancy level */ + return (devconfig_set_volume_redundancy_level(volume, val)); +} + +/* + * Get the default redundancy level for generic volumes. + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default redundancy level for generic + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_volume_redundancy_level( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *volume; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton volume element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_VOLUME, &volume, FALSE); + + switch (error) { + /* volume found for this disk set */ + case 0: + /* Get the redundancy level */ + if ((error = devconfig_get_volume_redundancy_level( + volume, val)) == 0) { + /* redundancy level found */ + return (0); + } + + /* FALLTHROUGH */ + + /* volume not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or volume couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default number of data paths for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default number of data paths + * for generic volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_volume_npaths( + defaults_t *defaults, + char *diskset, + uint16_t val) +{ + devconfig_t *volume = NULL; + int error = 0; + + /* Get/create singleton volume element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the npaths attribute */ + return (devconfig_set_volume_npaths(volume, val)); +} + +/* + * Get the default number of data paths for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default number of data paths for generic + * volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_volume_npaths( + defaults_t *defaults, + char *diskset, + uint16_t *val) +{ + char *disksets[2]; + devconfig_t *volume; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton volume element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_VOLUME, &volume, FALSE); + + switch (error) { + /* volume found for this disk set */ + case 0: + /* Get the npaths attribute */ + if ((error = devconfig_get_volume_npaths( + volume, val)) == 0) { + /* npaths attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* volume not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or volume couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} + +/* + * Set the default HSP creation flag for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * generic volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_set_volume_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t val) +{ + devconfig_t *volume = NULL; + int error = 0; + + /* Get/create singleton volume element for this disk set */ + if ((error = defaults_get_singleton_component( + defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) { + /* volume_set_error already called */ + return (error); + } + + /* Set the usehsp attribute */ + return (devconfig_set_volume_usehsp(volume, val)); +} + +/* + * Get the default HSP creation flag for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for generic + * volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +defaults_get_volume_usehsp( + defaults_t *defaults, + char *diskset, + boolean_t *val) +{ + char *disksets[2]; + devconfig_t *volume; + int error; + int i = 0; + + /* Check both the given and global (NULL) disk sets for the value */ + disksets[0] = diskset; + disksets[1] = NULL; + do { + /* Get/create singleton volume element for this disk set */ + error = defaults_get_singleton_component( + defaults, disksets[i], TYPE_VOLUME, &volume, FALSE); + + switch (error) { + /* volume found for this disk set */ + case 0: + /* Get the usehsp attribute */ + if ((error = devconfig_get_volume_usehsp( + volume, val)) == 0) { + /* usehsp attribute found */ + return (0); + } + + /* FALLTHROUGH */ + + /* volume not found for this disk set */ + case ENOENT: + break; + + /* Invalid disk set, or volume couldn't be created */ + default: + /* volume_set_error already called */ + return (error); + } + + /* Stop after the global (NULL) disk set has been searched */ + } while (disksets[i++] != NULL); + + return (ENOENT); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_defaults.h b/usr/src/cmd/lvm/metassist/common/volume_defaults.h new file mode 100644 index 0000000000..3d9cacacaa --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_defaults.h @@ -0,0 +1,853 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_DEFAULTS_H +#define _VOLUME_DEFAULTS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" + +#define DEFAULT_MIRROR_NSUBS 2 +#define DEFAULT_MIRROR_READ MIRROR_READ_ROUNDROBIN +#define DEFAULT_MIRROR_WRITE MIRROR_WRITE_PARALLEL +#define DEFAULT_MIRROR_PASS 1 +#define DEFAULT_STRIPE_INTERLACE 1024 * 64 +#define DEFAULT_STRIPE_MINCOMP 3 +#define DEFAULT_STRIPE_MAXCOMP 10 +#define DEFAULT_VOLUME_REDUND_LEVEL 0 +#define DEFAULT_VOLUME_NPATHS 1 + +/* For consistency, these should all have the same value */ +#define DEFAULT_MIRROR_USEHSP FALSE +#define DEFAULT_CONCAT_USEHSP FALSE +#define DEFAULT_STRIPE_USEHSP FALSE +#define DEFAULT_VOLUME_USEHSP FALSE + +/* + * default_t - struct to hold layout defaults + */ +typedef struct defaults { + /* + * List of devconfig_t, each of which represents disk set- + * specific defaults. Each disk set has a name, except for + * the global set, whose name is NULL. + */ + dlist_t *disksets; +} defaults_t; + +/* + * Constructor: Create a defaults_t struct populated with default + * values. This defaults_t must be freed. + * + * @param defaults + * RETURN: a pointer to a new defaults_t + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int new_defaults(defaults_t **defaults); + +/* + * Free memory (recursively) allocated to a defaults_t struct + * + * @param arg + * pointer to the defaults_t struct to free + */ +extern void free_defaults(void *arg); + +/* + * Set list of diskset specific defaults + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param disksets + * a dlist_t representing the defaults for specific + * named disk sets + */ +extern void defaults_set_disksets(defaults_t *defaults, dlist_t *disksets); +/* + * Get list of diskset specific defaults + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @return a dlist_t representing the defaults for specific + * named disk sets + */ +extern dlist_t *defaults_get_disksets(defaults_t *defaults); + +/* + * Get a disk set with the given name from the given defaults_t + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param name + * the name of the disk set whose defaults to retrieve, + * or NULL to get the defaults for all disk sets + * + * @param diskset + * RETURN: defaults for the given named disk set, or + * defaults for all disk sets if name is NULL + * + * @return ENOENT + * if the named disk set does not exist + * + * @return 0 + * if the named disk set exists + */ + +extern int defaults_get_diskset_by_name( + defaults_t *defaults, char *name, devconfig_t **diskset); + +/* + * Set name of the the default HSP to use + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param name + * the name of the default HSP to use + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_hsp_name( + defaults_t *defaults, char *diskset, char *name); +/* + * Get the name of the default HSP to use + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param name + * RETURN: the name of the default HSP to use + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_hsp_name( + defaults_t *defaults, char *diskset, char **name); + +/* + * Set the default number of submirrors for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default number of submirrors + * for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_mirror_nsubs( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default number of submirrors for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default number of submirrors for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_mirror_nsubs( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default read strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default read strategy for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_mirror_read( + defaults_t *defaults, char *diskset, mirror_read_strategy_t val); +/* + * Get the default read strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default read strategy for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_mirror_read( + defaults_t *defaults, char *diskset, mirror_read_strategy_t *val); + +/* + * Set the default write strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default write strategy for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_mirror_write( + defaults_t *defaults, char *diskset, mirror_write_strategy_t val); +/* + * Get the default write strategy for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default write strategy for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_mirror_write( + defaults_t *defaults, char *diskset, mirror_write_strategy_t *val); + +/* + * Set the default resync pass for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default resync pass for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_mirror_pass( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default resync pass for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default resync pass for mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_mirror_pass( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default HSP creation flag for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * mirrored volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_mirror_usehsp( + defaults_t *defaults, char *diskset, boolean_t val); +/* + * Get the default HSP creation flag for mirrored volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for mirrored + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_mirror_usehsp( + defaults_t *defaults, char *diskset, boolean_t *val); + +/* + * Set the default HSP creation flag for concatenated volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * concatenated volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_concat_usehsp( + defaults_t *defaults, char *diskset, boolean_t val); +/* + * Get the default HSP creation flag for concatenated volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for concatenated + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_concat_usehsp( + defaults_t *defaults, char *diskset, boolean_t *val); + +/* + * Set the default minimum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default minimum number of + * components for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_stripe_mincomp( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default minimum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default minimum number of components for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_stripe_mincomp( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default maximum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default maximum number of + * components for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_stripe_maxcomp( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default maximum number of components for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default maximum number of components for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_stripe_maxcomp( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default interlace for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default interlace for striped + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_stripe_interlace( + defaults_t *defaults, char *diskset, uint64_t val); +/* + * Get the default interlace for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default interlace for striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_stripe_interlace( + defaults_t *defaults, char *diskset, uint64_t *val); + +/* + * Set the default HSP creation flag for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * striped volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_stripe_usehsp( + defaults_t *defaults, char *diskset, boolean_t val); +/* + * Get the default HSP creation flag for striped volumes + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for striped + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_stripe_usehsp( + defaults_t *defaults, char *diskset, boolean_t *val); + +/* + * Set the default redundancy level for generic volumes. + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * If 0, a stripe will be created by default. If > 0, a + * mirror with this number of submirrors will be created + * by default. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_volume_redundancy_level( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default redundancy level for generic volumes. + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default redundancy level for generic + * volumes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_volume_redundancy_level( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default number of data paths for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default number of data paths + * for generic volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_volume_npaths( + defaults_t *defaults, char *diskset, uint16_t val); +/* + * Get the default number of data paths for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default number of data paths for generic + * volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_volume_npaths( + defaults_t *defaults, char *diskset, uint16_t *val); + +/* + * Set the default HSP creation flag for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * the value to set as the default HSP creation flag for + * generic volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_set_volume_usehsp( + defaults_t *defaults, char *diskset, boolean_t val); +/* + * Get the default HSP creation flag for generic volume + * + * @param defaults + * a defaults_t hierarchy representing default settings + * for all disk sets and specific disk sets + * + * @param diskset + * the name of the disk set to which to apply this + * default setting, or NULL to apply default + * setting to all disk sets + * + * @param val + * RETURN: the default HSP creation flag for generic + * volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int defaults_get_volume_usehsp( + defaults_t *defaults, char *diskset, boolean_t *val); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_DEFAULTS_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_devconfig.c b/usr/src/cmd/lvm/metassist/common/volume_devconfig.c new file mode 100644 index 0000000000..1146b89466 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_devconfig.c @@ -0,0 +1,1691 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "volume_devconfig.h" + +#include <string.h> +#include <ctype.h> +#include <meta.h> +#include "volume_nvpair.h" +#include "volume_error.h" +#include "volume_output.h" +#include "volume_string.h" + +/* + * Methods which manipulate a devconfig_t struct + */ + +/* + * Constructor: Create a devconfig_t struct. This devconfig_t must be + * freed with free_devconfig(). + * + * @param devconfig + * RETURN: a new devconfig_t + * + * @param type + * the type of devconfig_t to create + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +new_devconfig( + devconfig_t **devconfig, + component_type_t type) +{ + int error; + + *devconfig = (devconfig_t *)calloc(1, sizeof (devconfig_t)); + if (*devconfig == NULL) { + volume_set_error(gettext("new_devconfig() calloc() failed\n")); + return (-1); + } + + /* Create attribute list */ + if ((error = nvlist_alloc(&((*devconfig)->attributes), + NV_UNIQUE_NAME_TYPE, 0)) != 0) { + volume_set_error(gettext("devconfig_t nvlist_alloc() failed\n")); + free_devconfig(*devconfig); + return (error); + } + + if ((error = devconfig_set_type(*devconfig, type)) != 0) { + free_devconfig(*devconfig); + return (error); + } + + return (0); +} + +/* + * Free memory (recursively) allocated to a devconfig_t struct + * + * @param arg + * pointer to the devconfig_t to be freed + */ +void +free_devconfig( + void *arg) +{ + devconfig_t *devconfig = (devconfig_t *)arg; + + if (devconfig == NULL) { + return; + } + + /* Free the attributes nvlist */ + if (devconfig->attributes != NULL) { + nvlist_free(devconfig->attributes); + } + + /* Free available devices */ + if (devconfig->available != NULL) { + free_string_array(devconfig->available); + } + + /* Free unavailable devices */ + if (devconfig->unavailable != NULL) { + free_string_array(devconfig->unavailable); + } + + /* Free the components */ + if (devconfig->components != NULL) { + dlist_free_items(devconfig->components, free_devconfig); + } + + /* Free the devconfig itself */ + free(devconfig); +} + +/* + * Check the type of the given device. + * + * @param device + * the device whose type to check + * + * @param type + * the type of the device against which to compare + * + * @return B_TRUE if the device is of the given type, B_FALSE + * otherwise + */ +boolean_t +devconfig_isA( + devconfig_t *device, + component_type_t type) +{ + component_type_t curtype; + + if (device == NULL) { + return (B_FALSE); + } + + if (devconfig_get_type(device, &curtype) != 0) { + return (B_FALSE); + } + + if (curtype != type) { + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * Get the first component of the given type from the given + * devconfig_t. Create the component if create is B_TRUE. + * + * @return ENOENT + * if the requested component does not exist and its + * creation was not requested + * + * @return 0 + * if the requested component exists or was created + * + * @return non-zero + * if the requested component did not exist and could not + * be created + */ +int +devconfig_get_component( + devconfig_t *device, + component_type_t type, + devconfig_t **component, + boolean_t create) +{ + dlist_t *list; + int error = 0; + char *typestr = devconfig_type_to_str(type); + + oprintf(OUTPUT_DEBUG, gettext("Searching for singleton %s\n"), typestr); + + /* For each component of this device... */ + for (list = devconfig_get_components(device); + list != NULL; list = list->next) { + + *component = (devconfig_t *)list->obj; + + /* Is this subcomponent an instance of the given type? */ + if (*component != NULL && devconfig_isA(*component, type)) { + oprintf(OUTPUT_DEBUG, gettext("Found %s\n"), typestr); + return (0); + } + } + + /* No component found */ + error = ENOENT; + *component = NULL; + + oprintf(OUTPUT_DEBUG, gettext("%s not found\n"), typestr); + + if (create == B_TRUE) { + oprintf(OUTPUT_DEBUG, gettext("Creating %s\n"), typestr); + + /* + * An existing singleton component of the given type was + * not found under the given disk set. So, create one. + */ + if ((error = new_devconfig(component, type)) == 0) { + /* Attach new component to given device */ + devconfig_set_components( + device, dlist_append(dlist_new_item(*component), + devconfig_get_components(device), AT_TAIL)); + } + } + + return (error); +} + +/* + * Set the available devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to modify + * + * @param available + * A NULL-terminated array of device names + */ +void +devconfig_set_available( + devconfig_t *device, + char **available) +{ + device->available = available; +} + +/* + * Get the available devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return available + * A NULL-terminated array of device names + */ +char ** +devconfig_get_available( + devconfig_t *device) +{ + return (device->available); +} + +/* + * Set the unavailable devices which may not be used in creating this + * device + * + * @param device + * a devconfig_t representing the device to modify + * + * @param available + * A NULL-terminated array of device names + */ +void +devconfig_set_unavailable( + devconfig_t *device, + char **unavailable) +{ + device->unavailable = unavailable; +} + +/* + * Get the unavailable devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return unavailable + * A NULL-terminated array of device names + */ +char ** +devconfig_get_unavailable( + devconfig_t *device) +{ + return (device->unavailable); +} + +/* + * Set the subcomponent devices of a given device + * + * @param device + * a devconfig_t representing the device to examine + * + * @param components + * A dlist_t containing devconfig_t devices + */ +void +devconfig_set_components( + devconfig_t *device, + dlist_t *components) +{ + device->components = components; +} + +/* + * Get the subcomponent devices of a given device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return A dlist_t containing devconfig_t devices + */ +dlist_t * +devconfig_get_components( + devconfig_t *device) +{ + return (device->components); +} + +/* + * Set the device name + * + * @param device + * a devconfig_t representing the device to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_name( + devconfig_t *device, + char *name) +{ + return (set_string(device->attributes, ATTR_NAME, name)); +} + +/* + * Set the disk set name + * + * @param diskset + * a devconfig_t representing the diskset to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_diskset_name( + devconfig_t *diskset, + char *name) +{ + md_error_t error = mdnullerror; + + /* Verify syntax of disk set name */ + if (meta_set_checkname(name, &error)) { + volume_set_error(gettext("invalid disk set name: %s"), name); + return (-1); + } + + return (devconfig_set_name(diskset, name)); +} + +/* + * Set the device name + * + * @param hsp + * a devconfig_t representing the hsp to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_hsp_name( + devconfig_t *hsp, + char *name) +{ + /* Validate name */ + if (!is_hspname(name)) { + volume_set_error(gettext("invalid hot spare pool name: %s"), name); + return (-1); + } + + return (devconfig_set_name(hsp, name)); +} + +/* + * Set the device name + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_volume_name( + devconfig_t *volume, + char *name) +{ + /* Validate name */ + if (!is_metaname(name)) { + volume_set_error(gettext("invalid volume name: %s"), name); + return (-1); + } + + return (devconfig_set_name(volume, name)); +} + +/* + * Get the device name + * + * @param volume + * a devconfig_t representing the volume to examine + * + * @param name + * RETURN: the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_name( + devconfig_t *device, + char **name) +{ + int error = get_string(device->attributes, ATTR_NAME, name); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("device name not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the device type + * + * @param device + * a devconfig_t representing the device to modify + * + * @param type + * the value to set as the device type + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_type( + devconfig_t *device, + component_type_t type) +{ + return (set_uint16(device->attributes, ATTR_TYPE, (uint16_t)type)); +} + +/* + * Get the device type + * + * @param device + * a devconfig_t representing the device to examine + * + * @param type + * RETURN: the device type + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_type( + devconfig_t *device, + component_type_t *type) +{ + uint16_t val; + int error = get_uint16(device->attributes, ATTR_TYPE, &val); + + switch (error) { + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + case ENOENT: + volume_set_error(gettext("device type not set")); + error = ERR_ATTR_UNSET; + break; + + /* Success */ + case 0: + *type = (component_type_t)val; + } + + return (error); +} + +/* + * Set the device size (for volume, mirror, stripe, concat) in bytes + * + * Note that size in bytes in a 64-bit field cannot hold the size that + * can be accessed in a 16 byte CDB. Since CDBs operate on blocks, + * the max capacity is 2^73 bytes with 512 byte blocks. + * + * @param device + * a devconfig_t representing the device to modify + * + * @param size_in_bytes + * the value to set as the device size in bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_size( + devconfig_t *device, + uint64_t size_in_bytes) +{ + + /* Validate against limits */ + /* LINTED -- MIN_SIZE may be 0 */ + if (size_in_bytes < MIN_SIZE) { + volume_set_error(gettext("size (in bytes) too small: %llu"), + (unsigned long long)size_in_bytes); + return (-1); + } + + return (set_uint64(device->attributes, + ATTR_SIZEINBYTES, size_in_bytes)); +} + +/* + * Get the device size (for volume, mirror, stripe, concat) in bytes + * + * Note that size in bytes in a 64-bit field cannot hold the size that + * can be accessed in a 16 byte CDB. Since CDBs operate on blocks, + * the max capacity is 2^73 bytes with 512 byte blocks. + * + * @param device + * a devconfig_t representing the device to examine + * + * @param size_in_bytes + * RETURN: the device size in bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_size( + devconfig_t *device, + uint64_t *size_in_bytes) +{ + int error = get_uint64( + device->attributes, ATTR_SIZEINBYTES, size_in_bytes); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("size (in bytes) not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the device size in blocks + * + * @param device + * a devconfig_t representing the device to modify + * + * @param type + * the value to set as the device size in blocks + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_size_in_blocks( + devconfig_t *device, + uint64_t size_in_blocks) +{ + /* Validate against limits */ + /* LINTED -- MIN_SIZE_IN_BLOCKS may be 0 */ + if (size_in_blocks < MIN_SIZE_IN_BLOCKS) { + volume_set_error(gettext("size (in blocks) too small: %llu"), + (unsigned long long)size_in_blocks); + return (-1); + } + + return (set_uint64(device->attributes, + ATTR_SIZEINBLOCKS, size_in_blocks)); +} + +/* + * Get the device size in blocks + * + * @param device + * a devconfig_t representing the device to examine + * + * @param size_in_blocks + * RETURN: the device size in blocks + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_size_in_blocks( + devconfig_t *device, + uint64_t *size_in_blocks) +{ + int error = get_uint64( + device->attributes, ATTR_SIZEINBLOCKS, size_in_blocks); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("size (in blocks) not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the the slice index + * + * @param slice + * a devconfig_t representing the slice to modify + * + * @param index + * the value to set as the the slice index + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_slice_index( + devconfig_t *slice, + uint16_t index) +{ + return (set_uint16(slice->attributes, ATTR_SLICE_INDEX, index)); +} + +/* + * Get the slice index + * + * @param device + * a devconfig_t representing the device to examine + * + * @param index + * RETURN: the slice index + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_slice_index( + devconfig_t *slice, + uint16_t *index) +{ + int error = get_uint16(slice->attributes, ATTR_SLICE_INDEX, index); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("slice index not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the the slice start block + * + * @param slice + * a devconfig_t representing the slice to modify + * + * @param start_block + * the value to set as the the slice start block + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_slice_start_block( + devconfig_t *slice, + uint64_t start_block) +{ + return (set_uint64(slice->attributes, + ATTR_SLICE_STARTSECTOR, start_block)); +} + +/* + * Get the slice start block + * + * @param device + * a devconfig_t representing the device to examine + * + * @param start_block + * RETURN: the slice start block + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_slice_start_block( + devconfig_t *slice, + uint64_t *start_block) +{ + int error = get_uint64( + slice->attributes, ATTR_SLICE_STARTSECTOR, start_block); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("slice start block not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the number of subcomponents in mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param nsubs + * the value to set as the number of subcomponents in + * mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_mirror_nsubs( + devconfig_t *mirror, + uint16_t nsubs) +{ + /* Validate against limits */ + if (nsubs < 1 || nsubs > NMIRROR) { + volume_set_error( + gettext("number of submirrors (%d) out of valid range (%d-%d)"), + nsubs, 1, NMIRROR); + return (-1); + } + + return (set_uint16(mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs)); +} + +/* + * Get number of subcomponents in mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param nsubs + * RETURN: number of subcomponents in mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_mirror_nsubs( + devconfig_t *mirror, + uint16_t *nsubs) +{ + int error = get_uint16( + mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("number or submirrors not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the read strategy for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param read + * the value to set as the read strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_mirror_read( + devconfig_t *mirror, + mirror_read_strategy_t read) +{ + return (set_uint16(mirror->attributes, + ATTR_MIRROR_READ, (uint16_t)read)); +} + +/* + * Get read strategy for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param read + * RETURN: read strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_mirror_read( + devconfig_t *mirror, + mirror_read_strategy_t *read) +{ + uint16_t val; + int error = get_uint16(mirror->attributes, ATTR_MIRROR_READ, &val); + + switch (error) { + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + case ENOENT: + volume_set_error(gettext("mirror read strategy not set")); + error = ERR_ATTR_UNSET; + break; + + /* Success */ + case 0: + *read = (mirror_read_strategy_t)val; + } + + return (error); +} + +/* + * Set the write strategy for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param write + * the value to set as the write strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_mirror_write( + devconfig_t *mirror, + mirror_write_strategy_t write) +{ + return (set_uint16(mirror->attributes, + ATTR_MIRROR_WRITE, (uint16_t)write)); +} + +/* + * Get write strategy for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param write + * RETURN: write strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_mirror_write( + devconfig_t *mirror, + mirror_write_strategy_t *write) +{ + uint16_t val; + int error = get_uint16(mirror->attributes, ATTR_MIRROR_WRITE, &val); + + switch (error) { + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + case ENOENT: + volume_set_error(gettext("mirror write strategy not set")); + error = ERR_ATTR_UNSET; + break; + + /* Success */ + case 0: + *write = (mirror_write_strategy_t)val; + } + + return (error); +} + +/* + * Set the resync pass for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param pass + * the value to set as the resync pass for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_mirror_pass( + devconfig_t *mirror, + uint16_t pass) +{ + /* Validate against max value */ + if (pass > MD_PASS_MAX) { + volume_set_error( + gettext("mirror pass number (%d) out of valid range (0-%d)"), + pass, MD_PASS_MAX); + return (-1); + } + + return (set_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass)); +} + +/* + * Get resync pass for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param pass + * RETURN: resync pass for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_mirror_pass( + devconfig_t *mirror, + uint16_t *pass) +{ + int error = get_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("mirror pass number not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the minimum number of components in stripe + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param mincomp + * the value to set as the minimum number of components + * in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_stripe_mincomp( + devconfig_t *stripe, + uint16_t mincomp) +{ + /* Validate against minimum value */ + if (mincomp < MIN_NSTRIPE_COMP) { + volume_set_error(gettext( + "minimum stripe components (%d) below minimum allowable (%d)"), + mincomp, MIN_NSTRIPE_COMP); + return (-1); + } + + return (set_uint16(stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp)); +} + +/* + * Get minimum number of components in stripe + * + * @param device + * a devconfig_t representing the device to examine + * + * @param mincomp + * RETURN: minimum number of components in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_stripe_mincomp( + devconfig_t *stripe, + uint16_t *mincomp) +{ + int error = get_uint16( + stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error( + gettext("minimum number of stripe components not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the maximum number of components in stripe + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param maxcomp + * the value to set as the maximum number of components + * in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_stripe_maxcomp( + devconfig_t *stripe, + uint16_t maxcomp) +{ + /* Validate against minimum value */ + if (maxcomp < MIN_NSTRIPE_COMP) { + volume_set_error(gettext( + "maximum stripe components (%d) below minimum allowable (%d)"), + maxcomp, MIN_NSTRIPE_COMP); + return (-1); + } + + return (set_uint16(stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp)); +} + +/* + * Get maximum number of components in stripe + * + * @param device + * a devconfig_t representing the device to examine + * + * @param maxcomp + * RETURN: maximum number of components in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_stripe_maxcomp( + devconfig_t *stripe, + uint16_t *maxcomp) +{ + int error = get_uint16( + stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error( + gettext("maximum number of stripe components not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the stripe interlace + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param interlace + * the value to set as the stripe interlace + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_stripe_interlace( + devconfig_t *stripe, + uint64_t interlace) +{ + if (interlace < MININTERLACE || interlace > MAXINTERLACE) { + char *intstr = NULL; + char *minstr = NULL; + char *maxstr = NULL; + + /* Get string representations of interlaces */ + bytes_to_sizestr(interlace, &intstr, universal_units, B_FALSE); + bytes_to_sizestr(MININTERLACE, &minstr, universal_units, B_FALSE); + bytes_to_sizestr(MAXINTERLACE, &maxstr, universal_units, B_FALSE); + + volume_set_error( + gettext("interlace (%s) out of valid range (%s - %s)"), + intstr, minstr, maxstr); + + free(intstr); + free(minstr); + free(maxstr); + + return (-1); + } + + return (set_uint64(stripe->attributes, + ATTR_STRIPE_INTERLACE, interlace)); +} + +/* + * Get stripe interlace + * + * @param device + * a devconfig_t representing the device to examine + * + * @param interlace + * RETURN: stripe interlace + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_stripe_interlace( + devconfig_t *stripe, + uint64_t *interlace) +{ + int error = get_uint64( + stripe->attributes, ATTR_STRIPE_INTERLACE, interlace); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("stripe interlace not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the redundancy level for a volume. + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param rlevel + * If 0, a stripe will be created. If > 0, a mirror with + * this number of submirrors will be created. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_volume_redundancy_level( + devconfig_t *volume, + uint16_t rlevel) +{ + /* Validate against limits */ + if (rlevel > NMIRROR) { + volume_set_error(gettext( + "volume redundancy level (%d) out of valid range (%d-%d)"), + rlevel, 0, NMIRROR); + return (-1); + } + + return (set_uint16(volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel)); +} + +/* + * Get the redundancy level for a volume. + * + * @param device + * a devconfig_t representing the device to examine + * + * @param rlevel + * RETURN: the redundancy level for a volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_volume_redundancy_level( + devconfig_t *volume, + uint16_t *rlevel) +{ + int error = get_uint16( + volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("volume redundancy level not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the number of paths in volume + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param npaths + * the value to set as the number of paths in volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_volume_npaths( + devconfig_t *volume, + uint16_t npaths) +{ + /* Validate against limits */ + if (npaths < MIN_NDATAPATHS || npaths > MAX_NDATAPATHS) { + volume_set_error( + gettext("number of data paths (%d) out of valid range (%d-%d)"), + npaths, MIN_NDATAPATHS, MAX_NDATAPATHS); + return (-1); + } + + return (set_uint16(volume->attributes, ATTR_VOLUME_DATAPATHS, npaths)); +} + +/* + * Get number of paths in volume + * + * @param device + * a devconfig_t representing the device to examine + * + * @param npaths + * RETURN: number of paths in volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_volume_npaths( + devconfig_t *volume, + uint16_t *npaths) +{ + int error = get_uint16( + volume->attributes, ATTR_VOLUME_DATAPATHS, npaths); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("number of data paths not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Set the HSP creation option (for volume, stripe, concat, mirror) + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param usehsp + * the value to set as the HSP creation option + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_set_volume_usehsp( + devconfig_t *volume, + boolean_t usehsp) +{ + return (set_boolean(volume->attributes, ATTR_VOLUME_USEHSP, usehsp)); +} + +/* + * Get HSP creation option (for volume, stripe, concat, mirror) + * + * @param device + * a devconfig_t representing the device to examine + * + * @param usehsp + * RETURN: HSP creation option (for volume, stripe, + * concat, mirror) + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +devconfig_get_volume_usehsp( + devconfig_t *volume, + boolean_t *usehsp) +{ + int error = get_boolean( + volume->attributes, ATTR_VOLUME_USEHSP, usehsp); + + /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */ + if (error == ENOENT) { + volume_set_error(gettext("volume usehsp not set")); + error = ERR_ATTR_UNSET; + } + + return (error); +} + +/* + * Get the string representation of the volume's type + * + * @param type + * a valid component_type_t + * + * @return an internationalized string representing the given + * type + */ +char * +devconfig_type_to_str( + component_type_t type) +{ + char *str; + + switch (type) { + case TYPE_CONCAT: str = gettext("Concat"); break; + case TYPE_CONTROLLER: str = gettext("Controller"); break; + case TYPE_DISKSET: str = gettext("Diskset"); break; + case TYPE_DRIVE: str = gettext("Disk"); break; + case TYPE_EXTENT: str = gettext("Extent"); break; + case TYPE_HOST: str = gettext("Host"); break; + case TYPE_HSP: str = gettext("Hot Spare Pool"); break; + case TYPE_MIRROR: str = gettext("Mirror"); break; + case TYPE_RAID5: str = gettext("Raid5"); break; + case TYPE_SLICE: str = gettext("Slice"); break; + case TYPE_SOFTPART: str = gettext("Soft Partition"); break; + case TYPE_STRIPE: str = gettext("Stripe"); break; + case TYPE_TRANS: str = gettext("Trans"); break; + case TYPE_VOLUME: str = gettext("Volume"); break; + default: + case TYPE_UNKNOWN: str = gettext("Unknown"); break; + } + + return (str); +} + +/* + * Get the string representation of the mirror's read strategy + * + * @param read + * a valid mirror_read_strategy_t + * + * @return an internationalized string representing the given + * read strategy + */ +char * +devconfig_read_strategy_to_str( + mirror_read_strategy_t read) +{ + char *str; + + switch (read) { + case MIRROR_READ_ROUNDROBIN: str = gettext("ROUNDROBIN"); break; + case MIRROR_READ_GEOMETRIC: str = gettext("GEOMETRIC"); break; + case MIRROR_READ_FIRST: str = gettext("FIRST"); break; + default: str = ""; + } + + return (str); +} + +/* + * Get the string representation of the mirror's write strategy + * + * @param write + * a valid mirror_write_strategy_t + * + * @return an internationalized string representing the given + * write strategy + */ +char * +devconfig_write_strategy_to_str( + mirror_write_strategy_t write) +{ + char *str; + + switch (write) { + case MIRROR_WRITE_PARALLEL: str = gettext("PARALLEL"); break; + case MIRROR_WRITE_SERIAL: str = gettext("SERIAL"); break; + default: str = ""; + } + + return (str); +} + +#ifdef DEBUG +/* + * Dump the contents of a devconfig_t struct to stdout. + * + * @param device + * the devconfig_t to examine + * + * @param prefix + * a prefix string to print before each line + */ +void +devconfig_dump( + devconfig_t *device, + char *prefix) +{ + dlist_t *comps = NULL; + char **array = NULL; + char *str = NULL; + int i = 0; + + component_type_t type = TYPE_UNKNOWN; + boolean_t bool = B_FALSE; + uint16_t val16 = 0; + uint64_t val64 = 0; + mirror_read_strategy_t read; + mirror_write_strategy_t write; + + if (device == NULL) { + return; + } + + /* Type */ + if (devconfig_get_type(device, &type) == 0) { + printf("%s%s\n", prefix, devconfig_type_to_str(type)); + } + + /* Name */ + if (devconfig_get_name(device, &str) == 0) { + printf("%s name: %s\n", prefix, str); + } + + /* Size in bytes */ + if (devconfig_get_size(device, &val64) == 0) { + printf("%s size in bytes: %llu\n", prefix, val64); + } + + /* Size in blocks */ + if (devconfig_get_size_in_blocks(device, &val64) == 0) { + printf("%s size in blocks: %llu\n", prefix, val64); + } + + /* Use HSP */ + if (devconfig_get_volume_usehsp(device, &bool) == 0) { + printf("%s usehsp: %s\n", prefix, bool? "TRUE" : "FALSE"); + } + + switch (type) { + case TYPE_VOLUME: + /* Volume rlevel */ + if (devconfig_get_volume_redundancy_level( + device, &val16) == 0) { + printf("%s volume redundancy level: %d\n", prefix, val16); + } + + /* Volume npaths */ + if (devconfig_get_volume_npaths(device, &val16) == 0) { + printf("%s volume npaths: %d\n", prefix, val16); + } + break; + + case TYPE_MIRROR: + + /* Mirror nsubs */ + if (devconfig_get_mirror_nsubs(device, &val16) == 0) { + printf("%s mirror nsubs: %d\n", prefix, val16); + } + + /* Mirror read */ + if (devconfig_get_mirror_read(device, &read) == 0) { + printf("%s mirror read: %s\n", prefix, + devconfig_read_strategy_to_str(read)); + } + + /* Mirror write */ + if (devconfig_get_mirror_write(device, &write) == 0) { + printf("%s mirror write: %s\n", prefix, + devconfig_write_strategy_to_str(write)); + } + + /* Mirror pass */ + if (devconfig_get_mirror_pass(device, &val16) == 0) { + printf("%s mirror pass: %d\n", prefix, val16); + } + break; + + case TYPE_STRIPE: + /* Stripe mincomp */ + if (devconfig_get_stripe_mincomp(device, &val16) == 0) { + printf("%s stripe mincomp: %d\n", prefix, val16); + } + + /* Stripe maxcomp */ + if (devconfig_get_stripe_maxcomp(device, &val16) == 0) { + printf("%s stripe maxcomp: %d\n", prefix, val16); + } + + /* Stripe interlace */ + if (devconfig_get_stripe_interlace(device, &val64) == 0) { + printf("%s stripe interlace: %lld\n", prefix, val64); + } + break; + + case TYPE_SLICE: + /* Slice index */ + if (devconfig_get_slice_index(device, &val16) == 0) { + printf("%s slice index: %d\n", prefix, val16); + } + + /* Slice start block */ + if (devconfig_get_slice_start_block(device, &val64) == 0) { + printf("%s slice start block: %llu\n", prefix, val64); + } + break; + } + + array = devconfig_get_available(device); + if (array != NULL) { + printf("%s available:\n", prefix); + for (i = 0; array[i] != NULL; i++) { + printf("%s %s\n", prefix, array[i]); + } + } + + array = devconfig_get_unavailable(device); + if (array != NULL) { + printf("%s unavailable:\n", prefix); + for (i = 0; array[i] != NULL; i++) { + printf("%s %s\n", prefix, array[i]); + } + } + + printf("\n"); + + comps = devconfig_get_components(device); + if (comps != NULL) { + char buf[128]; + snprintf(buf, 128, "%s%s", prefix, " "); + for (; comps != NULL; comps = comps->next) { + devconfig_dump((devconfig_t *)comps->obj, buf); + } + } +} +#endif /* DEBUG */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_devconfig.h b/usr/src/cmd/lvm/metassist/common/volume_devconfig.h new file mode 100644 index 0000000000..3898554591 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_devconfig.h @@ -0,0 +1,998 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_DEVCONFIG_H +#define _VOLUME_DEVCONFIG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libnvpair.h> +#include "volume_dlist.h" +#include <sys/lvm/md_mdiox.h> + +/* + * String constants for XML element/attribute names. + */ +#define ELEMENT_AVAILABLE "available" +#define ELEMENT_COMMENT "comment" +#define ELEMENT_CONCAT "concat" +#define ELEMENT_DISK "disk" +#define ELEMENT_DISKSET "diskset" +#define ELEMENT_HSP "hsp" +#define ELEMENT_L10N "localization" +#define ELEMENT_MESSAGE "message" +#define ELEMENT_MIRROR "mirror" +#define ELEMENT_PARAM "param" +#define ELEMENT_SLICE "slice" +#define ELEMENT_STRIPE "stripe" +#define ELEMENT_TEXT "text" +#define ELEMENT_UNAVAILABLE "unavailable" +#define ELEMENT_VARIABLE "variable" +#define ELEMENT_VOLUME "volume" +#define ELEMENT_VOLUMECONFIG "volume-config" +#define ELEMENT_VOLUMEDEFAULTS "volume-defaults" +#define ELEMENT_VOLUMEREQUEST "volume-request" + +#define ATTR_LANG "xml:lang" +#define ATTR_MESSAGEID "msgid" +#define ATTR_MIRROR_NSUBMIRRORS "nsubmirrors" +#define ATTR_MIRROR_PASSNUM "passnum" +#define ATTR_MIRROR_READ "read" +#define ATTR_MIRROR_WRITE "write" +#define ATTR_NAME "name" +#define ATTR_SELECT "select" +#define ATTR_SIZEINBLOCKS "sizeinblocks" +#define ATTR_SIZEINBYTES "size" +#define ATTR_SLICE_INDEX "index" +#define ATTR_SLICE_STARTSECTOR "startsector" +#define ATTR_STRIPE_INTERLACE "interlace" +#define ATTR_STRIPE_MAXCOMP "maxcomp" +#define ATTR_STRIPE_MINCOMP "mincomp" +#define ATTR_TYPE "type" +#define ATTR_VOLUME_CREATE "create" +#define ATTR_VOLUME_DATAPATHS "datapaths" +#define ATTR_VOLUME_FAULTRECOVERY "faultrecovery" +#define ATTR_VOLUME_REDUNDANCY "redundancy" +#define ATTR_VOLUME_USEHSP "usehsp" + +#define NAME_L10N_MESSAGE_FILE "msgfile" +#define NAME_LANG "lang" + +/* + * Limits for attributes + */ +#define MIN_NSTRIPE_COMP 1 +#define MIN_SIZE 0 +#define MIN_SIZE_IN_BLOCKS 0 +#define MIN_NDATAPATHS 1 +#define MAX_NDATAPATHS 4 + +/* Attribute requested but not set */ +#define ERR_ATTR_UNSET -10001 + +/* + * Enumeration defining physical or logical device types + */ +typedef enum { + TYPE_UNKNOWN = 0, + TYPE_CONCAT = 1, + TYPE_CONTROLLER, + TYPE_DISKSET, + TYPE_DRIVE, + TYPE_EXTENT, + TYPE_HOST, + TYPE_HSP, + TYPE_MIRROR, + TYPE_RAID5, + TYPE_SLICE, + TYPE_SOFTPART, + TYPE_STRIPE, + TYPE_TRANS, + TYPE_VOLUME +} component_type_t; + +/* + * enumerated constants for SVM Mirror read strategies + */ +typedef enum { + MIRROR_READ_ROUNDROBIN = 0, + MIRROR_READ_GEOMETRIC, + MIRROR_READ_FIRST +} mirror_read_strategy_t; + +/* + * enumerated constants for SVM Mirror write strategies + */ +typedef enum { + MIRROR_WRITE_PARALLEL = 0, + MIRROR_WRITE_SERIAL +} mirror_write_strategy_t; + +/* + * devconfig_t - struct to hold a device configuration hierarchy + */ +typedef struct devconfig { + + /* Attributes of this device */ + nvlist_t *attributes; + + /* + * Available devices for use in construction of this device + * and its subcomponents + */ + char **available; + + /* + * Unavailable devices for use in construction of this device + * and its subcomponents + */ + char **unavailable; + + /* + * Subcomponents (devconfig_t) of this device + */ + dlist_t *components; +} devconfig_t; + +/* + * Function prototypes + */ + +/* + * Constructor: Create a devconfig_t struct. This devconfig_t must be + * freed with free_devconfig(). + * + * @param devconfig + * RETURN: a new devconfig_t + * + * @param type + * the type of devconfig_t to create + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int new_devconfig(devconfig_t **devconfig, component_type_t type); + +/* + * Free memory (recursively) allocated to a devconfig_t struct + * + * @param arg + * pointer to the devconfig_t to be freed + */ +extern void free_devconfig(void *arg); + +/* + * Check the type of the given device. + * + * @param device + * the device whose type to check + * + * @param type + * the type of the device against which to compare + * + * @return B_TRUE if the device is of the given type, B_FALSE + * otherwise + */ +extern boolean_t devconfig_isA(devconfig_t *device, component_type_t type); + +/* + * Get the first component of the given type from the given + * devconfig_t. Create the component if create is B_TRUE. + * + * @return ENOENT + * if the requested component does not exist and its + * creation was not requested + * + * @return 0 + * if the requested component exists or was created + * + * @return non-zero + * if the requested component did not exist and could not + * be created + */ +extern int devconfig_get_component(devconfig_t *device, + component_type_t type, devconfig_t **component, boolean_t create); + +/* + * Set the available devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to modify + * + * @param available + * A NULL-terminated array of device names + */ +extern void devconfig_set_available(devconfig_t *device, char **available); + +/* + * Get the available devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return available + * A NULL-terminated array of device names + */ +extern char ** devconfig_get_available(devconfig_t *device); + +/* + * Set the unavailable devices which may not be used in creating this + * device + * + * @param device + * a devconfig_t representing the device to modify + * + * @param available + * A NULL-terminated array of device names + */ +extern void devconfig_set_unavailable(devconfig_t *device, char **unavailable); + +/* + * Get the unavailable devices for use in creating this device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return unavailable + * A NULL-terminated array of device names + */ +extern char ** devconfig_get_unavailable(devconfig_t *device); + +/* + * Set the subcomponent devices of a given device + * + * @param device + * a devconfig_t representing the device to examine + * + * @param components + * A dlist_t containing devconfig_t devices + */ +extern void devconfig_set_components(devconfig_t *device, dlist_t *components); + +/* + * Get the subcomponent devices of a given device + * + * @param device + * a devconfig_t representing the device to examine + * + * @return A dlist_t containing devconfig_t devices + */ +extern dlist_t *devconfig_get_components(devconfig_t *device); + +/* + * Set the device name + * + * @param device + * a devconfig_t representing the device to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_name(devconfig_t *device, char *name); + +/* + * Set the disk set name + * + * @param diskset + * a devconfig_t representing the diskset to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_diskset_name(devconfig_t *diskset, char *name); + +/* + * Set the device name + * + * @param hsp + * a devconfig_t representing the hsp to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_hsp_name(devconfig_t *hsp, char *name); + +/* + * Set the device name + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param name + * the value to set as the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_volume_name(devconfig_t *volume, char *name); + +/* + * Get the device name + * + * @param volume + * a devconfig_t representing the volume to examine + * + * @param name + * RETURN: the device name + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_name(devconfig_t *device, char **name); + +/* + * Set the device type + * + * @param device + * a devconfig_t representing the device to modify + * + * @param type + * the value to set as the device type + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_type(devconfig_t *device, component_type_t type); + +/* + * Get the device type + * + * @param device + * a devconfig_t representing the device to examine + * + * @param type + * RETURN: the device type + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_type(devconfig_t *device, component_type_t *type); + +/* + * Set the device size (for volume, mirror, stripe, concat) in bytes + * + * Note that size in bytes in a 64-bit field cannot hold the size that + * can be accessed in a 16 byte CDB. Since CDBs operate on blocks, + * the max capacity is 2^73 bytes with 512 byte blocks. + * + * @param device + * a devconfig_t representing the device to modify + * + * @param size_in_bytes + * the value to set as the device size in bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_size(devconfig_t *device, uint64_t size_in_bytes); + +/* + * Get the device size (for volume, mirror, stripe, concat) in bytes + * + * Note that size in bytes in a 64-bit field cannot hold the size that + * can be accessed in a 16 byte CDB. Since CDBs operate on blocks, + * the max capacity is 2^73 bytes with 512 byte blocks. + * + * @param device + * a devconfig_t representing the device to examine + * + * @param size_in_bytes + * RETURN: the device size in bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_size(devconfig_t *device, uint64_t *size_in_bytes); + +/* + * Set the device size in blocks + * + * @param device + * a devconfig_t representing the device to modify + * + * @param type + * the value to set as the device size in blocks + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_size_in_blocks( + devconfig_t *device, uint64_t size_in_blocks); + +/* + * Get the device size in blocks + * + * @param device + * a devconfig_t representing the device to examine + * + * @param size_in_blocks + * RETURN: the device size in blocks + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_size_in_blocks( + devconfig_t *device, uint64_t *size_in_blocks); + +/* + * Set the the slice index + * + * @param slice + * a devconfig_t representing the slice to modify + * + * @param index + * the value to set as the the slice index + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_slice_index(devconfig_t *slice, uint16_t index); + +/* + * Get the slice index + * + * @param device + * a devconfig_t representing the device to examine + * + * @param index + * RETURN: the slice index + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_slice_index(devconfig_t *slice, uint16_t *index); + +/* + * Set the the slice start block + * + * @param slice + * a devconfig_t representing the slice to modify + * + * @param start_block + * the value to set as the the slice start block + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_slice_start_block( + devconfig_t *slice, uint64_t start_block); + +/* + * Get the slice start block + * + * @param device + * a devconfig_t representing the device to examine + * + * @param start_block + * RETURN: the slice start block + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_slice_start_block( + devconfig_t *slice, uint64_t *start_block); + +/* + * Set the number of subcomponents in mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param nsubs + * the value to set as the number of subcomponents in + * mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_mirror_nsubs(devconfig_t *mirror, uint16_t nsubs); + +/* + * Get number of subcomponents in mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param nsubs + * RETURN: number of subcomponents in mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_mirror_nsubs(devconfig_t *mirror, uint16_t *nsubs); + +/* + * Set the read strategy for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param read + * the value to set as the read strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_mirror_read( + devconfig_t *mirror, mirror_read_strategy_t read); + +/* + * Get read strategy for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param read + * RETURN: read strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_mirror_read( + devconfig_t *mirror, mirror_read_strategy_t *read); + +/* + * Set the write strategy for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param write + * the value to set as the write strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_mirror_write( + devconfig_t *mirror, mirror_write_strategy_t write); + +/* + * Get write strategy for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param write + * RETURN: write strategy for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_mirror_write( + devconfig_t *mirror, mirror_write_strategy_t *write); + +/* + * Set the resync pass for mirror + * + * @param mirror + * a devconfig_t representing the mirror to modify + * + * @param pass + * the value to set as the resync pass for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_mirror_pass(devconfig_t *mirror, uint16_t pass); + +/* + * Get resync pass for mirror + * + * @param device + * a devconfig_t representing the device to examine + * + * @param pass + * RETURN: resync pass for mirror + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_mirror_pass(devconfig_t *mirror, uint16_t *pass); + +/* + * Set the minimum number of components in stripe + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param mincomp + * the value to set as the minimum number of components + * in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_stripe_mincomp(devconfig_t *stripe, uint16_t mincomp); + +/* + * Get minimum number of components in stripe + * + * @param device + * a devconfig_t representing the device to examine + * + * @param mincomp + * RETURN: minimum number of components in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_stripe_mincomp(devconfig_t *stripe, uint16_t *mincomp); + +/* + * Set the maximum number of components in stripe + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param maxcomp + * the value to set as the maximum number of components + * in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_stripe_maxcomp(devconfig_t *stripe, uint16_t maxcomp); + +/* + * Get maximum number of components in stripe + * + * @param device + * a devconfig_t representing the device to examine + * + * @param maxcomp + * RETURN: maximum number of components in stripe + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_stripe_maxcomp(devconfig_t *stripe, uint16_t *maxcomp); + +/* + * Set the stripe interlace + * + * @param stripe + * a devconfig_t representing the stripe to modify + * + * @param interlace + * the value to set as the stripe interlace + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_stripe_interlace( + devconfig_t *stripe, uint64_t interlace); + +/* + * Get stripe interlace + * + * @param device + * a devconfig_t representing the device to examine + * + * @param interlace + * RETURN: stripe interlace + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_stripe_interlace( + devconfig_t *stripe, uint64_t *interlace); + +/* + * Set the redundancy level for a volume. + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param rlevel + * If 0, a stripe will be created. If > 0, a mirror with + * this number of submirrors will be created. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_volume_redundancy_level( + devconfig_t *volume, uint16_t rlevel); + +/* + * Get the redundancy level for a volume. + * + * @param device + * a devconfig_t representing the device to examine + * + * @param rlevel + * RETURN: the redundancy level for a volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_volume_redundancy_level( + devconfig_t *volume, uint16_t *rlevel); + +/* + * Set the number of paths in volume + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param npaths + * the value to set as the number of paths in volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_volume_npaths(devconfig_t *volume, uint16_t npaths); + +/* + * Get number of paths in volume + * + * @param device + * a devconfig_t representing the device to examine + * + * @param npaths + * RETURN: number of paths in volume + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_volume_npaths(devconfig_t *volume, uint16_t *npaths); + +/* + * Set the HSP creation option (for volume, stripe, concat, mirror) + * + * @param volume + * a devconfig_t representing the volume to modify + * + * @param usehsp + * the value to set as the HSP creation option + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_set_volume_usehsp(devconfig_t *volume, boolean_t usehsp); + +/* + * Get HSP creation option (for volume, stripe, concat, mirror) + * + * @param device + * a devconfig_t representing the device to examine + * + * @param usehsp + * RETURN: HSP creation option (for volume, stripe, + * concat, mirror) + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int devconfig_get_volume_usehsp(devconfig_t *volume, boolean_t *usehsp); + +/* + * Get the string representation of the volume's type + * + * @param type + * a valid component_type_t + * + * @return an internationalized string representing the given + * type + */ +extern char *devconfig_type_to_str(component_type_t type); + +/* + * Get the string representation of the mirror's read strategy + * + * @param read + * a valid mirror_read_strategy_t + * + * @return an internationalized string representing the given + * read strategy + */ +extern char *devconfig_read_strategy_to_str(mirror_read_strategy_t read); + +/* + * Get the string representation of the mirror's write strategy + * + * @param write + * a valid mirror_write_strategy_t + * + * @return an internationalized string representing the given + * write strategy + */ +extern char *devconfig_write_strategy_to_str(mirror_write_strategy_t write); + +#ifdef DEBUG +/* + * Dump the contents of a devconfig_t struct to stdout. + * + * @param device + * the devconfig_t to examine + * + * @param prefix + * a prefix string to print before each line + */ +extern void devconfig_dump(devconfig_t *device, char *prefix); +#endif /* DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_DEVCONFIG_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_dlist.c b/usr/src/cmd/lvm/metassist/common/volume_dlist.c new file mode 100644 index 0000000000..f836c2bc97 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_dlist.c @@ -0,0 +1,512 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "volume_dlist.h" + +#define _volume_dlist_C + +/* + * public constant definitions + */ +const boolean_t ASCENDING = TRUE; /* list ordering */ +const boolean_t DESCENDING = FALSE; +const boolean_t AT_TAIL = TRUE; /* list insertion location */ +const boolean_t AT_HEAD = FALSE; + +/* + * determine if the list contains an item + * that points at the object + */ +boolean_t +dlist_contains( + dlist_t *list, + void *obj, + int (compare)(void *, void *)) +{ + return (dlist_find(list, obj, compare) != NULL); +} + +/* + * locate the item in the list that points at the object + */ +dlist_t * +dlist_find( + dlist_t *list, + void *obj, + int (compare)(void *, void *)) +{ + dlist_t *iter; + + for (iter = list; iter != NULL; iter = iter->next) { + if ((compare)(obj, iter->obj) == 0) { + return (iter); + } + } + + return (NULL); +} + +/* + * insert item into list in the desired order (ascending or descending) + * using the comparison function provided. + * + * In the for loop, iter marks current position in the list + * and item is the item to be inserted. + * + * Iterate the list and find the correct place to insert temp. + * + * if (ascending && compare(item, iter) <= 0 || + * (descending && compare(item, iter) >= 0) + * item goes before iter + * else + * item goes after iter + */ +dlist_t * +dlist_insert_ordered( + dlist_t *item, + dlist_t *list, + boolean_t ascending, + int (compare)(void *, void *)) +{ + dlist_t *head = NULL; + dlist_t *iter = NULL; + int result = 0; + + if (list == NULL) { + + head = item; + + } else { + + head = list; + + for (iter = list; iter != NULL; iter = iter->next) { + + result = (compare)(item->obj, iter->obj); + + if ((ascending && (result <= 0)) || + (!ascending && (result >= 0))) { + + if (iter == head) { + head = item; + item->next = iter; + iter->prev = item; + } else { + item->prev = iter->prev; + item->prev->next = item; + iter->prev = item; + item->next = iter; + } + break; + } + + if (iter->next == NULL) { + /* end of list, so item becomes the new end */ + iter->next = item; + item->prev = iter; + break; + } + } + } + + return (head); +} + +/* + * Remove the first node pointing to same content as item from list, + * clear it's next and prev pointers, return new list head. + * + * The caller is responsible for freeing the removed item if it is no + * longer needed. + * + * The comparison function should be of the form: + * + * int compare(void *obj1, void* obj2); + * + * When called, obj1 will be the object passed into + * dlist_remove_equivalent_item and obj2 will be an object pointed to + * by an item in the list. + * + * The function should return 0 if the two objects are equivalent The + * function should return nonzero otherwise + * + * @param list + * the list containing the item to remove + * + * @param obj + * the object with which to compare each item + * + * @param compare + * the comparison function, passed obj and the obj member + * of each item, to return 0 if item should be removed + * + * @param removed + * RETURN: the removed item, or NULL if none was found + * + * @return the first element of the resulting list + */ +dlist_t * +dlist_remove_equivalent_item( + dlist_t *list, + void *obj, + int (compare)(void *, void *), + dlist_t **removed) +{ + dlist_t *item; + + *removed = NULL; + + if (list == NULL) { + return (list); + } + + item = dlist_find(list, obj, compare); + if (item == NULL) { + return (list); + } + + *removed = item; + + return (dlist_remove(item)); +} + +/* + * Remove an item from its list. Return the resulting list. + * + * @param item + * the item to remove, with prev and next pointers + * set to NULL + * + * @return the first element of the resulting list + */ +dlist_t * +dlist_remove( + dlist_t *item) +{ + dlist_t *head = NULL; + + if (item != NULL) { + if (item->next != NULL) { + item->next->prev = item->prev; + head = item->next; + } + + if (item->prev != NULL) { + item->prev->next = item->next; + head = item->prev; + } + + item->next = NULL; + item->prev = NULL; + + /* Find head of list */ + for (; head != NULL && head->prev != NULL; head = head->prev); + } + + return (head); +} + +/* + * append item to list, either beginning or end + */ +dlist_t * +dlist_append( + dlist_t *item, + dlist_t *list, + boolean_t attail) +{ + dlist_t *head = list; + + if (list == NULL) { + + head = item; + + } else if (item == NULL) { + + head = list; + + } else if (attail) { + + dlist_t *iter; + + /* append to end */ + for (iter = head; iter->next != NULL; iter = iter->next); + + iter->next = item; + item->prev = iter; + + } else { + /* insert at begining */ + item->next = head; + head->prev = item; + head = item; + } + + return (head); +} + +/* + * Create a dlist_t element for the given object and append to list. + * + * @param object + * the obj member of the dlist_t element to be created + * + * @param list + * the list to which to append the new dlist_t element + * + * @param attail + * whether to append at the beginning (AT_HEAD) or end + * (AT_TAIL) of the list + * + * @return 0 + * if successful + * + * @return ENOMEM + * if a dlist_t could not be allocated + */ +int +dlist_append_object( + void *object, + dlist_t **list, + boolean_t attail) +{ + dlist_t *item = dlist_new_item(object); + + if (item == NULL) { + return (ENOMEM); + } + + *list = dlist_append(item, *list, attail); + + return (0); +} + +/* + * Appends list2 to the end of list1. + * + * Returns the resulting list. + */ +dlist_t * +dlist_append_list( + dlist_t *list1, + dlist_t *list2) +{ + dlist_t *iter; + + if (list1 == NULL) { + return (list2); + } + + if (list2 != NULL) { + /* Find last element of list1 */ + for (iter = list1; iter->next != NULL; iter = iter->next); + + iter->next = list2; + list2->prev = iter; + } + + return (list1); +} + +/* + * compute number of items in list + */ +int +dlist_length( + dlist_t *list) +{ + dlist_t *iter; + int length = 0; + + for (iter = list; iter != NULL; iter = iter->next) + ++length; + + return (length); +} + +/* + * Allocate a new dlist_t structure and initialize the opaque object + * pointer the input object. + * + * @return A new dlist_t structure for the given object, or NULL + * if the memory could not be allocated. + */ +dlist_t * +dlist_new_item( + void *obj) +{ + dlist_t *item = (dlist_t *)calloc(1, sizeof (dlist_t)); + + if (item != NULL) { + item->obj = obj; + } + + return (item); +} + +/* + * Traverse the list pointed to by head and free each + * list node. If freefunc is non-NULL, call freefunc + * for each node's object. + */ +void +dlist_free_items( + dlist_t *head, + void (freefunc(void *))) +{ + while (head != NULL) { + dlist_t *item = head; + head = head->next; + + if (freefunc != NULL) { + freefunc(item->obj); + } + + free((void *) item); + } +} + +/* + * Order the given list such that the number of similar elements + * adjacent to each other are minimized. + * + * The algorithm is: + * + * 1. Sort similar items into categories. Two elements are considered + * similar if the given compare function returns 0. + * + * 2. Create a new list by iterating through each category and + * selecting an element from the category with the most elements. + * Avoid choosing an element from the last category chosen. + * + * @param list + * the list to order + * + * @param compare + * the comparison function, passed the obj members + * of two items, to return 0 if the items can be + * considered similar + * + * @return the first element of the resulting list + */ +dlist_t * +dlist_separate_similar_elements( + dlist_t *list, + int(compare)(void *, void *)) +{ + dlist_t **categories = NULL; + dlist_t *item; + int ncategories = 0; + int max_elements; + int lastcat; + + /* + * First, sort like items into categories, according to + * the passed-in compare function + */ + for (item = list; item != NULL; ) { + dlist_t *removed; + + /* Remove this item from the list */ + list = dlist_remove(item); + + /* Create new category */ + categories = (dlist_t **)realloc( + categories, ++ncategories * sizeof (dlist_t *)); + categories[ncategories - 1] = item; + + /* Add like items to same category */ + list = dlist_remove_equivalent_item( + list, item->obj, compare, &removed); + while (removed != NULL) { + /* Add removed item to category */ + dlist_append(removed, item, AT_TAIL); + list = dlist_remove_equivalent_item( + list, item->obj, compare, &removed); + } + + item = list; + } + + /* + * Next, create a new list, minimizing the number of adjacent + * elements from the same category + */ + list = NULL; + lastcat = -1; + do { + int i; + int curcat; + + /* + * Find the category with the most elements, other than + * the last category chosen + */ + max_elements = 0; + for (i = 0; i < ncategories; i++) { + int nelements; + + if (i == lastcat) { + continue; + } + + nelements = dlist_length(categories[i]); + if (nelements > max_elements) { + max_elements = nelements; + curcat = i; + } + } + + /* If no elements were found, use the last category chosen */ + if (max_elements == 0 && lastcat >= 0) { + max_elements = dlist_length(categories[lastcat]); + curcat = lastcat; + } + + /* Was a category with elements found? */ + if (max_elements != 0) { + /* Remove first element of chosen category */ + item = categories[curcat]; + categories[curcat] = dlist_remove(item); + + /* Add removed element to resulting list */ + list = dlist_append(item, list, AT_TAIL); + + lastcat = curcat; + } + } while (max_elements != 0); + + free(categories); + + return (list); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_dlist.h b/usr/src/cmd/lvm/metassist/common/volume_dlist.h new file mode 100644 index 0000000000..af0c5786cc --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_dlist.h @@ -0,0 +1,278 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_DLIST_H +#define _VOLUME_DLIST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* + * Structure defining a doubly linked list of arbitrary objects + */ +typedef struct dlist { + + struct dlist *next; + struct dlist *prev; + void *obj; + +} dlist_t; + +/* + * module globals + */ +extern const boolean_t ASCENDING; +extern const boolean_t DESCENDING; +extern const boolean_t AT_TAIL; +extern const boolean_t AT_HEAD; + +/* from types.h */ +#ifndef TRUE +#define TRUE B_TRUE +#endif + +#ifndef FALSE +#define FALSE B_FALSE +#endif + +/* + * doubly linked list utility methods + */ + +/* + * count the number of elements currently in the list + */ +extern int dlist_length(dlist_t *list); + +/* + * Traverse the list pointed to by head and free each + * list node. If freefunc is non-NULL, call freefunc + * for each node's object. + */ +extern void dlist_free_items(dlist_t *list, void (freefunc(void *))); + +/* + * append item to list. If atend is true, the item is + * added at the end of the list, otherwise it is added + * at the beginning. + * + * returns the possibly changed head of the list. + */ +extern dlist_t *dlist_append( + dlist_t *item, + dlist_t *list, + boolean_t atend); + +/* + * Create a dlist_t element for the given object and append to list. + * + * @param object + * the obj member of the dlist_t element to be created + * + * @param list + * the list to which to append the new dlist_t element + * + * @param attail + * whether to append at the beginning (AT_HEAD) or end + * (AT_TAIL) of the list + * + * @return 0 + * if successful + * + * @return ENOMEM + * if a dlist_t could not be allocated + */ +extern int dlist_append_object( + void *object, + dlist_t **list, + boolean_t attail); + +/* + * Appends list2 to the end of list1. + * + * Returns the resulting list. + */ +extern dlist_t *dlist_append_list( + dlist_t *list1, + dlist_t *list2); + +/* + * Remove the first node pointing to same content as item from list, + * clear it's next and prev pointers, return new list head. + * + * The caller is responsible for freeing the removed item if it is no + * longer needed. + * + * The comparison function should be of the form: + * + * int compare(void *obj1, void* obj2); + * + * When called, obj1 will be the object passed into + * dlist_remove_equivalent_item and obj2 will be an object pointed to by an + * item in the list. + * + * The function should return 0 if the two objects are equivalent The + * function should return nonzero otherwise + * + * @param list + * the list containing the item to remove + * + * @param obj + * the object with which to compare each item + * + * @param compare + * the comparison function, passed obj and the obj member + * of each item, to return 0 if item should be removed + * + * @param removed + * RETURN: the removed item, or NULL if none was found + * + * @return the first element of the resulting list + */ +extern dlist_t *dlist_remove_equivalent_item( + dlist_t *list, + void *obj, + int (compare)(void *obj1, void *obj2), + dlist_t **removed); + +/* + * Remove an item from its list. Return the resulting list. + * + * @param item + * the item to remove, with prev and next pointers + * set to NULL + * + * @return the first element of the resulting list + */ +dlist_t * +dlist_remove( + dlist_t *item); + +/* + * allocates memory for a new list item. The list item will + * point at obj. + * + * returns the new list item. + */ +extern dlist_t *dlist_new_item(void *obj); + +/* + * inserts item in the correct position within the list based on + * the comparison function. if ascending is true, the list will + * be in ascending order, otherwise descending. + * + * the comparison function should be of the form: + * + * int compare(void *obj1, void *obj2); + * + * When called, obj1 will be the object pointed to by the item to + * be added to the list, obj2 will be an object pointed to by an + * item currently in the list. + * + * The function should return 0 if the two objects are equivalent + * The function should return <0 if obj1 comes before obj2 + * The function should return >0 if obj1 comes after obj2 + * + * dlist_insert_ordered returns the possibly changed head + * of the list. + */ +extern dlist_t *dlist_insert_ordered( + dlist_t *item, + dlist_t *list, + boolean_t ascending, + int (compare)(void *obj1, void *obj2)); + +/* + * Locates the item in the list which contains object. + * + * the comparison function should be of the form: + * + * int compare(void *obj1, void *obj2); + * + * When called, obj1 will be the input object, obj2 will be + * an object pointed to by an item currently in the list. + * + * The function should return 0 if the two objects are equivalent + * The function should return non-zero otherwise + * + * dlist_find() returns the found item or NULL if one was not found. + */ +extern dlist_t *dlist_find( + dlist_t *list, + void *obj, + int (compare)(void *obj1, void *obj2)); + +/* + * Determines if list has an item which contains object. + * + * the comparison function should be of the form: + * + * int compare(void *obj1, void *obj2); + * + * When called, obj1 will be the input object, obj2 will be + * an object pointed to by an item currently in the list. + * + * The function should return 0 if the two objects are equivalent + * The function should return non-zero otherwise + * + * dlist_contains() returns TRUE if the object is already + * in the list or FALSE otherwise. + */ +extern boolean_t dlist_contains( + dlist_t *list, + void *obj, + int (compare)(void *obj1, void *obj2)); + +/* + * Order the given list such that the number of similar elements + * adjacent to each other are minimized. Two elements are considered + * similar if the given compare function returns 0. + * + * @param list + * the list to order + * + * @param compare + * the comparison function, passed the obj members + * of two items, to return 0 if the items can be + * considered similar + * + * @return the first element of the resulting list + */ +extern dlist_t * +dlist_separate_similar_elements( + dlist_t *list, + int(*equals)(void *, void *)); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_DLIST_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_error.c b/usr/src/cmd/lvm/metassist/common/volume_error.c new file mode 100644 index 0000000000..67c2927ace --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_error.c @@ -0,0 +1,97 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "volume_error.h" + +#define VOLUME_ERROR_BUFSIZE 1024 +static char volume_error[VOLUME_ERROR_BUFSIZE]; + +/* + * Retrieve the error string for the given error code. + * + * @param error + * If error is less than zero, it is assumed to be a + * custom error code. If error is greater than zero, it + * is assumed to be an error defined in errno.h. + * + * @return the error string set by volume_set_error() + * if error < 0 + * + * @return the error string returned by strerror() + * if error > 0 + */ +char * +get_error_string( + int error) +{ + if (error < 0) { + return (volume_error); + } + + if (error > 0) { + return (strerror(error)); + } + + return (NULL); +} + +/* + * Set the error string for the most recent error. This message can + * be retrieved with get_error_string(error), assuming error is less + * than zero. + * + * @param fmt + * printf format string + * + * @return the number of characters formatted + * if successful + * + * @return negative value + * if an error occurred + */ +/*PRINTFLIKE1*/ +int +volume_set_error( + char *fmt, + ...) +{ + int ret = 0; + + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(volume_error, VOLUME_ERROR_BUFSIZE, fmt, ap); + va_end(ap); + + return (ret); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_error.h b/usr/src/cmd/lvm/metassist/common/volume_error.h new file mode 100644 index 0000000000..30fa08acc6 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_error.h @@ -0,0 +1,72 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_ERROR_H +#define _VOLUME_ERROR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Retrieve the error string for the given error code. + * + * @param error + * If error is less than zero, it is assumed to be a + * custom error code. If error is greater than zero, it + * is assumed to be an error defined in errno.h. + * + * @return the error string set by volume_set_error() + * if error < 0 + * + * @return the error string returned by strerror() + * if error > 0 + */ +extern char *get_error_string(int error); + +/* + * Set the error string for the most recent error. This message can + * be retrieved with get_error_string(error), assuming error is less + * than zero. + * + * @param fmt + * printf format string + * + * @return the number of characters formatted + * if successful + * + * @return negative value + * if an error occurred + */ +extern int volume_set_error(char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_ERROR_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_nvpair.c b/usr/src/cmd/lvm/metassist/common/volume_nvpair.c new file mode 100644 index 0000000000..57592d8880 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_nvpair.c @@ -0,0 +1,726 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libintl.h> +#include <string.h> +#include "volume_nvpair.h" +#include "volume_error.h" + +/* + * ****************************************************************** + * + * Function prototypes + * + * ****************************************************************** + */ + +static nvpair_t *nvlist_walk_nvpair(nvlist_t *nvl, + const char *name, data_type_t type, nvpair_t *nvp); + +/* + * ****************************************************************** + * + * External functions + * + * ****************************************************************** + */ + +/* + * Get the named uint16 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint16 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_uint16( + nvlist_t *attrs, + char *which, + uint16_t *val) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT16, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_uint16(match, val); + } + + return (error); +} + +/* + * Set the named uint16 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_uint16( + nvlist_t *attrs, + char *which, + uint16_t val) +{ + int error = 0; + + if ((error = nvlist_add_uint16(attrs, which, val)) != 0) { + volume_set_error( + gettext("nvlist_add_int16(%s) failed: %d\n"), which, error); + } + + return (error); +} + +/* + * Get the named uint32 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint32 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_uint32( + nvlist_t *attrs, + char *which, + uint32_t *val) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT32, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_uint32(match, val); + } + + return (error); +} + +/* + * Set the named uint32 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_uint32( + nvlist_t *attrs, + char *which, + uint32_t val) +{ + int error = 0; + + if ((error = nvlist_add_uint32(attrs, which, val)) != 0) { + volume_set_error( + gettext("nvlist_add_int32(%s) failed: %d\n"), which, error); + } + + return (error); +} + +/* + * Get the named uint64 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint64 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_uint64( + nvlist_t *attrs, + char *which, + uint64_t *val) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT64, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_uint64(match, val); + } + + return (error); +} + +/* + * Set the named uint64 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_uint64( + nvlist_t *attrs, + char *which, + uint64_t val) +{ + int error = 0; + + if ((error = nvlist_add_uint64(attrs, which, val)) != 0) { + volume_set_error( + gettext("nvlist_add_int64(%s) failed: %d\n"), which, error); + } + + return (error); +} + +/* + * Set the named boolean in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_boolean( + nvlist_t *attrs, + char *which, + boolean_t val) +{ + /* + * Use set_uint16 to distinguish "attr = B_FALSE" from + * "attribute unset". + */ + return (set_uint16(attrs, which, val == B_TRUE ? 1 : 0)); +} + +/* + * Get the named boolean from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param boolval + * RETURN: the value of the requested boolean + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_boolean( + nvlist_t *attrs, + char *which, + boolean_t *boolval) +{ + int error; + uint16_t val; + + /* + * Use get_uint16 to distinguish "attr = B_FALSE" from + * "attribute unset". + */ + if ((error = get_uint16(attrs, which, &val)) == 0) { + *boolval = (val ? B_TRUE : B_FALSE); + } + + return (error); +} + +/* + * Get the named string from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param str + * RETURN: the requested string + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_string( + nvlist_t *attrs, + char *which, + char **str) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_STRING, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_string(match, str); + } + + return (error); +} + +/* + * Set the named string in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_string( + nvlist_t *attrs, + char *which, + char *val) +{ + int error = 0; + + if ((error = nvlist_add_string(attrs, which, val)) != 0) { + volume_set_error( + gettext("nvlist_add_string(%s) failed: %d\n"), which, error); + } + + return (error); +} + +/* + * Get the named uint16 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint16 array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_uint16_array( + nvlist_t *attrs, + char *which, + uint16_t **val, + uint_t *nelem) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT16_ARRAY, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_uint16_array(match, val, nelem); + } + + return (error); +} + +/* + * Set the named uint16 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested uint16 array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_uint16_array( + nvlist_t *attrs, + char *which, + uint16_t *val, + uint_t nelem) +{ + int error = 0; + + if ((error = nvlist_add_uint16_array( + attrs, which, val, nelem)) != 0) { + volume_set_error( + gettext("nvlist_add_uint16_array(%s) failed: %d.\n"), + which, error); + } + + return (error); +} + +/* + * Get the named uint64 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint64 array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_uint64_array( + nvlist_t *attrs, + char *which, + uint64_t **val, + uint_t *nelem) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT64_ARRAY, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_uint64_array(match, val, nelem); + } + + return (error); +} + +/* + * Set the named uint64 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested uint64 array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_uint64_array( + nvlist_t *attrs, + char *which, + uint64_t *val, + uint_t nelem) +{ + int error = 0; + + if ((error = nvlist_add_uint64_array( + attrs, which, val, nelem)) != 0) { + volume_set_error( + gettext("nvlist_add_uint64_array(%s) failed: %d.\n"), + which, error); + } + + return (error); +} + +/* + * Get the named string array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested string array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + */ +int +get_string_array( + nvlist_t *attrs, + char *which, + char ***val, + uint_t *nelem) +{ + int error; + nvpair_t *match = + nvlist_walk_nvpair(attrs, which, DATA_TYPE_STRING_ARRAY, NULL); + + if (match == NULL) { + error = ENOENT; + } else { + error = nvpair_value_string_array(match, val, nelem); + } + + return (error); +} + +/* + * Set the named string array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested string array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +int +set_string_array( + nvlist_t *attrs, + char *which, + char **val, + uint_t nelem) +{ + int error = 0; + + if ((error = nvlist_add_string_array( + attrs, which, val, nelem)) != 0) { + volume_set_error( + gettext("nvlist_add_string_array(%s) failed: %d.\n"), + which, error); + } + + return (error); +} + +/* + * ****************************************************************** + * + * Static functions + * + * ****************************************************************** + */ + +/* + * Get a handle to the next nvpair with the specified name and data + * type in the list following the given nvpair. + * + * Some variation of this function will likely appear in the libnvpair + * library per 4981923. + * + * @param nvl + * the nvlist_t to search + * + * @param name + * the string key for the pair to find in the list, or + * NULL to match any name + * + * @param type + * the data type for the pair to find in the list, or + * DATA_TYPE_UNKNOWN to match any type + * + * @param nvp + * the pair to search from in the list, or NULL to search + * from the beginning of the list + * + * @return the next nvpair in the list matching the given + * criteria, or NULL if no matching nvpair is found + */ +static nvpair_t * +nvlist_walk_nvpair( + nvlist_t *nvl, + const char *name, + data_type_t type, + nvpair_t *nvp) +{ + /* For each nvpair in the list following nvp... */ + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + + /* Does this pair's name match the given name? */ + if ((name == NULL || strcmp(nvpair_name(nvp), name) == 0) && + + /* Does this pair's type match the given type? */ + (type == DATA_TYPE_UNKNOWN || type == nvpair_type(nvp))) { + return (nvp); + } + } + + return (NULL); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_nvpair.h b/usr/src/cmd/lvm/metassist/common/volume_nvpair.h new file mode 100644 index 0000000000..de9aa134b0 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_nvpair.h @@ -0,0 +1,467 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_NVPAIR_H +#define _VOLUME_NVPAIR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libnvpair.h> + +/* + * Get the named uint16 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint16 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_uint16(nvlist_t *attrs, char *which, uint16_t *val); + +/* + * Set the named uint16 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +extern int set_uint16(nvlist_t *attrs, char *which, uint16_t val); + +/* + * Get the named uint32 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint32 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_uint32(nvlist_t *attrs, char *which, uint32_t *val); + +/* + * Set the named uint32 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +extern int set_uint32(nvlist_t *attrs, char *which, uint32_t val); + +/* + * Get the named uint64 from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint64 + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_uint64(nvlist_t *attrs, char *which, uint64_t *val); + +/* + * Set the named uint64 in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +extern int set_uint64(nvlist_t *attrs, char *which, uint64_t val); + +/* + * Set the named boolean in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +extern int set_boolean(nvlist_t *attrs, char *which, boolean_t val); + +/* + * Get the named boolean from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param boolval + * RETURN: the value of the requested boolean + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_boolean(nvlist_t *attrs, char *which, boolean_t *boolval); + +/* + * Get the named string from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param str + * RETURN: the requested string + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_string(nvlist_t *attrs, char *which, char **str); + +/* + * Set the named string in the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value to set + * + * @return 0 + * if successful + * + * @return EINVAL + * if there is an invalid argument + * + * @return ENOMEM + * if there is insufficient memory + */ +extern int set_string(nvlist_t *attrs, char *which, char *val); + +/* + * Get the named uint16 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint16 array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_uint16_array( + nvlist_t *attrs, char *which, uint16_t **val, uint_t *nelem); + +/* + * Set the named uint16 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested uint16 array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int set_uint16_array( + nvlist_t *attrs, char *which, uint16_t *val, uint_t nelem); + +/* + * Get the named uint64 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested uint64 array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_uint64_array( + nvlist_t *attrs, char *which, uint64_t **val, uint_t *nelem); + +/* + * Set the named uint64 array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested uint64 array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int set_uint64_array( + nvlist_t *attrs, char *which, uint64_t *val, uint_t nelem); + +/* + * Get the named string array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * RETURN: the value of the requested string array + * + * @param nelem + * RETURN: the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int get_string_array( + nvlist_t *attrs, char *which, char ***val, uint_t *nelem); + +/* + * Set the named string array from the given nvlist_t. + * + * @param attrs + * the nvlist_t to search + * + * @param which + * the string key for this element in the list + * + * @param val + * the value of the requested string array + * + * @param nelem + * the number of elements in the array + * + * @return 0 + * if successful + * + * @return ENOENT + * if no matching name-value pair is found + * + * @return ENOTSUP + * if an encode/decode method is not supported + * + * @return EINVAL + * if there is an invalid argument + */ +extern int set_string_array( + nvlist_t *attrs, char *which, char **val, uint_t nelem); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_NVPAIR_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_output.c b/usr/src/cmd/lvm/metassist/common/volume_output.c new file mode 100644 index 0000000000..b6b177d0c5 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_output.c @@ -0,0 +1,177 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "volume_output.h" +#include "volume_error.h" + +static int max_verbosity = OUTPUT_QUIET; +static FILE *output = NULL; + +/* + * Set the maximum level of verbosity to be reported to the user. + * Strings sent to oprintf() with a higher verbosity level than this + * maximum level will not be reported to the user. + * + * @param verbosity + * One of the predefined constants: + * OUTPUT_QUIET + * OUTPUT_TERSE + * OUTPUT_VERBOSE + * OUTPUT_DEBUG + * + * @param stream + * The stream to print all qualifying output to. + * + * @return 0 on success, non-zero otherwise. + */ +int +set_max_verbosity( + int verbosity, + FILE *stream) +{ + int error = 0; + + switch (verbosity) { + case OUTPUT_QUIET: + case OUTPUT_TERSE: + case OUTPUT_VERBOSE: + case OUTPUT_DEBUG: + max_verbosity = verbosity; + output = stream; + break; + + default: + volume_set_error( + gettext("%d: invalid verbosity level"), verbosity); + error = -1; + } + + return (error); +} + +/* + * Get the maximum level of verbosity to be reported to the user. + * + * @return OUTPUT_QUIET + * + * @return OUTPUT_TERSE + * + * @return OUTPUT_VERBOSE + * + * @return OUTPUT_DEBUG + */ +int +get_max_verbosity() +{ + return (max_verbosity); +} + +/* + * Prints the given formatted string arguments to a predefined stream, + * if the given verbosity is less than or equal to the set maximum + * verbosity. + * + * @param verbosity + * Same as for set_max_verbosity() + * + * @param fmt, ... + * printf-style arguments + * + * @return the number of characters output + * if successful + * + * @return negative value + * if unsuccessful + */ +int +oprintf( + int verbosity, + char *fmt, + ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = oprintf_va(verbosity, fmt, ap); + va_end(ap); + + return (ret); +} + +/* + * Identical to oprintf but with a va_list instead of variable length + * argument list. This function is provided for external printf-style + * wrappers. + * + * @param verbosity + * Same as for set_max_verbosity() + * + * @param fmt + * printf format string + * + * @param ap + * a va_list containing remaining printf-style arguments + * + * @return the number of characters output + * if successful + * + * @return negative value + * if unsuccessful + */ +/*PRINTFLIKE2*/ +int +oprintf_va( + int verbosity, + char *fmt, + va_list ap) +{ + int ret = 0; + + /* Is this verbosity high enough to print? */ + if (output != NULL && verbosity <= max_verbosity) { +#ifdef DEBUG + if (getenv(METASSIST_DEBUG_ENV) != NULL) { + time_t now = time(NULL); + struct tm *time = localtime(&now); + fprintf(output, "%.2d:%.2d:%.2d: ", + time->tm_hour, time->tm_min, time->tm_sec); + } +#endif + ret = vfprintf(output, fmt, ap); + } + + return (ret); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_output.h b/usr/src/cmd/lvm/metassist/common/volume_output.h new file mode 100644 index 0000000000..fbff0ed01c --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_output.h @@ -0,0 +1,130 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_OUTPUT_H +#define _VOLUME_OUTPUT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <stdarg.h> + +#ifdef DEBUG +/* + * The environment variable that must be set for metassist to + * enable debug output + */ +#define METASSIST_DEBUG_ENV "METASSIST_DEBUG" +#endif + +/* Verbosity levels */ +#define OUTPUT_QUIET 0 +#define OUTPUT_TERSE 1 +#define OUTPUT_VERBOSE 2 +#define OUTPUT_DEBUG 3 + +/* + * Set the maximum level of verbosity to be reported to the user. + * Strings sent to oprintf() with a higher verbosity level than this + * maximum level will not be reported to the user. + * + * @param verbosity + * One of the predefined constants: + * OUTPUT_QUIET + * OUTPUT_TERSE + * OUTPUT_VERBOSE + * OUTPUT_DEBUG + * + * @param stream + * The stream to print all qualifying output to. + * + * @return 0 on success, non-zero otherwise. + */ +extern int set_max_verbosity(int verbosity, FILE *stream); + +/* + * Get the maximum level of verbosity to be reported to the user. + * + * @return OUTPUT_QUIET + * + * @return OUTPUT_TERSE + * + * @return OUTPUT_VERBOSE + * + * @return OUTPUT_DEBUG + */ +extern int get_max_verbosity(); + +/* + * Prints the given formatted string arguments to a predefined stream, + * if the given verbosity is less than or equal to the set maximum + * verbosity. + * + * @param verbosity + * Same as for set_max_verbosity() + * + * @param fmt, ... + * printf-style arguments + * + * @return the number of characters output + * if successful + * + * @return negative value + * if unsuccessful + */ +extern int oprintf(int verbosity, char *fmt, ...); + +/* + * Identical to oprintf but with a va_list instead of variable length + * argument list. This function is provided for external printf-style + * wrappers. + * + * @param verbosity + * Same as for set_max_verbosity() + * + * @param fmt + * printf format string + * + * @param ap + * a va_list containing remaining printf-style arguments + * + * @return the number of characters output + * if successful + * + * @return negative value + * if unsuccessful + */ +extern int oprintf_va(int verbosity, char *fmt, va_list ap); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_OUTPUT_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_request.c b/usr/src/cmd/lvm/metassist/common/volume_request.c new file mode 100644 index 0000000000..665616089c --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_request.c @@ -0,0 +1,178 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libintl.h> +#include "volume_request.h" +#include "volume_error.h" + +/* + * Methods which manipulate a request_t struct + */ + +/* + * Constructor: Create a request_t struct. This request_t must be + * freed. + * + * @param request + * RETURN: a pointer to a new request_t + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +new_request( + request_t **request) +{ + int error; + devconfig_t *diskset_req; + devconfig_t *diskset_config; + + *request = (request_t *)calloc(1, sizeof (request_t)); + if (*request == NULL) { + (void) volume_set_error(gettext("new_request calloc() failed\n")); + return (-1); + } + + /* Create a new diskset_req */ + if ((error = new_devconfig(&diskset_req, TYPE_DISKSET)) != 0) { + free_request(*request); + return (error); + } + request_set_diskset_req(*request, diskset_req); + + /* Create a new diskset_config */ + if ((error = new_devconfig(&diskset_config, TYPE_DISKSET)) != 0) { + free_request(*request); + return (error); + } + request_set_diskset_config(*request, diskset_config); + + return (0); +} + +/* + * Free memory (recursively) allocated to a request_t struct + * + * @param arg + * pointer to the request_t struct to free + */ +void +free_request( + void *arg) +{ + request_t *request = (request_t *)arg; + + if (request == NULL) { + return; + } + + /* Free the diskset_req */ + if (request->diskset_req != NULL) { + free_devconfig(request->diskset_req); + } + + /* Free the diskset_config */ + if (request->diskset_config != NULL) { + free_devconfig(request->diskset_config); + } + + /* Free the devconfig itself */ + free(request); +} + +/* + * Set the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to modify + * + * @param diskset + * The devconfig_t representing the toplevel (disk set) + * device in the volume request hierarchy + */ +void +request_set_diskset_req( + request_t *request, + devconfig_t *diskset) +{ + request->diskset_req = diskset; +} + +/* + * Get the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to examine + * + * @return The devconfig_t representing the toplevel (disk set) + * device in the volume request hierarchy + */ +devconfig_t * +request_get_diskset_req( + request_t *request) +{ + return (request->diskset_req); +} + +/* + * Set/get the disk set at the top of the proposed volume hierarchy + * + * @param request + * The request_t representing the request to modify + * + * @param diskset + * The devconfig_t representing the toplevel (disk set) + * device in the proposed volume hierarchy + */ +void +request_set_diskset_config( + request_t *request, + devconfig_t *diskset) +{ + request->diskset_config = diskset; +} + +/* + * Get the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to examine + * + * @return The devconfig_t representing the toplevel (disk set) + * device in the proposed volume hierarchy + */ +devconfig_t * +request_get_diskset_config( + request_t *request) +{ + return (request->diskset_config); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_request.h b/usr/src/cmd/lvm/metassist/common/volume_request.h new file mode 100644 index 0000000000..5ba1d06d81 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_request.h @@ -0,0 +1,134 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_REQUEST_H +#define _VOLUME_REQUEST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" + +/* + * request_t - struct to hold a layout request + */ +typedef struct request { + /* + * The devconfig_t representing the disk set at the top of the + * request hierarchy. This hierarchy represents the requested + * volume configuration, as read from the volume-request. + */ + devconfig_t *diskset_req; + + /* + * The devconfig_t representing the disk set at the top of the + * resulting proposed volume hierarchy. This hierarchy + * represents the volume configuration proposed by the layout + * engine. This configuration will eventually be converted to + * a volume-spec. + */ + devconfig_t *diskset_config; +} request_t; + +/* + * Constructor: Create a request_t struct. This request_t must be + * freed. + * + * @param request + * RETURN: a pointer to a new request_t + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int new_request(request_t **request); + +/* + * Free memory (recursively) allocated to a request_t struct + * + * @param arg + * pointer to the request_t struct to free + */ +extern void free_request(void *arg); + +/* + * Set the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to modify + * + * @param diskset + * The devconfig_t representing the toplevel (disk set) + * device in the volume request hierarchy + */ +extern void request_set_diskset_req(request_t *request, devconfig_t *diskset); + +/* + * Get the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to examine + * + * @return The devconfig_t representing the toplevel (disk set) + * device in the volume request hierarchy + */ +extern devconfig_t *request_get_diskset_req(request_t *request); + +/* + * Set/get the disk set at the top of the proposed volume hierarchy + * + * @param request + * The request_t representing the request to modify + * + * @param diskset + * The devconfig_t representing the toplevel (disk set) + * device in the proposed volume hierarchy + */ +extern void request_set_diskset_config( + request_t *request, devconfig_t *diskset); + +/* + * Get the disk set at the top of the request hierarchy + * + * @param request + * The request_t representing the request to examine + * + * @return The devconfig_t representing the toplevel (disk set) + * device in the proposed volume hierarchy + */ +extern devconfig_t *request_get_diskset_config(request_t *request); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_REQUEST_H */ diff --git a/usr/src/cmd/lvm/metassist/common/volume_string.c b/usr/src/cmd/lvm/metassist/common/volume_string.c new file mode 100644 index 0000000000..0391fa7fc2 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_string.c @@ -0,0 +1,462 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "volume_string.h" + +#include <ctype.h> +#include <errno.h> +#include <libintl.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "volume_error.h" +#include "volume_output.h" + +/* + * ****************************************************************** + * + * Function prototypes + * + * ****************************************************************** + */ + +static void *append_to_pointer_array(void **array, void *pointer); + +/* + * ****************************************************************** + * + * Data + * + * ****************************************************************** + */ + +/* All-inclusive valid size units */ +units_t universal_units[] = { + {"BLOCKS", BYTES_PER_BLOCK}, + {"KB", BYTES_PER_KILOBYTE}, + {"MB", BYTES_PER_MEGABYTE}, + {"GB", BYTES_PER_GIGABYTE}, + {"TB", BYTES_PER_TERABYTE}, + {NULL, 0} +}; + +/* + * ****************************************************************** + * + * External functions + * + * ****************************************************************** + */ + +/* + * Concatenates a list of strings. The result must be free()d. + * + * @param numargs + * The number of strings to concatenate. + * + * @param ... + * The strings (type char *) to concatenate. + * + * @return the concatenated string + * if succesful + * + * @return NULL + * if memory could not be allocated + */ +char * +stralloccat( + int numargs, + ...) +{ + va_list vl; + int i; + int len = 1; + char *cat; + + /* Determine length of concatenated string */ + va_start(vl, numargs); + for (i = 0; i < numargs; i++) { + char *str = va_arg(vl, char *); + if (str != NULL) { + len += strlen(str); + } + } + va_end(vl); + + /* Allocate memory for concatenation plus a trailing NULL */ + cat = (char *)calloc(1, len * sizeof (char)); + + if (cat == NULL) { + return (NULL); + } + + /* Concatenate strings */ + va_start(vl, numargs); + for (i = 0; i < numargs; i++) { + char *str = va_arg(vl, char *); + if (str != NULL) { + strcat(cat, str); + } + } + va_end(vl); + + return (cat); +} + +/* + * Convert the given string to a uint16_t, verifying that the value + * does not exceed the lower or upper bounds of a uint16_t. + * + * @param str + * the string to convert + * + * @param num + * the addr of the uint16_t + * + * @return 0 + * if the given string was converted to a uint16_t + * + * @return -1 + * if the string could could not be converted to a number + * + * @return -2 + * if the converted number exceeds the lower or upper + * bounds of a uint16_t + */ +int +str_to_uint16( + char *str, + uint16_t *num) +{ + long long lnum; + int error = 0; + + /* Convert string to long long */ + if (sscanf(str, "%lld", &lnum) != 1) { + error = -1; + } else { + + /* + * Verify that the long long value does not exceed the + * lower or upper bounds of a uint16_t + */ + + /* Maximum value of uint16_t */ + uint16_t max = (uint16_t)~0ULL; + + if (lnum < 0 || lnum > max) { + error = -2; + } else { + *num = lnum; + } + } + + return (error); +} + +/* + * Converts the given long long into a string. This string must be + * freed. + * + * @param num + * the long long to convert + * + * @param str + * the addr of the string + * + * @return 0 + * if successful + * + * @return ENOMEM + * if the physical limits of the system are exceeded by + * size bytes of memory which cannot be allocated + * + * @return EAGAIN + * if there is not enough memory available to allocate + * size bytes of memory + */ +int +ll_to_str( + long long num, + char **str) +{ + int error = 0; + + /* Allocate memory for the string */ + if ((*str = calloc(1, LONG_LONG_STR_SIZE * sizeof (char))) == NULL) { + error = errno; + } else { + /* Convert the integer to a string */ + snprintf(*str, LONG_LONG_STR_SIZE, "%lld", num); + } + + return (error); +} + +/* + * Convert a size specification to bytes. + * + * @param str + * a size specification strings of the form + * <value><units>, where valid <units> are specified by + * the units argument and <value> is the (floating-point) + * multiplier of the units + * + * @param bytes + * RETURN: the result of converting the given size string + * to bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +sizestr_to_bytes( + char *str, + uint64_t *bytes, + units_t units[]) +{ + char *unit_str; + long double d; + int error = 0; + int i; + + /* Convert <value> string to double */ + if ((d = strtod(str, &unit_str)) == 0) { + volume_set_error(gettext("invalid size string: %s"), str); + error = -1; + } else { + + /* Trim leading white space */ + while (isspace(*unit_str) != 0) { + ++unit_str; + } + + /* Convert to bytes based on <units> */ + for (i = 0; units[i].unit_str != NULL; i++) { + if (strcasecmp(unit_str, units[i].unit_str) == 0) { + d *= units[i].bytes_per_unit; + break; + } + } + + /* Was a valid unit string found? */ + if (units[i].unit_str == NULL) { + volume_set_error( + gettext("missing or invalid units indicator in size: %s"), + str); + error = -1; + } + } + + if (error) { + *bytes = 0; + } else { + *bytes = (uint64_t)d; + oprintf(OUTPUT_DEBUG, + gettext("converted \"%s\" to %llu bytes\n"), str, *bytes); + } + + return (error); +} + +/* + * Convert bytes to a size specification string. + * + * @param bytes + * the number of bytes + * + * @param str + * RETURN: a size specification strings of the form + * <value><units>, where valid <units> are specified by + * the units argument and <value> is the (floating-point) + * multiplier of the units. This string must be freed. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +int +bytes_to_sizestr( + uint64_t bytes, + char **str, + units_t units[], + boolean_t round) +{ + int i, len, error = 0; + double value; + const char *format; + units_t use_units = units[0]; + + /* Determine the units to use */ + for (i = 0; units[i].unit_str != NULL; i++) { + if (bytes >= units[i].bytes_per_unit) { + use_units = units[i]; + } + } + + value = ((long double)bytes / use_units.bytes_per_unit); + + /* Length of string plus trailing NULL */ + len = LONG_LONG_STR_SIZE + strlen(use_units.unit_str) + 1; + + if (round) { + value = floor(value + 0.5F); + format = "%.0f%s"; + } else { + format = "%.2f%s"; + } + + /* Append units to string */ + *str = calloc(1, len * sizeof (char)); + if (*str == NULL) { + error = errno; + } else { + snprintf(*str, len, format, value, use_units.unit_str); + } + + return (error); +} + +/* + * Appends a copy of the given string to the given string array, + * ensuring that the last element in the array is NULL. This array + * must be freed via free_string_array. + * + * Note when an error occurs and NULL is returned, array is not freed. + * Subsequently callers should save a pointer to the original array + * until success is verified. + * + * @param array + * the array to append to, or NULL to create a new array + * + * @param str + * the string to copy and add to the array + * + * @return a pointer to the realloc'd (and possibly moved) array + * if succesful + * + * @return NULL + * if unsuccesful + */ +char ** +append_to_string_array( + char **array, + char *str) +{ + char *copy = strdup(str); + + if (copy == NULL) { + return (NULL); + } + + return ((char **)append_to_pointer_array((void **)array, copy)); +} + +/* + * Frees each element of the given string array, then frees the array + * itself. + * + * @param array + * a NULL-terminated string array + */ +void +free_string_array( + char **array) +{ + int i; + + /* Free each available element */ + for (i = 0; array[i] != NULL; i++) { + free(array[i]); + } + + /* Free the array itself */ + free((void *)array); +} + +/* + * ****************************************************************** + * + * Static functions + * + * ****************************************************************** + */ + +/* + * Appends the given pointer to the given pointer array, ensuring that + * the last element in the array is NULL. + * + * Note when an error occurs and NULL is returned, array is not freed. + * Subsequently callers should save a pointer to the original array + * until success is verified. + * + * @param array + * the array to append to, or NULL to create a new array + * + * @param pointer + * the pointer to add to the array + * + * @return a pointer to the realloc'd (and possibly moved) array + * if succesful + * + * @return NULL + * if unsuccesful + */ +static void * +append_to_pointer_array( + void **array, + void *pointer) +{ + void **newarray = NULL; + int i = 0; + + if (array != NULL) { + /* Count the elements currently in the array */ + for (i = 0; array[i] != NULL; ++i); + } + + /* realloc, adding a slot for the new pointer */ + newarray = (void **)realloc(array, (i + 2) * sizeof (*array)); + + if (newarray != NULL) { + /* Append pointer and terminal NULL */ + newarray[i] = pointer; + newarray[i+1] = NULL; + } + + return (newarray); +} diff --git a/usr/src/cmd/lvm/metassist/common/volume_string.h b/usr/src/cmd/lvm/metassist/common/volume_string.h new file mode 100644 index 0000000000..a66b38b23c --- /dev/null +++ b/usr/src/cmd/lvm/metassist/common/volume_string.h @@ -0,0 +1,203 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_STRING_H +#define _VOLUME_STRING_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The length of the string when the longest long long is converted to + * a string + */ +#define LONG_LONG_STR_SIZE 128 + +#define BYTES_PER_BLOCK 512 +#define BYTES_PER_KILOBYTE 1024 +#define BYTES_PER_MEGABYTE 1024 * 1024 +#define BYTES_PER_GIGABYTE 1024 * 1024 * 1024 +#define BYTES_PER_TERABYTE (uint64_t)1024 * 1024 * 1024 * 1024 + +/* + * Describes units when converting from bytes to string and back. + */ +typedef struct { + char *unit_str; + uint64_t bytes_per_unit; +} units_t; + +/* All-inclusive valid size units */ +extern units_t universal_units[]; + +/* + * Concatenates a list of strings. The result must be free()d. + * + * @param numargs + * The number of strings to concatenate. + * + * @param ... + * The strings (type char *) to concatenate. + * + * @return the concatenated string + * if succesful + * + * @return NULL + * if memory could not be allocated + */ +extern char *stralloccat(int numargs, ...); + +/* + * Convert the given string to a uint16_t, verifying that the value + * does not exceed the lower or upper bounds of a uint16_t. + * + * @param str + * the string to convert + * + * @param num + * the addr of the uint16_t + * + * @return 0 + * if the given string was converted to a uint16_t + * + * @return -1 + * if the string could could not be converted to a number + * + * @return -2 + * if the converted number exceeds the lower or upper + * bounds of a uint16_t + */ +extern int str_to_uint16(char *str, uint16_t *num); + +/* + * Converts the given long long into a string. This string must be + * freed. + * + * @param num + * the long long to convert + * + * @param str + * the addr of the string + * + * @return 0 + * if successful + * + * @return ENOMEM + * if the physical limits of the system are exceeded by + * size bytes of memory which cannot be allocated + * + * @return EAGAIN + * if there is not enough memory available to allocate + * size bytes of memory + */ +extern int ll_to_str(long long num, char **str); + +/* + * Convert a size specification to bytes. + * + * @param str + * a size specification strings of the form + * <value><units>, where valid <units> are specified by + * the units argument and <value> is the (floating-point) + * multiplier of the units + * + * @param bytes + * RETURN: the result of converting the given size string + * to bytes + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int sizestr_to_bytes(char *str, uint64_t *bytes, units_t units[]); + +/* + * Convert bytes to a size specification string. + * + * @param bytes + * the number of bytes + * + * @param str + * RETURN: a size specification strings of the form + * <value><units>, where valid <units> are specified by + * the units argument and <value> is the (floating-point) + * multiplier of the units. This string must be freed. + * + * @return 0 + * if successful + * + * @return non-zero + * if an error occurred. Use get_error_string() to + * retrieve the associated error message. + */ +extern int bytes_to_sizestr( + uint64_t bytes, char **str, units_t units[], boolean_t round); + +/* + * Appends a copy of the given string to the given string array, + * ensuring that the last element in the array is NULL. This array + * must be freed via free_string_array. + * + * Note when an error occurs and NULL is returned, array is not freed. + * Subsequently callers should save a pointer to the original array + * until success is verified. + * + * @param array + * the array to append to, or NULL to create a new array + * + * @param str + * the string to copy and add to the array + * + * @return a pointer to the realloc'd (and possibly moved) array + * if succesful + * + * @return NULL + * if unsuccesful + */ +extern char ** append_to_string_array(char **array, char *str); + +/* + * Frees each element of the given string array, then frees the array + * itself. + * + * @param array + * a NULL-terminated string array + */ +extern void free_string_array(char **array); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_STRING_H */ diff --git a/usr/src/cmd/lvm/metassist/controller/Makefile b/usr/src/cmd/lvm/metassist/controller/Makefile new file mode 100644 index 0000000000..738a74e9fb --- /dev/null +++ b/usr/src/cmd/lvm/metassist/controller/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +SRCS = getopt_ext.c metassist.c +OBJS = $(SRCS:%.c=%.o) +HDRS = $(SRCS:%.c=%.h) +MSGFILES = $(SRCS:%.c=%.i) + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +INCLUDES += -I.. -I../common -I../xml -I../layout \ + -I/usr/include/libxml2 +CFLAGS += $(INCLUDES) + +POFILE = controllerp.po + +include $(METASSIST_TOPLEVEL)/Makefile.targ + +# Build .po file from message files +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) diff --git a/usr/src/cmd/lvm/metassist/controller/getopt_ext.c b/usr/src/cmd/lvm/metassist/controller/getopt_ext.c new file mode 100644 index 0000000000..459bb9f8db --- /dev/null +++ b/usr/src/cmd/lvm/metassist/controller/getopt_ext.c @@ -0,0 +1,151 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <string.h> +#include "volume_error.h" +#include "getopt_ext.h" + +/* + * Functions + */ + +/* + * Identical to getopt(3), except that + * + * 1. If "-" is the first character of optstring, each non-option argv + * element is handled as if it were the argument of an option with + * character code GETOPT_NON_OPTION_ARG. The result is that + * GETOPT_DONE_PARSING will not be returned until the end of the + * argument list has been reached. + * + * This mirrors the functionality provided by GNU getopt. + * + * 2. GETOPT_ERR_INVALID_OPT or GETOPT_ERR_MISSING_ARG is returned + * instead of '?'. Subsequently "-?" can be used as a valid + * option. + * + * 3. GETOPT_DONE_PARSING, GETOPT_ERR_INVALID_ARG, or + * GETOPT_NON_OPTION_ARG is returned instead of -1. + * + * @param argc + * The number of arguments in the array + * + * @param argv + * The argument array + * + * @param optstring + * The option letters, with ':' following options with + * required arguments. See note about "-" as the first + * character. + * + * @return GETOPT_ERR_INVALID_OPT + * if the option is not found in optstring + * + * GETOPT_ERR_MISSING_ARG + * if the option requires an argument which is missing + * + * GETOPT_ERR_INVALID_ARG + * if "-" is not the first character in optstring and a + * non-option argument is encountered + * + * GETOPT_NON_OPTION_ARG + * if "-" is the first character in optstring and a + * non-option argument is encountered + * + * GETOPT_DONE_PARSING + * if the end of the argument list is reached + * + * <optopt> + * the option character itself, if none of the above + * scenarios applies. + */ +extern int +getopt_ext( + int argc, + char * const argv[], + const char *optstring) +{ + int c; + int handle_non_options = (*optstring == '-'); + + /* Is "-" the first character of optstring? */ + if (handle_non_options) { + /* getopt(3) doesn't understand "-" */ + optstring++; + } + + switch (c = getopt(argc, argv, optstring)) { + + /* + * getopt(3) returns -1 when 1) it encounters a non-option + * argument or 2) reaches the end of the argument list. + * Distinguish from the two possibilities. + */ + case -1: + if (optind < argc) { + optarg = argv[optind]; + + /* Non-option argument found */ + if (handle_non_options) { + /* Non-option arguments are valid */ + c = GETOPT_NON_OPTION_ARG; + optind++; + } else { + /* Non-option arguments are invalid */ + c = GETOPT_ERR_INVALID_ARG; + } + } else { + /* End of the argument list reached */ + c = GETOPT_DONE_PARSING; + } + break; + + /* + * getopt(3) returns '?' when 1) the "-?" option is + * encountered, 2) an invalid option is given or 3) a + * valid option requiring an argument is found but no + * argument is specified. Distinguish from the three + * possibilities. + */ + case '?': + /* Is this an error or was -? encountered? */ + if (optopt != '?') { + if (strchr(optstring, optopt) == NULL) { + /* Invalid option */ + c = GETOPT_ERR_INVALID_OPT; + optarg = argv[optind-1]; + } else { + /* Valid option without required argument */ + c = GETOPT_ERR_MISSING_ARG; + } + } + } + + return (c); +} diff --git a/usr/src/cmd/lvm/metassist/controller/getopt_ext.h b/usr/src/cmd/lvm/metassist/controller/getopt_ext.h new file mode 100644 index 0000000000..6b36477ce0 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/controller/getopt_ext.h @@ -0,0 +1,115 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _GETOPTEXT_H +#define _GETOPTEXT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Returned chars for getopt_ext + */ + +/* A non-option argument was found */ +#define GETOPT_NON_OPTION_ARG 1 + +/* All arguments have been parsed */ +#define GETOPT_DONE_PARSING -1 + +/* An invalid option was found */ +#define GETOPT_ERR_INVALID_OPT -2 + +/* An invalid non-option argument was found */ +#define GETOPT_ERR_INVALID_ARG -3 + +/* No argument for valid option expecting an argument */ +#define GETOPT_ERR_MISSING_ARG -4 + +/* + * Function prototypes + */ + +/* + * Identical to getopt(3), except that + * + * 1. If "-" is the first character of optstring, each non-option argv + * element is handled as if it were the argument of an option with + * character code GETOPT_NON_OPTION_ARG. The result is that + * GETOPT_DONE_PARSING will not be returned until the end of the + * argument list has been reached. + * + * This mirrors the functionality provided by GNU getopt. + * + * 2. GETOPT_ERR_INVALID_OPT or GETOPT_ERR_MISSING_ARG is returned + * instead of '?'. Subsequently "-?" can be used as a valid + * option. + * + * 3. GETOPT_DONE_PARSING, GETOPT_ERR_INVALID_ARG, or + * GETOPT_NON_OPTION_ARG is returned instead of -1. + * + * @param argc + * The number of arguments in the array + * + * @param argv + * The argument array + * + * @param optstring + * The option letters, with ':' following options with + * required arguments. See note about "-" as the first + * character. + * + * @return GETOPT_ERR_INVALID_OPT + * if the option is not found in optstring + * + * GETOPT_ERR_MISSING_ARG + * if the option requires an argument which is missing + * + * GETOPT_ERR_INVALID_ARG + * if "-" is not the first character in optstring and a + * non-option argument is encountered + * + * GETOPT_NON_OPTION_ARG + * if "-" is the first character in optstring and a + * non-option argument is encountered + * + * GETOPT_DONE_PARSING + * if the end of the argument list is reached + * + * <optopt> + * the option character itself, if none of the above + * scenarios applies. + */ +extern int getopt_ext(int argc, char * const argv[], const char *optstring); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPTEXT_H */ diff --git a/usr/src/cmd/lvm/metassist/controller/metassist.c b/usr/src/cmd/lvm/metassist/controller/metassist.c new file mode 100644 index 0000000000..aeb9655937 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/controller/metassist.c @@ -0,0 +1,1511 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Front end CLI to metassist. Parses command line, reads in data + * files, provides main() entry point into metassist. Here's the + * complete data validation stack for the project: + * + * 1. Controller validates command line syntax/order of arguments. + * + * 2. XML parser validates XML syntax, conformance with DTD + * + * 3. xml_convert validates proper conversion from string to + * size/integer/float/boolean/etc. + * + * 4. devconfig_t mutators validate limits/boundaries/min/max/names of + * data. References md_mdiox.h and possibly libmeta. + * + * 5. layout validates on remaining issues, including existence of + * given devices, feasibility of request, suitability of specified + * components, and subtle misuse of data structure (like both size + * and components specified). + */ + +#include "metassist.h" + +#include <errno.h> +#include <libintl.h> + +#include <math.h> +#include <signal.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sys/wait.h> +#include <unistd.h> +#include "getopt_ext.h" +#include "locale.h" +#include "volume_error.h" +#include "volume_output.h" +#include "volume_request.h" +#include "volume_defaults.h" +#include "volume_string.h" +#include "xml_convert.h" +#include "layout.h" + +/* + * Function prototypes + */ + +static void clean_up(); +static void interrupthandler(int x); +static int copy_arg(char *option, char *value, char **saveto); +static xmlDocPtr create_volume_request_XML(); +static int handle_common_opts(int c, boolean_t *handled); +static int parse_create_opts(int argc, char *argv[]); +static int parse_opts(int argc, char *argv[]); +static int parse_tokenized_list(const char *string, dlist_t **list); +static int parse_verbose_arg(char *arg, int *verbosity); +static void print_help_create(FILE *stream); +static void print_help_main(FILE *stream); +static void print_manual_reference(FILE *stream); +static void print_usage(FILE *stream); +static void print_usage_create(FILE *stream); +static void print_usage_main(FILE *stream); +static int print_version(FILE *stream); +static int get_doc_from_file( + char *file, char **valid_types, xmlDocPtr *doc, char **root); +static int get_volume_request_or_config(xmlDocPtr *doc, char **root); +static int handle_commands(char *commands); +static int handle_config(devconfig_t *config); +static int handle_request(request_t *request, defaults_t *defaults); +static int write_temp_file(char *text, mode_t mode, char **file); + +/* + * Data + */ + +/* Holds argv[0] */ +char *progname; + +/* The action to take */ +int action = ACTION_EXECUTE; + +/* Holds the name of the temporary command file */ +char *commandfile = NULL; + +/* The metassist subcommand */ +int subcmd = SUBCMD_NONE; + +/* The volume-request XML file to read */ +char *arg_inputfile = NULL; + +/* The size of the requested volume */ +char *arg_size = NULL; + +/* The disk set to use */ +char *arg_diskset = NULL; + +/* The volume name to use */ +char *arg_name = NULL; + +/* Redundancy level */ +char *arg_redundancy = NULL; + +/* Number of datapaths */ +char *arg_datapaths = NULL; + +/* Whether to implement fault recovery */ +boolean_t faultrecovery = B_FALSE; + +/* Whether to output the config file */ +boolean_t output_configfile = B_FALSE; + +/* Whether to output the command file instead of */ +boolean_t output_commandfile = B_FALSE; + +/* List of available devices */ +dlist_t *available = NULL; + +/* List of unavailable devices */ +dlist_t *unavailable = NULL; + +/* + * Functions + */ + +/* + * Frees alloc'd memory, to be called prior to exiting. + */ +static void +clean_up() +{ + /* Remove temporary command file */ + if (commandfile != NULL) { + /* Ignore failure */ + unlink(commandfile); + } + + /* Free allocated argument strings */ + if (commandfile != NULL) free(commandfile); + if (arg_diskset != NULL) free(arg_diskset); + if (arg_name != NULL) free(arg_name); + if (arg_inputfile != NULL) free(arg_inputfile); + + /* Free available dlist and strings within */ + dlist_free_items(available, free); + + /* Free unavailable dlist and strings within */ + dlist_free_items(unavailable, free); + + /* Clean up XML data structures */ + cleanup_xml(); +} + +/* + * Signal handler, called to exit gracefully + */ +static void +interrupthandler( + int sig) +{ + char sigstr[SIG2STR_MAX]; + + if (sig2str(sig, sigstr) != 0) { + sigstr[0] = '\0'; + } + + fprintf(stderr, + gettext("Signal %d (%s) caught -- exiting...\n"), sig, sigstr); + + /* Allow layout to cleanup on abnormal exit */ + layout_clean_up(); + + clean_up(); + exit(1); +} + +/* + * Copies and saves the given argument, verifying that the argument + * has not already been saved. + * + * @param option + * The flag preceding or type of the argument. Used only + * in the error message when an option has already been + * saved to *saveto. + * + * @param value + * The argument to be copied. + * + * @param saveto + * Changed to point to the copied data. This must point + * to NULL data initially, or it will be assumed that + * this argument has already been set. This memory must + * be free()d by the caller. + * + * @return 0 on success, non-zero otherwise. + */ +static int +copy_arg( + char *option, + char *value, + char **saveto) +{ + int error = 0; + + /* Has this string already been set? */ + if (*saveto != NULL) { + volume_set_error( + gettext("%s: option specified multiple times"), option); + error = -1; + } else + + if ((*saveto = strdup(value)) == NULL) { + error = ENOMEM; + } + + return (error); +} + +/* + * Generates the XML volume request corresponding to the command-line + * parameters. No DTD node is included in this request. + * + * @return The XML request, or NULL if an error ocurred in + * generating the text. This memory must be freed with + * XMLFree(). + */ +static xmlDocPtr +create_volume_request_XML() +{ + xmlDocPtr doc; + xmlNodePtr request, volume; + + /* Create the XML document */ + doc = xmlNewDoc((xmlChar *)"1.0"); + + /* Create the root node */ + request = xmlNewDocNode( + doc, NULL, (xmlChar *)ELEMENT_VOLUMEREQUEST, NULL); + xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)request); + + /* diskset element */ + if (arg_diskset != NULL) { + xmlNodePtr node = xmlNewChild( + request, NULL, (xmlChar *)ELEMENT_DISKSET, NULL); + xmlSetProp(node, + (xmlChar *)ATTR_NAME, (xmlChar *)arg_diskset); + } + + /* available elements */ + if (available != NULL) { + dlist_t *item; + for (item = available; item != NULL; item = item->next) { + xmlNodePtr node = xmlNewChild( + request, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL); + xmlSetProp(node, + (xmlChar *)ATTR_NAME, (xmlChar *)item->obj); + } + } + + /* unavailable elements */ + if (unavailable != NULL) { + dlist_t *item; + for (item = unavailable; item != NULL; item = item->next) { + xmlNodePtr node = xmlNewChild( + request, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL); + xmlSetProp(node, + (xmlChar *)ATTR_NAME, (xmlChar *)item->obj); + } + } + + /* volume element */ + volume = xmlNewChild(request, NULL, (xmlChar *)ELEMENT_VOLUME, NULL); + + /* Volume name - optional */ + if (arg_name != NULL) { + xmlSetProp(volume, + (xmlChar *)ATTR_NAME, (xmlChar *)arg_name); + } + + /* Volume size - required */ + xmlSetProp(volume, (xmlChar *)ATTR_SIZEINBYTES, (xmlChar *)arg_size); + + /* Volume redundancy - optional */ + if (arg_redundancy != NULL) { + xmlSetProp(volume, + (xmlChar *)ATTR_VOLUME_REDUNDANCY, (xmlChar *)arg_redundancy); + } + + /* Volume fault recovery - optional */ + if (faultrecovery == B_TRUE) { + xmlSetProp(volume, + (xmlChar *)ATTR_VOLUME_FAULTRECOVERY, (xmlChar *)"TRUE"); + } + + /* Volume datapaths - optional */ + if (arg_datapaths != NULL) { + xmlSetProp(volume, + (xmlChar *)ATTR_VOLUME_DATAPATHS, (xmlChar *)arg_datapaths); + } + + if (get_max_verbosity() >= OUTPUT_DEBUG) { + xmlChar *text; + /* Get the text dump */ + xmlDocDumpFormatMemory(doc, &text, NULL, 1); + oprintf(OUTPUT_DEBUG, + gettext("Generated volume-request:\n%s"), text); + xmlFree(text); + } + + return (doc); +} + +/* + * Checks the given flag for options common to all subcommands. + * + * @param c + * The option letter. + * + * @param handled + * RETURN: whether the given option flag was handled. + * + * @return Non-zero if an error occurred or the given option was + * invalid or incomplete, 0 otherwise. + */ +static int +handle_common_opts( + int c, + boolean_t *handled) +{ + int error = 0; + + /* Level of verbosity to report */ + int verbosity; + + *handled = B_TRUE; + + switch (c) { + case COMMON_SHORTOPT_VERBOSITY: + if ((error = parse_verbose_arg(optarg, &verbosity)) == 0) { + set_max_verbosity(verbosity, stderr); + } + break; + + case COMMON_SHORTOPT_VERSION: + if ((error = print_version(stdout)) == 0) { + clean_up(); + exit(0); + } + break; + + case GETOPT_ERR_MISSING_ARG: + volume_set_error( + gettext("option missing a required argument: -%c"), optopt); + error = -1; + break; + + case GETOPT_ERR_INVALID_OPT: + volume_set_error(gettext("invalid option: -%c"), optopt); + error = -1; + break; + + case GETOPT_ERR_INVALID_ARG: + volume_set_error(gettext("invalid argument: %s"), optarg); + error = -1; + break; + + default: + *handled = B_FALSE; + } + + return (error); +} + +/* + * Parse the command line options for the create subcommand. + * + * @param argc + * The number of arguments in the array + * + * @param argv + * The argument array + */ +static int +parse_create_opts( + int argc, + char *argv[]) +{ + int c; + int error = 0; + + /* + * Whether a volume request is specified on the command line + * (vs. a inputfile) + */ + boolean_t request_on_command_line = B_FALSE; + + /* Examine next arg */ + while (!error && (c = getopt_ext( + argc, argv, CREATE_SHORTOPTS)) != GETOPT_DONE_PARSING) { + + boolean_t handled; + + /* Check for args common to all scopes */ + error = handle_common_opts(c, &handled); + if (error == 0 && handled == B_FALSE) { + + /* Check for args specific to this scope */ + switch (c) { + + /* Help */ + case COMMON_SHORTOPT_HELP: + print_help_create(stdout); + clean_up(); + exit(0); + break; + + /* Config file */ + case CREATE_SHORTOPT_CONFIGFILE: + action &= ~ACTION_EXECUTE; + action |= ACTION_OUTPUT_CONFIG; + break; + + /* Command file */ + case CREATE_SHORTOPT_COMMANDFILE: + action &= ~ACTION_EXECUTE; + action |= ACTION_OUTPUT_COMMANDS; + break; + + /* Disk set */ + case CREATE_SHORTOPT_DISKSET: + error = copy_arg( + argv[optind - 2], optarg, &arg_diskset); + request_on_command_line = B_TRUE; + break; + + /* Name */ + case CREATE_SHORTOPT_NAME: + error = copy_arg( + argv[optind - 2], optarg, &arg_name); + request_on_command_line = B_TRUE; + break; + + /* Redundancy */ + case CREATE_SHORTOPT_REDUNDANCY: + error = copy_arg( + argv[optind - 2], optarg, &arg_redundancy); + request_on_command_line = B_TRUE; + break; + + /* Data paths */ + case CREATE_SHORTOPT_DATAPATHS: + error = copy_arg( + argv[optind - 2], optarg, &arg_datapaths); + request_on_command_line = B_TRUE; + break; + + /* Fault recovery */ + case CREATE_SHORTOPT_FAULTRECOVERY: + faultrecovery = B_TRUE; + request_on_command_line = B_TRUE; + break; + + /* Available devices */ + case CREATE_SHORTOPT_AVAILABLE: + error = parse_tokenized_list(optarg, &available); + request_on_command_line = B_TRUE; + break; + + /* Unavailable devices */ + case CREATE_SHORTOPT_UNAVAILABLE: + error = parse_tokenized_list(optarg, &unavailable); + request_on_command_line = B_TRUE; + break; + + /* Size */ + case CREATE_SHORTOPT_SIZE: + request_on_command_line = B_TRUE; + error = copy_arg( + argv[optind - 1], optarg, &arg_size); + break; + + /* Input file */ + case CREATE_SHORTOPT_INPUTFILE: + error = copy_arg(gettext("request/configuration file"), + optarg, &arg_inputfile); + break; + + default: + /* Shouldn't be here! */ + volume_set_error( + gettext("unexpected option: %c (%d)"), c, c); + error = -1; + } + } + } + + /* + * Now that the arguments have been parsed, verify that + * required options were specified. + */ + if (!error) { + /* Third invocation method -- two required arguments */ + if (request_on_command_line == B_TRUE) { + if (arg_inputfile != NULL) { + volume_set_error( + gettext("invalid option(s) specified with input file")); + error = -1; + } else + + if (arg_size == NULL) { + volume_set_error(gettext("no size specified")); + error = -1; + } else + + if (arg_diskset == NULL) { + volume_set_error(gettext("no disk set specified")); + error = -1; + } + } else + + /* First or second invocation method -- one required argument */ + if (arg_inputfile == NULL) { + volume_set_error(gettext("missing required arguments")); + error = -1; + } + + /* + * The CREATE_SHORTOPT_CONFIGFILE and + * CREATE_SHORTOPT_COMMANDFILE arguments are mutually + * exclusive. Verify that these were not both specified. + */ + if (!error && + action & ACTION_OUTPUT_CONFIG && + action & ACTION_OUTPUT_COMMANDS) { + volume_set_error( + gettext("-%c and -%c are mutually exclusive"), + CREATE_SHORTOPT_CONFIGFILE, + CREATE_SHORTOPT_COMMANDFILE); + error = -1; + } + } + + return (error); +} + +/* + * Parse the main command line options. + * + * @param argc + * The number of arguments in the array + * + * @param argv + * The argument array + * + * @return 0 on success, non-zero otherwise. + */ +static int +parse_opts( + int argc, + char *argv[]) +{ + int c; + int error = 0; + + /* Examine next arg */ + while (!error && (c = getopt_ext( + argc, argv, MAIN_SHORTOPTS)) != GETOPT_DONE_PARSING) { + + boolean_t handled; + + /* Check for args common to all scopes */ + error = handle_common_opts(c, &handled); + + if (error == 0 && handled == B_FALSE) { + + /* Check for args specific to this scope */ + switch (c) { + + /* Help */ + case COMMON_SHORTOPT_HELP: + print_help_main(stdout); + clean_up(); + exit(0); + break; + + /* Non-option arg */ + case GETOPT_NON_OPTION_ARG: + + /* See if non-option arg is subcommand */ + if (strcmp(optarg, MAIN_SUBCMD_CREATE) == 0) { + subcmd = SUBCMD_CREATE; + error = parse_create_opts(argc, argv); + } else { + /* Argument not recognized */ + volume_set_error( + gettext("%s: invalid argument"), optarg); + error = -1; + } + break; + + default: + /* Shouldn't be here! */ + volume_set_error( + gettext("unexpected option: %c (%d)"), c, c); + error = -1; + } + } else + + /* + * Check invalid arguments to see if they are valid + * options out of place. + * + * NOTE: IN THE FUTURE, A CODE BLOCK SIMILAR TO THIS + * ONE SHOULD BE ADDED FOR EACH NEW SUBCOMMAND. + */ + if (c == GETOPT_ERR_INVALID_OPT && + strchr(CREATE_SHORTOPTS, optopt) != NULL) { + /* Provide a more enlightening error message */ + volume_set_error( + gettext("-%c specified before create subcommand"), optopt); + } + } + + /* Parsing appears to be successful */ + if (!error) { + + /* Was a subcommand specified? */ + if (subcmd == SUBCMD_NONE) { + volume_set_error(gettext("no subcommand specified")); + error = -1; + } + } + + return (error); +} + +/* + * Convert a string containing a comma/space-separated list into a + * dlist. + * + * @param string + * a comma/space-separated list + * + * @param list + * An exisiting dlist to append to, or NULL to create a + * new list. + * + * @return The head node of the dlist_t, whether it was newly + * created or passed in. On memory allocation error, + * errno will be set and processing will stop. + */ +static int +parse_tokenized_list( + const char *string, + dlist_t **list) +{ + char *stringdup; + char *device; + char *dup; + dlist_t *item; + int error = 0; + + /* Don't let strtok alter original argument */ + if ((stringdup = strdup(string)) == NULL) { + error = ENOMEM; + } else { + + /* For each device in the string list... */ + while ((device = strtok(stringdup, DEVICELISTDELIM)) != NULL) { + + /* Duplicate the device string */ + if ((dup = strdup(device)) == NULL) { + error = ENOMEM; + break; + } + + /* Create new dlist_t for this device */ + if ((item = dlist_new_item((void *)dup)) == NULL) { + error = ENOMEM; + free(dup); + break; + } + + /* Append item to list */ + *list = dlist_append(item, *list, B_TRUE); + + /* strtok needs NULL pointer on subsequent calls */ + stringdup = NULL; + } + + free(stringdup); + } + + return (error); +} + +/* + * Parses the given verbosity level argument string. + * + * @param arg + * A string representation of a verbosity level + * + * @param verbosity + * RETURN: the verbosity level + * + * @return 0 if the given verbosity level string cannot + * be interpreted, non-zero otherwise + */ +static int +parse_verbose_arg( + char *arg, + int *verbosity) +{ + int level; + + /* Scan for int */ + if (sscanf(arg, "%d", &level) == 1) { + + /* Argument was an integer */ + switch (level) { + case OUTPUT_QUIET: + case OUTPUT_TERSE: + case OUTPUT_VERBOSE: +#ifdef DEBUG + case OUTPUT_DEBUG: +#endif + + *verbosity = level; + return (0); + } + } + + volume_set_error(gettext("%s: invalid verbosity level"), arg); + return (-1); +} + +/* + * Print the help message for the command. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_help_create( + FILE *stream) +{ + print_usage_create(stream); + + /* BEGIN CSTYLED */ + fprintf(stream, gettext("\ +\n\ +Create Solaris Volume Manager volumes.\n\ +\n\ +-F <inputfile>\n\ + Specify the volume request or volume configuration file to\n\ + process.\n\ +\n\ +-s <set>\n\ + Specify the disk set to use when creating volumes.\n\ +\n\ +-S <size>\n\ + Specify the size of the volume to be created.\n\ +\n\ +-a <device1,device2,...>\n\ + Explicitly specify the devices that can be used in the\n\ + creation of this volume.\n\ +\n\ +-c Output the command script that would implement the specified or\n\ + generated volume configuration.\n\ +\n\ +-d Output the volume configuration that satisfies the specified or\n\ + generated volume request.\n\ +\n\ +-f Specify whether the volume should support automatic component\n\ + replacement after a fault.\n\ +\n\ +-n <name>\n\ + Specify the name of the new volume.\n\ +\n\ +-p <n>\n\ + Specify the number of required paths to the storage volume.\n\ +\n\ +-r <n>\n\ + Specify the redundancy level (0-4) of the data.\n\ +\n\ +-u <device1,device2,...>\n\ + Explicitly specify devices to exclude in the creation of this\n\ + volume.\n\ +\n\ +-v <value>\n\ + Specify the level of verbosity.\n\ +\n\ +-V Display program version information.\n\ +\n\ +-? Display help information.\n")); + + /* END CSTYLED */ + + print_manual_reference(stream); +} + +/* + * Print the help message for the command. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_help_main( + FILE *stream) +{ + print_usage_main(stream); + + /* BEGIN CSTYLED */ + fprintf(stream, gettext("\ +\n\ +Provide assistance, through automation, with common Solaris Volume\n\ +Manager tasks.\n\ +\n\ +-V Display program version information.\n\ +\n\ +-? Display help information. This option can follow <subcommand>\n\ + for subcommand-specific help.\n\ +\n\ +The accepted values for <subcommand> are:\n\ +\n\ +create Create Solaris Volume Manager volumes.\n")); + /* END CSTYLED */ + + print_manual_reference(stream); +} + +/* + * Print the help postscript for the command. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_manual_reference( + FILE *stream) +{ + fprintf(stream, gettext("\nFor more information, see %s(1M).\n"), + progname); +} + +/* + * Print the program usage to the given file stream. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_usage( + FILE *stream) +{ + switch (subcmd) { + case SUBCMD_CREATE: + print_usage_create(stream); + break; + + case SUBCMD_NONE: + default: + print_usage_main(stream); + } +} + +/* + * Print the program usage to the given file stream. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_usage_create( + FILE *stream) +{ + /* Create a blank the length of progname */ + char *blank = strdup(progname); + memset(blank, ' ', strlen(blank) * sizeof (char)); + + /* BEGIN CSTYLED */ + fprintf(stream, gettext("\ +Usage: %1$s create [-v <n>] [-c] -F <configfile>\n\ + %1$s create [-v <n>] [-c|-d] -F <requestfile>\n\ + %1$s create [-v <n>] [-c|-d]\n\ + %2$s [-f] [-n <name>] [-p <datapaths>] [-r <redundancy>]\n\ + %2$s [-a <available>[,<available>,...]]\n\ + %2$s [-u <unavailable>[,<unavailable>,...]]\n\ + %2$s -s <setname> -S <size>\n\ + %1$s create -V\n\ + %1$s create -?\n"), progname, blank); + /* END CSTYLED */ + + free(blank); +} + +/* + * Print the program usage to the given file stream. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static void +print_usage_main( + FILE *stream) +{ + /* BEGIN CSTYLED */ + fprintf(stream, gettext("\ +Usage: %1$s <subcommand> [-?] [options]\n\ + %1$s -V\n\ + %1$s -?\n"), progname); + /* END CSTYLED */ +} + +/* + * Print the program version to the given file stream. + * + * @param stream + * stdout or stderr, as appropriate. + */ +static int +print_version( + FILE *stream) +{ + int error = 0; + struct utsname uname_info; + + if (uname(&uname_info) < 0) { + error = -1; + volume_set_error(gettext("could not determine version")); + } else { + fprintf(stream, gettext("%s %s"), progname, uname_info.version); + } + + fprintf(stream, "\n"); + + return (error); +} + +/* + * Get an xmlDocPtr by parsing the given file. + * + * @param file + * The file to read + * + * @param valid_types + * An array of the allowable root elements. If the root + * element of the parsed XML file is not in this list, an + * error is returned. + * + * @param doc + * RETURN: the XML document + * + * @param root + * RETURN: the root element of the document + * + * @return 0 if the given XML file was successfully parsed, + * non-zero otherwise + */ +static int +get_doc_from_file( + char *file, + char **valid_types, + xmlDocPtr *doc, + char **root) +{ + int error = 0; + + *root = NULL; + + /* + * Create XML doc by reading the specified file using the + * default SAX handler (which has been modified in init_xml()) + */ + *doc = xmlSAXParseFile((xmlSAXHandlerPtr) + &xmlDefaultSAXHandler, file, 0); + + if (*doc != NULL) { + int i; + xmlNodePtr root_elem = xmlDocGetRootElement(*doc); + + /* Is this a valid root element? */ + for (i = 0; valid_types[i] != NULL; i++) { + if (xmlStrcmp(root_elem->name, + (const xmlChar *)valid_types[i]) == 0) { + *root = valid_types[i]; + } + } + + /* Was a valid root element found? */ + if (*root == NULL) { + xmlFreeDoc(*doc); + } + } + + /* Was a valid root element found? */ + if (*root == NULL) { + volume_set_error( + gettext("%s: invalid or malformed XML file"), file); + error = -1; + } + + return (error); +} + +/* + * Creates a volume-request or volume-config XML document, based on the + * arguments passed into the command. + * + * @param doc + * RETURN: the XML document, or NULL if no valid document + * could be created. + * + * @param root + * RETURN: the root element of the document + * + * @return 0 if a volume-request or volume-config XML document + * could be read or created, non-zero otherwise + */ +static int +get_volume_request_or_config( + xmlDocPtr *doc, + char **root) +{ + int error = 0; + + if (arg_inputfile == NULL) { + /* Create a volume-request based on quality of service */ + *doc = create_volume_request_XML(); + + if (*doc == NULL) { + volume_set_error(gettext("error creating volume request")); + error = -1; + *root = NULL; + } else { + *root = ELEMENT_VOLUMEREQUEST; + } + } else { + char *valid[] = { + ELEMENT_VOLUMEREQUEST, + ELEMENT_VOLUMECONFIG, + NULL + }; + + error = get_doc_from_file(arg_inputfile, valid, doc, root); + } + + return (error); +} + +/* + * Handle processing of the given meta* commands. Commands are + * written to a file, the file is optionally executed, and optionally + * deleted. + * + * @param commands + * The commands to write to the command script file. + * + * @return 0 on success, non-zero otherwise. + */ +static int +handle_commands( + char *commands) +{ + int error = 0; + + if (action & ACTION_OUTPUT_COMMANDS) { + printf("%s", commands); + } + + if (action & ACTION_EXECUTE) { + + /* Write a temporary file with 744 permissions */ + if ((error = write_temp_file(commands, + S_IRWXU | S_IRGRP | S_IROTH, &commandfile)) == 0) { + + char *command; + + /* Create command line to execute */ + if (get_max_verbosity() >= OUTPUT_VERBOSE) { + /* Verbose */ + command = stralloccat(3, + commandfile, " ", COMMAND_VERBOSE_FLAG); + } else { + /* Terse */ + command = strdup(commandfile); + } + + if (command == NULL) { + volume_set_error(gettext("could not allocate memory")); + error = -1; + } else { + + oprintf(OUTPUT_VERBOSE, + gettext("Executing command script: %s\n"), command); + + /* Execute command */ + switch (error = system(command)) { + /* system() failed */ + case -1: + error = errno; + break; + + /* Command succeded */ + case 0: + break; + + /* Command failed */ + default: + volume_set_error( + /* CSTYLED */ + gettext("execution of command script failed with status %d"), + WEXITSTATUS(error)); + error = -1; + } + free(command); + } + } + } + + return (error); +} + +/* + * Handle processing of the given volume-config devconfig_t. The + * devconfig_t is first converted to XML. Then, depending + * on user input to the command, the XML is either written to a file + * or converted to a command script and passed on to + * handle_commands(). + * + * @param config + * A devconfig_t representing a valid volume-config. + * + * @return 0 on success, non-zero otherwise. + */ +static int +handle_config( + devconfig_t *config) +{ + int error; + xmlDocPtr doc; + + /* Get the xml document for the config */ + if ((error = config_to_xml(config, &doc)) == 0) { + + /* Get the text dump */ + xmlChar *text; + xmlDocDumpFormatMemory(doc, &text, NULL, 1); + + /* Should we output the config file? */ + if (action & ACTION_OUTPUT_CONFIG) { + printf("%s", text); + } else { + oprintf(OUTPUT_DEBUG, + gettext("Generated volume-config:\n%s"), text); + } + + xmlFree(text); + + /* Proceed to command generation? */ + if (action & ACTION_OUTPUT_COMMANDS || + action & ACTION_EXECUTE) { + char *commands; + + /* Get command script from the file */ + if ((error = xml_to_commands(doc, &commands)) == 0) { + if (commands == NULL) { + volume_set_error( + gettext("could not convert XML to commands")); + error = -1; + } else { + error = handle_commands(commands); + free(commands); + } + } + } + + xmlFreeDoc(doc); + } + + return (error); +} + +/* + * Handle processing of the given volume-request request_t and + * volume-defaults defaults_t. A layout is generated from these + * structures and the resulting volume-config devconfig_t is passed on + * to handle_config(). + * + * @param request + * A request_t representing a valid volume-request. + * + * @param defaults + * A defaults_t representing a valid volume-defaults. + * + * @return 0 on success, non-zero otherwise. + */ +static int +handle_request( + request_t *request, + defaults_t *defaults) +{ + int error; + + /* Get layout for given request and system defaults */ + if ((error = get_layout(request, defaults)) == 0) { + + /* Retrieve resulting volume config */ + devconfig_t *config = request_get_diskset_config(request); + + if (config != NULL) { + error = handle_config(config); + } + } + + return (error); +} + +/* + * Write the given text to a temporary file with the given + * permissions. If the file already exists, return an error. + * + * @param text + * The text to write to the file. + * + * @param mode + * The permissions to give the file, passed to chmod(2). + * + * @param file + * RETURN: The name of the file written. Must be + * free()d. + * + * @return 0 on success, non-zero otherwise. + */ +static int +write_temp_file( + char *text, + mode_t mode, + char **file) +{ + int error = 0; + + /* + * Create temporary file name -- "XXXXXX" is replaced with + * unique char sequence by mkstemp() + */ + *file = stralloccat(3, "/tmp/", progname, "XXXXXX"); + + if (*file == NULL) { + volume_set_error(gettext("out of memory")); + error = -1; + } else { + int fildes; + FILE *out = NULL; + + /* Open temp file */ + if ((fildes = mkstemp(*file)) != -1) { + out = fdopen(fildes, "w"); + } + + if (out == NULL) { + volume_set_error(gettext( + "could not open file for writing: %s"), *file); + error = -1; + } else { + + fprintf(out, "%s", text); + fclose(out); + + if (mode != 0) { + if (chmod(*file, mode)) { + volume_set_error( + gettext("could not change permissions of file: %s"), + *file); + error = -1; + } + } + + /* Remove file on error */ + if (error != 0) { + unlink(*file); + } + } + + /* Free *file on error */ + if (error != 0) { + free(*file); + *file = NULL; + } + } + + return (error); +} + +/* + * Main entry to metassist. See the print_usage_* functions* for + * usage. + * + * @return 0 on successful exit, non-zero otherwise + */ +int +main( + int argc, + char *argv[]) +{ + int error = 0; + int printusage = 0; + +#ifdef DEBUG + time_t start = time(NULL); +#endif + + /* + * Get the locale set up before calling any other routines + * with messages to ouput. Just in case we're not in a build + * environment, make sure that TEXT_DOMAIN gets set to + * something. + */ +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + /* Set program name, strip directory */ + if ((progname = strrchr(argv[0], '/')) != NULL) { + progname++; + } else { + progname = argv[0]; + } + + /* Set up signal handlers to exit gracefully */ + { + struct sigaction act; + act.sa_handler = interrupthandler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGHUP, &act, (struct sigaction *)0); + sigaction(SIGINT, &act, (struct sigaction *)0); + sigaction(SIGQUIT, &act, (struct sigaction *)0); + sigaction(SIGTERM, &act, (struct sigaction *)0); + } + + /* Set default verbosity level */ + set_max_verbosity(OUTPUT_TERSE, stderr); + + /* Verify we're running as root */ + if (geteuid() != 0) { + volume_set_error(gettext("must be run as root")); + error = -1; + } else { + + /* Disable error messages from getopt */ + opterr = 0; + + /* Parse command-line options */ + if ((error = parse_opts(argc, argv)) == 0) { + xmlDocPtr doc; + char *root; + + /* Initialize XML defaults */ + init_xml(); + + /* Read volume-request/config file */ + if ((error = get_volume_request_or_config(&doc, &root)) == 0) { + + /* Is this a volume-config? */ + if (strcmp(root, ELEMENT_VOLUMECONFIG) == 0) { + + /* Was the -d flag specified? */ + if (action & ACTION_OUTPUT_CONFIG) { + /* -d cannot be used with -F <configfile> */ + volume_set_error(gettext( + "-%c incompatible with -%c <configfile>"), + CREATE_SHORTOPT_CONFIGFILE, + CREATE_SHORTOPT_INPUTFILE); + error = -1; + printusage = 1; + } else { + devconfig_t *config; + if ((error = xml_to_config(doc, &config)) == 0) { + error = handle_config(config); + free_devconfig(config); + } + } + } else + + /* Is this a volume-request? */ + if (strcmp(root, ELEMENT_VOLUMEREQUEST) == 0) { + request_t *request; + + if ((error = xml_to_request(doc, &request)) == 0) { + + xmlDocPtr defaults_doc; + char *valid[] = { + ELEMENT_VOLUMEDEFAULTS, + NULL + }; + + /* Read defaults file */ + if ((error = get_doc_from_file(VOLUME_DEFAULTS_LOC, + valid, &defaults_doc, &root)) == 0) { + + defaults_t *defaults; + + oprintf(OUTPUT_DEBUG, + gettext("Using defaults file: %s\n"), + VOLUME_DEFAULTS_LOC); + + /* Parse defaults XML */ + if ((error = xml_to_defaults( + defaults_doc, &defaults)) == 0) { + error = handle_request(request, defaults); + free_defaults(defaults); + } + + xmlFreeDoc(defaults_doc); + } + + free_request(request); + } + } + + xmlFreeDoc(doc); + } + } else { + printusage = 1; + } + } + + /* Handle any errors that were propogated */ + if (error != 0) { + char *message = get_error_string(error); + + if (message != NULL && strlen(message)) { + fprintf(stderr, "%s: %s\n", progname, message); + + if (printusage) { + fprintf(stderr, "\n"); + } + } + + if (printusage) { + print_usage(stderr); + } + } + +#ifdef DEBUG + /* Print run report to stderr if METASSIST_DEBUG is set */ + if (getenv(METASSIST_DEBUG_ENV) != NULL) { + time_t end = time(NULL); + struct tm *time; + int i; +#define TIMEFMT "%8s: %.2d:%.2d:%.2d\n" + + fprintf(stderr, " Command:"); + for (i = 0; i < argc; i++) { + fprintf(stderr, " %s", argv[i]); + } + fprintf(stderr, "\n"); + + fprintf(stderr, " Version: "); + print_version(stderr); + + time = localtime(&start); + fprintf(stderr, TIMEFMT, "Start", + time->tm_hour, time->tm_min, time->tm_sec); + + time = localtime(&end); + fprintf(stderr, TIMEFMT, "End", + time->tm_hour, time->tm_min, time->tm_sec); + + end -= start; + time = gmtime(&end); + fprintf(stderr, TIMEFMT, "Duration", + time->tm_hour, time->tm_min, time->tm_sec); + } +#endif + + clean_up(); + + return (error != 0); +} diff --git a/usr/src/cmd/lvm/metassist/controller/metassist.h b/usr/src/cmd/lvm/metassist/controller/metassist.h new file mode 100644 index 0000000000..d107c093bb --- /dev/null +++ b/usr/src/cmd/lvm/metassist/controller/metassist.h @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _METASSIST_H +#define _METASSIST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Location of the volume-defaults.xml file */ +#define VOLUME_DEFAULTS_LOC "/etc/default/metassist.xml" + +/* Available/unavailable device list delimiters */ +#define DEVICELISTDELIM ", " + +/* Command-line arguments */ +#define COMMON_SHORTOPT_HELP '?' +#define COMMON_SHORTOPT_VERBOSITY 'v' +#define COMMON_SHORTOPT_VERSION 'V' +#define CREATE_SHORTOPT_AVAILABLE 'a' +#define CREATE_SHORTOPT_COMMANDFILE 'c' +#define CREATE_SHORTOPT_DATAPATHS 'p' +#define CREATE_SHORTOPT_DISKSET 's' +#define CREATE_SHORTOPT_FAULTRECOVERY 'f' +#define CREATE_SHORTOPT_INPUTFILE 'F' +#define CREATE_SHORTOPT_NAME 'n' +#define CREATE_SHORTOPT_REDUNDANCY 'r' +#define CREATE_SHORTOPT_SIZE 'S' +#define CREATE_SHORTOPT_CONFIGFILE 'd' +#define CREATE_SHORTOPT_UNAVAILABLE 'u' +#define CREATE_SHORTOPTS "a:cdfF:n:p:r:s:S:u:v:V?" +#define MAIN_SHORTOPTS "-v:V?" +#define MAIN_SUBCMD_CREATE "create" + +#define SUBCMD_NONE 0 +#define SUBCMD_CREATE 1 + +/* Command action masks */ +#define ACTION_EXECUTE 1 +#define ACTION_OUTPUT_CONFIG 2 +#define ACTION_OUTPUT_COMMANDS 4 + +/* Verbose flag sent to generated shell script */ +#define COMMAND_VERBOSE_FLAG "-v" + +/* The name used to invoke the command */ +extern char *progname; + +#ifdef __cplusplus +} +#endif + +#endif /* _METASSIST_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/Makefile b/usr/src/cmd/lvm/metassist/layout/Makefile new file mode 100644 index 0000000000..6f02f010f8 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/Makefile @@ -0,0 +1,64 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +SRCS= \ + layout.c \ + layout_concat.c \ + layout_device_cache.c \ + layout_device_util.c \ + layout_discovery.c \ + layout_dlist_util.c \ + layout_hsp.c \ + layout_messages.c \ + layout_mirror.c \ + layout_request.c \ + layout_slice.c \ + layout_stripe.c \ + layout_svm_util.c \ + layout_validate.c + +OBJS = $(SRCS:%.c=%.o) +HDRS = $(SRCS:%.c=%.h) +MSGFILES = $(SRCS:%.c=%.i) + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +INCLUDES += -I../common -I../controller -I../../../../lib/libdiskmgt/common \ + -I../../../../lib/lvm/libmeta/common/hdrs +CFLAGS += $(INCLUDES) + +POFILE = layoutp.po + +include $(METASSIST_TOPLEVEL)/Makefile.targ + +# Build .po file from message files +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) diff --git a/usr/src/cmd/lvm/metassist/layout/layout.c b/usr/src/cmd/lvm/metassist/layout/layout.c new file mode 100644 index 0000000000..3a432dedad --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout.c @@ -0,0 +1,1142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <string.h> +#include <libintl.h> + +#include "volume_error.h" +#include "volume_defaults.h" +#include "volume_dlist.h" +#include "volume_output.h" +#include "volume_request.h" + +#include "layout.h" +#include "layout_request.h" + +#include "layout_concat.h" +#include "layout_discovery.h" +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_dlist_util.h" +#include "layout_hsp.h" +#include "layout_mirror.h" +#include "layout_slice.h" +#include "layout_stripe.h" +#include "layout_svm_util.h" +#include "layout_validate.h" + +#define _LAYOUT_C + +static int layout_init(devconfig_t *request, defaults_t *defaults); +static int layout_diskset(request_t *request, dlist_t *results); + +static int process_request(devconfig_t *request, dlist_t **results); +static int process_qos_request(devconfig_t *request, dlist_t **results); +static int process_hsp_request(devconfig_t *request, dlist_t **results); + +/* + * stuff for making/updating the HSP to service devices + * created by the toplevel request + */ +static devconfig_t *_hsp_request = NULL; +static dlist_t *_hsp_devices = NULL; +static void set_hsp_request(devconfig_t *request); +static void unset_hsp_request(); + +/* + * struct to track which disks have been explicitly modified + * during the layout process... + * + * disk is the dm_descriptor_t of the modified disk + * accessname is the name to access the disk thru + * slices is the list of modified slices on the disk + */ +typedef struct { + dm_descriptor_t disk; + char *accessname; + dlist_t *slices; +} moddisk_t; + +/* + * modified_disks is a list of moddisk_t structs + * tracking disks have been modified during layout. + */ +static dlist_t *_modified_disks = NULL; + +static int collect_modified_disks(devconfig_t *request, dlist_t *results); +static int add_modified_disks_to_diskset( + dlist_t *devices, + devconfig_t *diskset); +static int release_modified_disks(); +static int get_removed_slices_for_disks( + dlist_t *mod_disks); +static int get_modified_slices_for_disks( + dlist_t *moddisks); +static int compare_disk_to_moddisk_disk( + void *disk, + void *moddisk); + +static int convert_device_names(devconfig_t *request, dlist_t *devs); + +/* + * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults) + * + * INPUT: request - a devconfig_t pointer to the toplevel request + * defaults - a results_t pointer to the defaults + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: Public entry point to layout module. + */ +int +get_layout( + request_t *request, + defaults_t *defaults) +{ + devconfig_t *diskset_req = NULL; + dlist_t *iter = NULL; + dlist_t *results = NULL; + int error = 0; + + if ((diskset_req = request_get_diskset_req(request)) != NULL) { + + /* initialize using the the top-level disk set request... */ + if ((error = layout_init(diskset_req, defaults)) != 0) { + return (error); + } + + oprintf(OUTPUT_TERSE, + gettext("\nProcessing volume request...\n")); + + iter = devconfig_get_components(diskset_req); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + + /* process each volume request, stop on any error */ + devconfig_t *subreq = (devconfig_t *)iter->obj; + dlist_t *subres = NULL; + + ((error = process_request(subreq, &subres)) != 0) || + (error = collect_modified_disks(subreq, subres)) || + (error = convert_device_names(subreq, subres)); + if (error == 0) { + results = dlist_append(subres, results, AT_TAIL); + } + } + + if (error == 0) { + /* process HSP request */ + dlist_t *subres = NULL; + error = process_hsp_request(diskset_req, &subres); + if (error == 0) { + results = dlist_append(subres, results, AT_TAIL); + } + } + + if (error == 0) { + oprintf(OUTPUT_TERSE, + gettext("\nAssembling volume specification...\n")); + /* determine required diskset modifications */ + error = layout_diskset(request, results); + } + + layout_clean_up(); + + if (error == 0) { + oprintf(OUTPUT_TERSE, + gettext("\nVolume request completed successfully.\n")); + } + + } else { + volume_set_error( + gettext("Malformed request, missing top level " + "disk set request.")); + } + + return (error); +} + +/* + * FUNCTION: layout_clean_up() + * + * PURPOSE: function which handles the details of cleaning up cached + * data and any other memory allocated during the layout + * process. + * + * release physical device data structs + * release SVM logical device data structs + * release validation data structs + * release modified device data structs + * release request processing data structs + * + * This function is also exported as part of the public + * interface to the layout module, clients of layout + * are required to call this function if get_layout() + * was called and was not allowed to return. For example, + * if SIGINT was received while a layout request was in + * process. + */ +void +layout_clean_up() +{ + (void) release_request_caches(); + (void) release_validation_caches(); + + (void) release_slices_to_remove(); + (void) release_modified_slices(); + (void) release_modified_disks(); + + (void) release_reserved_slices(); + (void) release_used_slices(); + + (void) release_usable_devices(); + (void) release_svm_names(get_request_diskset()); + (void) release_known_devices(); + + (void) unset_hsp_request(NULL); + (void) unset_request_defaults(NULL); + (void) unset_request_diskset(NULL); + (void) unset_toplevel_request(NULL); +} + +/* + * FUNCTION: layout_init(devconfig_t *diskset, defaults_t *defaults) + * + * INPUT: diskset - a devconfig_t pointer to the toplevel request + * defaults - a results_t pointer to the defaults + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: function which handles the details of initializing the layout + * module prior to processing a request. + * + * Determines the requested disk set and validates it. + * + * Scans the physical device configuration. + * Scans the SVM logical device configuration. + * + * Initializes layout private global data structures and does + * semantic validation of the request. + */ +static int +layout_init( + devconfig_t *diskset, + defaults_t *defaults) +{ + dlist_t *iter = NULL; + int error = 0; + char *dsname = NULL; + + ((error = validate_basic_svm_config()) != 0) || + + /* determine & validate requested disk set name */ + (error = devconfig_get_name(diskset, &dsname)) || + (error = set_request_diskset(dsname)) || + + /* discover known physical and logical devices */ + (error = discover_known_devices()) || + (error = scan_svm_names(dsname)) || + + /* validate and remember toplevel request */ + (error = set_toplevel_request(diskset)) || + + /* validate and remember defaults for this request */ + (error = set_request_defaults(defaults)); + + if (error != 0) { + return (error); + } + + oprintf(OUTPUT_TERSE, + gettext("\nValidating volume request...\n")); + + iter = devconfig_get_components(diskset); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + devconfig_t *subreq = (devconfig_t *)iter->obj; + error = validate_request(subreq); + } + + if (error == 0) { + error = discover_usable_devices(dsname); + } + + if (error == 0) { + /* final validation on explicitly requested components */ + error = validate_reserved_slices(); + } + + if (error == 0) { + /* final validation on request sizes vs. actual avail space */ + error = validate_request_sizes(diskset); + } + + return (error); +} + +/* + * FUNCTION: process_request(devconfig_t *req, dlist_t **results) + * + * INPUT: req - a devconfig_t pointer to the current request + * results - pointer to a list of resulting volumes + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: function which handles the details of an explicit + * volume request. + * + * Determines the requested volume type, whether the + * request contains specific subcomponents and dispatches + * to the appropriate layout function for that type. + * + * Resulting volumes are appended to the results list. + * + * Note that an HSP request is held until all the volumes + * in the request have been successfully composed. This + * ensures that HSP spare sizing can be appropriate to + * those volumes. + */ +static int +process_request( + devconfig_t *req, + dlist_t **results) +{ + component_type_t type = TYPE_UNKNOWN; + uint64_t nbytes = 0; /* requested volume size */ + dlist_t *comps = NULL; + int ncomps = 0; + int error = 0; + + (void) devconfig_get_type(req, &type); + (void) devconfig_get_size(req, &nbytes); + comps = devconfig_get_components(req); + + if (type == TYPE_HSP) { + /* HSP processing needs to happen after all other volumes. */ + /* set the HSP request aside until all other requests have */ + /* been completed successfully */ + set_hsp_request(req); + return (0); + } + + oprintf(OUTPUT_TERSE, "\n"); + oprintf(OUTPUT_VERBOSE, "******************\n"); + + ncomps = dlist_length(comps); + + if (type == TYPE_STRIPE) { + if (ncomps > 0) { + return (populate_explicit_stripe(req, results)); + } else { + return (layout_stripe(req, nbytes, results)); + } + } + + if (type == TYPE_CONCAT) { + if (ncomps > 0) { + return (populate_explicit_concat(req, results)); + } else { + return (layout_concat(req, nbytes, results)); + } + } + + if (type == TYPE_MIRROR) { + if (ncomps > 0) { + return (populate_explicit_mirror(req, results)); + } else { + uint16_t nsubs = 0; + if ((error = get_mirror_nsubs(req, &nsubs)) != 0) { + return (error); + } else { + return (layout_mirror(req, nsubs, nbytes, results)); + } + } + } + + if (type == TYPE_VOLUME) { + error = process_qos_request(req, results); + } + + return (error); +} + +/* + * FUNCTION: process_qos_request(devconfig_t *req, dlist_t **results) + * + * INPUT: req - a devconfig_t pointer to the current request + * results - pointer to a list of resulting volumes + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: function which handles the details of mapping an implicit + * volume request of QoS attributes into a volume type. + * + * Resulting volumes are appended to the results list. + */ +static int +process_qos_request( + devconfig_t *req, + dlist_t **results) +{ + int error = 0; + + uint64_t nbytes = 0; + uint16_t rlevel = 0; + + /* get QoS attributes */ + (void) devconfig_get_size(req, &nbytes); + + if ((error = get_volume_redundancy_level(req, &rlevel)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + rlevel = 0; + } + } + + if (error == 0) { + if (rlevel == 0) { + error = layout_stripe(req, nbytes, results); + } else { + error = layout_mirror(req, rlevel, nbytes, results); + } + } + + return (error); +} + +/* + * FUNCTION: layout_diskset(request_t *req, dlist_t **results) + * + * INPUT: req - a request_t pointer to the toplevel request + * results - pointer to the list of composed result volumes + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: function which handles the details of completing an layout + * request. + * + * Determines if the disk set specified in the request currently + * exists and sets it up for creation if it doesn't. + * + * Adds new disks required by the result volumes to the disk set. + * + * Attaches the result volumes to the disk set result. + * + * Convert slice and disk names to preferred names. + * + * Attaches the disk set result to the toplevel request. + */ +static int +layout_diskset( + request_t *request, + dlist_t *results) +{ + int error = 0; + devconfig_t *diskset = NULL; + dlist_t *comps = NULL; + + ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) || + (error = devconfig_set_name(diskset, get_request_diskset())) || + (error = add_modified_disks_to_diskset(results, diskset)); + if (error != 0) { + free_devconfig(diskset); + return (error); + } + + /* add resulting volumes */ + if (results != NULL) { + comps = devconfig_get_components(diskset); + comps = dlist_append(results, comps, AT_TAIL); + devconfig_set_components(diskset, comps); + } + + request_set_diskset_config(request, diskset); + + return (error); +} + +/* + * FUNCTION: convert_device_names(devconfig_t request, dlist_t *devices) + * + * INPUT: request - a devconfig_t request pointer + * devices - a list of devconfig_t devices + * + * RETURNS: int - 0 - on success + * !0 - on any error + * + * PURPOSE: Utility function to convert any slice or disk drive + * names in a result devconfig_t to the preferred name + * which should be used to access the device. + * + * This convert the temporary names used by layout to + * the proper DID or /dev/dsk alias. + */ +static int +convert_device_names( + devconfig_t *request, + dlist_t *devices) +{ + int error = 0; + dlist_t *iter; + + for (iter = devices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + component_type_t type = TYPE_UNKNOWN; + dm_descriptor_t disk = (dm_descriptor_t)0; + char *devname = NULL; + char *diskname = NULL; + char *slicename = NULL; + uint16_t index; + + if ((error = devconfig_get_type(dev, &type)) == 0) { + switch (type) { + + case TYPE_MIRROR: + case TYPE_STRIPE: + case TYPE_CONCAT: + case TYPE_HSP: + + error = convert_device_names(request, + devconfig_get_components(dev)); + + break; + + case TYPE_SLICE: + + ((error = devconfig_get_name(dev, &devname)) != 0) || + (error = devconfig_get_slice_index(dev, &index)) || + (error = get_disk_for_named_slice(devname, &disk)) || + (error = get_device_access_name(request, disk, + &diskname)) || + (error = make_slicename_for_diskname_and_index( + diskname, index, &slicename)); + + if ((error == 0) && (slicename != NULL)) { + error = devconfig_set_name(dev, slicename); + free(slicename); + } + + break; + } + } + } + + return (error); +} + +/* + * FUNCTION: add_modified_disk(devconfig_t request, dm_descriptor_t disk); + * + * INPUT: request - a pointr to a devconfig_t request + * disk - dm_descriptor_t handle for a disk that has been modified + * + * SIDEEFFECTS: adds an entry to the _modified_disks list which tracks + * the disks that have been explicitly modified by + * the layout code. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Adds the input disk to the list of those that have been + * modified. + * + * Disks are modified during layout for two reasons: + * + * 1. any disk that is to be added to the disk set gets + * an explicitly updated label. + * + * 2. once a disk is in the disk set, existing slices + * may be resized or new slices can be added. + */ +int +add_modified_disk( + devconfig_t *request, + dm_descriptor_t disk) +{ + dlist_t *iter = NULL; + moddisk_t *moddisk = NULL; + dlist_t *item = NULL; + int error = 0; + + for (iter = _modified_disks; iter != NULL; iter = iter->next) { + moddisk = (moddisk_t *)iter->obj; + if (compare_descriptor_names( + (void *)moddisk->disk, (void *)disk) == 0) { + /* already in list */ + return (0); + } + } + + moddisk = (moddisk_t *)calloc(1, sizeof (moddisk_t)); + if (moddisk == NULL) { + error = ENOMEM; + } else { + char *aname = NULL; + error = get_device_access_name(request, disk, &aname); + if (error == 0) { + + /* add to list of modified disks */ + moddisk->disk = disk; + moddisk->accessname = aname; + moddisk->slices = NULL; + + if ((item = dlist_new_item((void *)moddisk)) == NULL) { + free(moddisk); + error = ENOMEM; + } else { + _modified_disks = + dlist_append(item, _modified_disks, AT_HEAD); + } + } + } + + return (error); +} + +/* + * FUNCTION: collect_modified_disks(devconfig_t *request, dlist_t* devs) + * + * INPUT: devs - pointer to a list of composed volumes + * OUTPUT: none - + * SIDEEFFECT: updates the module global list _modified_disks + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper to maintain the list of disks to be added to the + * disk set. + * + * Iterates the input list of devices and determines which + * disks they use. If a disk is not in the _modified_disks + * list, it is added. + */ +static int +collect_modified_disks( + devconfig_t *request, + dlist_t *devs) +{ + int error = 0; + + char *sname = NULL; + dm_descriptor_t disk = (dm_descriptor_t)0; + + for (; (devs != NULL) && (error == 0); devs = devs->next) { + + devconfig_t *dev = (devconfig_t *)devs->obj; + component_type_t type = TYPE_UNKNOWN; + + if ((error = devconfig_get_type(dev, &type)) == 0) { + + switch (type) { + case TYPE_MIRROR: + case TYPE_STRIPE: + case TYPE_CONCAT: + case TYPE_HSP: + + error = collect_modified_disks(request, + devconfig_get_components(dev)); + break; + + case TYPE_SLICE: + + ((error = devconfig_get_name(dev, &sname)) != 0) || + (error = get_disk_for_named_slice(sname, &disk)) || + (error = add_modified_disk(request, disk)); + + break; + } + } + } + + return (error); +} + +/* + * FUNCTION: add_modified_disks_to_diskset(dlist_t *devices, + * devconfig_t *diskset) + * + * INPUT: devices - pointer to a list of devices + * + * OUTPUT: diskset - pointer to a devconfig_t representing the disk set, + * updated to include modified disks and slices as + * components. + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper to add devconfig_t structs for disks and slices + * to the disk set. + * + * Updates the list of _modified_disks by examining the input + * list of composed devices. + * + * Iterates _modified_disks and creates a devconfig_t component + * for each disk in the list, the list of disks is then attached + * to the input disk set. + * + * Modified slices for disks in the disk set are added as well. + */ +static int +add_modified_disks_to_diskset( + dlist_t *results, + devconfig_t *diskset) +{ + int error = 0; + + dlist_t *iter; + dlist_t *list = NULL; + char *dsname = get_request_diskset(); + + /* add modified disks to disk set's component list */ + list = devconfig_get_components(diskset); + + oprintf(OUTPUT_TERSE, + gettext(" Collecting modified disks...\n")); + + /* collect removed slices for modified disks */ + error = get_removed_slices_for_disks(_modified_disks); + + /* collect modified slices for modified disks */ + error = get_modified_slices_for_disks(_modified_disks); + + for (iter = _modified_disks; + (iter != NULL) && (error == 0); + iter = iter->next) { + + moddisk_t *moddisk = (moddisk_t *)iter->obj; + dm_descriptor_t disk = moddisk->disk; + devconfig_t *newdisk = NULL; + boolean_t in_set = B_FALSE; + + oprintf(OUTPUT_VERBOSE, " %s\n", moddisk->accessname); + + error = is_disk_in_diskset(disk, dsname, &in_set); + if ((error == 0) && (in_set != B_TRUE)) { + /* New disk, add it to the disk set */ + ((error = new_devconfig(&newdisk, TYPE_DRIVE)) != 0) || + (error = devconfig_set_name(newdisk, moddisk->accessname)); + if (error == 0) { + dlist_t *item = dlist_new_item(newdisk); + if (item == NULL) { + error = ENOMEM; + } else { + list = dlist_append(item, list, AT_TAIL); + oprintf(OUTPUT_DEBUG, + gettext(" must add %s to disk set \"%s\"\n"), + moddisk->accessname, dsname); + } + } else { + free_devconfig(newdisk); + } + } + + if ((error == 0) && (moddisk->slices != NULL)) { + /* move moddisk's slice list to disk set comp list */ + list = dlist_append(moddisk->slices, list, AT_TAIL); + moddisk->slices = NULL; + } + } + + if (error == 0) { + devconfig_set_components(diskset, list); + } else { + dlist_free_items(list, NULL); + } + + return (error); +} + +/* + * FUNCTIONS: void release_modified_disks() + * + * INPUT: none - + * OUTPUT: none - + * + * PURPOSE: cleanup the module global list of disks that need + * to be added to the disk set to satisfy the request. + */ +static int +release_modified_disks() +{ + dlist_t *iter = _modified_disks; + + for (; iter != NULL; iter = iter->next) { + moddisk_t *moddisk = (moddisk_t *)iter->obj; + if (moddisk->slices != NULL) { + dlist_free_items(moddisk->slices, free_devconfig); + moddisk->slices = NULL; + } + free(moddisk); + iter->obj = NULL; + } + + dlist_free_items(_modified_disks, NULL); + _modified_disks = NULL; + + return (0); +} + +/* + * FUNCTION: get_removed_slices_for_disks(dlist_t *mod_disks) + * + * INPUT: mod_disks - a list of moddisk_t structs + * + * OUTPUT: mod_disks - the list of moddisk_t structs updated with + * the slices to be removed for each disk + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper to create a list of devconfig_t structs + * for slices on the input disks which need to be + * removed from the system. + * + * Iterates the list of slices to be removed and + * creates a devconfig_t component for each slice + * in the list that is on any of the input modified + * disks. + * + * Slice names are constructed using the modified disk's + * access name to ensure that the correct alias is + * used to get to the slice. + */ +static int +get_removed_slices_for_disks( + dlist_t *mod_disks) +{ + int error = 0; + dlist_t *iter = NULL; + + /* collect slices to be removed for the modified disks */ + for (iter = get_slices_to_remove(); + (iter != NULL) && (error == 0); + iter = iter->next) { + + rmvdslice_t *rmvd = (rmvdslice_t *)iter->obj; + dm_descriptor_t disk = (dm_descriptor_t)0; + moddisk_t *moddisk = NULL; + char *sname = NULL; + devconfig_t *newslice = NULL; + dlist_t *item = NULL; + + (void) get_disk_for_named_slice(rmvd->slice_name, &disk); + + if ((item = dlist_find(mod_disks, (void *)disk, + compare_disk_to_moddisk_disk)) == NULL) { + /* slice on disk that we don't care about */ + continue; + } + + moddisk = (moddisk_t *)item->obj; + + /* create output slice struct for the removed slice */ + ((error = make_slicename_for_diskname_and_index( + moddisk->accessname, rmvd->slice_index, &sname)) != 0) || + (error = new_devconfig(&newslice, TYPE_SLICE)) || + (error = devconfig_set_name(newslice, sname)) || + (error = devconfig_set_size_in_blocks(newslice, 0)); + + /* add to the moddisk's list of slices */ + if (error == 0) { + if ((item = dlist_new_item(newslice)) == NULL) { + free_devconfig(newslice); + error = ENOMEM; + } else { + moddisk->slices = + dlist_append(item, moddisk->slices, AT_TAIL); + } + } else { + free_devconfig(newslice); + } + } + + return (error); +} + +/* + * FUNCTION: get_modified_slices_for_disks(dlist_t *mod_disks) + * + * INPUT: mod_disks - a list of moddisk_t structs + * + * OUTPUT: mod_disks - the list of moddisk_t structs updated with + * the modified slices for each disk + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper to create a list of devconfig_t structs + * for slices on the input disks which have been + * modified for use by layout. + * + * Iterates the list of modified slices and creates a + * devconfig_t component for each slice in the list + * that is on any of the input modified disks. + * + * Slice names are constructed using the modified disk's + * access name to ensure that the correct alias is + * used to get to the slice. + */ +int +get_modified_slices_for_disks( + dlist_t *mod_disks) +{ + int error = 0; + dlist_t *iter = NULL; + + for (iter = get_modified_slices(); + (iter != NULL) && (error == 0); + iter = iter->next) { + + modslice_t *mods = (modslice_t *)iter->obj; + devconfig_t *slice = mods->slice_devcfg; + devconfig_t *newslice = NULL; + dm_descriptor_t disk; + moddisk_t *moddisk; + dlist_t *item; + char *sname = NULL; + uint64_t stblk = 0; + uint64_t nblks = 0; + uint16_t index; + + /* only add modified slices that were sources */ + if ((mods->times_modified == 0) || + (mods->src_slice_desc != (dm_descriptor_t)0)) { + continue; + } + + (void) devconfig_get_name(slice, &sname); + (void) get_disk_for_named_slice(sname, &disk); + + if ((item = dlist_find(mod_disks, (void *)disk, + compare_disk_to_moddisk_disk)) == NULL) { + /* slice on disk that we don't care about */ + continue; + } + + moddisk = (moddisk_t *)item->obj; + + /* create output slice struct for the modified slice */ + ((error = devconfig_get_slice_start_block(slice, + &stblk)) != 0) || + (error = devconfig_get_size_in_blocks(slice, &nblks)) || + (error = devconfig_get_slice_index(slice, &index)) || + (error = make_slicename_for_diskname_and_index( + moddisk->accessname, index, &sname)) || + (error = new_devconfig(&newslice, TYPE_SLICE)) || + (error = devconfig_set_name(newslice, sname)) || + (error = devconfig_set_slice_start_block(newslice, stblk)) || + (error = devconfig_set_size_in_blocks(newslice, nblks)); + + /* add to the moddisk's list of slices */ + if (error == 0) { + if ((item = dlist_new_item(newslice)) == NULL) { + free_devconfig(newslice); + error = ENOMEM; + } else { + moddisk->slices = + dlist_append(item, moddisk->slices, AT_TAIL); + } + } else { + free_devconfig(newslice); + } + } + + return (error); +} + +/* + * FUNCTION: compare_disk_to_moddisk_disk(void *disk, void *moddisk) + * + * INPUT: disk - opaque pointer to a dm_descriptor_t + * moddisk - opaque moddisk_t pointer + * + * RETURNS: int - 0 - if disk == moddisk->disk + * !0 - otherwise + * + * PURPOSE: dlist_t helper which compares the input disk dm_descriptor_t + * handle to the disk dm_descriptor_t handle in the input + * moddisk_t struct. + * + * Comparison is done via compare_descriptor_names. + */ +static int +compare_disk_to_moddisk_disk( + void *disk, + void *moddisk) +{ + assert(disk != (dm_descriptor_t)0); + assert(moddisk != NULL); + + return (compare_descriptor_names((void *)disk, + (void *)((moddisk_t *)moddisk)->disk)); +} + +/* + * FUNCTIONS: void set_hsp_request() + * + * INPUT: none - + * OUTPUT: none - + * + * PURPOSE: set the module global HSP request struct. + */ +static void +set_hsp_request( + devconfig_t *req) +{ + _hsp_request = req; +} + +/* + * FUNCTIONS: void unset_hsp_request() + * + * INPUT: none - + * OUTPUT: none - + * + * PURPOSE: unset the module global HSP request struct. + */ +static void +unset_hsp_request() +{ + _hsp_request = NULL; +} + +/* + * FUNCTION: process_hsp_request(devconfig_t *req, dlist_t **results) + * INPUT: req - pointer to the toplevel disk set devconfig_t request + * results - pointer to a list of composed results + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper which determines HSP processing for the + * composed volumes which need HSP spares. + */ +static int +process_hsp_request( + devconfig_t *req, + dlist_t **results) +{ + int error = 0; + + if (_hsp_request != NULL) { + oprintf(OUTPUT_TERSE, + gettext("\nProcessing HSP...\n")); + } + + if (_hsp_devices == NULL) { + /* no devices -> no HSP */ + oprintf(OUTPUT_VERBOSE, + gettext(" No devices require hot spares...\n")); + } else { + + oprintf(OUTPUT_TERSE, "\n"); + + ((error = layout_hsp(req, _hsp_request, _hsp_devices, + results)) != 0) || + (error = collect_modified_disks(_hsp_request, *results)) || + (error = convert_device_names(_hsp_request, *results)); + } + + return (error); +} + +/* + * FUNCTION: add_to_hsp_list(dlist_t* list) + * INPUT: devs - pointer to a list of composed volumes + * OUTPUT: none - + * SIDEEFFECT: updates the module global list _hsp_devices + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper to update the list of devices which need HSP spares. + * + * Iterates the input list of devices and adds them them to the + * module provate list of devices needing spares. + */ +int +add_to_hsp_list( + dlist_t *list) +{ + dlist_t *iter = NULL; + int error = 0; + + for (iter = list; iter != NULL; iter = iter->next) { + dlist_t *item = NULL; + + if ((item = dlist_new_item(iter->obj)) == NULL) { + error = ENOMEM; + break; + } + _hsp_devices = dlist_append(item, _hsp_devices, AT_HEAD); + } + + return (error); +} + +/* + * FUNCTION: string_case_compare( + * char *str1, char *str2) + * + * INPUT: str1 - char * + * str2 - char * + * + * RETURNS: int - <0 - if str1 < str2 + * 0 - if str1 == str2 + * >0 - if str1 > str2 + * + * PURPOSE: More robust case independent string comparison function. + * + * Assumes str1 and str2 are both char * + * + * Compares the lengths of each and if equivalent compares + * the strings using strcasecmp. + */ +int +string_case_compare( + char *str1, + char *str2) +{ + int result = 0; + + assert(str1 != NULL); + assert(str2 != NULL); + + if ((result = (strlen(str1) - strlen(str2))) == 0) { + result = strcasecmp(str1, str2); + } + + return (result); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout.h b/usr/src/cmd/lvm/metassist/layout/layout.h new file mode 100644 index 0000000000..3cb99f80ae --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_LAYOUT_H +#define _VOLUME_LAYOUT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_request.h" +#include "volume_defaults.h" + +/* + * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults) + * + * INPUT: request - a devconfig_t pointer to the toplevel request + * defaults - a results_t pointer to the defaults + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: Public entry point to layout module. + */ +extern int get_layout(request_t *request, defaults_t *defaults); + +/* + * FUNCTION: layout_clean_up() + * INPUT: + * OUTPUT: + * SIDEEFFECTS: releases all memory allocated during layout processing + * + * PURPOSE: function which handles the details of cleaning up memory + * allocated while processing a request. + * + * This function must be called explicitly if a call to + * get_layout() was terminated abnormally, for example, + * if the user terminates the calling process with a SIGINT. + */ +extern void layout_clean_up(); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_LAYOUT_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_concat.c b/usr/src/cmd/lvm/metassist/layout/layout_concat.c new file mode 100644 index 0000000000..3fe529763d --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_concat.c @@ -0,0 +1,673 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <libintl.h> + +#include "libdiskmgt.h" + +#include "volume_error.h" +#include "volume_defaults.h" +#include "volume_devconfig.h" +#include "volume_dlist.h" +#include "volume_output.h" +#include "volume_request.h" + +#include "layout_concat.h" +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_messages.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_svm_util.h" + +#define _LAYOUT_CONCAT_C + +static int +compose_concat_within_hba( + devconfig_t *request, + dlist_t *hbas, + uint64_t nbytes, + devconfig_t **concat); + +static int +assemble_concat( + devconfig_t *request, + dlist_t *comps, + devconfig_t **concat); + +/* + * FUNCTION: layout_concat(devconfig_t *request, uint64_t nbytes, + * dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * nbytes - the desired capacity of the concat + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main layout driver for composing concat volumes. + * + * Attempts to construct a concat of size nbytes. + * + * Several different layout strategies are tried in order + * of preference until one succeeds or there are none left. + * + * 1 - concat within an HBA + * . requires sufficient space available on the HBA + * + * 2 - concat across all available similar HBAs + * + * 3 - concat across all available HBAs + * + * get available HBAs + * + * group HBAs by characteristics + * for (each HBA grouping) and (concat not composed) { + * select next HBA group + * for (strategy[1,2]) and (concat not composed) { + * compose concat using HBAs in group + * } + * } + * + * if (concat not composed) { + * for (strategy[3]) and (concat not composed) { + * compose concat using all HBAs + * } + * } + * + * if (concat composed) { + * append composed concat to results + * } + */ +int +layout_concat( + devconfig_t *request, + uint64_t nbytes, + dlist_t **results) +{ + /* + * these enums define the # of strategies and the preference order + * in which they are tried + */ + typedef enum { + CONCAT_WITHIN_SIMILAR_HBA = 0, + CONCAT_ACROSS_SIMILAR_HBAS, + N_SIMILAR_HBA_STRATEGIES + } similar_hba_strategy_order_t; + + typedef enum { + CONCAT_ACROSS_ANY_HBAS = 0, + N_ANY_HBA_STRATEGIES + } any_hba_strategy_order_t; + + dlist_t *usable_hbas = NULL; + dlist_t *similar_hba_groups = NULL; + dlist_t *iter = NULL; + devconfig_t *concat = NULL; + + int error = 0; + + (error = get_usable_hbas(&usable_hbas)); + if (error != 0) { + volume_set_error(gettext("There are no usable HBAs.")); + return (error); + } + + print_layout_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes); + + if (dlist_length(usable_hbas) == 0) { + print_no_hbas_msg(); + return (-1); + } + + error = group_similar_hbas(usable_hbas, &similar_hba_groups); + if (error != 0) { + return (error); + } + + for (iter = similar_hba_groups; + (error == 0) && (concat == NULL) && (iter != NULL); + iter = iter->next) { + + dlist_t *hbas = (dlist_t *)iter->obj; + + similar_hba_strategy_order_t order; + + for (order = CONCAT_WITHIN_SIMILAR_HBA; + (order < N_SIMILAR_HBA_STRATEGIES) && + (concat == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + + switch (order) { + + case CONCAT_WITHIN_SIMILAR_HBA: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 1: use disks from a single HBA - concat within HBA\n")); +/* END CSTYLED */ + + error = compose_concat_within_hba( + request, selhbas, nbytes, &concat); + } + + break; + + case CONCAT_ACROSS_SIMILAR_HBAS: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 2: use disks from all similar HBAs - concat across HBAs\n")); +/* END CSTYLED */ + + error = populate_concat( + request, nbytes, disks, + NULL, &concat); + } + + break; + + default: + break; + } + + dlist_free_items(disks, NULL); + dlist_free_items(selhbas, NULL); + } + } + + for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { + dlist_free_items((dlist_t *)iter->obj, NULL); + } + dlist_free_items(similar_hba_groups, NULL); + + /* try all HBAs */ + if (concat == NULL && error == 0) { + + any_hba_strategy_order_t order; + + for (order = CONCAT_ACROSS_ANY_HBAS; + (order < N_ANY_HBA_STRATEGIES) && + (concat == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + + switch (order) { + + case CONCAT_ACROSS_ANY_HBAS: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_VERBOSE, + gettext(" -->Strategy 3: use disks from all available HBAs - concat across HBAs\n")); +/* END CSTYLED */ + + error = populate_concat( + request, nbytes, disks, + NULL, &concat); + } + + break; + + default: + break; + } + + dlist_free_items(disks, NULL); + dlist_free_items(selhbas, NULL); + } + } + + if (concat != NULL) { + + dlist_t *item = dlist_new_item(concat); + if (item == NULL) { + error = ENOMEM; + } else { + + *results = dlist_append(item, *results, AT_TAIL); + + print_layout_success_msg(); + } + + } else if (error != 0) { + + print_debug_failure_msg( + devconfig_type_to_str(TYPE_CONCAT), + get_error_string(error)); + + } else { + + print_insufficient_resources_msg( + devconfig_type_to_str(TYPE_CONCAT)); + error = -1; + } + + return (error); +} + +static int +compose_concat_within_hba( + devconfig_t *request, + dlist_t *hbas, + uint64_t nbytes, + devconfig_t **concat) +{ + int error = 0; + + dlist_t *iter = NULL; + + for (iter = hbas; + (iter != NULL) && (*concat == NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + dlist_t *disks = NULL; + uint64_t space = 0; + char *name; + + /* check for sufficient space on the HBA */ + ((error = get_display_name(hba, &name)) != 0) || + (error = hba_get_avail_disks_and_space(request, + hba, &disks, &space)); + + if (error == 0) { + if (space >= nbytes) { + error = populate_concat(request, nbytes, disks, + NULL, concat); + } else { + print_hba_insufficient_space_msg(name, space); + } + } + + dlist_free_items(disks, NULL); + } + + return (error); +} + +/* + * FUNCTION: populate_concat(devconfig_t *request, uint64_t nbytes, + * dlist_t *disks, dlist_t *othervols, + * devconfig_t **concat) + * + * INPUT: request - pointer to a request devconfig_t + * nbytes - desired concat size + * disks - pointer to a list of availalb disks + * othervols - pointer to a list of other volumes whose + * composition may affect this concat + * (e.g., submirrors of the same mirror) + * + * OUTPUT: concat - pointer to a devconfig_t to hold resulting concat + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper to populate a concat with the specified aggregate + * capacity using slices on disks in the input list. + * + * If the othervols list is not empty, the slice components + * chosen for the concat must not on the same disks as any + * of the other volumes. + * + * If sufficient slice components can be found, the concat + * is assembled and returned. + */ +int +populate_concat( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + dlist_t *othervols, + devconfig_t **concat) +{ + dlist_t *other_hbas = NULL; + dlist_t *other_disks = NULL; + + dlist_t *slices = NULL; + dlist_t *comps = NULL; + + uint16_t npaths = 0; + uint64_t capacity = 0; + int error = 0; + + *concat = NULL; + + ((error = disks_get_avail_slices(request, disks, &slices)) != 0) || + (error = get_volume_npaths(request, &npaths)); + if (error != 0) { + dlist_free_items(slices, NULL); + return (error); + } + + print_populate_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes); + + if (slices == NULL) { + print_populate_no_slices_msg(); + return (0); + } + + /* determine HBAs and disks used by othervols */ + error = get_hbas_and_disks_used_by_volumes(othervols, + &other_hbas, &other_disks); + if (error != 0) { + dlist_free_items(other_hbas, NULL); + dlist_free_items(other_disks, NULL); + return (error); + } + + print_populate_choose_slices_msg(); + + while (capacity < nbytes) { + + devconfig_t *comp = NULL; + dlist_t *item = NULL; + dlist_t *rmvd = NULL; + char *cname = NULL; + uint64_t csize = 0; + + /* BEGIN CSTYLED */ + /* + * 1st B_TRUE: require a different disk than those used by + * comps and othervols + * 1st B_FALSE: slice with size less that requested is acceptable + * 2nd B_FALSE: do not add an extra cylinder when resizing slice, + * this is only necessary for Stripe components whose sizes + * get rounded down to an interlace multiple and then down + * to a cylinder boundary. + * + */ + /* END CSTYLED */ + error = choose_slice((nbytes-capacity), npaths, slices, comps, + other_hbas, other_disks, B_TRUE, B_FALSE, B_FALSE, &comp); + + if ((error == 0) && (comp != NULL)) { + + item = dlist_new_item(comp); + if (item == NULL) { + error = ENOMEM; + } else { + + /* add selected component to comp list */ + comps = dlist_append(item, comps, AT_HEAD); + + /* remove it from the available list */ + slices = dlist_remove_equivalent_item(slices, (void *) comp, + compare_devconfig_and_descriptor_names, &rmvd); + + if (rmvd != NULL) { + free(rmvd); + } + + /* add the component slice to the used list */ + if ((error = devconfig_get_name(comp, &cname)) == 0) { + error = add_used_slice_by_name(cname); + } + + /* increment concat's capacity */ + if ((error == 0) && + (error = devconfig_get_size(comp, &csize)) == 0) { + capacity += csize; + } + } + + } else { + /* no possible slice */ + break; + } + } + + dlist_free_items(slices, NULL); + dlist_free_items(other_hbas, NULL); + dlist_free_items(other_disks, NULL); + + if (capacity >= nbytes) { + + error = assemble_concat(request, comps, concat); + + if (error == 0) { + print_populate_success_msg(); + } else { + /* undo any slicing done for the concat */ + dlist_free_items(comps, free_devconfig_object); + } + + } else if (error == 0) { + + if (capacity > 0) { + dlist_free_items(comps, free_devconfig_object); + print_insufficient_capacity_msg(capacity); + } else { + print_populate_no_slices_msg(); + } + + } + + return (error); +} + +/* + * FUNCTION: populate_explicit_concat(devconfig_t *request, + * dlist_t **results) + * + * INPUT: request - pointer to a request devconfig_t + * + * OUTPUT: results - pointer to a list of volume devconfig_t results + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Processes the input concat request that specifies explicit + * slice components. + * + * The components have already been validated and reserved, + * all that is required is to create devconfig_t structs + * for each requested slice. + * + * The net size of the concat is determined by the slice + * components. + * + * The concat devconfig_t is assembled and appended to the + * results list. + * + * This function is also called from + * layout_mirror.populate_explicit_mirror() + */ +int +populate_explicit_concat( + devconfig_t *request, + dlist_t **results) +{ + int error = 0; + + dlist_t *comps = NULL; + dlist_t *iter = NULL; + dlist_t *item = NULL; + + devconfig_t *concat = NULL; + + print_layout_explicit_msg(devconfig_type_to_str(TYPE_CONCAT)); + + /* assemble components */ + iter = devconfig_get_components(request); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + + devconfig_t *rqst = (devconfig_t *)iter->obj; + dm_descriptor_t rqst_slice = NULL; + char *rqst_name = NULL; + devconfig_t *comp = NULL; + + /* slice components have been validated */ + /* turn each into a devconfig_t */ + ((error = devconfig_get_name(rqst, &rqst_name)) != 0) || + (error = slice_get_by_name(rqst_name, &rqst_slice)) || + (error = create_devconfig_for_slice(rqst_slice, &comp)); + + if (error == 0) { + + print_layout_explicit_added_msg(rqst_name); + + item = dlist_new_item((void *)comp); + if (item == NULL) { + error = ENOMEM; + } else { + comps = dlist_append(item, comps, AT_TAIL); + } + } + } + + if (error == 0) { + error = assemble_concat(request, comps, &concat); + } + + if (error == 0) { + if ((item = dlist_new_item(concat)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + print_populate_success_msg(); + } + } else { + dlist_free_items(comps, free_devconfig); + } + + return (error); +} + +/* + * FUNCTION: assemble_concat(devconfig_t *request, dlist_t *comps, + * devconfig_t **concat) + * + * INPUT: request - pointer to a devconfig_t of the current request + * comps - pointer to a list of slice components + * + * OUPUT: concat - pointer to a devconfig_t to hold final concat + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which creates and populates a concat devconfig_t + * struct using information from the input request and the + * list of slice components. + * + * Determines the name of the concat either from the request + * or from the default naming scheme. + * + * Attaches the input list of components to the devconfig. + */ +static int +assemble_concat( + devconfig_t *request, + dlist_t *comps, + devconfig_t **concat) +{ + char *name = NULL; + int error = 0; + + if ((error = new_devconfig(concat, TYPE_CONCAT)) == 0) { + /* set concat name, use requested name if specified */ + if ((error = devconfig_get_name(request, &name)) != 0) { + if (error != ERR_ATTR_UNSET) { + volume_set_error(gettext("error getting requested name\n")); + } else { + error = 0; + } + } + + if (error == 0) { + if (name == NULL) { + if ((error = get_next_volume_name(&name, + TYPE_CONCAT)) == 0) { + error = devconfig_set_name(*concat, name); + free(name); + } + } else { + error = devconfig_set_name(*concat, name); + } + } + } + + if (error == 0) { + + /* compute and save true size of concat */ + if (error == 0) { + uint64_t nblks = 0; + dlist_t *iter; + + for (iter = comps; + (error == 0) && (iter != NULL); + iter = iter->next) { + + devconfig_t *comp = (devconfig_t *)iter->obj; + uint64_t comp_nblks = 0; + + if ((error = devconfig_get_size_in_blocks(comp, + &comp_nblks)) == 0) { + nblks += comp_nblks; + } + } + + if (error == 0) { + error = devconfig_set_size_in_blocks(*concat, nblks); + } + } + } + + if (error == 0) { + devconfig_set_components(*concat, comps); + } else { + free_devconfig(*concat); + *concat = NULL; + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_concat.h b/usr/src/cmd/lvm/metassist/layout/layout_concat.h new file mode 100644 index 0000000000..fb2d072d80 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_concat.h @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_CONCAT_H +#define _LAYOUT_CONCAT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" +#include "volume_dlist.h" + +extern int layout_concat( + devconfig_t *request, + uint64_t nbytes, + dlist_t **results); + +extern int populate_concat( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + dlist_t *othervols, + devconfig_t **concat); + +extern int populate_explicit_concat( + devconfig_t *request, + dlist_t **results); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_CONCAT_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c new file mode 100644 index 0000000000..80a2865214 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c @@ -0,0 +1,893 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <libintl.h> + +#include <string.h> +#include <sys/param.h> +#include <sys/types.h> +#include <errno.h> +#include <search.h> + +#include "volume_dlist.h" +#include "volume_error.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_dlist_util.h" +#include "layout_request.h" + +/* + * Implementation note: + * The current caches are implemented as linked lists of data + * structures described below. Cached object lookup uses hsearch() + * where possible to minimize the inefficiency of linear search. + */ + +/* + * The name and attribute maps use hesarch() for faster lookup + */ +static const uint32_t MAX_CACHED_OBJECTS = 50000; + +/* + * The attribute cache is maintained as a list of these + * structs which map a device name to attributes. The + * device name is the unique device name returned from + * the device library, typically a devfs path. It should + * not be confused with the "display" name of the device + * which is typically a CTD or DID name. + */ +typedef struct { + char *name; + nvlist_t *attrs; +} attr_cache_t; + +static dlist_t *_attr_cache = NULL; + +/* + * The name cache is maintained via a list of these structs + * which map a descriptor to its name. + * The descriptor is saved as a string for hsearch() + */ +typedef struct { + char *desc; + char *name; +} name_cache_t; +static dlist_t *_name_cache = NULL; + +/* + * The desc cache is maintained as a list of these + * structs which map a device display name (CTD or DID) + * or alias to a descriptor. + */ +typedef struct { + char *name; + dm_descriptor_t desc; +} desc_cache_t; + +static dlist_t *_desc_cache = NULL; + +/* + * Since each of the lookup caches shares the same hsearch() + * hash table, the names used as lookup keys for the desc_cache_t + * and attr_cache_t may cause collisions. + * + * The desc_cache_t map alters the device name by prepending + * this string to avoid collisions. + */ +static const char *DESC_CACHE_KEY_PREFIX = "desc_cache"; + +/* + * The set of descriptors to be returned to libdiskmgt is + * maintained via a list of dm_descriptor_t handles. + * descriptors are added by new_descriptor() and + * cache_descriptor_to_free(). + */ +typedef struct { + dm_descriptor_t desc; + boolean_t virtual; +} desc_free_t; +static dlist_t *_desc_to_free = NULL; + +static char *find_cached_name(dm_descriptor_t desc); +static nvlist_t *find_cached_attrs(char *name); + +static int add_descriptor_to_free(dm_descriptor_t desc); + +static void release_name_cache(); +static void release_desc_to_free_cache(); +static void release_attribute_cache(); +static void release_descriptor_cache(); + +static uint32_t interal_name_count = 0; + +/* + * FUNCTION: create_device_caches() + * + * PURPOSE: Helper which initializes the module's private data + * structures. + */ +int +create_device_caches() +{ + if (hcreate(MAX_CACHED_OBJECTS) == 0) { + return (ENOMEM); + } + + return (0); +} + +/* + * FUNCTION: release_device_caches() + * + * PURPOSE: Helper which cleans up memory allocated to the module's + * private data structures. + */ +int +release_device_caches() +{ + release_name_cache(); + release_desc_to_free_cache(); + release_attribute_cache(); + release_descriptor_cache(); + + return (0); +} + +/* + * FUNCTION: free_desc_cache_object(void *obj) + * + * INPUT: obj - opaque pointer + * + * PURPOSE: Frees memory associated with an entry in the + * desc cache. + * + * Assumes that the input object is a pointer + * to a desc_cache_t struct. + */ +static void +free_desc_cache_object( + void *obj) +{ + if (obj == NULL) { + return; + } + + free(((desc_cache_t *)obj)->name); + free(obj); +} +/* + * FUNCTION: release_descriptor_cache() + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Frees all entries in the name cache. + */ +static void +release_descriptor_cache() +{ + oprintf(OUTPUT_DEBUG, + gettext(" destroying descriptor cache (%d items)\n"), + dlist_length(_desc_cache)); + + dlist_free_items(_desc_cache, free_desc_cache_object); + _desc_cache = NULL; +} + +/* + * FUNCTION: add_cached_descriptor(char *name, dm_descriptor_t desc) + * + * INPUT: name - a device name + * desc - a dm_descriptor_t handle + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Adds an entry to the descriptor cache using the input + * descriptor and name. + * + * Note that all of the lookup caches shares the same hsearch() + * hash table and that the names used as lookup keys for the + * desc_cache_t and attr_cache_t cause collisions. + * + * The desc_cache_t map alters the device name to avoid collisions. + */ +int +add_cached_descriptor( + char *name, + dm_descriptor_t desc) +{ + desc_cache_t *dcp; + char buf[MAXNAMELEN+1]; + dlist_t *item; + ENTRY entry; + + if ((dcp = (desc_cache_t *) + calloc(1, sizeof (desc_cache_t))) == NULL) { + return (ENOMEM); + } + + dcp->desc = desc; + + (void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name); + dcp->name = strdup(buf); + if (dcp->name == NULL) { + free(dcp); + return (ENOMEM); + } + + /* + * insert into the hashtable... ignore the return from hsearch(), + * there is no existing entry corresponding to desc since the + * map was already searched just before this function is called, + * see get_name() below + */ + entry.key = dcp->name; + entry.data = (void *)dcp; + (void) hsearch(entry, ENTER); + + /* insert into the list cache... */ + if ((item = dlist_new_item((void *)dcp)) == NULL) { + free(dcp); + return (ENOMEM); + } + + _desc_cache = dlist_append(item, _desc_cache, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: dm_descriptor_t find_cached_descriptor(char *name) + * + * INPUT: char * - pointer to a name or alias. + * + * RETURNS: dm_descriptor_t - dm_descriptor_t handle cached under the + * input name if a match is found. A null descriptor + * is returned if no match is found. + * + * PURPOSE: Searches for the desc that has been cached for + * the input device name. + * + * Note that all of the lookup caches shares the same hsearch() + * hash table and that the names used as lookup keys for the + * desc_cache_t and attr_cache_t cause collisions. + * + * The desc_cache_t map alters the device name to avoid collisions. + */ +dm_descriptor_t +find_cached_descriptor( + char *name) +{ + ENTRY item; + ENTRY *cached_item = NULL; + char buf[MAXNAMELEN+1]; + dm_descriptor_t desc = (dm_descriptor_t)0; + + (void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name); + item.key = buf; + + /* get descriptor associated with this name */ + if ((cached_item = hsearch(item, FIND)) != NULL) { + /* LINTED */ + desc = ((desc_cache_t *)cached_item->data)->desc; + } + + return (desc); +} + +/* + * FUNCTION: free_name_cache_object(void *obj) + * + * INPUT: obj - opaque pointer + * + * PURPOSE: Frees memory associated with an entry in the + * name cache. + * + * Assumes that the input object is a pointer + * to a name_cache_t struct. + */ +static void +free_name_cache_object( + void *obj) +{ + if (obj == NULL) { + return; + } + + free(((name_cache_t *)obj)->desc); + free(((name_cache_t *)obj)->name); + free(obj); +} + +/* + * FUNCTION: release_name_cache() + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Frees all entries in the name cache. + */ +static void +release_name_cache() +{ + oprintf(OUTPUT_DEBUG, + gettext(" destroying name cache (%d items)\n"), + dlist_length(_name_cache)); + + dlist_free_items(_name_cache, free_name_cache_object); + _name_cache = NULL; +} + +/* + * FUNCTION: add_cached_name(dm_descriptor_t desc, char *name) + * + * INPUT: desc - a dm_descriptor_t handle + * name - a device name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Adds an entry to the name cache using the input + * descriptor and name. + */ +int +add_cached_name( + dm_descriptor_t desc, + char *name) +{ + name_cache_t *ncp; + char buf[MAXNAMELEN+1]; + dlist_t *item; + ENTRY entry; + + if ((ncp = (name_cache_t *) + calloc(1, sizeof (name_cache_t))) == NULL) { + return (ENOMEM); + } + + (void) snprintf(buf, MAXNAMELEN, "%llu", desc); + ncp->desc = strdup(buf); + if (ncp->desc == NULL) { + free(ncp); + return (ENOMEM); + } + + ncp->name = strdup(name); + if (ncp->name == NULL) { + free(ncp->desc); + free(ncp); + return (ENOMEM); + } + + /* + * insert into the hashtable... ignore the return from hsearch(), + * there is no existing entry corresponding to desc since the + * map was already searched just before this function is called, + * see get_name() below + */ + entry.key = ncp->desc; + entry.data = (void *)ncp; + (void) hsearch(entry, ENTER); + + /* insert into the list cache... */ + if ((item = dlist_new_item((void *)ncp)) == NULL) { + free(ncp->desc); + free(ncp->name); + free(ncp); + return (ENOMEM); + } + + _name_cache = dlist_append(item, _name_cache, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: char *find_cached_name(dm_descriptor_t desc) + * + * INPUT: desc - a dm_descriptor_t handle + * + * RETURNS: char * - pointer to the name cached for the descriptor. + * Null otherwise. + * + * PURPOSE: Searches for the name that has been cached for + * the input dm_descriptor_t. + * + * Search linked list. + */ +static char * +find_cached_name( + dm_descriptor_t desc) +{ + char buf[MAXNAMELEN+1]; + ENTRY item; + ENTRY *cached_item = NULL; + char *name = NULL; + + (void) snprintf(buf, MAXNAMELEN, "%llu", desc); + item.key = buf; + + /* get name associated with this descriptor */ + if ((cached_item = hsearch(item, FIND)) != NULL) { + /* LINTED */ + name = ((name_cache_t *)cached_item->data)->name; + } + + return (name); +} + +/* + * FUNCTION: get_name(dm_descriptor_t desc, + * char_t **name) + * + * INPUT: desc - a dm_descriptor_t handle + * + * OUTPUT: name - pointer to char * to hold the name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Searches for the name that has been cached for the + * input dm_descriptor_t. + * + * Names are cached using the dm_descriptor. + * If no name has yet been cached, it is retrieved from + * libdiskmgt and added to the cache. + * + * Names are cached so that all name strings obtained from + * libdiskmgt will get properly released when layout completes. + */ +int +get_name( + dm_descriptor_t desc, + char **name) +{ + + int dm_free = 1; + int error = 0; + + if ((desc != (dm_descriptor_t)0) && + (*name = find_cached_name(desc)) == NULL) { + + /* not in descriptor->name cache/map, add it */ + + if (is_virtual_slice(desc) != B_TRUE) { + + dm_desc_type_t type; + + *name = dm_get_name(desc, &error); + if (error != 0) { + volume_set_error( + gettext("failed to get name for descriptor: %d\n"), + error); + return (-1); + } + + /* + * some devices can be unnamed... + * assign a unique internal name if necessary + */ + if (*name == NULL) { + char buf[MAXNAMELEN]; + + dm_free = 0; + (void) snprintf(buf, MAXNAMELEN-1, "temp-name-%lu", + interal_name_count++); + *name = strdup(buf); + if (*name == NULL) { + volume_set_error( + gettext("failed to get name for descriptor: %d\n"), + errno); + return (-1); + } + oprintf(OUTPUT_DEBUG, + gettext("unnamed descriptor %llu assigned %s\n"), + desc, *name); + } + + /* + * media can have the same name as the associated drive + * which hoses the attribute caching scheme, so unique-ify + */ + if ((type = dm_get_type(desc)) == DM_MEDIA) { + char buf[MAXNAMELEN]; + (void) snprintf(buf, MAXNAMELEN-1, "%s-%d", *name, type); + error = add_cached_name(desc, buf); + } else { + error = add_cached_name(desc, *name); + } + if (dm_free) + dm_free_name(*name); + else + free(*name); + + if (error == 0) { + /* return copied name */ + *name = find_cached_name(desc); + } else { + *name = NULL; + } + } + } + + return (error); +} + +/* + * FUNCTION: free_attr_cache_object(void *obj) + * + * INPUT: obj - opaque pointer + * + * PURPOSE: Frees memory associated with an entry in the + * attribute cache. + * + * Assumes that the input object is a pointer + * to a attr_cache_t struct. + */ +static void +free_attr_cache_object( + void *obj) +{ + if (obj == NULL) { + return; + } + + nvlist_free(((attr_cache_t *)obj)->attrs); + free(obj); +} + +/* + * FUNCTION: release_attribute_cache() + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Frees all entries in the attribute cache. + */ +void +release_attribute_cache() +{ + oprintf(OUTPUT_DEBUG, + gettext(" destroying attribute cache (%d items)\n"), + dlist_length(_attr_cache)); + + dlist_free_items(_attr_cache, free_attr_cache_object); + _attr_cache = NULL; + + /* cleanup attribute cache lookup hashtable */ + hdestroy(); +} + +/* + * FUNCTION: add_cached_attributes(char *name, nvlist_t *attrs) + * + * INPUT: name - a device name + * attrs - pointer to an nvlist_t attribute structure + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Adds an entry to the attribute cache using the input + * name and attributes. + * + * Uses a linked list to cache attributes. + * Keeps a parallel hash table for faster lookup. + */ +int +add_cached_attributes( + char *name, + nvlist_t *attrs) +{ + attr_cache_t *acp = NULL; + dlist_t *item = NULL; + ENTRY *exist = NULL; + ENTRY entry; + + /* insert into the hashtable... */ + entry.key = name; + entry.data = (void *)attrs; + + if ((exist = hsearch(entry, ENTER)) != NULL) { + /* replace the existing attrs entry */ + exist->data = (void *)attrs; + } + + if ((acp = (attr_cache_t *)calloc(1, sizeof (attr_cache_t))) == NULL) { + return (ENOMEM); + } + + acp->name = name; + acp->attrs = attrs; + + /* and cache of attr structs to be freed */ + if ((item = dlist_new_item((void *)acp)) == NULL) { + free(acp); + return (ENOMEM); + } + + _attr_cache = dlist_append(item, _attr_cache, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: nvlist_t *find_cached_attrs(char *name) + * + * INPUT: name - a device name + * + * RETURNS: nvlist_t * - pointer to an nvlist_t attribute structure + * cached under 'name'. Null otherwise. + * + * PURPOSE: Searches for the nvlist attributes that have been + * cached for the input name. + */ +static nvlist_t * +find_cached_attrs( + char *name) +{ + ENTRY item; + ENTRY *cached_item = NULL; + nvlist_t *attrs = NULL; + + item.key = name; + + /* get attributes cached under this name */ + if ((cached_item = hsearch(item, FIND)) != NULL) { + /* LINTED */ + attrs = (nvlist_t *)cached_item->data; + } + + return (attrs); +} + +/* + * FUNCTION: get_cached_attributes(dm_descriptor_t desc, + * nvlist_t **attrs) + * + * INPUT: desc - a dm_descriptor_t handle + * + * OUTPUT: attrs - pointer to an nvlist_t attribute structure + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Searches for the nvlist attributes that have been + * cached for the input dm_descriptor_t. + * + * Attributes are cached using the name associated with + * the descriptor. If no attributes have yet been cached + * they are retrieved from libdiskmgt and added to the + * cache. + * + * Attributes are cached so that layout may store transient + * data relevant to the layout process. + */ +int +get_cached_attributes( + dm_descriptor_t desc, + nvlist_t **attrs) +{ + int error = 0; + char *name = NULL; + + if ((desc != (dm_descriptor_t)0) && + (error = get_name(desc, &name)) == 0) { + + if ((*attrs = find_cached_attrs(name)) == NULL) { + /* get attrs and cache them */ + *attrs = dm_get_attributes(desc, &error); + if (error == 0) { + error = add_cached_attributes(name, *attrs); + } + } + } + + return (error); +} + +/* + * FUNCTION: new_descriptor(dm_descriptor_t *desc) + * + * INPUT: desc - a pointer to a dm_descriptor_t to hold + * the result. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Allocates a new dm_descriptor_t handle. + * + * This is necessary because the process may have to + * create "virtual" objects to represent devices that + * do not yet exist on the system and hence are unknown + * to libdiskmgt and diskmgtd. + * + * A unique handle is created for such objects and may + * be used by layout to access the virtual devices as + * if they were obtained from libdiskmgt. + */ +int +new_descriptor( + dm_descriptor_t *desc) +{ + desc_free_t *dfp; + dlist_t *item; + + *desc = NULL; + + if ((dfp = (desc_free_t *) + calloc(1, sizeof (desc_free_t))) == NULL) { + return (ENOMEM); + } + + dfp->desc = (uintptr_t)dfp; + dfp->virtual = B_TRUE; + + if ((item = dlist_new_item((void *)dfp)) == NULL) { + free(dfp); + return (ENOMEM); + } + + _desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD); + + *desc = (uintptr_t)dfp; + + return (0); +} + +/* + * FUNCTION: add_descriptors_to_free(dm_descriptor_t *desc) + * + * INPUT: desc - an array of dm_descriptor_t handles from + * libdiskmgt + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Function which accepts an array of dm_descriptor_t handles + * that need to be returned to libdiskmgt. + * + * The array is iterated and each handle is passed to + * add_descriptor_to_free. + */ +int +add_descriptors_to_free( + dm_descriptor_t *desc_list) +{ + int i = 0; + + if (desc_list != NULL) { + for (i = 0; desc_list[i] != NULL; i++) { + (void) add_descriptor_to_free(desc_list[i]); + } + } + + return (0); +} + +/* + * FUNCTION: add_descriptor_to_free(dm_descriptor_t desc) + * + * INPUT: desc - dm_descriptor_t handle from libdiskmgt + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Remembers a dm_descriptor_t handle which needs to be + * returned to libdiskmgt. These handles represent memory + * allocated by the the diskmgtd and must be returned in + * order for that memory to be released. + * + * The handles are cached for the duration of layout + * processing so that layout is guaranteed to have + * unique handles for all objects received from + * libdiskmgt. + * + * The caching is accomplished by adding the handle to + * a list of desc_free_t structs. + */ +static int +add_descriptor_to_free( + dm_descriptor_t desc) +{ + desc_free_t *dfp = NULL; + dlist_t *item = NULL; + + if (desc == (dm_descriptor_t)0) { + return (0); + } + + if (is_virtual_slice(desc) == B_TRUE) { + /* don't return virtual slice descriptors to libdiskmgt */ + return (0); + } + + if ((dfp = calloc(1, sizeof (desc_free_t))) == NULL) { + return (ENOMEM); + } + + dfp->desc = desc; + dfp->virtual = B_FALSE; + + if ((item = dlist_new_item((void *)dfp)) == NULL) { + free(dfp); + return (ENOMEM); + } + + _desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: release_desc_to_free_cache() + * + * PURPOSE: Frees all entries in the desc_to_free cache. + * + * Iterates the _desc_to_free list and builds an + * array with all dm_descriptor_t handles that were + * obtained from libdiskmgt. Passing this array to + * dm_free_descriptors() is faster than calling + * dm_free_descriptor() to free individual handles. + */ +void +release_desc_to_free_cache() +{ + dlist_t *iter; + dm_descriptor_t *array; + int i = 0; + + oprintf(OUTPUT_DEBUG, + gettext(" destroying desc_to_free cache (%d items)\n"), + dlist_length(_desc_to_free)); + + array = (dm_descriptor_t *)calloc( + dlist_length(_desc_to_free) + 1, sizeof (dm_descriptor_t)); + + if (array != NULL) { + for (iter = _desc_to_free; iter != NULL; iter = iter->next) { + desc_free_t *dfp = (desc_free_t *)iter->obj; + if (dfp->virtual == B_FALSE) { + array[i++] = dfp->desc; + } + } + array[i] = (dm_descriptor_t)0; + dm_free_descriptors(array); + } + + /* + * If the calloc failed, the descriptors aren't explicitly freed, + * but the libdiskmgt daemon will eventually reclaim them after + * a period of inactivity. + */ + dlist_free_items(_desc_to_free, free); + + _desc_to_free = NULL; +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h new file mode 100644 index 0000000000..6328e6b7ec --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h @@ -0,0 +1,101 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_DEVICE_CACHE_H +#define _LAYOUT_DEVICE_CACHE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This module manages cached copies of a dm_descriptor_t's nvpair + * list of attributes and its device name. The caches are used to + * make sure that the memory allocated to these objects is correctly + * released after the layout process has finished. The cached attrs + * also allow the layout code to store and retrieve transient, + * layout-private data in the same data structure as the other + * relevant device information. + * + * There are two primary caches of information: + * + * descriptor->name - which maps a dm_descriptor_t handle to + * the associated device's name + * + * name->attributes - which maps a device name to an nvlist_t + * attribute collection. + * + * These two data structures thus allow the following lookup chain: + * descriptor->name->attributes. + * + * The attributes are accessed by device name because the it is the + * unique identifier for the device. The descriptor returned by + * libdiskmgt is just an arbitrary handle, multiple calls into the + * library may return different descriptors for the same device. + * + * Descriptors are also get re-cycled by the library which could + * result in the same descriptor being used to represent different + * devices (although not concurrently). To prevent such recycling + * all of the descriptors are held until the layout process has + * completed. + * + * Performance testing indicated that searching the lists of known + * devices by display (CTD or DID) name or alias was a significant + * bottleneck. A mapping from display name to descriptor was added + * to address this. + * + * The module should be initialized once by calling create_device_caches() + * prior to any call which accesses data maintained by the cache. + * + * The caches should be flushed after all accesses have completed by + * calling release_device_caches. + */ + +#include "libdiskmgt.h" +#include "layout_device_util.h" + +extern int create_device_caches(); +extern int release_device_caches(); + +extern int add_cached_descriptor(char *name, dm_descriptor_t desc); +extern dm_descriptor_t find_cached_descriptor(char *name); + +extern int add_cached_name(dm_descriptor_t desc, char *name); +extern int get_name(dm_descriptor_t desc, char **name); + +extern int add_cached_attributes(char *name, nvlist_t *attrs); +extern int get_cached_attributes(dm_descriptor_t desc, nvlist_t **list); + +extern int new_descriptor(dm_descriptor_t *desc); +extern int add_descriptors_to_free(dm_descriptor_t *desc_list); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_DEVICE_CACHE_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_util.c b/usr/src/cmd/lvm/metassist/layout/layout_device_util.c new file mode 100644 index 0000000000..1da277e4e3 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_device_util.c @@ -0,0 +1,3458 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/vtoc.h> +#include <sys/dktp/fdisk.h> +#include <errno.h> +#include <meta.h> + +#include <libdiskmgt.h> +#include "meta_repartition.h" + +#define _LAYOUT_DEVICE_UTIL_C + +#include "volume_dlist.h" +#include "volume_error.h" +#include "volume_output.h" +#include "volume_nvpair.h" + +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_slice.h" + +/* + * Macros to produce a quoted string containing the value of a + * preprocessor macro. For example, if SIZE is defined to be 256, + * VAL2STR(SIZE) is "256". This is used to construct format + * strings for scanf-family functions below. + */ +#define QUOTE(x) #x +#define VAL2STR(x) QUOTE(x) + +/* private utilities for disks */ +static int disk_get_uint64_attribute( + dm_descriptor_t disk, + char *attr, + uint64_t *val); + +static int disk_get_boolean_attribute( + dm_descriptor_t disk, + char *attr, + boolean_t *bool); + +static int disk_get_rpm( + dm_descriptor_t disk, + uint32_t *val); + +static int disk_get_sync_speed( + dm_descriptor_t disk, + uint32_t *val); + +static int disk_has_virtual_slices( + dm_descriptor_t disk, + boolean_t *bool); + +static int disk_get_virtual_slices( + dm_descriptor_t disk, + dlist_t **list); + +static int disk_get_reserved_indexes( + dm_descriptor_t disk, + uint16_t **array); + +static int disk_get_associated_desc( + dm_descriptor_t disk, + dm_desc_type_t assoc_type, + char *assoc_type_str, + dlist_t **list); + +/* utilities for slices */ +static int slice_get_uint64_attribute( + dm_descriptor_t slice, + char *attr, + uint64_t *val); + +static int slice_set_attribute( + dm_descriptor_t slice, + char *attr, + uint64_t val); + +/* + * Virtual slices are created to represent slices that will be + * on the system after disks have been added to the destination + * diskset. For the purposes of layout, these slices must + * look & function just as real slices that are currently on + * the system. + */ +static dlist_t *_virtual_slices = NULL; + +/* temporary implementation */ +static int virtual_repartition_drive( + dm_descriptor_t disk, + mdvtoc_t *vtocp); + +static int disk_add_virtual_slice( + dm_descriptor_t disk, + dm_descriptor_t slice); + +static int virtual_slice_get_disk( + dm_descriptor_t slice, + dm_descriptor_t *diskp); + +/* + * attribute names for layout private information stored in + * device nvpair attribute lists. + */ +static char *ATTR_RESERVED_INDEX = "vdu_reserved_index"; +static char *ATTR_VIRTUAL_SLICES = "vdu_virtual_slices"; +static char *ATTR_DISK_FOR_SLICE = "vdu_disk_for_slice"; +static char *ATTR_DEV_CTD_NAME = "vdu_device_ctd_name"; +static char *ATTR_HBA_N_DISKS = "vdu_hba_n_usable_disks"; + +/* + * FUNCTION: is_ctd_like_slice_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name follows an alternate slice + * naming scheme similar to CTD + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is of the form XXXsNNN + * (e.g., whizzy0s1) + */ +boolean_t +is_ctd_like_slice_name( + char *name) +{ + uint_t s = 0; + uint_t d = 0; + int l = 0; + boolean_t is = B_FALSE; + + /* The format strings below match and discard the non-numeric part. */ + if ((sscanf(name, "/dev/dsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 || + sscanf(name, "/dev/rdsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 || + sscanf(name, "%*[^0-9/]%us%u%n", &d, &s, &l) == 2) && + (l == strlen(name))) { + is = B_TRUE; + } + + return (is); +} + +/* + * FUNCTION: is_bsd_like_slice_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name follows an alternate slice + * BSD-like naming scheme + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is of the form XXXNNN[a-h] + * (e.g., whizzy0a) + */ +boolean_t +is_bsd_like_slice_name( + char *name) +{ + uint_t d = 0; + int l = 0; + boolean_t is = B_FALSE; + + /* The format strings below match and discard the non-numeric part. */ + if ((sscanf(name, "/dev/dsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 || + sscanf(name, "/dev/rdsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 || + sscanf(name, "%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1) && + (l == strlen(name))) { + is = B_TRUE; + } + + return (is); +} + +/* + * FUNCTION: is_did_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name is from the DID namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is from the DID namespace. + */ +boolean_t +is_did_name( + char *name) +{ + return (is_did_slice_name(name) || is_did_disk_name(name)); +} + +/* + * FUNCTION: is_did_slice_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a slice from the DID + * namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is a slice from the DID namespace. + */ +boolean_t +is_did_slice_name( + char *name) +{ + uint_t d = 0, s = 0; + int l = 0; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/did/rdsk/d%us%u%n", &d, &s, &l) == 2 || + sscanf(name, "/dev/did/dsk/d%us%u%n", &d, &s, &l) == 2 || + sscanf(name, "d%us%u%n", &d, &s, &l) == 2) || + (l == strlen(name))) { + is = B_TRUE; + } + + return (is); +} + +/* + * FUNCTION: is_did_disk_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a disk from the DID + * namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is a disk from the DID namespace. + */ +boolean_t +is_did_disk_name( + char *name) +{ + uint_t d = 0; + int l = 0; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/did/rdsk/d%u%n", &d, &l) == 1 || + sscanf(name, "/dev/did/dsk/d%u%n", &d, &l) == 1 || + sscanf(name, "d%u%n", &d, &l) == 1) && + (l == strlen(name))) { + is = B_TRUE; + } + + return (is); +} + +/* + * FUNCTION: is_ctd_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name is from the CTD namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is from the CTD namespace. + * + * {/dev/dsk/, /dev/rdsk/}cXtXdXsX + * {/dev/dsk/, /dev/rdsk/}cXtXdX + * {/dev/dsk/, /dev/rdsk/}cXdXsX + * {/dev/dsk/, /dev/rdsk/}cXdX + */ +boolean_t +is_ctd_name( + char *name) +{ + return (is_ctd_slice_name(name) || is_ctd_disk_name(name) || + is_ctd_target_name(name) || is_ctd_ctrl_name(name)); +} + +/* + * FUNCTION: is_ctd_slice_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a slice from the CTD + * namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is a slice name from the + * CTD namespace. + * + * {/dev/dsk/, /dev/rdsk/}cXt<WWN>dXsX + * {/dev/dsk/, /dev/rdsk/}cXtXdXsX + * {/dev/dsk/, /dev/rdsk/}cXdXsX + */ +boolean_t +is_ctd_slice_name( + char *name) +{ + uint_t c = 0, t = 0, d = 0, s = 0; + char buf[MAXNAMELEN+1]; + int l = 0; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/dsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || + sscanf(name, "/dev/rdsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || + sscanf(name, "c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || + sscanf(name, "/dev/dsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 || + sscanf(name, "/dev/rdsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 || + sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 3 || + sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 2) && + (l == strlen(name))) { + is = B_TRUE; + } else if ( + (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2) && (l == strlen(name))) { + char *dev_pos; + + /* see if buf ends with "dXsX" */ + if (((dev_pos = strrchr(buf, 'd')) != NULL) && + (sscanf(dev_pos, "d%us%u%n", &d, &s, &l) == 2) && + (l == strlen(dev_pos))) { + + char wwn[MAXNAMELEN+2]; + + /* buf ends with "dXsX", truncate at the 'd' */ + *dev_pos = '\0'; + + /* prepend "0X" to remainder and try to scan as a hex WWN */ + (void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf); + if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) { + is = B_TRUE; + } + } + } + + return (is); +} + +/* + * FUNCTION: is_ctd_disk_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a disk from the CTD + * namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is a disk name from the + * CTD namespace. + * + * {/dev/dsk/, /dev/rdsk/}cXt<WWN>dX + * {/dev/dsk/, /dev/rdsk/}cXtXdX + * {/dev/dsk/, /dev/rdsk/}cXdX + */ +boolean_t +is_ctd_disk_name( + char *name) +{ + uint_t c = 0, t = 0, d = 0; + int l = 0; + char buf[MAXNAMELEN+1]; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/dsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 || + sscanf(name, "/dev/rdsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 || + sscanf(name, "c%ut%ud%u%n", &c, &t, &d, &l) == 3 || + sscanf(name, "/dev/dsk/c%ud%u%n", &c, &d, &l) == 2 || + sscanf(name, "/dev/rdsk/c%ud%n%n", &c, &d, &l) == 2 || + sscanf(name, "c%ud%u%n", &c, &d, &l) == 2) && + (l == strlen(name))) { + is = B_TRUE; + } else if ((sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2) && (l == strlen(name))) { + char *dev_pos; + + /* see if buf ends with "dX" */ + if (((dev_pos = strrchr(buf, 'd')) != NULL) && + (sscanf(dev_pos, "d%u%n", &d, &l) == 1) && + (l == strlen(dev_pos))) { + + char wwn[MAXNAMELEN+2]; + + /* buf ends with "dX", truncate at the 'd' */ + *dev_pos = '\0'; + + /* prepend "0X" to remainder and try to scan as a hex WWN */ + (void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf); + if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) { + is = B_TRUE; + } + } + } + + return (is); +} + +/* + * FUNCTION: is_ctd_disk_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a target from the CTD + * namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is a target name from the + * CTD namespace. + * + * {/dev/dsk/, /dev/rdsk/}cXt<WWN> + * {/dev/dsk/, /dev/rdsk/}cXtX + */ +boolean_t +is_ctd_target_name( + char *name) +{ + uint_t c = 0, t = 0; + int l = 0; + char buf[MAXNAMELEN+1]; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/dsk/c%ut%u%n", &c, &t, &l) == 2 || + sscanf(name, "/dev/rdsk/c%ut%u%n", &c, &t, &l) == 2 || + sscanf(name, "c%ut%u%n", &c, &t, &l) == 2) && + (l == strlen(name))) { + is = B_TRUE; + } else if ( + (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, buf, &l) == 2 || + sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n", + &c, &buf, &l) == 2) && (l == strlen(name))) { + + char wwn[MAXNAMELEN+2]; + + /* prepend "0X" to buf and try to scan as a hex WWN */ + (void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf); + if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) { + is = B_TRUE; + } + } + + return (is); +} + +/* + * FUNCTION: is_ctd_ctrl_name(char *name) + * INPUT: name - a char * + * + * RETURNS: boolean_t - B_TRUE - if name represents a controller/hba + * from the CTD namespace + * B_FALSE - otherwise + * + * PURPOSE: Determines if the input name is an HBA name from the + * CTD namespace. + * + * {/dev/dsk/, /dev/rdsk/}cX + */ +boolean_t +is_ctd_ctrl_name( + char *name) +{ + uint_t c = 0; + int l = 0; + boolean_t is = B_FALSE; + + if ((sscanf(name, "/dev/dsk/c%u%n", &c, &l) == 1 || + sscanf(name, "/dev/rdsk/c%u%n", &c, &l) == 1 || + sscanf(name, "c%u%n", &c, &l) == 1) && + (l == strlen(name))) { + is = B_TRUE; + } + + return (is); +} + +/* + * FUNCTION: set_display_name(dm_descriptor_t desc, char *name) + * get_display_name(dm_descriptor_t desc, char **name) + * + * INPUT: desc - a dm_descriptor_t handle for a device + * name - a char * name + * + * OUTPUT: **name - a pointer to a char * to hold the display + * name associated with the input descriptor. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helpers to set/get the input descriptor's display name. + * + * Only slices, disks and HBAs should have display names. + * + * The attribute is only set in the cached copy of + * the device's nvpair attribute list. This function + * does not affect the underlying physical device. + * + * An entry is added in the name->descriptor cache + * so the descriptor can be found by name quickly. + */ +int +set_display_name( + dm_descriptor_t desc, + char *name) +{ + nvlist_t *attrs = NULL; + int error = 0; + + ((error = add_cached_descriptor(name, desc)) != 0) || + (error = get_cached_attributes(desc, &attrs)) || + (error = set_string(attrs, ATTR_DEV_CTD_NAME, name)); + + return (error); +} + +int +get_display_name( + dm_descriptor_t desc, + char **name) +{ + nvlist_t *attrs = NULL; + int error = 0; + + ((error = get_cached_attributes(desc, &attrs)) != 0) || + (error = get_string(attrs, ATTR_DEV_CTD_NAME, name)); + + return (error); +} + +/* + * FUNCTION: disk_get_slices(dm_descriptor_t disk, dlist_t **list) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *list - a pointer to list to hold the results. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Collect all of the known slices for the input disk. + * + * These slices may be actual slices which currently exist + * on the disk, or virtual slices which will exist when the + * disk is added to the destination diskset. + */ +int +disk_get_slices( + dm_descriptor_t disk, + dlist_t **list) +{ + dm_descriptor_t *media = NULL; + boolean_t virtual = B_FALSE; + int i = 0; + int error = 0; + + *list = 0; + + if ((error = disk_has_virtual_slices(disk, &virtual)) != 0) { + return (error); + } + + if (virtual == B_TRUE) { + error = disk_get_virtual_slices(disk, list); + } + + /* add real slices from disk's media... */ + media = dm_get_associated_descriptors(disk, DM_MEDIA, &error); + (void) add_descriptors_to_free(media); + + if (error == 0) { + /* if there's no media, this is a removeable drive */ + if (media != NULL && *media != NULL) { + + /* examine media's slices... */ + dm_descriptor_t *slices = NULL; + slices = dm_get_associated_descriptors(*media, + DM_SLICE, &error); + (void) add_descriptors_to_free(slices); + + if (error != 0) { + print_get_assoc_desc_error(disk, gettext("slice"), error); + } else { + for (i = 0; (slices[i] != NULL) && (error == 0); i++) { + dlist_t *item = dlist_new_item((void *)slices[i]); + if (item == NULL) { + error = ENOMEM; + } else { + *list = dlist_append(item, *list, AT_TAIL); + } + } + free(slices); + } + free(media); + } + } else { + print_get_assoc_desc_error(disk, gettext("media"), error); + } + + return (error); +} + +int +get_virtual_slices( + dlist_t **list) +{ + *list = _virtual_slices; + + return (0); +} + +/* + * FUNCTION: virtual_repartition_drive(dm_descriptor_t disk, + * mdvtoc_t *vtocp) + * + * INPUT: disk - the disk to be virtually repartitioned + * + * OUTPUT: vtocp - a poitner to a mdvtoc struct to hold the results + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which emulates the repartitioning that is done + * when a disk is added to a diskset. + * + * Modified version of meta_partition_drive which uses info + * from libdiskmgt to accomplish the repartitioning. + * + * This exists to allow the layout module to run with a + * simulated hardware environment. + * + * XXX This method absolutely does not produce the exact + * same result as meta_repartition_drive: only information + * required by the layout code is returned. Basically, + * a slice 7 (or 6 on EFI labelled disks) is created and + * sized, the remained of the available cylinders are put + * into slice 0. + * + * XXX2 This method is required until there is resolution + * on whether metassist testing will be done using the + * hardware simulation mechanism libdiskmgt provides. + * Doing so will also require parts of libmeta to be + * simulated as well. Some research has been done into + * building an alternate libmeta.so containing + * implementations of the functions used by metassist + * that are compatible with the simulated hardware. + * Actual work is currently on hold. + */ +static int +virtual_repartition_drive( + dm_descriptor_t disk, + mdvtoc_t *vtocp) +{ + uint_t replicaslice = 7; + unsigned long long cylsize; + unsigned long long drvsize; + uint_t reservedcyl; + ushort_t resflag; + unsigned long long ressize; + diskaddr_t replica_start; + diskaddr_t replica_size; + diskaddr_t data_start; + diskaddr_t data_size; + + boolean_t efi = B_FALSE; + uint64_t ncyls = 0; + uint64_t nheads = 0; + uint64_t nsects = 0; + int error = 0; + + /* + * At this point, ressize is used as a minimum value. Later it + * will be rounded up to a cylinder boundary. ressize is in + * units of disk sectors. + */ + ressize = MD_DBSIZE + VTOC_SIZE; + resflag = V_UNMNT; + + ((error = disk_get_is_efi(disk, &efi)) != 0) || + (error = disk_get_ncylinders(disk, &ncyls)) || + (error = disk_get_nheads(disk, &nheads)) || + (error = disk_get_nsectors(disk, &nsects)); + if (error != 0) { + return (error); + } + + if (efi) { + replicaslice = 6; + } + + /* + * Both cylsize and drvsize are in units of disk sectors. + * + * The intended results are of type unsigned long long. Since + * each operand of the first multiplication is of type + * unsigned int, we risk overflow by multiplying and then + * converting the result. Therefore we explicitly cast (at + * least) one of the operands, forcing conversion BEFORE + * multiplication, and avoiding overflow. The second + * assignment is OK, since one of the operands is already of + * the desired type. + */ + cylsize = ((unsigned long long)nheads) * nsects; + drvsize = cylsize * ncyls; + + /* + * How many cylinders must we reserve for slice seven to + * ensure that it meets the previously calculated minimum + * size? + */ + reservedcyl = (ressize + cylsize - 1) / cylsize; + + /* + * It seems unlikely that someone would pass us too small a + * disk, but it's still worth checking for... + */ + if (reservedcyl >= ncyls) { + volume_set_error( + gettext("disk is too small to hold a metadb replica")); + return (-1); + } + + replica_start = 0; + replica_size = reservedcyl * cylsize; + data_start = reservedcyl * cylsize; + data_size = drvsize - (reservedcyl * cylsize); + + /* + * fill in the proposed VTOC information. + */ + + /* We need at least replicaslice partitions in the proposed vtoc */ + vtocp->nparts = replicaslice + 1; + vtocp->parts[MD_SLICE0].start = data_start; + vtocp->parts[MD_SLICE0].size = data_size; + vtocp->parts[MD_SLICE0].tag = V_USR; + vtocp->parts[replicaslice].start = replica_start; + vtocp->parts[replicaslice].size = replica_size; + vtocp->parts[replicaslice].flag = resflag; + vtocp->parts[replicaslice].tag = V_USR; + + return (0); +} + +/* + * FUNCTION: create_virtual_slices(dlist_t *disks) + * + * INPUT: possibles - a list of dm_descriptor_t disk handles for + * disks known to be available for use by layout. + * + * SIDEEFFECT: populates the private of virtual slices. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which creates virtual slices for each disk which + * could be added to a diskset if necessary... + * + * Iterate the input list of available disks and see what the + * slicing would be if the disk were added to a diskset. + * + * For the resulting slices, create virtual slice descriptors + * and attributes for these slices and add them to the list of + * available slices. + */ +int +create_virtual_slices( + dlist_t *disks) +{ + int error = 0; + dlist_t *iter; + boolean_t sim = B_FALSE; + static char *simfile = "METASSISTSIMFILE"; + + sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0)); + + /* see what slices each of the disks will have when added to a set */ + for (iter = disks; error == 0 && iter != NULL; iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + dlist_t *slices = NULL; + mdvtoc_t vtoc; + char *dname; + int i = 0; + + if ((error = get_display_name(disk, &dname)) != 0) { + break; + } + + if (sim != B_TRUE) { + + /* sim disabled: use meta_repartition_drive() */ + + md_error_t mderror = mdnullerror; + int opts = (MD_REPART_FORCE | MD_REPART_DONT_LABEL); + mdsetname_t *sp; + mddrivename_t *dnp; + + /* disk is in the local set */ + sp = metasetname(MD_LOCAL_NAME, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + error = -1; + break; + } + + dnp = metadrivename(&sp, dname, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + error = -1; + break; + } + + if (meta_repartition_drive( + sp, dnp, opts, &vtoc, &mderror) != 0) { + volume_set_error( + gettext("failed to repartition disk %s\n"), + dname); + error = -1; + break; + } + + } else { + + /* sim enabled: use faked repartition code */ + if (virtual_repartition_drive(disk, &vtoc) != 0) { + volume_set_error( + gettext("failed simulated repartition of %s\n"), + dname); + error = -1; + break; + } + } + + /* BEGIN CSTYLED */ + /* + * get the existing slices on the disk, if the repartition + * was successful, these slices need to have their size, start + * blk and size in blks set to 0 + */ + /* END CSTYLED */ + if ((error = disk_get_slices(disk, &slices)) == 0) { + dlist_t *iter2 = slices; + for (; iter2 != NULL; iter2 = iter2->next) { + dm_descriptor_t sp = (uintptr_t)iter2->obj; + ((error = slice_set_start_block(sp, 0)) != 0) || + (error = slice_set_size_in_blocks(sp, 0)) || + (error = slice_set_size(sp, 0)); + } + dlist_free_items(slices, NULL); + } + + /* scan VTOC, find slice with the free space */ + for (i = 0; i < vtoc.nparts; i++) { + + if (vtoc.parts[i].tag == V_USR && + vtoc.parts[i].flag != V_UNMNT) { + + /* non-replica slice with free space */ + char buf[MAXPATHLEN]; + (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i); + + if ((error = add_virtual_slice(buf, + (uint32_t)i, + (uint64_t)vtoc.parts[i].start, + (uint64_t)vtoc.parts[i].size, + disk)) != 0) { + break; + } + + } else if (vtoc.parts[i].tag == V_RESERVED) { + + /* skip EFI reserved slice */ + continue; + + } else if (vtoc.parts[i].tag == V_USR && + vtoc.parts[i].flag == V_UNMNT) { + + /* BEGIN CSTYLED */ + /* + * Make the replica slice 0 sized -- this will + * force the disk to be repartitioned by + * metaset when it is added to the disk set. + * + * XXX this is a temporary workaround until + * 4712873 is integrated... + */ + /* BEGIN CSTYLED */ + char buf[MAXPATHLEN]; + (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i); + add_slice_to_remove(buf, i); + + /* replica slice, stop here */ + break; + } + } + } + + return (error); +} + +/* + * FUNCTION: add_virtual_slice(char *name, uint32_t index, + * uint64_t startblk, uint64_t sizeblks, + * dm_descriptor_t disk) + * + * INPUT: name - the name of the new virtual slice + * index - the VTOC index ... + * startblk - the start block ... + * sizeblks - the size in blocks ... + * disk - the parent disk ... + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which adds the appropriate data structures to + * represent a new virtual slice. + * + * allocates a new descriptor + * adds entries to name->desc and desc->name caches + * allocates an attribute nvpair list + * fills in the relevant attributes for the slice + * associates the slice with its parent disk + * adds an entry to the list of all virtual slices + * generates aliases if the associated disk has aliases. + */ +int +add_virtual_slice( + char *name, + uint32_t index, + uint64_t startblk, + uint64_t sizeblks, + dm_descriptor_t disk) +{ + dm_descriptor_t sp; + nvlist_t *attrs; + char *sname; + dlist_t *aliases = NULL; + dlist_t *item = NULL; + int error = 0; + + if ((error = nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0)) != 0) { + return (error); + } + + /* create descriptor */ + ((error = new_descriptor(&sp)) != 0) || + /* cache name for the descriptor */ + (error = add_cached_name(sp, name)) || + /* cache descriptor for the name */ + (error = add_cached_descriptor(name, sp)) || + + /* fill in attributes */ + (error = set_string(attrs, ATTR_DEV_CTD_NAME, name)) || + (error = set_uint32(attrs, DM_INDEX, index)) || + (error = set_uint64(attrs, DM_START, startblk)) || + (error = set_uint64(attrs, DM_SIZE, sizeblks)) || + (error = set_uint64(attrs, ATTR_DISK_FOR_SLICE, (uint64_t)disk)) || + + /* add attributes to the cache */ + (error = get_name(sp, &sname)) || + (error = add_cached_attributes(sname, attrs)) || + + /* connect slice to disk */ + (error = disk_add_virtual_slice(disk, sp)) || + (error = get_display_name(disk, &name)) || + (error = get_aliases(disk, &aliases)); + + if (error != 0) { + return (error); + } + + /* generate slice's aliases if the disk has aliases */ + if (aliases != NULL) { + char buf[MAXNAMELEN]; + + for (; aliases != NULL; aliases = aliases->next) { + (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", + (char *)aliases->obj, index); + error = set_alias(sp, buf); + } + dlist_free_items(aliases, free); + } + + if ((item = dlist_new_item((void *)sp)) == NULL) { + return (ENOMEM); + } + + _virtual_slices = dlist_append(item, _virtual_slices, AT_HEAD); + + oprintf(OUTPUT_DEBUG, + gettext(" created virtual slice %s start: %llu, size: %llu\n"), + sname, startblk, sizeblks); + + return (error); +} + +/* + * FUNCTION: release_virtual_slices() + * + * PURPOSE: Helper which cleans up the module private list of virtual + * slices. + * + * The descriptors for the virtual slices are cleaned up + * in device_cache_util.free_cached_descriptors + */ +void +release_virtual_slices() +{ + dlist_free_items(_virtual_slices, NULL); + _virtual_slices = NULL; +} + +/* + * FUNCTION: disk_add_virtual_slice(dm_descriptor_t disk, + * dm_descriptor_t slice) + * + * INPUT: disk - a dm_descriptor_t disk handle + * slice - a dm_descriptor_t virtual slice handle + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which adds a virtual slice to the input disk's + * list of virtual slices. + * + * The disk's virtual slice dm_descriptor_t handles are + * stored in the disk's nvpair attribute list. + */ +static int +disk_add_virtual_slice( + dm_descriptor_t disk, + dm_descriptor_t slice) +{ + nvlist_t *attrs = NULL; + uint64_t *old_slices = NULL; + uint64_t *new_slices = NULL; + uint_t nelem = 0; + int i = 0; + int error = 0; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint64_array( + attrs, ATTR_VIRTUAL_SLICES, &old_slices, &nelem)) != 0) { + if (error != ENOENT) { + return (error); + } + error = 0; + } + + /* make a new array */ + new_slices = (uint64_t *)calloc(nelem + 1, sizeof (uint64_t)); + if (new_slices != NULL) { + + for (i = 0; i < nelem; i++) { + new_slices[i] = old_slices[i]; + } + new_slices[i] = slice; + + error = set_uint64_array( + attrs, ATTR_VIRTUAL_SLICES, new_slices, nelem); + + free(new_slices); + + } else { + error = ENOMEM; + } + + return (error); +} + +/* + * FUNCTION: disk_has_virtual_slices(dm_descriptor_t disk, boolean_t *bool) + * + * INPUT: disk - a dm_descriptor_t disk handle + * + * OUTPUT: bool - B_TRUE - if the disk has virtual slices + * B_FALSE - otherwise + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which determines if the input disk has virtual slices. + * + * If a disk has virtual slices, their dm_descriptor_t handles + * will be stored in the disk's nvpair attribute list. + */ +static int +disk_has_virtual_slices( + dm_descriptor_t disk, + boolean_t *bool) +{ + nvlist_t *attrs = NULL; + uint64_t *slices = NULL; + uint_t nelem = 0; + int error = 0; + + *bool = B_FALSE; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint64_array( + attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) { + if (error == ENOENT) { + error = 0; + nelem = 0; + } else { + /* count actual number of elements */ + int i = 0; + while (i < nelem) { + if (slices[i] != -1) { + ++i; + } + } + nelem = i; + } + } + + *bool = (nelem != 0); + + return (error); +} + +/* + * FUNCTION: disk_get_virtual_slices(dm_descriptor_t disk, boolean_t *bool) + * + * INPUT: disk - a dm_descriptor_t disk handle + * + * OUTPUT: list - a dlist_t list of dm_descriptor_t handles for the + * disk's virtual slices. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which retrieves a list of the input disk's virtual + * slices. + * + * If a disk has virtual slices, their dm_descriptor_t handles + * will be stored in the disk's nvpair attribute list. + */ +static int +disk_get_virtual_slices( + dm_descriptor_t disk, + dlist_t **list) +{ + nvlist_t *attrs = NULL; + uint64_t *slices = NULL; + uint_t nelem = 0; + int error = 0; + int i = 0; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint64_array( + attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) { + if (error != ENOENT) { + return (error); + } + + return (0); + } + + for (i = 0; i < nelem && slices[i] != -1; i++) { + dlist_t *item = NULL; + + if ((item = dlist_new_item((void*)slices[i])) == NULL) { + error = ENOMEM; + break; + } + + *list = dlist_append(item, *list, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: is_virtual_slice(dm_descriptor_t desc) + * + * INPUT: desc - a dm_descriptor_t handle + * + * RETURNS: boolean_t - B_TRUE if the input descriptor is for + * a virtual slice. + * B_FALSE otherwise + * + * PURPOSE: Helper which determines whether the input descriptor + * corresponds to a virtual slice. + * + * All virtual slices are stored in a module private list. + * This list is iterated to see if it contains the input + * descriptor. + */ +boolean_t +is_virtual_slice( + dm_descriptor_t desc) +{ + return (dlist_contains(_virtual_slices, + (void*)desc, compare_descriptors)); +} + +/* + * FUNCTION: disk_get_available_slice_index(dm_descriptor_t disk, + * uint32_t *newindex) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *newindex - a pointer to a uint32_t to hold the available + * index. If no index is available, the value pointed + * to is not modified. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: examine the input disk's list of slices and find an unused + * slice index. The replica slice (index 7 or 6) is always + * off-limits -- it shows up as in use. Slice 0 should only + * be used as a last resort. + * + * If an available index is found, it is stored into newindex. + * Otherwise, newindex is unchanged. This allows the caller to + * pass in an index and check if it has been modified on return. + * + * V_NUMPAR is used as the number of available slices, + * SPARC systems have V_NUMPAR == 8, X86 have V_NUMPAR == 16. + * + * EFI disks have only 7. + */ +int +disk_get_available_slice_index( + dm_descriptor_t disk, + uint32_t *newindex) +{ + dlist_t *iter = NULL; + dlist_t *slices = NULL; + uint32_t index = 0; + uint16_t *reserved = NULL; + boolean_t *used = NULL; + boolean_t is_efi = B_FALSE; + int error = 0; + int i = 0; + int nslices = V_NUMPAR; + + if (((error = disk_get_slices(disk, &slices)) != 0) || + (error = disk_get_is_efi(disk, &is_efi)) != 0) { + return (error); + } + + if (is_efi == B_TRUE) { + /* limit possible indexes to 7 for EFI */ + nslices = 7; + } + + used = (boolean_t *)calloc(nslices, sizeof (boolean_t)); + if (used == NULL) { + oprintf(OUTPUT_DEBUG, + gettext("failed allocating slice index array\n"), + NULL); + return (ENOMEM); + } + + /* eliminate indexes that are reserved */ + if ((error = disk_get_reserved_indexes(disk, &reserved)) != 0) { + return (error); + } + + if (reserved != NULL) { + for (i = 0; i < nslices; i++) { + if (reserved[i] == 1) { + used[i] = B_TRUE; + } + } + } + + /* eliminate slices that are in use (have a size > 0) */ + /* 0 sized slices unused slices */ + for (iter = slices; iter != NULL; iter = iter->next) { + dm_descriptor_t sp = (uintptr_t)iter->obj; + uint64_t size = 0; + + ((error = slice_get_index(sp, &index)) != 0) || + (error = slice_get_size_in_blocks(sp, &size)); + if (error != 0) { + return (error); + } + + if (size > 0) { + used[(int)index] = B_TRUE; + } + } + dlist_free_items(slices, NULL); + + for (i = 0; i < nslices; i++) { + + /* skip the index passed in */ + if (i == *newindex) { + continue; + } + + if (used[i] != B_TRUE) { + index = i; + break; + } + } + + if (i != nslices) { + /* return unused slice index */ + *newindex = index; + } + + free((void *)used); + + return (0); +} + +/* + * FUNCTION: disk_get_media_type(dm_descriptor_t slice, uint32_t *type) + * + * INPUT: slice - a dm_descriptor_t handle for a disk + * + * OUTPUT: *type - a pointer to a uint32_t to hold the + * current type value for the media on which + * the input slice resides. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Retrieves the media type for the disk. + * + * Get the media associate with the input disk descriptor + * and determine its type. + */ +int +disk_get_media_type( + dm_descriptor_t disk, + uint32_t *type) +{ + int error = 0; + dm_descriptor_t *mdp = NULL; + + mdp = dm_get_associated_descriptors(disk, DM_MEDIA, &error); + (void) add_descriptors_to_free(mdp); + + if (error != 0) { + print_get_assoc_desc_error(disk, gettext("media"), error); + } else { + /* disk should have exactly 1 media */ + if ((mdp != NULL) && (*mdp != NULL)) { + nvlist_t *attrs = dm_get_attributes(*mdp, &error); + if ((error == 0) && (attrs != NULL)) { + error = get_uint32(attrs, DM_MTYPE, type); + } + + nvlist_free(attrs); + } + /* no media: removeable drive */ + } + + if (mdp != NULL) { + free(mdp); + } + + return (error); +} + +/* + * FUNCTION: disk_get_rpm(dm_descriptor_t disk, uint32_t *val) + * disk_get_sync_speed(dm_descriptor_t disk, uint32_t *val) + * disk_get_size_in_blocks(dm_descriptor_t disk, uint64_t *val) + * disk_get_blocksize(dm_descriptor_t disk, uint64_t *val) + * disk_get_ncylinders(dm_descriptor_t disk, uint64_t *val) + * disk_get_nheads(dm_descriptor_t disk, uint64_t *val) + * disk_get_nsectors(dm_descriptor_t disk, uint64_t *val) + * disk_get_is_efi(dm_descriptor_t disk, boolean_t *val) + * disk_get_is_online(dm_descriptor_t disk, boolean_t *val) + * disk_get_media_type(dm_descriptor_t disk, uint32_t *type) + * disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *val) + * disk_get_start_block(dm_descriptor_t disk, uint64_t *val) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *bool - a pointer to a variable of the appropriate + * type to hold the current value for the attribute + * of interest. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrappers around disk_get_XXX_attribute that know + * which attribute needs to be retrieved and also handle + * any necesasry type or units conversions. + */ +static int +disk_get_rpm( + dm_descriptor_t disk, + uint32_t *val) +{ + uint64_t val64 = 0; + int error = 0; + + if ((error = disk_get_uint64_attribute( + disk, DM_RPM, &val64)) != 0) { + return (error); + } + + *val = (uint32_t)val64; + + return (error); +} + +int +disk_get_drive_type( + dm_descriptor_t disk, + uint32_t *val) +{ + uint64_t val64 = 0; + int error = 0; + + if ((error = disk_get_uint64_attribute( + disk, DM_DRVTYPE, &val64)) != 0) { + return (error); + } + + *val = (uint32_t)val64; + + return (error); +} + +static int +disk_get_sync_speed( + dm_descriptor_t disk, + uint32_t *val) +{ + uint64_t val64 = 0; + int error = 0; + + if ((error = disk_get_uint64_attribute( + disk, DM_SYNC_SPEED, &val64)) != 0) { + return (error); + } + + *val = (uint32_t)val64; + + return (error); +} + +/* returns number of usable blocks */ +int +disk_get_size_in_blocks( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_NACCESSIBLE, val)); +} + +/* returns first usable block on disk */ +int +disk_get_start_block( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_START, val)); +} + +int +disk_get_blocksize( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_BLOCKSIZE, val)); +} + +int +disk_get_ncylinders( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_NCYLINDERS, val)); +} + +int +disk_get_nheads( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_NHEADS, val)); +} + +int +disk_get_nsectors( + dm_descriptor_t disk, + uint64_t *val) +{ + return (disk_get_uint64_attribute(disk, DM_NSECTORS, val)); +} + +/* + * FUNCTION: disk_get_is_online(dm_descriptor_t disk, boolean_t *val) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Determine if the input disk is "online". + * + * Check the status bit of the drive, if it is 1 the drive + * is online, if it is 0 the drive is offline. + */ +int +disk_get_is_online( + dm_descriptor_t disk, + boolean_t *val) +{ + uint64_t status = 0; + int error = 0; + + *val = B_FALSE; + + error = disk_get_uint64_attribute(disk, DM_STATUS, &status); + if (error == 0) { + *val = (status == 1) ? B_TRUE : B_FALSE; + } + + return (error); +} + +/* + * FUNCTION: disk_get_is_efi(dm_descriptor_t disk, boolean_t *bool) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Determine if the input disk is labeled with an EFI label. + * + * The label type is actually a property of the media + * associated with the disk, so retrieve the media and + * check if it is EFI labeled. + */ +int +disk_get_is_efi( + dm_descriptor_t disk, + boolean_t *bool) +{ + return (disk_get_boolean_attribute(disk, DM_EFI, bool)); +} + +/* + * FUNCTION: disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *bool) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Determine if the input disk has an FDISK partition. + */ +int +disk_get_has_fdisk( + dm_descriptor_t disk, + boolean_t *bool) +{ + return (disk_get_boolean_attribute(disk, DM_FDISK, bool)); +} + +/* + * FUNCTION: disk_get_has_solaris_partition(dm_descriptor_t disk, boolean_t *bool) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Determine if the input disk has a Solaris FDISK partition. + */ +int +disk_get_has_solaris_partition( + dm_descriptor_t disk, + boolean_t *bool) +{ + boolean_t has_fdisk = B_FALSE; + int error = 0; + + if ((error = disk_get_has_fdisk(disk, &has_fdisk)) != 0) { + return (error); + } + + *bool = B_FALSE; + + if (has_fdisk == B_TRUE) { + /* get disk's media */ + dm_descriptor_t *media; + media = dm_get_associated_descriptors(disk, DM_MEDIA, &error); + (void) add_descriptors_to_free(media); + if (error != 0) { + print_get_assoc_desc_error(disk, gettext("media"), error); + } else if ((media != NULL) && (*media != NULL)) { + /* get media's partitions */ + dm_descriptor_t *parts; + parts = dm_get_associated_descriptors( + media[0], DM_PARTITION, &error); + (void) add_descriptors_to_free(parts); + if (error != 0) { + print_get_assoc_desc_error(media[0], + gettext("partitions"), error); + } else { + /* search partitions for one with type Solaris */ + int i = 0; + for (; (parts != NULL) && (parts[i] != NULL) && + (error == 0) && (*bool == B_FALSE); i++) { + nvlist_t *attrs = dm_get_attributes(parts[i], &error); + uint32_t ptype = 0; + if ((error == 0) && (attrs != NULL)) { + error = get_uint32(attrs, DM_PTYPE, &ptype); + if ((error == 0) && + (ptype == SUNIXOS || ptype == SUNIXOS2)) { + *bool = B_TRUE; + } + } + nvlist_free(attrs); + } + } + + free(parts); + free(media); + } + + /* if there was no media, it was a removeable drive */ + } + + return (error); +} + +static int +disk_get_boolean_attribute( + dm_descriptor_t disk, + char *attr, + boolean_t *bool) +{ + nvlist_t *attrs = NULL; + int error = 0; + + *bool = B_FALSE; + + if ((strcmp(attr, DM_EFI) == 0) || + (strcmp(attr, DM_FDISK) == 0)) { + + /* + * these attributes are actually on the media, + * not the disk... so get the media descriptor + * for this disk + */ + dm_descriptor_t *media; + + media = dm_get_associated_descriptors(disk, DM_MEDIA, &error); + (void) add_descriptors_to_free(media); + + if (error != 0) { + print_get_assoc_desc_error(disk, gettext("media"), error); + } else if ((media != NULL) && (*media != NULL)) { + /* if there's no media, it is a removeable drive */ + error = get_cached_attributes(media[0], &attrs); + } + free(media); + + } else { + error = get_cached_attributes(disk, &attrs); + if (error != 0) { + print_get_desc_attr_error(disk, gettext("drive"), attr, error); + } + } + + if (error != 0) { + return (error); + } + + if (nvlist_lookup_boolean(attrs, attr) == 0) { + *bool = B_TRUE; + } + + return (error); +} + +static int +disk_get_uint64_attribute( + dm_descriptor_t disk, + char *attr, + uint64_t *val) +{ + nvlist_t *attrs = NULL; + uint32_t ui32 = 0; + int error = 0; + + /* + * these attributes are actually on the media, + * not the disk... so get the media descriptor + * for this disk + */ + if ((strcmp(attr, DM_SIZE) == 0) || + (strcmp(attr, DM_START) == 0) || + (strcmp(attr, DM_NACCESSIBLE) == 0) || + (strcmp(attr, DM_BLOCKSIZE) == 0) || + (strcmp(attr, DM_NCYLINDERS) == 0) || + (strcmp(attr, DM_NHEADS) == 0) || + (strcmp(attr, DM_NSECTORS) == 0)) { + + dm_descriptor_t *media; + + media = dm_get_associated_descriptors(disk, DM_MEDIA, &error); + (void) add_descriptors_to_free(media); + + if (error != 0) { + print_get_assoc_desc_error(disk, gettext("media"), error); + } else if ((media == NULL) || (*media == NULL)) { + print_get_assoc_desc_error(disk, gettext("media"), error); + error = -1; + } else { + error = get_cached_attributes(media[0], &attrs); + free(media); + } + + } else { + error = get_cached_attributes(disk, &attrs); + if (error != 0) { + print_get_desc_attr_error(disk, gettext("drive"), attr, error); + } + } + + if (error != 0) { + return (error); + } + + if (strcmp(attr, DM_SIZE) == 0 || + strcmp(attr, DM_NACCESSIBLE) == 0 || + strcmp(attr, DM_START) == 0) { + error = get_uint64(attrs, attr, val); + } else if (strcmp(attr, DM_BLOCKSIZE) == 0 || + strcmp(attr, DM_NCYLINDERS) == 0 || + strcmp(attr, DM_NHEADS) == 0 || + strcmp(attr, DM_NSECTORS) == 0 || + strcmp(attr, DM_RPM) == 0 || + strcmp(attr, DM_DRVTYPE) == 0 || + strcmp(attr, DM_SYNC_SPEED) == 0 || + strcmp(attr, DM_STATUS) == 0) { + error = get_uint32(attrs, attr, &ui32); + *val = (uint64_t)ui32; + } + + return (error); +} + +/* + * FUNCTION: group_similar_hbas(dlist_t *hbas, dlist_t **list) + * + * INPUT: hbas - a list of HBA dm_descriptor_t handles. + * + * OUTPUT: **list - a pointer to a list to hold the lists of HBAs + * grouped by characteristics. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Examine the input HBAs and collate them into separate + * lists, grouped by their type and the protocols they + * support. + * + * The returned list of list is arranged in decreasing order + * of preference, "better" HBAs come first. + * + * find all MPXIO controllers + * find all similar FC HBAs + * find all similar SCSI HBAs + * fast{wide}80 + * fast{wide}40 + * fast{wide}20 + * clock uint32 ?? + * find all similar ATA/IDE HBAs + * find all similar USB HBAs + */ +int +group_similar_hbas( + dlist_t *hbas, + dlist_t **list) +{ + /* preference order of HBAs */ + enum { + HBA_FIBRE_MPXIO = 0, + HBA_SCSI_MPXIO, + HBA_FIBRE, + HBA_SCSI_FW80, + HBA_SCSI_FW40, + HBA_SCSI_FW20, + HBA_SCSI_F80, + HBA_SCSI_F40, + HBA_SCSI_F20, + HBA_SCSI, + HBA_ATA, + HBA_USB, + HBA_LAST + }; + + dlist_t *groups = NULL; + dlist_t *iter = NULL; + dlist_t *item = NULL; + dlist_t *lists[HBA_LAST]; + + int error = 0; + int i = 0; + + (void) memset(lists, '\0', HBA_LAST * sizeof (dlist_t *)); + + for (iter = hbas; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + char *type = NULL; + + /* if item doesn't go into a list it must be freed */ + if ((item = dlist_new_item((void *)hba)) == NULL) { + error = ENOMEM; + continue; + } + + if ((error = hba_get_type(hba, &type)) != 0) { + free(item); + continue; + } + + if (strcmp(type, DM_CTYPE_FIBRE) == 0) { + + boolean_t ismpxio = B_FALSE; + + if ((error = hba_is_multiplex(hba, &ismpxio)) == 0) { + if (ismpxio) { + lists[HBA_FIBRE_MPXIO] = + dlist_append(item, + lists[HBA_FIBRE_MPXIO], AT_TAIL); + } else { + lists[HBA_FIBRE] = + dlist_append(item, + lists[HBA_FIBRE], AT_TAIL); + } + } else { + free(item); + } + + } else if (strcmp(type, DM_CTYPE_SCSI) == 0) { + + /* determine subtype */ + boolean_t iswide = B_FALSE; + boolean_t ismpxio = B_FALSE; + boolean_t is80 = B_FALSE; + boolean_t is40 = B_FALSE; + boolean_t is20 = B_FALSE; + + ((error = hba_supports_wide(hba, &iswide)) != 0) || + (error = hba_is_multiplex(hba, &ismpxio)) || + (error = hba_is_fast_80(hba, &is80)) || + (error = hba_is_fast_40(hba, &is40)) || + (error = hba_is_fast_20(hba, &is20)); + + if (error == 0) { + + if (ismpxio) { + + lists[HBA_SCSI_MPXIO] = + dlist_append(item, + lists[HBA_SCSI_MPXIO], AT_TAIL); + + } else if (is80) { + + if (iswide) { + lists[HBA_SCSI_FW80] = + dlist_append(item, + lists[HBA_SCSI_FW80], AT_TAIL); + } else { + lists[HBA_SCSI_F80] = + dlist_append(item, + lists[HBA_SCSI_F80], AT_TAIL); + } + + } else if (is40) { + + if (iswide) { + lists[HBA_SCSI_FW40] = + dlist_append(item, + lists[HBA_SCSI_FW40], AT_TAIL); + } else { + lists[HBA_SCSI_F40] = + dlist_append(item, + lists[HBA_SCSI_F40], AT_TAIL); + } + + } else if (is20) { + + if (iswide) { + lists[HBA_SCSI_FW20] = + dlist_append(item, + lists[HBA_SCSI_FW20], AT_TAIL); + } else { + lists[HBA_SCSI_F20] = + dlist_append(item, + lists[HBA_SCSI_F20], AT_TAIL); + } + + } else { + lists[HBA_SCSI] = + dlist_append(item, lists[HBA_SCSI], AT_TAIL); + } + + } else { + free(item); + } + + } else if (strcmp(type, DM_CTYPE_ATA) == 0) { + lists[HBA_ATA] = + dlist_append(item, lists[HBA_ATA], AT_TAIL); + } else if (strcmp(type, DM_CTYPE_USB) == 0) { + lists[HBA_USB] = + dlist_append(item, lists[HBA_USB], AT_TAIL); + } else if (strcmp(type, DM_CTYPE_UNKNOWN) == 0) { + oprintf(OUTPUT_DEBUG, + gettext("found an HBA with unknown type\n")); + free(item); + } + } + + if (error == 0) { + /* collect individual lists into a list of lists */ + for (i = 0; (i < HBA_LAST) && (error == 0); i++) { + if (lists[i] != NULL) { + if ((item = dlist_new_item(lists[i])) == NULL) { + error = ENOMEM; + } else { + groups = dlist_append(item, groups, AT_TAIL); + } + } + } + } + + if (error != 0) { + for (i = 0; i < HBA_LAST; i++) { + dlist_free_items(lists[i], NULL); + lists[i] = NULL; + } + + if (groups != NULL) { + dlist_free_items(groups, NULL); + } + } + + *list = groups; + + return (error); +} + +/* + * FUNCTION: hba_group_usable_disks(dm_descriptor_t hba, dlist_t **list) + * + * INPUT: hba - a dm_descriptor_t handle for a slice + * + * OUTPUT: **list - a pointer to a list to hold the lists of disks + * grouped by characteristics. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Examine the disks assocated with the HBA and collates them + * into separate lists, grouped by similar characteristics. + * + * get disks on HBA + * check disks against _usable_disks list + * group disks by similarities: + * sync-speed uint32 + * wide boolean + * rpm uint32 + * + * XXX this function is currently unused. At some point, + * it may be useful to group disks by performance + * characteristics and use "better" disks before others. + */ +int +hba_group_usable_disks( + dm_descriptor_t hba, + dlist_t **list) +{ + dm_descriptor_t *disk = NULL; + char *name = NULL; + int i = 0; + int error = 0; + + disk = dm_get_associated_descriptors(hba, DM_DRIVE, &error); + (void) add_descriptors_to_free(disk); + + if (error != 0) { + print_get_assoc_desc_error(hba, gettext("drive"), error); + return (error); + } else if ((disk == NULL) || (*disk == NULL)) { + print_get_assoc_desc_error(hba, gettext("drive"), error); + error = -1; + } + + for (i = 0; (disk[i] != NULL) && (error == 0); i++) { + + uint32_t dtype = DM_DT_UNKNOWN; + dlist_t *usable = NULL; + + /* ignore non fixed media drives */ + if (((error = disk_get_drive_type(disk[i], &dtype)) != 0) || + (dtype != DM_DT_FIXED)) { + continue; + } + + if (dlist_contains(usable, &disk[i], + compare_descriptor_names) == B_TRUE) { + + uint64_t bsize = 0; + uint64_t ncyls = 0; + uint64_t nsects = 0; + uint64_t nheads = 0; + uint32_t rpm = 0; + uint32_t sync = 0; + + name = NULL; + ((error = get_display_name(disk[i], &name)) != 0) || + (error = disk_get_blocksize(disk[i], &bsize)) || + (error = disk_get_nheads(disk[i], &nheads)) || + (error = disk_get_nsectors(disk[i], &nsects)) || + (error = disk_get_ncylinders(disk[i], &ncyls)) || + (error = disk_get_rpm(disk[i], &rpm)) || + (error = disk_get_sync_speed(disk[i], &sync)); + if (error != 0) { + continue; + } + + oprintf(OUTPUT_VERBOSE, + gettext("found an available disk: %s\n\t" + "sync_speed = %u, rpm = %u, " + "nsect = %llu, blksiz = %llu\n"), + name, sync, rpm, nsects, bsize); + + /* add to the appropriate list */ + } + } + + if (disk != NULL) { + free(disk); + } + + return (error); +} + +/* + * FUNCTION: hba_get_n_avail_disks(dm_descriptor_t hba, uint16_t *val) + * hba_set_n_avail_disks(dm_descriptor_t hba, uint16_t val) + * + * INPUT: hba - a dm_descriptor_t handle for a slice + * + * OUTPUT: *val - a pointer to a uint16_t to hold the current number + * of available disks for the input HBA. + * + * RETURNS: int - 0 on success + * !0 otherwise. + */ +int +hba_set_n_avail_disks( + dm_descriptor_t hba, + uint16_t val) +{ + nvlist_t *attrs; + int error = 0; + + ((error = get_cached_attributes(hba, &attrs)) != 0) || + (error = set_uint16(attrs, ATTR_HBA_N_DISKS, val)); + + return (error); +} + +int +hba_get_n_avail_disks( + dm_descriptor_t hba, + uint16_t *val) +{ + nvlist_t *attrs; + int error = 0; + + *val = 0; + + ((error = get_cached_attributes(hba, &attrs)) != 0) || + (error = get_uint16(attrs, ATTR_HBA_N_DISKS, val)); + + return (error); +} + +/* + * FUNCTION: hba_get_type(dm_descriptor_t hba, char **type) + * + * INPUT: hba - a dm_descriptor_t handle for a HBA + * + * OUTPUT: **type - a char * to hold the current type value for + * the HBA. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Retrieves the type attribute for the HBA. + */ +int +hba_get_type( + dm_descriptor_t hba, + char **type) +{ + nvlist_t *attrs; + int error = 0; + + *type = NULL; + + ((error = get_cached_attributes(hba, &attrs)) != 0) || + (error = get_string(attrs, DM_CTYPE, type)); + + return (error); +} + +/* + * FUNCTION: hba_is_fast(dm_descriptor_t hba, boolean_t *bool) + * hba_is_fast20(dm_descriptor_t hba, boolean_t *bool) + * hba_is_fast40(dm_descriptor_t hba, boolean_t *bool) + * hba_is_fast80(dm_descriptor_t hba, boolean_t *bool) + * hba_is_multiplex(dm_descriptor_t hba, boolean_t *bool) + * hba_is_wide(dm_descriptor_t hba, boolean_t *bool) + * + * INPUT: hba - a dm_descriptor_t handle for a HBA + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the + * boolean value of the predicate. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrappers around hba_supports_protocol which determines + * if the input HBA supports the protocol of interest. + */ +int +hba_is_fast( + dm_descriptor_t hba, + boolean_t *bool) +{ + return (hba_supports_protocol(hba, DM_FAST, bool)); +} + +int +hba_is_fast_20( + dm_descriptor_t hba, + boolean_t *bool) +{ + return (hba_supports_protocol(hba, DM_FAST20, bool)); +} + +int +hba_is_fast_40( + dm_descriptor_t hba, + boolean_t *bool) +{ + return (hba_supports_protocol(hba, DM_FAST40, bool)); +} + +int +hba_is_fast_80( + dm_descriptor_t hba, + boolean_t *bool) +{ + return (hba_supports_protocol(hba, DM_FAST80, bool)); +} + +int +hba_is_multiplex( + dm_descriptor_t hba, + boolean_t *bool) +{ + return (hba_supports_protocol(hba, DM_MULTIPLEX, bool)); +} + +int +hba_supports_wide( + dm_descriptor_t hba, + boolean_t *bool) +{ + nvlist_t *attrs = NULL; + int error = 0; + + *bool = B_FALSE; + + if ((error = get_cached_attributes(hba, &attrs)) != 0) { + return (error); + } + + *bool = (0 == nvlist_lookup_boolean(attrs, DM_WIDE)); + + return (error); +} + +/* + * FUNCTION: hba_supports_protocol(dm_descriptor_t hba, char *attr, + * boolean_t *bool) + * + * INPUT: hba - a dm_descriptor_t handle for a HBA + * attr - a protocol "name" + * + * OUTPUT: *bool - a pointer to a boolean_t to hold the + * boolean value of the predicate. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Checks the HBAs attributes to see if it is known to + * support the protocol of interest. + * + * If the protocol is supported, it will have an entry + * in the nvpair attribute list that can be retrieved. + * + * If the entry cannot be retrieved, the protocol is not + * supported. + */ +int +hba_supports_protocol( + dm_descriptor_t hba, + char *attr, + boolean_t *bool) +{ + nvlist_t *attrs = NULL; + int error = 0; + + *bool = B_FALSE; + + if ((error = get_cached_attributes(hba, &attrs)) != 0) { + return (error); + } + + *bool = (0 == nvlist_lookup_boolean(attrs, attr)); + + return (error); +} + +/* + * FUNCTION: slice_set_size(dm_descriptor_t slice, uint64_t size) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: size - a uint64_t value representing the size of the + * slice. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrapper around slice_set_uint64_attribute which converts + * the input size in bytes to blocks prior to storing it. + * + * This function is used when an existing slice gets resized + * to provide space for a new slice. It is necessary to update + * the slice's size so that it is accurate. + */ +int +slice_set_size( + dm_descriptor_t slice, + uint64_t size) +{ + dm_descriptor_t disk = NULL; + uint64_t blksize = 0; + int error = 0; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = disk_get_blocksize(disk, &blksize)) || + (error = slice_set_size_in_blocks(slice, (uint64_t)(size / blksize))); + + return (error); +} + +/* + * FUNCTION: slice_set_size_in_blocks(dm_descriptor_t slice, uint64_t size) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: size - a uint64_t value representing the size of the + * slice. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrapper around slice_set_uint64_attribute to set the slice + * size. + * + * This function is used when an existing slice gets resized + * to provide space for a new slice. It is necessary to update + * the slice's size so that it is accurate. + */ +int +slice_set_size_in_blocks( + dm_descriptor_t slice, + uint64_t size) +{ + return (slice_set_attribute(slice, DM_SIZE, size)); +} + +/* + * FUNCTION: slice_set_start_block(dm_descriptor_t slice, uint64_t start) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: size - a uint64_t value representing the start block of the + * slice. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrapper around slice_set_attribute. + * + * This function is used when an existing slice gets adjusted + * due to being resized or combined with another slice. + */ +int +slice_set_start_block( + dm_descriptor_t slice, + uint64_t start) +{ + return (slice_set_attribute(slice, DM_START, start)); +} + +/* + * FUNCTION: slice_get_start_block(dm_descriptor_t slice, uint64_t *val) + * slice_get_size_in_blocks(dm_descriptor_t slice, uint64_t *val) + * slice_get_start(dm_descriptor_t slice, uint64_t *val) + * slice_get_size(dm_descriptor_t slice, uint64_t *val) + * slice_get_index(dm_descriptor_t slice, uint64_t *val) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: *val - a pointer to a uint64_t to hold the + * current value of the desired attribute. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Wrappers around slice_get_uint64_attribute which retrieve + * specific attribute values. + */ +int +slice_get_start_block( + dm_descriptor_t slice, + uint64_t *val) +{ + return (slice_get_uint64_attribute(slice, DM_START, val)); +} + +int +slice_get_size_in_blocks( + dm_descriptor_t slice, + uint64_t *val) +{ + return (slice_get_uint64_attribute(slice, DM_SIZE, val)); +} + +int +slice_get_start( + dm_descriptor_t slice, + uint64_t *val) +{ + dm_descriptor_t disk = NULL; + uint64_t blksize = 0; + uint64_t nblks = 0; + int error = 0; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = disk_get_blocksize(disk, &blksize)) || + (error = slice_get_start_block(slice, &nblks)); + + if (error == 0) { + *val = (blksize * nblks); + } + + return (error); +} + +int +slice_get_size( + dm_descriptor_t slice, + uint64_t *val) +{ + dm_descriptor_t disk = NULL; + uint64_t blksize = 0; + uint64_t nblks = 0; + int error = 0; + + *val = 0; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = slice_get_size_in_blocks(slice, &nblks)) || + (error = disk_get_blocksize(disk, &blksize)); + + if (error == 0) { + *val = (blksize * nblks); + } + + return (error); +} + +int +slice_get_index( + dm_descriptor_t slice, + uint32_t *val) +{ + uint64_t index = 0; + int error = 0; + + if ((error = slice_get_uint64_attribute( + slice, DM_INDEX, &index)) != 0) { + return (error); + } + + *val = (uint32_t)index; + + return (0); +} + +/* + * FUNCTION: slice_set_uint64_attribute(dm_descriptor_t slice, + * char *attr, uint64_t val) + * slice_get_uint64_attribute(dm_descriptor_t slice, + * char *attr, uint64_t *val) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * attr - a char * attribute name + * val - auint64_t value + * + * OUTPUT: *val - a pointer to a uint64_t to hold the + * current value of the named attribute. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helpers to set/get the value for a slice's attribute. + * + * Consolidate the details of getting/setting slice + * attributes. Some attributes are actually stored as + * uint32_t or uint16_t values, these functions mask + * the type conversions. + */ +static int +slice_get_uint64_attribute( + dm_descriptor_t slice, + char *attr, + uint64_t *val) +{ + nvlist_t *attrs = NULL; + uint32_t ui32 = 0; + int error = 0; + + if ((error = get_cached_attributes(slice, &attrs)) != 0) { + return (error); + } + + if (strcmp(attr, DM_INDEX) == 0) { + error = get_uint32(attrs, attr, &ui32); + *val = (uint64_t)ui32; + } else if (strcmp(attr, DM_START) == 0) { + error = get_uint64(attrs, attr, val); + } else if (strcmp(attr, DM_SIZE) == 0) { + error = get_uint64(attrs, attr, val); + } else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) { + error = get_uint64(attrs, attr, val); + } + + if (error != 0) { + print_get_desc_attr_error(slice, "slice", attr, error); + } + + return (error); +} + +/* + * Set a slice attribute. The attribute is only set in the cached + * copy of the slice's nvpair attribute list. This function does + * NOT affect the underlying physical device. + */ +static int +slice_set_attribute( + dm_descriptor_t slice, + char *attr, + uint64_t val) +{ + nvlist_t *attrs = NULL; + int error = 0; + + if ((error = get_cached_attributes(slice, &attrs)) != 0) { + return (error); + } + + if (strcmp(attr, DM_INDEX) == 0) { + error = set_uint32(attrs, attr, (uint32_t)val); + } else if (strcmp(attr, DM_START) == 0) { + error = set_uint64(attrs, attr, val); + } else if (strcmp(attr, DM_SIZE) == 0) { + error = set_uint64(attrs, attr, val); + } else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) { + error = set_uint64(attrs, attr, val); + } + + if (error != 0) { + print_set_desc_attr_error(slice, "slice", attr, error); + } + + return (error); +} + +/* + * FUNCTION: virtual_slice_get_disk(dm_descriptor_t slice, + * dm_descriptor_t *diskp) + * + * INPUT: slice - a dm_descriptor_t virtual slice handle + * diskp - pointer to a dm_descriptor_t disk handle + * to return the slice's disk + * + * OUTPUT: the disk associated with the virtual slice. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which determines the disk that the input virtual + * slice "belongs" to. + * + * The virtual slice's disk is stored in the slice's nvpair + * attribute list when the slice gets created. + */ +static int +virtual_slice_get_disk( + dm_descriptor_t slice, + dm_descriptor_t *diskp) +{ + uint64_t disk = 0; + int error = 0; + + if ((error = slice_get_uint64_attribute( + slice, ATTR_DISK_FOR_SLICE, &disk)) != 0) { + return (error); + } + + *diskp = (dm_descriptor_t)disk; + + if (disk == 0) { + print_get_desc_attr_error(slice, "virtual slice", "disk", error); + return (-1); + } + + return (0); +} + +/* + * FUNCTION: slice_get_disk(dm_descriptor_t disk, dm_descriptor_t *diskp) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: diskp - a pointer to a dm_descriptor_t to hold the + * disk associated with the input slice + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the disk for a slice device. + * + * A slice is actually connected to its disk thru an intermediate + * device known as the "media". The media concept exists to + * model drives with removeable disk media. For the purposes + * of layout, such devices aren't relevant and the intermediate + * media can mostly be ignored. + */ +int +slice_get_disk( + dm_descriptor_t slice, + dm_descriptor_t *diskp) +{ + dm_descriptor_t *media = NULL; + + int i = 0; + int error = 0; + + *diskp = 0; + + if (is_virtual_slice(slice)) { + return (virtual_slice_get_disk(slice, diskp)); + } + + media = dm_get_associated_descriptors(slice, DM_MEDIA, &error); + (void) add_descriptors_to_free(media); + + if (error != 0) { + print_get_assoc_desc_error(slice, gettext("media"), error); + } else if ((media == NULL) || (*media == NULL)) { + print_get_assoc_desc_error(slice, gettext("media"), error); + error = -1; + } + + if (error != 0) { + return (error); + } + + /* slice should have exactly 1 media */ + for (i = 0; (media[i] != NULL) && (*diskp == NULL); i++) { + /* get disk from media */ + dm_descriptor_t *disks = NULL; + disks = dm_get_associated_descriptors(media[i], DM_DRIVE, &error); + (void) add_descriptors_to_free(disks); + + if ((error == 0) && (disks != NULL) && (disks[0] != NULL)) { + *diskp = disks[0]; + } + free(disks); + } + + if (media != NULL) { + free(media); + } + + if (*diskp == 0) { + print_get_desc_attr_error(slice, + gettext("slice"), gettext("disk"), ENODEV); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: slice_get_hbas(dm_descriptor_t slice, dlist_t **list) + * + * INPUT: slice - a dm_descriptor_t handle for a slice + * + * OUTPUT: list - a pointer to a dlist_t list to hold the + * HBAs associated with the input slice + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the known HBAs for a slice device. + * + */ +int +slice_get_hbas( + dm_descriptor_t slice, + dlist_t **list) +{ + dm_descriptor_t disk = NULL; + int error = 0; + + *list = NULL; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = disk_get_hbas(disk, list)); + + if (*list == NULL) { + print_get_desc_attr_error(slice, "slice", "HBA", ENODEV); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: disk_get_associated_desc(dm_descriptor_t disk, + * dm_desc_type_t assoc_type, char *assoc_type_str, + * dlist_t **list) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * assoc_type - the type of associated object to get + * assoc_type_str - a char * string for the associated type + * + * OUTPUT: list - a pointer to a dlist_t list to hold the + * objects associated with the input disk + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the associated objects of the + * requested type for a disk device. + */ +static int +disk_get_associated_desc( + dm_descriptor_t disk, + dm_desc_type_t assoc_type, + char *assoc_type_str, + dlist_t **list) +{ + int i = 0; + int error = 0; + + dm_descriptor_t *assoc = + dm_get_associated_descriptors(disk, assoc_type, &error); + + (void) add_descriptors_to_free(assoc); + + if (error == 0) { + for (i = 0; + (assoc != NULL) && (assoc[i] != NULL) && (error == 0); + i++) { + dlist_t *item = dlist_new_item((void *)assoc[i]); + if (item == NULL) { + error = ENOMEM; + } else { + *list = dlist_append(item, *list, AT_TAIL); + } + } + } else { + print_get_assoc_desc_error(disk, assoc_type_str, error); + } + + if (assoc != NULL) { + free(assoc); + } + + if (error != 0) { + dlist_free_items(*list, NULL); + *list = NULL; + } + + return (error); +} + +/* + * FUNCTION: disk_get_hbas(dm_descriptor_t disk, dlist_t **list) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: list - a pointer to a dlist_t list to hold the + * HBAs associated with the input disk + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the known HBAs for a disk device. + * + */ +int +disk_get_hbas( + dm_descriptor_t disk, + dlist_t **list) +{ + return (disk_get_associated_desc(disk, DM_CONTROLLER, + gettext("controller"), list)); +} + +/* + * FUNCTION: disk_get_paths(dm_descriptor_t disk, dlist_t **list) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: list - a pointer to a dlist_t list to hold the + * paths associated with the input disk + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the known paths for a disk device. + * + * Paths are managed by the MPXIO driver, they represent hardware + * paths to the disk drive managed by the MPXIO and not visible + * externally, unlike aliases which are. + */ +int +disk_get_paths( + dm_descriptor_t disk, + dlist_t **list) +{ + return (disk_get_associated_desc(disk, DM_PATH, + gettext("path"), list)); +} + +/* + * FUNCTION: disk_get_aliases(dm_descriptor_t disk, dlist_t **list) + * + * INPUT: disk - a dm_descriptor_t handle for a disk + * + * OUTPUT: list - a pointer to a dlist_t list to hold the + * alias descriptors associated with the input disk + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves the known aliases for a disk device. + * + * Aliases are the different CTD names for the disk drive when + * MPXIO is not enabled for multipathed drives. + */ +int +disk_get_aliases( + dm_descriptor_t disk, + dlist_t **list) +{ + return (disk_get_associated_desc(disk, DM_ALIAS, + gettext("alias"), list)); +} + +/* + * FUNCTION: compare_string_to_desc_name_or_alias( + * void *str, void *desc) + * + * INPUT: str - opaque pointer + * descr - opaque pointer + * + * RETURNS: int - <0 - if str < desc.name + * 0 - if str == desc.name + * >0 - if str > desc.name + * + * PURPOSE: dlist_t helper which compares a string to the name + * and aliases associated with the input dm_descriptor_t + * handle. + * + * Comparison is done via compare_device_names. + */ +static int +compare_string_to_desc_name_or_alias( + void *str, + void *desc) +{ + char *dname = NULL; + int result = -1; + + assert(str != (char *)NULL); + assert(desc != (dm_descriptor_t)0); + + (void) get_display_name((uintptr_t)desc, &dname); + + /* try name first, then aliases */ + if ((result = compare_device_names(str, dname)) != 0) { + dlist_t *aliases = NULL; + + (void) get_aliases((uintptr_t)desc, &aliases); + if ((aliases != NULL) && (dlist_contains(aliases, + str, compare_device_names) == B_TRUE)) { + result = 0; + } + dlist_free_items(aliases, free); + } + + return (result); +} + +/* + * FUNCTION: hba_get_by_name(char *name, dm_descriptor_t *hba) + * + * INPUT: name - a char * disk name + * + * OUTPUT: hba - a pointer to a dm_descriptor_t to hold the + * HBA corresponding to the input name, if found + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which iterates the known HBAs, searching for + * the one matching name. + * + * If no HBA matches the name, 0 is returned and the + * value of 'hba' will be (dm_descriptor_t)0; + */ +int +hba_get_by_name( + char *name, + dm_descriptor_t *hba) +{ + int error = 0; + dlist_t *list = NULL; + dlist_t *item = NULL; + + *hba = (dm_descriptor_t)0; + + if (name == NULL) { + return (0); + } + + if ((error = get_known_hbas(&list)) != 0) { + return (error); + } + + if ((item = dlist_find(list, name, + compare_string_to_desc_name_or_alias)) != NULL) { + *hba = (uintptr_t)item->obj; + } + + return (error); +} + +/* + * FUNCTION: disk_get_by_name(char *name, dm_descriptor_t *disk) + * + * INPUT: name - a char * disk name + * + * OUTPUT: disk - a pointer to a dm_descriptor_t to hold the + * disk corresponding to the input name, if found + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which retrieves a dm_descriptor_t disk handle + * by name. + * + * If no disk is found for the input name, variations of + * the name are tried. + * + * If the input name is unqualified, an appropriate leading + * path is prepended. + * + * If the input name is qualified, the leading path is + * removed. + * + * If no disk is found for the variations, 0 is returned + * and the value of 'disk' will be (dm_descriptor_t)0; + */ +int +disk_get_by_name( + char *name, + dm_descriptor_t *disk) +{ + assert(name != (char *)NULL); + + *disk = find_cached_descriptor(name); + if (*disk == (dm_descriptor_t)0) { + if (name[0] == '/') { + /* fully qualified, try unqualified */ + char *cp = strrchr(name, '/'); + if (cp != NULL) { + *disk = find_cached_descriptor(cp + 1); + } + } else { + /* unqualified, try fully qualified */ + char buf[MAXNAMELEN+1]; + if (is_ctd_disk_name(name)) { + (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name); + } else if (is_did_disk_name(name)) { + (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name); + } + *disk = find_cached_descriptor(buf); + } + } + + /* + * since the descriptor cache includes HBAs, disks and slices, + * what gets returned may not be a disk... make sure it is + */ + if (*disk != (dm_descriptor_t)0) { + if (dm_get_type(*disk) != DM_DRIVE) { + *disk = (dm_descriptor_t)0; + } + } + + return (0); +} + +/* + * FUNCTION: slice_get_by_name(char *name, dm_descriptor_t *slice) + * + * INPUT: name - a char * slice name + * + * OUTPUT: slice - a pointer to a dm_descriptor_t to hold the + * slice corresponding to the input name, if found. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which iterates the known slices, searching for + * the one matching name. + * + * If no slice is found for the input name, variations of + * the name are tried. + * + * If the input name is unqualified, an appropriate leading + * path is prepended. + * + * If the input name is qualified, the leading path is + * removed. + * + * If no slice matches the variations, 0 is returned and the + * value of 'slice' will be (dm_descriptor_t)0; + */ +int +slice_get_by_name( + char *name, + dm_descriptor_t *slice) +{ + assert(name != (char *)NULL); + + *slice = find_cached_descriptor(name); + if (*slice == (dm_descriptor_t)0) { + if (name[0] == '/') { + /* fully qualified, try unqualified */ + char *cp = strrchr(name, '/'); + if (cp != NULL) { + *slice = find_cached_descriptor(cp + 1); + } + } else { + /* unqualified, try fully qualified */ + char buf[MAXNAMELEN+1]; + if (is_ctd_slice_name(name) || is_ctd_like_slice_name(name) || + is_bsd_like_slice_name(name)) { + (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name); + } else if (is_did_slice_name(name)) { + (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name); + } + *slice = find_cached_descriptor(buf); + } + } + + /* + * since the descriptor cache includes HBAs, disks and slices, + * what gets returned may not be a slice... make sure it is + */ + if (*slice != (dm_descriptor_t)0) { + if (dm_get_type(*slice) != DM_SLICE && + is_virtual_slice(*slice) != B_TRUE) { + *slice = (dm_descriptor_t)0; + } + } + + return (0); +} + +/* + * FUNCTION: extract_hbaname(char *name, char **hbaname) + * + * INPUT: slicename - a char * device name + * + * OUTPUT: hbaname - a pointer to a char * to hold the hbaname derived + * from the input name. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which extracts the HBA name from the input name. + * + * If the input name is in ctd form, extracts just the cX part, + * by truncating everything following the last 't'. + * + * Of course on X86, with IDE drives, there is no 't' in the + * ctd name, so start by truncating everything following 'd' + * and then look for 't'. + * + * The returned string must be passed to free(). + */ +int +extract_hbaname( + char *name, + char **hbaname) +{ + char *cp; + + if (is_ctd_name(name)) { + if ((*hbaname = strdup(name)) == NULL) { + return (ENOMEM); + } + if ((cp = strrchr(*hbaname, 'd')) != NULL) { + *cp = '\0'; + } + if ((cp = strrchr(*hbaname, 't')) != NULL) { + *cp = '\0'; + } + } + + return (0); +} + +/* + * FUNCTION: extract_diskname(char *slicename, char **diskname) + * + * INPUT: slicename - a char * slice name + * + * OUTPUT: diskname - a pointer to a char * to hold the diskname derived + * from the input slicename. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which extracts the disk's name from a slice name. + * + * Checks to see if the input slicename is in ctd or did form, + * and if so, truncates everything following the last 's'. + * + * If the input slicename is BSD-like, truncate the last + * character (a-h). + * + * The returned string must be passed to free(). + */ +int +extract_diskname( + char *slicename, + char **diskname) +{ + char *cp; + + if (is_ctd_slice_name(slicename) || is_did_slice_name(slicename) || + is_ctd_like_slice_name(slicename)) { + + if ((*diskname = strdup(slicename)) == NULL) { + return (ENOMEM); + } + if ((cp = strrchr(*diskname, 's')) != NULL) { + *cp = '\0'; + } + + } else if (is_bsd_like_slice_name(slicename)) { + + if ((*diskname = strdup(slicename)) == NULL) { + return (ENOMEM); + } + (*diskname)[strlen((*diskname)-1)] = '\0'; + + } + + return (0); +} + +/* + * FUNCTION: get_disk_for_named_slice(char *slicename, + * dm_descriptor_t disk) + * + * INPUT: slicename - a char * slice name + * + * OUTPUT: disk - a pointer to a dm_descriptor_t to hold the + * disk corresponding to the input name, if found + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which locates the disk dm_descriptor_t handle for + * the input slice name. + * + * If no disk matches the name, 0 is returned and the + * value of 'disk' will be (dm_descriptor_t)0; + */ +int +get_disk_for_named_slice( + char *slicename, + dm_descriptor_t *disk) +{ + dm_descriptor_t slice = (dm_descriptor_t)0; + int error = 0; + + assert(slicename != NULL); + + /* find disk for slice */ + if ((error = slice_get_by_name(slicename, &slice)) == 0) { + + if (slice != (dm_descriptor_t)0) { + error = slice_get_disk(slice, disk); + } else { + /* named slice was created by layout: */ + /* need to find disk by name */ + char *dname; + + error = extract_diskname(slicename, &dname); + if (error == 0) { + error = disk_get_by_name(dname, disk); + } + free(dname); + } + } + + assert(*disk != (dm_descriptor_t)0); + + return (error); +} + +/* + * FUNCTION: disk_get_reserved_indexes(dm_descriptor_t disk, + * uint16_t **array) + * + * INPUT: disk - a dm_descriptor_t disk handle + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Retrieves the input disk's list of reserved slice indices. + * + * The list of reserved indices is stored as an array in + * the disk's nvpair attribute list. + */ +static int +disk_get_reserved_indexes( + dm_descriptor_t disk, + uint16_t **array) +{ + nvlist_t *attrs = NULL; + uint_t nelem = 0; + int error = 0; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint16_array( + attrs, ATTR_RESERVED_INDEX, array, &nelem)) != 0) { + if (error == ENOENT) { + /* no reserved indices yet */ + error = 0; + } + } + + return (error); +} + +/* + * FUNCTION: disk_reserve_index(dm_descriptor_t disk, uint16_t index) + * + * INPUT: disk - a disk dm_descirptor_t handle + * undex - a VTOC slice index + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Reserves the input VTOC slice index for the input disk. + * + * The list of reserved indices is stored as an array in + * the disk's nvpair attribute list. + */ +int +disk_reserve_index( + dm_descriptor_t disk, + uint16_t index) +{ + nvlist_t *attrs = NULL; + uint16_t *oldindexes = NULL; + uint16_t *newindexes = NULL; + uint_t nelem = 0; + int error = 0; + int i = 0; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint16_array( + attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) { + if (error != ENOENT) { + return (error); + } + /* no reserved indices yet */ + error = 0; + } + + /* add new index */ + newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t)); + if (newindexes != NULL) { + for (i = 0; i < nelem; i++) { + newindexes[i] = oldindexes[i]; + } + newindexes[(int)index] = 1; + + error = set_uint16_array(attrs, ATTR_RESERVED_INDEX, + newindexes, VTOC_SIZE); + + free(newindexes); + } else { + error = ENOMEM; + } + return (error); +} + +/* + * FUNCTION: disk_release_index(dm_descriptor_t disk, uint16_t index) + * + * INPUT: disk - a disk dm_descirptor_t handle + * undex - a VTOC slice index + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Releases the input VTOC slice index for the input disk. + * The index was previously reserved by disk_reserve_index() + */ +int +disk_release_index( + dm_descriptor_t disk, + uint16_t index) +{ + nvlist_t *attrs = NULL; + uint16_t *oldindexes = NULL; + uint16_t *newindexes = NULL; + uint_t nelem = 0; + int error = 0; + int i = 0; + + if ((error = get_cached_attributes(disk, &attrs)) != 0) { + return (error); + } + + if ((error = get_uint16_array( + attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) { + if (error != ENOENT) { + return (error); + } + error = 0; + } + + newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t)); + if (newindexes != NULL) { + for (i = 0; i < nelem; i++) { + newindexes[i] = oldindexes[i]; + } + + /* release index */ + newindexes[(int)index] = 0; + + error = set_uint16_array(attrs, ATTR_RESERVED_INDEX, + newindexes, VTOC_SIZE); + + free(newindexes); + } else { + error = ENOMEM; + } + + return (error); +} + +/* + * FUNCTION: print_get_assoc_desc_error(dm_descriptor_t desc, char *which, + * int error) + * + * INPUT: desc - a dm_descriptor_t handle + * which - a char * indicating which association + * error - an integer error value + * + * PURPOSE: Utility function to print an error message for a failed + * call to dm_get_associated_descriptors(). + * + * Extracts the device's CTD name and formats an error message. + */ +void +print_get_assoc_desc_error( + dm_descriptor_t desc, + char *which, + int error) +{ + char *name = ""; + + (void) get_display_name(desc, &name); + oprintf(OUTPUT_TERSE, + gettext("dm_get_associated_descriptors(%s) for " + "'%s' failed: %d\n"), + which, name, error); + + volume_set_error( + gettext("Unexpected error getting associated " + "descriptors for '%s'"), + name); +} + +/* + * FUNCTION: print_get_desc_attr_error(dm_descriptor_t desc, + * char *devtype, char *attr, int error) + * + * INPUT: desc - a dm_descriptor_t handle + * devtype - a char * device type that's being accessed + * attr - a char * attribute name + * error - an integer error value + * + * PURPOSE: Shared utility function to print an error message for a failed + * call to retrieve an attribute for a descriptor. + * + * Extracts the device's CTD name and formats an error message. + */ +void +print_get_desc_attr_error( + dm_descriptor_t desc, + char *devtype, + char *attr, + int error) +{ + char *name = ""; + + (void) get_display_name(desc, &name); + oprintf(OUTPUT_TERSE, + gettext("'%s' get attribute (%s.%s) error: %d\n"), + name, devtype, attr, error); + + volume_set_error( + gettext("Unexpected error getting attribute '%s.%s' for '%s'"), + devtype, attr, name); +} + +/* + * FUNCTION: print_set_desc_attr_error(dm_descriptor_t desc, + * char *devtype, char *attr, int error) + * + * INPUT: desc - a dm_descriptor_t handle + * devtype - a char * device type that's being accessed + * attr - a char * attribute name + * error - an integer error value + * + * PURPOSE: Shared utility function to print an error message for a failed + * call to set an attribute for a descriptor. + * + * Extracts the device's CTD name and formats an error message. + */ +void +print_set_desc_attr_error( + dm_descriptor_t desc, + char *devtype, + char *attr, + int error) +{ + char *name = ""; + + (void) get_display_name(desc, &name); + oprintf(OUTPUT_TERSE, + gettext("'%s' set attribute (%s.%s) error: %d\n"), + name, devtype, attr, error); + + volume_set_error( + gettext("Unexpected error setting attribute '%s.%s' for '%s'"), + devtype, attr, name); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_util.h b/usr/src/cmd/lvm/metassist/layout/layout_device_util.h new file mode 100644 index 0000000000..d8970ac5d5 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_device_util.h @@ -0,0 +1,140 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_DEVICE_UTIL_H +#define _LAYOUT_DEVICE_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libdiskmgt.h> + +extern boolean_t is_alt_slice_name(char *name); +extern boolean_t is_did_name(char *name); +extern boolean_t is_did_slice_name(char *name); +extern boolean_t is_did_disk_name(char *name); +extern boolean_t is_ctd_name(char *name); +extern boolean_t is_ctd_slice_name(char *name); +extern boolean_t is_ctd_disk_name(char *name); +extern boolean_t is_ctd_target_name(char *name); +extern boolean_t is_ctd_ctrl_name(char *name); + +extern int set_display_name(dm_descriptor_t desc, char *name); +extern int get_display_name(dm_descriptor_t slice, char **name); + +extern int slice_get_by_name(char *name, dm_descriptor_t *slicep); +extern int disk_get_by_name(char *name, dm_descriptor_t *diskp); +extern int hba_get_by_name(char *name, dm_descriptor_t *hbap); + +extern int extract_diskname(char *slicename, char **diskname); +extern int extract_hbaname(char *slicename, char **hbaname); + +extern int get_disk_for_named_slice(char *slicename, + dm_descriptor_t *diskp); + +/* + * functions to manipulate devices + */ +extern int group_similar_hbas(dlist_t *hbas, dlist_t **list); +extern int hba_is_multiplex(dm_descriptor_t hba, boolean_t *bool); + +extern int hba_set_n_avail_disks(dm_descriptor_t hba, uint16_t val); +extern int hba_get_n_avail_disks(dm_descriptor_t hba, uint16_t *val); + +extern int hba_get_type(dm_descriptor_t hba, char **type); +extern int hba_is_fast(dm_descriptor_t hba, boolean_t *bool); +extern int hba_is_fast_20(dm_descriptor_t hba, boolean_t *bool); +extern int hba_is_fast_40(dm_descriptor_t hba, boolean_t *bool); +extern int hba_is_fast_80(dm_descriptor_t hba, boolean_t *bool); +extern int hba_supports_protocol( + dm_descriptor_t hba, char *attr, boolean_t *bool); +extern int hba_supports_wide(dm_descriptor_t hba, boolean_t *bool); + +extern int disk_get_available_slice_index( + dm_descriptor_t diskp, uint32_t *index); + +extern int disk_get_hbas(dm_descriptor_t disk, dlist_t **list); +extern int disk_get_paths(dm_descriptor_t disk, dlist_t **list); +extern int disk_get_slices(dm_descriptor_t disk, dlist_t **list); +extern int disk_get_aliases(dm_descriptor_t disk, dlist_t **list); +extern int disk_get_blocksize(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_ncylinders(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_size_in_blocks(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_start_block(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_nheads(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_nsectors(dm_descriptor_t disk, uint64_t *val); +extern int disk_get_is_efi(dm_descriptor_t disk, boolean_t *val); +extern int disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *val); +extern int disk_get_has_solaris_partition(dm_descriptor_t disk, + boolean_t *val); +extern int disk_get_is_online(dm_descriptor_t disk, boolean_t *val); +extern int disk_get_drive_type(dm_descriptor_t disk, uint32_t *val); +extern int disk_get_media_type(dm_descriptor_t disk, uint32_t *type); +extern int disk_reserve_index(dm_descriptor_t disk, uint16_t index); +extern int disk_release_index(dm_descriptor_t disk, uint16_t index); + +extern int slice_get_hbas(dm_descriptor_t slice, dlist_t **list); +extern int slice_get_disk(dm_descriptor_t slice, dm_descriptor_t *diskp); +extern int slice_get_size(dm_descriptor_t slice, uint64_t *val); +extern int slice_get_index(dm_descriptor_t slice, uint32_t *val); +extern int slice_get_size_in_blocks(dm_descriptor_t slice, uint64_t *val); +extern int slice_get_start_block(dm_descriptor_t slice, uint64_t *val); +extern int slice_get_start(dm_descriptor_t slice, uint64_t *val); + +extern int slice_set_size(dm_descriptor_t slice, uint64_t size); +extern int slice_set_size_in_blocks(dm_descriptor_t slice, uint64_t size); +extern int slice_set_start_block(dm_descriptor_t slice, uint64_t start); + +/* + * virtual slice utilities. + */ +extern int create_virtual_slices(dlist_t *unused); +extern int add_virtual_slice(char *name, uint32_t index, + uint64_t startblk, uint64_t sizeblks, dm_descriptor_t disk); + +extern void release_virtual_slices(); +extern int get_virtual_slices(dlist_t **list); +extern boolean_t is_virtual_slice(dm_descriptor_t slice); + +/* + * shared error output functions for dm_descriptor_t objects + */ +extern void print_get_assoc_desc_error( + dm_descriptor_t desc, char *which, int error); +extern void print_get_desc_attr_error( + dm_descriptor_t desc, char *devtype, char *attr, int error); + +extern void print_set_desc_attr_error( + dm_descriptor_t desc, char *devtype, char *attr, int error); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_DEVICE_UTIL_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_discovery.c b/usr/src/cmd/lvm/metassist/layout/layout_discovery.c new file mode 100644 index 0000000000..28df972765 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_discovery.c @@ -0,0 +1,2454 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <limits.h> +#include <libdiskmgt.h> +#include <libintl.h> + +#include <meta.h> + +#define _LAYOUT_DISCOVERY_C + +#include "volume_dlist.h" +#include "volume_error.h" +#include "volume_nvpair.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_dlist_util.h" +#include "layout_discovery.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_svm_util.h" + +/* + * lists of device dm_descriptor_t handles discovered during + * the initial system probe. Lists are populated by + * discover_known_devices. + * + * "bad" slices are those that are known to libdiskmgt but + * cannot be accessed. An example would be a slice that has + * disappeared due to disk re-slicing: libdiskmgt may have a + * cached handle for it, but the slice no longer exists. + * + * "bad" disks are thoese that are known to libdiskmgt but + * cannot be accessed. An example would be a disk that has + * failed or has gone offline: libdiskmgt may have a cached + * handle for it, but the disk does not respond. + */ +static dlist_t *_bad_slices = NULL; +static dlist_t *_bad_disks = NULL; + +static dlist_t *_known_slices = NULL; +static dlist_t *_known_disks = NULL; +static dlist_t *_known_hbas = NULL; + +/* + * helper functions for building known device lists, used by + * discover_known_devices. + */ +static int generate_known_slices(dlist_t *disks, dlist_t **known, + dlist_t **bad); +static int generate_known_disks(dlist_t **known, dlist_t **bad); +static int generate_known_hbas(dlist_t *disks, dlist_t **known); +static int generate_known_hba_name( + dm_descriptor_t hba, + dm_descriptor_t alias, + dm_descriptor_t disk); + +static void print_known_devices(); +static void print_device_list(dlist_t *devices); + +/* + * lists of device dm_descriptor_t handles that are usable by layout. + * These devices must still pass the user specified available/unavailable + * filter before they're actually considered available. + * + * Lists are populated by discover_usable_devices. + */ +static dlist_t *_usable_slices = NULL; +static dlist_t *_usable_disks = NULL; +static dlist_t *_usable_hbas = NULL; + +/* + * private flag that remembers if any HBA is known to support MPXIO + */ +static boolean_t _mpxio_enabled = B_FALSE; + +/* + * The slice_class struct is used to group slices by usage class. + */ +typedef struct { + char *usage; /* usage description */ + dlist_t *sliceinfo; /* list with info about each slice with usage */ +} slice_class_t; + +#define USE_DISKSET "diskset" + +static int check_slice_usage( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + boolean_t *avail, + dlist_t **bad, + dlist_t **classes); + +static int check_svm_slice_usage( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + boolean_t *avail, + dlist_t **classes); + +static int save_slice_classification( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + char *usage, + char *usage_detail, + dlist_t **classes); + +static int generate_usable_disks_and_slices_in_local_set( + dlist_t **classes, + dlist_t **bad_disks, + dlist_t **usable_disks, + dlist_t **usable_slices); + +static int generate_usable_disks_and_slices_in_named_set( + char *dsname, + dlist_t **classes, + dlist_t **bad_slices, + dlist_t **usable_disks, + dlist_t **usable_slices); + +static int create_usable_slices( + dm_descriptor_t disk, + dlist_t *used, + dlist_t *unused, + dlist_t **usable); + +static int add_new_usable( + dm_descriptor_t disk, + uint64_t stblk, + uint64_t nblks, + dlist_t **next_unused, + dlist_t **usable); + +static int update_slice_attributes( + dm_descriptor_t slice, + uint64_t stblk, + uint64_t nblks, + uint64_t nbytes); + +static int generate_usable_hbas( + dlist_t *disks, + dlist_t **usable); + +static void print_usable_devices(); + +static void print_unusable_devices( + dlist_t *badslices, + dlist_t *baddisks, + dlist_t *usedslices); + +static char *get_slice_usage_msg( + char *usage); + +/* + * virtual slices... + */ +static int generate_virtual_slices( + dlist_t *avail_disks_local_set, + dlist_t **usable); + +/* + * multipathed disks have aliases, as do slices on those disks. + * these need to be tracked since the user may specify them. + * A multi-pathed disk is one connected to the system thru + * more than one physical HBA, each connection gets a distinct + * name in the device tree and they're all more or less equivalent. + * No indication as to how many possible physical connections a + * disk may have, so we pick an arbitrary number of aliases to + * support. There is nothing significant about this number, + * it just controls the number of alias slots that get allocated. + */ +#define MAX_ALIASES 8 + +/* + * attribute name for layout private information stored in + * device nvpair attribute lists. + */ +static char *ATTR_DEVICE_ALIASES = "layout_device_aliases"; + +static int compare_start_blocks( + void *desc1, void *desc2); + +static int compare_desc_display_names( + void *desc1, void *desc2); + +/* + * FUNCTION: is_mpxio_enabled() + * + * RETURNS: boolean_t - B_TRUE - if MPXIO appears enabled for the system + * B_FALSE - otherwise + * + * PURPOSE: returns the value of _mpxio_enabled which is set to B_TRUE + * during system configuration discovery if any of the knwon + * HBAs advertises itself as a "multiplex" controller. + */ +boolean_t +is_mpxio_enabled() +{ + return (_mpxio_enabled); +} + +/* + * FUNCTION: discover_known_devices() + * + * SIDEEFFECT: populates the module private lists of known devices + * (_known_slices, _known_disks, _known_hbas). + * + * All known devices will also have had their CTD + * short names inferred and stored. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Load physical devices discovered thru libdiskmgt. + */ +int +discover_known_devices() +{ + int error = 0; + + oprintf(OUTPUT_TERSE, + gettext("\nScanning system physical " + "device configuration...\n")); + + /* initialize layout_device_cache */ + ((error = create_device_caches()) != 0) || + + (error = generate_known_disks(&_known_disks, &_bad_disks)) || + (error = generate_known_slices(_known_disks, &_known_slices, + &_bad_slices)) || + (error = generate_known_hbas(_known_disks, &_known_hbas)); + + if (error == 0) { + print_known_devices(); + } + + return (error); +} + +/* + * FUNCTION: release_known_devices() + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Unloads all state currently held for known + * physical devices. + */ +int +release_known_devices( + char *diskset) +{ + /* these lists are module private */ + dlist_free_items(_bad_slices, NULL); + dlist_free_items(_bad_disks, NULL); + dlist_free_items(_known_slices, NULL); + dlist_free_items(_known_disks, NULL); + dlist_free_items(_known_hbas, NULL); + + _bad_slices = NULL; + _bad_disks = NULL; + _known_slices = NULL; + _known_disks = NULL; + _known_hbas = NULL; + + /* clean up state kept in layout_device_cache */ + release_device_caches(); + + return (0); +} + +/* + * FUNCTION: discover_usable_devices(char *diskset) + * + * INPUT: diskset - a char * diskset name. + * + * SIDEEFFECT: Traverses the lists of known devices and populates the + * module private lists of usable devices (_usable_slices, + * _usable_disks, _usable_hbas), as well as the module + * private list of used slices. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Process the known devices and determine which of them are + * usable for generating volumes in the specified diskset. + * + * The specified diskset's name cannot be NULL or 0 length. + */ +int +discover_usable_devices( + char *diskset) +{ + int error = 0; + + dlist_t *used_classes = NULL; + dlist_t *iter = NULL; + + if (diskset == NULL || diskset[0] == '\0') { + volume_set_error( + gettext("a diskset name must be specified in " + "the request\n")); + return (-1); + } + + oprintf(OUTPUT_TERSE, + gettext("\nDetermining usable physical devices " + "for disk set \"%s\"...\n"), + diskset); + + error = generate_usable_disks_and_slices_in_local_set( + &used_classes, &_bad_slices, &_usable_disks, &_usable_slices); + if (error == 0) { + + error = generate_usable_disks_and_slices_in_named_set( + diskset, &used_classes, &_bad_slices, &_usable_disks, + &_usable_slices); + if (error == 0) { + + error = generate_usable_hbas(_usable_disks, &_usable_hbas); + if (error == 0) { + + print_usable_devices(); + print_unusable_devices( + _bad_slices, _bad_disks, used_classes); + } + } + } + + /* + * free slice classification usage and lists, items are char* + * the used_classes structure is only filled in if verbose + * output was requested. + */ + for (iter = used_classes; iter != NULL; iter = iter->next) { + slice_class_t *class = (slice_class_t *)iter->obj; + free(class->usage); + dlist_free_items(class->sliceinfo, free); + } + + dlist_free_items(used_classes, free); + return (error); +} + +/* + * FUNCTION: release_usable_devices() + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Unloads all state currently held for usable + * physical devices. + */ +int +release_usable_devices() +{ + /* list items are shared with _known_XXX lists */ + + dlist_free_items(_usable_slices, NULL); + dlist_free_items(_usable_disks, NULL); + dlist_free_items(_usable_hbas, NULL); + + _usable_slices = NULL; + _usable_disks = NULL; + _usable_hbas = NULL; + + /* clean up state kept in layout_device_util */ + release_virtual_slices(); + + return (0); +} + +/* + * FUNCTION: get_known_slices(dlist_t **list) + * get_known_disks(dlist_t **list) + * get_known_hbas(dlist_t **list) + * + * OUTPUT: list - a dlist_t pointer to hold the returned list of + * devices. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Public accessors for the module private lists of + * available devices. + */ +int +get_known_slices( + dlist_t **list) +{ + *list = _known_slices; + + return (0); +} + +int +get_known_disks( + dlist_t **list) +{ + *list = _known_disks; + + return (0); +} + +int +get_known_hbas( + dlist_t **list) +{ + *list = _known_hbas; + + return (0); +} + +/* make fully qualified DID device name */ +static char * +make_fully_qualified_did_device_name( + char *device) +{ + static char buf[MAXPATHLEN]; + + if (device != NULL && strrchr(device, '/') == NULL) { + (void) snprintf(buf, MAXPATHLEN-1, "%s/%s", + "/dev/did/dsk", device); + return (buf); + } + + return (device); +} + +/* + * FUNCTION: generate_known_disks(dlist_t **known, + * dlist_t **bad) + * + * INPUT: NONE + * + * OUTPUT: known - populated list of known disks + * bad - populated list of known bad disks + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Does the system configuration discovery to determine + * what disks are known to be attached to the system. + * + * Determines the CTD name for each disk and saves it. + */ +static int +generate_known_disks( + dlist_t **known, + dlist_t **bad) +{ + int i; + int error = 0; + dm_descriptor_t *ddp; + + ddp = dm_get_descriptors(DM_DRIVE, NULL, &error); + (void) add_descriptors_to_free(ddp); + + *known = NULL; + + if (error != 0) { + volume_set_error( + gettext("Error discovering system hardware configuration,\n" + "unable to communicate with libdiskmgt or diskmgtd.\n")); + return (-1); + } + + if ((ddp == NULL) || (ddp[0] == NULL)) { + volume_set_error(gettext("there are no known disks\n")); + return (-1); + } + + /* iterate all returned disks and add them to the known list */ + for (i = 0; (ddp[i] != NULL) && (error == 0); i++) { + dm_descriptor_t disk = (dm_descriptor_t)ddp[i]; + dlist_t *aliases = NULL; + uint32_t mtype = DM_MT_UNKNOWN; + uint32_t dtype = DM_DT_UNKNOWN; + boolean_t bad_disk = B_FALSE; + boolean_t online = B_TRUE; + +#if defined(i386) + /* on X86, disks must have a solaris FDISK partition */ + boolean_t solpart = B_FALSE; +#endif /* defined(i386) */ + + if (((error = disk_get_is_online(disk, &online)) == 0 && + online == B_FALSE) || error == ENODEV) { + /* if the disk is offline, report it as bad */ + bad_disk = B_TRUE; + error = 0; + } else + + if (error == 0 && + (((error = disk_get_media_type(disk, &mtype)) != 0) || + ((error = disk_get_drive_type(disk, &dtype)) != 0)) && + error == ENODEV) { + /* + * if any disk attribute access fails with ENODEV + * report it as bad + */ + bad_disk = B_TRUE; + error = 0; + } else { + + /* + * Determine whether disk is fixed by checking its + * drive type. If drive type is unknown, check media + * type. + */ + int isfixed = (dtype == DM_DT_FIXED || + (dtype == DM_DT_UNKNOWN && mtype == DM_MT_FIXED)); + + if (!isfixed) { + continue; /* ignore non-fixed disks */ + } + +#if defined(i386) + if (((error = disk_get_has_solaris_partition(disk, + &solpart)) != 0) || (solpart != B_TRUE)) { + + /* X86 drive has no solaris partition, report as bad */ + oprintf(OUTPUT_DEBUG, + gettext("%s has no solaris FDISK partition.\n")); + + bad_disk = B_TRUE; + } +#endif /* defined(i386) */ + + } + + if (bad_disk) { + /* remember bad disks and continue */ + if (dlist_contains(*bad, + (void *)disk, compare_descriptor_names) != B_TRUE) { + dlist_t *item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + *bad = dlist_append(item, *bad, AT_TAIL); + } + } + continue; + } + + /* get disk name and multipath aliases */ + if ((error = disk_get_aliases(disk, &aliases)) == 0) { + dlist_t *iter; + boolean_t disk_name_set = B_FALSE; + + for (iter = aliases; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t ap = (uintptr_t)iter->obj; + char *alias; + + if ((error = get_name(ap, &alias)) == 0) { + /* save first alias as display name */ + if (disk_name_set != B_TRUE) { + /* make sure DID disk alias is fully qualified */ + + if (is_did_disk_name(alias) == B_TRUE) { + char *qual_name = + make_fully_qualified_did_device_name(alias); + + set_display_name(disk, qual_name); + oprintf(OUTPUT_DEBUG, + gettext("DID disk name: %s\n"), + qual_name); + } else { + set_display_name(disk, alias); + oprintf(OUTPUT_DEBUG, + gettext("disk name: %s\n"), + alias); + } + disk_name_set = B_TRUE; + + } else { + /* save others as aliases */ + set_alias(disk, alias); + oprintf(OUTPUT_DEBUG, + gettext(" alias: %s\n"), + alias); + } + } + } + + dlist_free_items(aliases, NULL); + } + + if (error == 0) { + dlist_t *item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + *known = + dlist_insert_ordered(item, *known, + ASCENDING, compare_desc_display_names); + } + } + } + + if (ddp != NULL) { + free(ddp); + } + + return (error); +} + +/* + * FUNCTION: generate_known_slices(dlist_t *disks, + * dlist_t **known, dlist_t **bad) + * + * OUTPUT: disks - a pointer to a list of known disks + * known - a pointer to a dlist_t list to hold the known slices + * bad - a pointer to a dlist_t to hold the bad slices + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Examines input list of known disks and determines the slices + * attached to each. + * + * Some slices returned from libdiskmgt may not really exist, + * this is detected when trying to get more information about + * the slice -- ENODEV is returned. Any such slices will be + * added to the bad slice list. + */ +static int +generate_known_slices( + dlist_t *disks, + dlist_t **known, + dlist_t **bad) +{ + dlist_t *iter; + int error = 0; + + /* iterate list of disks and add their slices to the known list */ + for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + dlist_t *slices = NULL; + dlist_t *iter1; + char *dname = NULL; + boolean_t disk_ctd_alias_derived = B_FALSE; + + if (((error = disk_get_slices(disk, &slices)) != 0) || + ((error = get_display_name(disk, &dname)) != 0)) { + continue; + } + + for (iter1 = slices; + (iter1 != NULL) && (error == 0); + iter1 = iter1->next) { + + dm_descriptor_t slice = (uintptr_t)iter1->obj; + uint32_t index = 0; + nvlist_t *attrs = NULL; + char *sname = NULL; + + if (((error = get_name(slice, &sname)) != 0) || + ((error = slice_get_index(slice, &index)) != 0) || + ((error = get_cached_attributes(slice, &attrs)) != 0)) { + + if (error == ENODEV) { + /* bad slice, remember it and continue */ + dlist_t *item = dlist_new_item((void *)slice); + if (item == NULL) { + error = ENOMEM; + } else { + *bad = dlist_insert_ordered( + item, *bad, + ASCENDING, compare_descriptor_names); + error = 0; + } + } + continue; + } + + if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) && + (disk_ctd_alias_derived == B_FALSE)) { + /* BEGIN CSTYLED */ + /* + * If the slice name is a DID name, get the local CTD + * name for slice, extract the disk name and add it as + * an alias for the disk. + * + * This is the only way to derive the CTD alias for the + * disk when DID is enabled. + * + * The disk_ctd_alias_derived flag ensure the disk's + * CTD alias is only set once. + * + * The slice's CTD aliases are then derived from the + * disk's CTD alias in the normal, non-DID name processing + * which happens below. + */ + /* END CSTYLED */ + char *local = NULL; + if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME, + &local)) != 0) { + if (error == ENOENT) { + /* no local name -> no DID */ + error = 0; + } + } else { + char *localdisk = NULL; + char *diskonly = NULL; + if ((error = extract_diskname(local, + &localdisk)) == 0) { + if ((diskonly = strrchr(localdisk, '/')) != NULL) { + ++diskonly; + } else { + diskonly = localdisk; + } + oprintf(OUTPUT_DEBUG, + gettext(" set DID disk CTD alias: %s\n"), + diskonly); + error = set_alias(disk, diskonly); + free(localdisk); + disk_ctd_alias_derived = B_TRUE; + } + } + } + + /* derive slice display name from disk's display name */ + if (error == 0) { + if ((error = make_slicename_for_diskname_and_index( + dname, index, &sname)) == 0) { + error = set_display_name(slice, sname); + } + } + + /* set slice aliases using disk aliases */ + if (error == 0) { + dlist_t *aliases = NULL; + if ((error = get_aliases(disk, &aliases)) == 0) { + + dlist_t *iter2 = aliases; + for (; (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + + char *dalias = (char *)iter2->obj; + char *salias = NULL; + + if ((error = make_slicename_for_diskname_and_index( + dalias, index, &salias)) == 0) { + error = set_alias(slice, salias); + free(salias); + } + } + dlist_free_items(aliases, free); + } + } + + if (error == 0) { + dlist_t *item = dlist_new_item((void *)slice); + if (item == NULL) { + error = ENOMEM; + } else { + *known = + dlist_insert_ordered( + item, *known, + ASCENDING, compare_desc_display_names); + } + } + } + + dlist_free_items(slices, NULL); + } + + return (error); +} + +/* + * FUNCTION: generate_known_hbas(dlist_t *disks, dlist_t **known) + * + * INPUT: diskset - a char * diskset name. + * + * OUTPUT: populates the list of known HBAs. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Examines known disk list and derives the list of known HBAs. + * + * Determines the CTD name for an HBA and saves it. + */ +static int +generate_known_hbas( + dlist_t *disks, + dlist_t **known) +{ + dlist_t *iter; + int error = 0; + + /* + * for each known disk follow its HBA connections and + * assemble the list of known HBAs. + */ + for (iter = disks; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + dlist_t *hbas = NULL; + dlist_t *iter2 = NULL; + dlist_t *iter3 = NULL; + dlist_t *aliases = NULL; + char *dname = NULL; + + ((error = get_display_name(disk, &dname)) != 0) || + (error = disk_get_aliases(disk, &aliases)) || + (error = disk_get_hbas(disk, &hbas)); + + if (error == 0) { + + if ((hbas == NULL) || (dlist_length(hbas) == 0)) { + + oprintf(OUTPUT_DEBUG, + gettext("Disk %s has no HBA/Controller?!\n"), + dname); + error = -1; + + dlist_free_items(hbas, NULL); + dlist_free_items(aliases, NULL); + + continue; + } + + for (iter2 = hbas, iter3 = aliases; + iter2 != NULL && iter3 != NULL; + iter2 = iter2->next, iter3 = iter3->next) { + + dm_descriptor_t hba = (uintptr_t)iter2->obj; + dm_descriptor_t alias = (uintptr_t)iter3->obj; + dlist_t *item = NULL; + + /* scan list of known HBAs and see if known */ + if (dlist_contains(*known, + (void*)hba, compare_descriptor_names) == B_TRUE) { + /* known HBA */ + continue; + } + + /* see if HBA supports MPXIO */ + if ((error == 0) && (_mpxio_enabled != B_TRUE)) { + hba_is_multiplex(hba, &_mpxio_enabled); + } + + /* generate a CTD name for HBA */ + error = generate_known_hba_name(hba, alias, disk); + if (error == 0) { + /* add to known HBA list */ + if ((item = dlist_new_item((void *)hba)) == NULL) { + error = ENOMEM; + } else { + *known = + dlist_insert_ordered(item, *known, + ASCENDING, compare_desc_display_names); + } + } + } + } + + dlist_free_items(aliases, NULL); + dlist_free_items(hbas, NULL); + } + + return (error); +} + +/* + * FUNCTION: generate_known_hba_name(dm_descriptor_t hba, + * dm_descriptor_t alias, char *diskname) + * + * INPUT: hba - a dm_descriptor_t HBA handle. + * alias - a dm_descriptor_t disk alias handle. + * diskname - a char * disk name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Sets the CTD name for the input HBA. + * + * The CTD name for the HBA is generated from the input + * disk alias (ex: cXdXtXsX) or from the disk name if + * the input alias is a DID name (ex: dX). + */ +static int +generate_known_hba_name( + dm_descriptor_t hba, + dm_descriptor_t alias, + dm_descriptor_t disk) +{ + char *hbaname = NULL; + char *aliasname = NULL; + int error = 0; + + ((error = get_name(alias, &aliasname)) != 0) || + (error = extract_hbaname(aliasname, &hbaname)); + if (error != 0) { + free(hbaname); + return (error); + } + + /* see if the input alias is a DID name... */ + if (is_did_disk_name(aliasname) == B_TRUE) { + + /* look for a non-DID name in disk's aliases */ + dlist_t *aliases = NULL; + error = get_aliases(disk, &aliases); + + for (; (error == 0) && (aliases != NULL); + aliases = aliases->next) { + + aliasname = (char *)aliases->obj; + if (is_did_disk_name(aliasname) != B_TRUE) { + /* this is the "local" CTD name generated by */ + /* generate_known_disks() above */ + error = extract_hbaname(aliasname, &hbaname); + if ((error == 0) && (hbaname != NULL)) { + set_display_name(hba, hbaname); + break; + } + } + } + dlist_free_items(aliases, free); + + } else { + /* use whatever was derived from the alias name */ + set_display_name(hba, hbaname); + } + + return (error); +} + +/* + * FUNCTION: print_known_devices() + * + * PURPOSE: Print out the known devices. + * + * Iterates the lists of known slices, disks and HBAs + * and prints out their CTD and device names. + */ +static void +print_known_devices( + char *diskset) +{ + int i = 0; + struct { + char *msg; + dlist_t *list; + } devs[3]; + + devs[0].msg = gettext("HBA/Controllers"); + devs[0].list = _known_hbas; + devs[1].msg = gettext("disks"); + devs[1].list = _known_disks; + devs[2].msg = gettext("slices"); + devs[2].list = _known_slices; + + for (i = 0; i < 3; i++) { + + oprintf(OUTPUT_VERBOSE, + gettext("\n These %s are known:\n\n"), + devs[i].msg); + + print_device_list(devs[i].list); + } +} + +/* + * FUNCTION: get_usable_slices(dlist_t **list) + * + * OUTPUT: list - a dlist_t pointer to hold the returned list of + * devices. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Public accessors the the modules private lists of + * available devices. + * + * The functions are keyed by diskset name in the event + * objects in different disksets are loaded concurrently. + */ +int +get_usable_slices( + dlist_t **list) +{ + *list = _usable_slices; + + return (0); +} + +int +get_usable_disks( + dlist_t **list) +{ + *list = _usable_disks; + + return (0); +} + +int +get_usable_hbas( + dlist_t **list) +{ + *list = _usable_hbas; + + return (0); +} + +/* + * FUNCTION: generate_usable_disks_and_slices_in_local_set(dlist_t **classes, + * dlist_t **bad_disks, dlist_t **usable_disks, + * dlist_t **usable_slices) + * + * OUTPUT: used_classes - a pointer to a list of slice_class_t structs + * updated with known slices that have detected uses + * added to the correct class'e list of slices. + * bad_disks - a pointer to a list of bad/unusable disks updated + * with any bad disks that were detected + * useable_disks - a pointer to a list of usable disks + * useable_slices - a pointer to a list of usable slices + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Scans the disks in the local set to determine which are + * usable during layout processing. + * + * Determines which are usable by layout using usages detected + * by libdiskmgt. + */ +static int +generate_usable_disks_and_slices_in_local_set( + dlist_t **classes, + dlist_t **bad_slices, + dlist_t **usable_disks, + dlist_t **usable_slices) +{ + char *dsname = MD_LOCAL_NAME; + dlist_t *disks; + dlist_t *iter; + int error; + + /* Get disks in local set */ + error = get_disks_in_diskset(dsname, &disks); + if (error != 0) { + return (error); + } + + /* For each disk in this set... */ + for (iter = disks; iter != NULL && error == 0; iter = iter->next) { + dm_descriptor_t disk = (uintptr_t)iter->obj; + dlist_t *slices; + + /* Get slices on this disk */ + error = disk_get_slices(disk, &slices); + if (error == 0) { + dlist_t *iter2; + + /* + * Assume disk is available until a bad or unavailable + * slice is found + */ + boolean_t avail = B_TRUE; + boolean_t bad_disk = B_FALSE; + + /* For each slice on this disk... */ + for (iter2 = slices; + iter2 != NULL && error == 0 && + avail == B_TRUE && bad_disk == B_FALSE; + iter2 = iter2->next) { + + dm_descriptor_t slice = (uintptr_t)iter2->obj; + dlist_t *bad_slices_on_this_disk = NULL; + + /* Is this slice available? */ + error = check_slice_usage(dsname, slice, + disk, &avail, &bad_slices_on_this_disk, classes); + + /* Is the slice bad (inaccessible)? */ + if (error != 0 && bad_slices_on_this_disk != NULL) { + bad_disk = B_TRUE; + *bad_slices = dlist_append_list( + *bad_slices, bad_slices_on_this_disk); + } + } + + /* Is the disk available? */ + if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) { + error = dlist_append_object( + (void *)disk, usable_disks, AT_TAIL); + } + + dlist_free_items(slices, NULL); + } + } + + dlist_free_items(disks, NULL); + + if (error == 0) { + /* BEGIN CSTYLED */ + /* + * Now reslice usable disks in the local set to + * simulate the slices they'll have when they're added + * to the named disk set, and add these resulting + * virtual slices to the list of available slices. + */ + /* END CSTYLED */ + error = generate_virtual_slices(*usable_disks, usable_slices); + } + + return (error); +} + +/* + * FUNCTION: generate_virtual_slices(dlist_t *unused, dlist_t **usable) + * + * INPUT: slice_classes - a list of unused slice dm_descriptor_t handles. + * + * OUTPUT: usable - pointer to the list of usable slices, updated + * with any created virtual slices. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which creates virtual slices for each disk which + * could be added to a diskset if necessary... + * + * Search the input list of slice classes for the entry + * containing slices known to be available for use by layout. + * + * Iterate the list of unused slices and determine the set + * of unique disks. + * + * For each unique disk, create virtual slice descriptors to + * represent those that will exist if/when the disk is added + * to the diskset. + * + * Add theese virtual slices to the list of usable slices. + */ +static int +generate_virtual_slices( + dlist_t *avail_disks_local_set, + dlist_t **usable) +{ + dlist_t *iter = NULL; + int error = 0; + + /* generate virtual slices */ + error = create_virtual_slices(avail_disks_local_set); + if (error == 0) { + + get_virtual_slices(&iter); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + + dlist_t *item = dlist_new_item((void *) iter->obj); + if (item == NULL) { + error = ENOMEM; + } else { + *usable = + dlist_insert_ordered(item, *usable, + ASCENDING, compare_desc_display_names); + } + } + } + + return (error); +} + +/* + * FUNCTION: generate_usable_disks_and_slices_in_named_set(char *dsname, + * dlist_t **classes, dlist_t **bad_slices, + * dlist_t **usable_slices, dlist_t **usable_disks) + * + * INPUT: dsname - a char * diskset name. + * + * OUTPUT: classes - pointer to a list of slice_class_t structs, + * updated to include slices in the disk set with + * known uses. + * bad_slices - pointer to a list of bad/unusable slices, + * updated to include slices in the disk set that + * are inaccessible or no longer existent. + * usable_slices - pointer to a list of usable slices in the + * disk set. + * usable_disks - pointer to a list of usable disks in the + * disk set. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: 1. determine the disks in the named disk set + * 2. determine the used slices on the disks + * 3. determine the unused slices on the disks + * 4. look for unused space on the disks and collect it + * into an existing unused slice, or create a new + * virtual slice. + */ +static int +generate_usable_disks_and_slices_in_named_set( + char *dsname, + dlist_t **classes, + dlist_t **bad_slices, + dlist_t **usable_disks, + dlist_t **usable_slices) +{ + dlist_t *disks = NULL; + dlist_t *iter = NULL; + int error = 0; + + error = get_disks_in_diskset(dsname, &disks); + if (error != 0) { + return (error); + } + + /* For each disk... */ + for (iter = disks; + iter != NULL && error == 0; + iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + dlist_t *iter2; + dlist_t *slices = NULL; + dlist_t *bad_slices_on_this_disk = NULL; + dlist_t *used_slices_on_this_disk = NULL; + dlist_t *unused_slices_on_this_disk = NULL; + boolean_t bad_disk = B_FALSE; + + error = disk_get_slices(disk, &slices); + if (error != 0) { + break; + } + + /* Determine the used, unused, and bad slices on the disk */ + + /* For each slice... */ + for (iter2 = slices; + iter2 != NULL && error == 0 && bad_disk == B_FALSE; + iter2 = iter2->next) { + + dm_descriptor_t slice = (uintptr_t)iter2->obj; + + boolean_t rsvd = B_FALSE; + boolean_t avail = B_FALSE; + + /* Get slice usage */ + if (((error = is_reserved_slice(slice, &rsvd)) == 0) && + ((error = check_slice_usage(dsname, slice, disk, &avail, + &bad_slices_on_this_disk, classes)) == 0)) { + + /* Is the slice bad (inaccessible)? */ + if (bad_slices_on_this_disk != NULL) { + *bad_slices = dlist_append_list( + *bad_slices, bad_slices_on_this_disk); + /* + * Since one slice on this disk is bad, don't + * use any slices on this disk + */ + bad_disk = B_TRUE; + } else { + + dlist_t *item = dlist_new_item((void *)slice); + if (item == NULL) { + error = ENOMEM; + } else { + /* Add slice to used/unused list as appropriate */ + if (avail == B_TRUE && rsvd == B_FALSE) { + unused_slices_on_this_disk = dlist_append( + item, unused_slices_on_this_disk, AT_TAIL); + } else { + used_slices_on_this_disk = + dlist_insert_ordered(item, + used_slices_on_this_disk, + ASCENDING, compare_start_blocks); + } + } + } + } + } + + /* Done iterating slices */ + + if (error == 0 && bad_disk == B_FALSE) { + /* For each unused slice... */ + for (iter2 = unused_slices_on_this_disk; + iter2 != NULL && error == 0; + iter2 = iter2->next) { + + dm_descriptor_t slice = (uintptr_t)iter2->obj; + error = update_slice_attributes(slice, 0, 0, 0); + + /* Only do this once */ + if (error == 0 && iter2 == unused_slices_on_this_disk) { + error = add_modified_disk(NULL, disk); + } + } + + if (error == 0) { + /* Create usable slices from the used/unused slice lists */ + error = create_usable_slices(disk, used_slices_on_this_disk, + unused_slices_on_this_disk, usable_slices); + if (error == 0) { + error = dlist_append_object( + (void *)disk, usable_disks, AT_TAIL); + } + } + } + + dlist_free_items(slices, NULL); + dlist_free_items(used_slices_on_this_disk, NULL); + dlist_free_items(unused_slices_on_this_disk, NULL); + } + + return (error); +} + +/* + * FUNCTION: create_usable_slices(dm_descriptor_t disk, dlist_t *used, + * dlist_t *unused, dlist_t **usable); + * + * INPUT: disk - a dm_descriptor_t disk handle + * used - pointer to a list of pvt_t structs + * representing existing used slices + * on the input disk. + * unused - pointer to a list of pvt_t structs + * representing existing unused slices + * on the input disk. + * + * OUTPUT: usable - pointer to a list of pvts representing slices + * which can be used for new volume layouts. + * + * Slices in this list have any available space on the + * disk collected into the fewest, lowest indexed slices + * possible. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: helper for generate_usable_slices_and_disks_in_diskset() which + * turns any detected free space on the input disk into one or + * more slices. + */ +static int +create_usable_slices( + dm_descriptor_t disk, + dlist_t *used, + dlist_t *unused, + dlist_t **usable) +{ + dlist_t *iter; + int error = 0; + boolean_t first = B_TRUE; + dlist_t *next_unused = unused; + + char *dname = NULL; + uint64_t disk_firstblk = 0; + uint64_t disk_nblks = 0; + uint64_t disk_endblk = 0; + + oprintf(OUTPUT_DEBUG, + gettext("\n create_usable_slices for disk\n")); + + /* get necessary info about disk: */ + error = get_display_name(disk, &dname); + if (error != 0) { + return (error); + } + + /* disk start block is first usable block */ + error = disk_get_start_block(disk, &disk_firstblk); + if (error != 0) { + return (error); + } + + /* disk size determines last usable disk block */ + error = disk_get_size_in_blocks(disk, &disk_nblks); + if (error != 0) { + return (error); + } + + disk_endblk = disk_firstblk + disk_nblks - 1; + + /* search for gaps before, between and after used slices */ + for (iter = used; iter != NULL && error == 0; iter = iter->next) { + + dm_descriptor_t cur = (uintptr_t)iter->obj; + + uint64_t cur_stblk = 0; + uint64_t cur_nblks = 0; + uint64_t cur_endblk = 0; + uint32_t cur_index = 0; + + uint64_t new_stblk = 0; + uint64_t new_endblk = 0; + + char *sname = NULL; + (void) get_display_name(cur, &sname); + + if (((error = slice_get_index(cur, &cur_index)) != 0) || + ((error = slice_get_start_block(cur, &cur_stblk)) != 0) || + ((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) { + continue; + } + + cur_endblk = cur_stblk + cur_nblks - 1; + + oprintf(OUTPUT_DEBUG, + gettext(" used slice %d (%10llu to %10llu)\n"), + cur_index, cur_stblk, cur_endblk); + + if (first == B_TRUE) { + /* first slice: make sure it starts at disk_firstblk */ + first = B_FALSE; + if (cur_stblk != disk_firstblk) { + /* close gap at beginning of disk */ + new_stblk = disk_firstblk; + new_endblk = cur_stblk - 1; + + oprintf(OUTPUT_DEBUG, + gettext(" unused space before first " + "used slice\n")); + } + } + + if (iter->next != NULL) { + /* check for gap between slices */ + dm_descriptor_t next = (uintptr_t)iter->next->obj; + uint64_t next_stblk = 0; + uint32_t next_index = 0; + + if (((error = slice_get_start_block(next, &next_stblk)) == 0) && + ((error = slice_get_index(next, &next_index)) == 0)) { + if (cur_endblk != next_stblk - 1) { + /* close gap between slices */ + new_stblk = cur_endblk + 1; + new_endblk = next_stblk - 1; + + oprintf(OUTPUT_DEBUG, + gettext(" unused space between slices " + "%d and %d\n"), cur_index, next_index); + } + } + + } else { + /* last slice: make sure it includes last block on disk */ + if (cur_endblk != disk_endblk) { + /* close gap at end of disk */ + new_stblk = cur_endblk + 1; + new_endblk = disk_endblk; + + oprintf(OUTPUT_DEBUG, + gettext(" unused space after last slice " + "cur_endblk: %llu disk_endblk: %llu\n"), + cur_endblk, disk_endblk); + } + } + + if ((error == 0) && (new_endblk != 0)) { + error = add_new_usable(disk, new_stblk, + new_endblk - new_stblk + 1, &next_unused, usable); + } + } + + if (error != 0) { + dlist_free_items(*usable, free); + *usable = NULL; + } + + return (error); +} + +/* + * FUNCTION: add_new_usable(dm_descriptor_t disk, uint64_t stblk, + * uint64_t nblks, dlist_t **next_unused, + * dlist_t **usable); + * + * INPUT: disk - a dm_descriptor_t disk handle + * stblk - start block of the usable space + * nblks - number of usable blocks + * next_unused - pointer to the next unused slice + * + * OUTPUT: next_unused - updated pointer to the next unused slice + * usable - possibly updated pointer to a list of slices on + * the disk with usable space + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: helper for create_usable_slices() which turns free space + * on the input disk into a usable slice. + * + * If possible an existing unused slice will be recycled + * into a usable slice. If there are none, a new virtual + * slice will be created. + */ +static int +add_new_usable( + dm_descriptor_t disk, + uint64_t stblk, + uint64_t nblks, + dlist_t **next_unused, + dlist_t **usable) +{ + dm_descriptor_t new_usable = 0; + int error = 0; + + /* try to use an existing unused slice for the usable slice */ + if (*next_unused != NULL) { + new_usable = (uintptr_t)((*next_unused)->obj); + *next_unused = (*next_unused)->next; + + oprintf(OUTPUT_DEBUG, + gettext("\trecyling used slice into usable slice " + "start: %llu, end: %llu\n"), + stblk, stblk + nblks + 1); + } + + if (new_usable == NULL) { + /* no unused slices, try to make a new virtual slice */ + uint32_t index = UINT32_MAX; + error = disk_get_available_slice_index(disk, &index); + if ((error == 0) && (index != UINT32_MAX)) { + + char *dname = NULL; + error = get_display_name(disk, &dname); + if (error == 0) { + + char buf[MAXNAMELEN]; + (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index); + error = add_virtual_slice(buf, index, 0, 0, disk); + if (error == 0) { + /* retrieve the virtual slice */ + error = slice_get_by_name(buf, &new_usable); + } + } + } + } + + if ((error == 0) && (new_usable != (dm_descriptor_t)0)) { + /* BEGIN CSTYLED */ + /* + * have an unused slice, update its attributes to reflect + * the usable space it represents + */ + /* END CSTYLED */ + uint64_t disk_blksz = 0; + error = disk_get_blocksize(disk, &disk_blksz); + if (error == 0) { + error = update_slice_attributes(new_usable, stblk, + nblks, nblks * disk_blksz); + if (error == 0) { + error = dlist_append_object( + (void *)new_usable, usable, AT_TAIL); + } + } + } + + return (error); +} + +/* + * FUNCTION: update_slice_attributes(dm_descriptor_t slice, uint64_t stblk, + * uint64_t nblks, uint64_t nbytes) + * + * INPUT: slice - a dm_descriptor_t slice handle + * stblk - start block of the usable space + * nblks - size of slice in blocks + * nbytes - size of slice in bytes + * + * SIDEEFFECT: adds a modification record for the slice. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: utility which updates several slice attributes in one call. + */ +static int +update_slice_attributes( + dm_descriptor_t slice, + uint64_t stblk, + uint64_t nblks, + uint64_t nbytes) +{ + char *sname = NULL; + uint32_t index = 0; + int error = 0; + + if ((error = get_display_name(slice, &sname)) == 0) { + if ((error = slice_get_index(slice, &index)) == 0) { + if ((error = slice_set_start_block(slice, stblk)) == 0) { + if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) { + if (nblks == 0) { + error = add_slice_to_remove(sname, index); + } else { + error = assemble_modified_slice((dm_descriptor_t)0, + sname, index, stblk, nblks, nbytes, NULL); + } + } + } + } + } + + return (error); +} + +/* + * FUNCTION: generate_usable_hbas(dlist_t *slices, + * dlist_t **usable) + * + * INPUT: disks - a list of usable disks. + * + * OUTPUT: usable - a populated list of usable HBAs. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Examines usable disk list and derives the list of usable HBAs. + * + */ +static int +generate_usable_hbas( + dlist_t *disks, + dlist_t **usable) +{ + dlist_t *iter; + int error = 0; + + /* + * for each usable disk, follow its HBA connections and + * add them to the list of usable HBAs. + */ + for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) { + + dm_descriptor_t dp = NULL; + dlist_t *hbas = NULL; + dlist_t *iter2 = NULL; + + dp = (uintptr_t)iter->obj; + + error = disk_get_hbas(dp, &hbas); + if (error == 0) { + + for (iter2 = hbas; + (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + + dm_descriptor_t hba = (uintptr_t)iter2->obj; + dlist_t *item = NULL; + + /* scan list of usable HBAs and see if known */ + if (dlist_contains(*usable, + (void*)hba, compare_descriptor_names) == B_TRUE) { + /* known HBA, continue to next HBA/alias */ + continue; + } + + /* add this HBA to the usable list */ + if ((item = dlist_new_item((void *)hba)) == NULL) { + error = ENOMEM; + } else { + *usable = + dlist_insert_ordered(item, *usable, + ASCENDING, compare_desc_display_names); + } + } + } + + dlist_free_items(hbas, NULL); + } + + return (error); +} + +/* + * FUNCTION: check_slice_usage(char *dsname, dm_descriptor_t slice, + * dm_descriptor_t disk, boolean_t *avail, + * dlist_t **bad, dlist_t **classes) + * + * INPUT: dsname - a char * diskset name. + * slice - a dm_descriptor_t handle for a known slices. + * disk - a dm_descriptor_t handle the slice's disk. + * + * OUTPUT: avail - a boolean_t to hold the slice's availability. + * bad - pointer to a list of bad/unusable slices, + * possibly updated if the input slice + * was determined to be inaccessible. + * classes - pointer to a list of slice_class_t structs, + * possibly updated to include the input slice + * if it has a known use. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Handles the details of + * determining usage and/or availability of a single slice. + * + * Queries the device library for the input slice's detectable + * usage status. + * + * If the slice has a detected usage, its name is added to + * the appropriate slice_class_t list in the input list of + * slice classes, this is only done if verbose output was + * requested. + */ +static int +check_slice_usage( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + boolean_t *avail, + dlist_t **bad, + dlist_t **classes) +{ + boolean_t online = B_FALSE; + boolean_t used = B_FALSE; + nvlist_t *stats = NULL; + char *name = NULL; + char *used_by = NULL; + char *use_detail = NULL; + int error = 0; + + *avail = B_FALSE; + + if (((error = get_display_name(slice, &name)) != 0) || + (error = disk_get_is_online(disk, &online))) { + return (error); + } + + /* + * if the disk is known to be offline, skip getting status + * for the slice since it will just fail and return ENODEV. + */ + if (online != B_TRUE) { + error = ENODEV; + } else { + stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error); + } + + if (error != 0) { + if (error == ENODEV) { + dlist_t *item = dlist_new_item((void *)slice); + oprintf(OUTPUT_TERSE, + gettext("Warning: unable to get slice information " + "for %s, it will not be used.\n"), name); + + if (item == NULL) { + error = ENOMEM; + } else { + error = 0; + *bad = dlist_insert_ordered(item, *bad, ASCENDING, + compare_desc_display_names); + } + } else { + oprintf(OUTPUT_TERSE, + gettext("check_slice_usage: dm_get_stats for " + "%s failed %d\n"), + name, error); + } + + return (error); + } + + /* + * check if/how the slice is currently being used, + * device library provides this info in the nvpair_t list: + * + * stat_type is DM_SLICE_STAT_USE + * used_by: string (mount, svm, lu, vxvm, fs) + * used_name: string + * + */ + if (stats != NULL) { + error = get_string(stats, DM_USED_BY, &used_by); + if (error != 0) { + if (error == ENOENT) { + used_by = NULL; + error = 0; + } else { + oprintf(OUTPUT_TERSE, + gettext("check_slice_usage: dm_get_stats.%s for " + "%s failed %d\n"), + DM_USED_BY, name, error); + } + } + + if (error == 0) { + error = get_string(stats, DM_USED_NAME, &use_detail); + if (error != 0) { + if (error == ENOENT) { + use_detail = NULL; + error = 0; + } else { + oprintf(OUTPUT_TERSE, + gettext("check_slice_usage: " + "dm_get_stats.%s for " + "%s failed %d\n"), + DM_USED_NAME, name, error); + } + } + } + } + + if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) { + + /* was detected usage SVM? */ + if (string_case_compare(used_by, DM_USE_SVM) == 0) { + + /* check use_detail, it is in the form diskset:name */ + if (strncmp("diskset:", use_detail, 8) == 0) { + + /* check disk set name */ + char *str = strrchr(use_detail, ':'); + if ((str != NULL) && + (string_case_compare(str+1, dsname) == 0)) { + + /* slice in the right diskset */ + error = check_svm_slice_usage( + dsname, slice, disk, &used, classes); + + } else { + + /* slice in other diskset */ + save_slice_classification( + dsname, slice, disk, used_by, use_detail, + classes); + used = B_TRUE; + } + + } else { + + /* slice is volume component */ + save_slice_classification( + dsname, slice, disk, used_by, use_detail, + classes); + used = B_TRUE; + } + + } else { + + /* save usage */ + save_slice_classification( + dsname, slice, disk, used_by, use_detail, + classes); + used = B_TRUE; + } + } + + nvlist_free(stats); + + if (error == 0) { + if (used == B_TRUE) { + *avail = B_FALSE; + } else { + *avail = B_TRUE; + } + } + + return (error); +} + +/* + * FUNCTION: check_svm_slice_usage(char *dsname, dm_descriptor_t slice, + * dm_descriptor_t disk, boolean_t *used, + * dlist_t **classes) + * + * INPUT: dsname - a char * diskset name. + * slice - a dm_descriptor_t handle for a known slices. + * disk - a dm_descriptor_t handle the slice's disk. + * + * OUTPUT: used - a boolean_t to hold the slice usage status. + * classes - pointer to a list of slice_class_t possibly updated + * with the input slice's SVM specific usage + * classification. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Handles the finer details of + * a single slice is being used in the context of SVM. + * + * Currently, one thing is checked: + * + * 1. determine if the slice is reserved for metadb replicas. + * The convention for disks in disksets is that a single slice + * (index 6 or 7) is set aside for metadb replicas. + * + * If this condition does not hold, the slice is considered + * available for use by layout and 'used' is set to B_FALSE. + */ +static int +check_svm_slice_usage( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + boolean_t *used, + dlist_t **classes) +{ + boolean_t is_replica = B_FALSE; + uint32_t index = 0; + char *diskname = NULL; + int error = 0; + + ((error = slice_get_index(slice, &index)) != 0) || + (error = get_display_name(disk, &diskname)) || + (error = is_reserved_replica_slice_index( + dsname, diskname, index, &is_replica)); + + if (error == 0) { + if (is_replica == B_TRUE) { + /* is replica slice -> used */ + save_slice_classification(dsname, slice, disk, DM_USE_SVM, + gettext("reserved for metadb replicas"), classes); + *used = B_TRUE; + } else { + *used = B_FALSE; + } + } + + return (error); +} + +/* + * FUNCTION: save_slice_classification(char *dsname, dm_descriptor_t slice, + * dm_descriptor_t disk, char *used_by, char *usage_detail, + * dlist_t **classes) + * + * INPUT: dsname - a char * disk set name + * slice - a dm_descriptor_t slice handle. + * disk - a dm_descriptor_t handle for the slice's disk. + * used_by - a char * usage classification. + * usage_detail - a char * usage description for the slice. + * + * OUTPUT: classes - a list of slice_class_t updated to hold a usage + * entry for the input slicexs. + * + * SIDEEFFECT: adds the input slice to the list of known, used slices. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Adds an entry to the + * appropriate slice_class_t list of slices. If there is + * not an appropriate slice_class_t entry in the input list + * of classes, one is added. + * + * As a performance optimization the slice usage classification + * information is only saved if verbose output was requested by + * the user. + */ +static int +save_slice_classification( + char *dsname, + dm_descriptor_t slice, + dm_descriptor_t disk, + char *usage, + char *usage_detail, + dlist_t **classes) +{ + int error = 0; + + error = add_used_slice(slice); + + if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) { + + dlist_t *iter; + dlist_t *item; + slice_class_t *class = NULL; + + /* locate class struct matching 'usage' */ + for (iter = *classes; iter != NULL; iter = iter->next) { + class = (slice_class_t *)iter->obj; + if (string_case_compare(usage, class->usage) == 0) { + break; + } + } + + if (iter == NULL) { + /* add a new class to the list of classes */ + class = (slice_class_t *)calloc(1, sizeof (slice_class_t)); + if (class == NULL) { + error = ENOMEM; + } else { + class->usage = strdup(usage); + if (class->usage == NULL) { + free(class); + class = NULL; + error = ENOMEM; + } else { + item = dlist_new_item((void *)class); + if (item == NULL) { + free(class->usage); + free(class); + class = NULL; + error = ENOMEM; + } else { + *classes = dlist_append(item, *classes, AT_TAIL); + } + } + } + } + + if ((error == 0) && (class != NULL)) { + + char buf[BUFSIZ]; + char *dup = NULL; + char *slicename = NULL; + + (void) get_display_name(slice, &slicename); + (void) snprintf(buf, BUFSIZ-1, " %s: %s", + slicename, usage_detail); + if ((dup = strdup(buf)) == NULL) { + error = ENOMEM; + } else { + if ((item = dlist_new_item((void *)dup)) == NULL) { + free(dup); + error = ENOMEM; + } else { + class->sliceinfo = + dlist_insert_ordered( + item, class->sliceinfo, + ASCENDING, compare_strings); + } + } + } + } + + return (error); +} + +/* + * FUNCTION: print_usable_devices() + * + * PURPOSE: Print out the devices determined to be available for + * use by layout. + * + * Iterates the lists of usable slices, disks and HBAs + * and prints out their CTD and device names. + */ +static void +print_usable_devices() +{ + int i = 0; + + struct { + char *msg; + dlist_t *list; + } devs[3]; + + devs[0].msg = gettext("HBA/Controllers"); + devs[0].list = _usable_hbas; + devs[1].msg = gettext("disks"); + devs[1].list = _usable_disks; + devs[2].msg = gettext("slices"); + devs[2].list = _usable_slices; + + for (i = 0; i < 3; i++) { + + oprintf(OUTPUT_VERBOSE, + gettext("\n These %s are usable:\n\n"), + devs[i].msg); + + print_device_list(devs[i].list); + } +} + +/* + * FUNCTION: print_device_list(dlist_t *devices) + * + * INPUT: devices - a list of device descriptor handles + * + * PURPOSE: A helper for the print_XXX_devices() routines which iterates + * the input list and prints out each device name, CTD name and + * alias(es). + */ +static void +print_device_list( + dlist_t *devices) +{ + dlist_t *iter = NULL; + + for (iter = devices; iter != NULL; iter = iter->next) { + + dm_descriptor_t device = ((uintptr_t)iter->obj); + char *name = NULL; + char *ctd = NULL; + dlist_t *aliases = NULL; + + (void) get_display_name(device, &ctd); + (void) get_name(device, &name); + oprintf(OUTPUT_VERBOSE, + " %-25s %s\n", (ctd != NULL ? ctd : ""), name); + + (void) get_aliases(device, &aliases); + for (; aliases != NULL; aliases = aliases->next) { + oprintf(OUTPUT_VERBOSE, + gettext(" (alias: %s)\n"), + (char *)aliases->obj); + } + + dlist_free_items(aliases, free); + } +} + +/* + * FUNCTION: print_unusable_devices( + * dlist_t *bad_slices, dlist_t *bad_disks, + * dlist_t *used_classes) + * + * INPUT: used_classes - a list of slice_class_t structs + * + * PURPOSE: Print out the devices determined to be unavailable for + * use by layout. + * + * Iterates the input list of slice classifications and prints + * out a description of the class and the slices so classified. + * + * Also iterates the lists of bad slices and disks (those that + * libdiskmgt returned descriptors for but cannot be accessed) + * and notes them as unusable. + */ +static void +print_unusable_devices( + dlist_t *bad_slices, + dlist_t *bad_disks, + dlist_t *used_classes) +{ + dlist_t *iter = NULL; + dlist_t *slices = NULL; + char *preamble; + + struct { + char *msg; + dlist_t *list; + } devs[2]; + + /* report bad disks and slices */ + devs[0].msg = gettext("disks"); + devs[0].list = bad_disks; + devs[1].msg = gettext("slices"); + devs[1].list = bad_slices; + + if (bad_disks != NULL) { + oprintf(OUTPUT_VERBOSE, +#if defined(sparc) + gettext("\n These disks are not usable, they may " + "may be offline or cannot be accessed:\n\n")); +#elif defined(i386) + gettext("\n These disks are not usable, they may " + "may be offline,\n missing a Solaris FDISK " + "partition or cannot be accessed:\n\n")); +#endif + print_device_list(bad_disks); + } + + if (bad_slices != NULL) { + oprintf(OUTPUT_VERBOSE, gettext( + "\n These slices, and subsequently the disks on which they\n" + "reside, are not usable, they cannot be accessed:\n\n")); + print_device_list(bad_slices); + } + + /* report used slices and usages */ + preamble = gettext("\n These slices are not usable, %s:\n\n"); + for (iter = used_classes; iter != NULL; iter = iter->next) { + slice_class_t *class = (slice_class_t *)iter->obj; + + if (class->sliceinfo != NULL) { + + oprintf(OUTPUT_VERBOSE, preamble, + get_slice_usage_msg(class->usage)); + + slices = class->sliceinfo; + for (; slices != NULL; slices = slices->next) { + oprintf(OUTPUT_VERBOSE, " %s\n", (char *)slices->obj); + } + } + } + +} + +/* + * FUNCTION: char * get_slice_usage_msg(char *usage) + * + * INPUT: usage - char * string representing a slice usage classification + * + * OUTPUT: char * "friendly" usage message + * + * PURPOSE: the input usage string comes from libdiskmgt and is very terse. + * + * Convert it into a friendlier usage description suitable for user + * consumption. + */ +static char * +get_slice_usage_msg( + char *usage) +{ + char *str = NULL; + + if (string_case_compare(usage, DM_USE_MOUNT) == 0) { + str = gettext("they have mounted filesystems"); + } else if (string_case_compare(usage, DM_USE_FS) == 0) { + str = gettext("they appear to have unmounted filesystems"); + } else if (string_case_compare(usage, DM_USE_SVM) == 0) { + str = gettext("they are utilized by SVM"); + } else if (string_case_compare(usage, DM_USE_VXVM) == 0) { + str = gettext("they are utilized by VxVm"); + } else if (string_case_compare(usage, DM_USE_LU) == 0) { + str = gettext("they are utilized by LiveUpgrade"); + } else if (string_case_compare(usage, DM_USE_DUMP) == 0) { + str = gettext("they are reserved as dump devices"); + } else if (string_case_compare(usage, USE_DISKSET) == 0) { + str = gettext("they have disk set issues"); + } else { + /* libdiskmgt has detected a usage unknown to layout */ + str = usage; + } + + return (str); +} + +/* + * FUNCTION: set_alias(dm_descriptor_t desc, char *alias) + * + * INPUT: desc - a dm_descriptor_t handle. + * alias - a char * alias for the device represented + * by the descriptor. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Adds the specified alias to the known aliases for the + * device associated with the input descriptor. + */ +int +set_alias( + dm_descriptor_t desc, + char *alias) +{ + nvlist_t *attrs = NULL; + char **old_aliases = NULL; + char **new_aliases = NULL; + uint_t nelem = 0; + int error = 0; + int i = 0; + + if ((error = get_cached_attributes(desc, &attrs)) != 0) { + return (error); + } + + if ((error = get_string_array( + attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) { + if (error != ENOENT) { + return (error); + } + /* no aliases yet */ + error = 0; + } + + /* add new alias */ + new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *)); + if (new_aliases != NULL) { + + for (i = 0; i < nelem && i < MAX_ALIASES; i++) { + char *dup = strdup(old_aliases[i]); + if (dup != NULL) { + new_aliases[i] = dup; + } else { + error = ENOMEM; + } + } + + if (error == 0) { + if (i == MAX_ALIASES) { + volume_set_error( + gettext("Maximum number of device aliases " + "(8) reached\n"), + MAX_ALIASES); + error = -1; + + } else { + new_aliases[i] = alias; + error = set_string_array(attrs, ATTR_DEVICE_ALIASES, + new_aliases, i + 1); + } + } + + free(new_aliases); + } + + if (error == 0) { + /* cache descriptor under this alias */ + error = add_cached_descriptor(alias, desc); + } + + return (error); +} + +/* + * FUNCTION: get_aliases(dm_descriptor_t desc, dlist_t **list) + * + * INPUT: desc - a dm_descriptor_t handle. + * + * OUTPUT: list - a dlist_t list pointing to the list of + * aliases associated with the device + * represented by the descriptor. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Retrieves aliases for the input descriptor and + * appends them to the input list. + * + * The list of returned items must be freed by calling + * dlist_free_items(list, free) + */ +int +get_aliases( + dm_descriptor_t desc, + dlist_t **list) +{ + nvlist_t *attrs = NULL; + char **aliases = NULL; + uint_t nelem = 0; + int error = 0; + int i; + + if ((error = get_cached_attributes(desc, &attrs)) != 0) { + return (error); + } + + if ((error = get_string_array( + attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) { + if (error == ENOENT) { + /* no aliases */ + return (0); + } + } + + for (i = 0; i < nelem; i++) { + dlist_t *item; + char *dup; + + if ((dup = strdup(aliases[i])) == NULL) { + error = ENOMEM; + break; + } + + if ((item = dlist_new_item(dup)) == NULL) { + free(dup); + error = ENOMEM; + break; + } + + *list = dlist_append(item, *list, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: compare_start_blocks( + * void *obj1, void *obj2) + * + * INPUT: desc1 - opaque pointer to a dm_descriptor_t + * desc2 - opaque pointer to a dm_descriptor_t + * + * RETURNS: int - <0 - if desc1.stblk < desc2.stblk + * 0 - if desc1.stblk == desc2.stblk + * >0 - if desc1.stblk > desc.stblk + * + * PURPOSE: dlist_t helper which compares the start blocks of + * the two input dm_descriptor_t slice handles. + */ +static int +compare_start_blocks( + void *desc1, + void *desc2) +{ + uint64_t stblk1 = 0; + uint64_t stblk2 = 0; + + assert(desc1 != (dm_descriptor_t)0); + assert(desc2 != (dm_descriptor_t)0); + + (void) slice_get_start_block((uintptr_t)desc1, &stblk1); + (void) slice_get_start_block((uintptr_t)desc2, &stblk2); + + return (stblk1 - stblk2); +} + +/* + * FUNCTION: compare_desc_display_names( + * void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer to a dm_descriptor_t + * desc2 - opaque pointer to a dm_descriptor_t + * + * RETURNS: int - <0 - if desc1.name < desc2.name + * 0 - if desc1.name == desc2.name + * >0 - if desc1.name > desc.name + * + * PURPOSE: dlist_t helper which compares the CTD names of the + * two input dm_descriptor_t objects. + */ +static int +compare_desc_display_names( + void *desc1, + void *desc2) +{ + char *name1 = NULL; + char *name2 = NULL; + + assert(desc1 != (dm_descriptor_t)0); + assert(desc2 != (dm_descriptor_t)0); + + (void) get_display_name((uintptr_t)desc1, &name1); + (void) get_display_name((uintptr_t)desc2, &name2); + + return (string_case_compare(name1, name2)); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_discovery.h b/usr/src/cmd/lvm/metassist/layout/layout_discovery.h new file mode 100644 index 0000000000..edcc661f26 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_discovery.h @@ -0,0 +1,89 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_DISCOVERY_H +#define _LAYOUT_DISCOVERY_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libdiskmgt.h" + +/* + * scan physical devices and build lists of known devices. + */ +extern int discover_known_devices(); + +/* + * release lists of known devices. + */ +extern int release_known_devices(); + +/* + * scan known devices and build lists of usable devices. + */ +extern int discover_usable_devices(char *diskset); + +/* + * release lists of usable devices. + */ +extern int release_usable_devices(); + +/* + * functions to access lists of known devices for the system, + * constructed by load_physical_devices + */ +extern int get_known_slices(dlist_t **list); +extern int get_known_disks(dlist_t **list); +extern int get_known_hbas(dlist_t **list); + +/* + * functions to access lists of devices for the named diskset + * constructed by load_physical_devices + */ +extern int get_usable_slices(dlist_t **list); +extern int get_usable_disks(dlist_t **list); +extern int get_usable_hbas(dlist_t **list); + +/* + * predicate indicating whether MPXIO appears enabled for the system + */ +extern boolean_t is_mpxio_enabled(); + +/* + * functions that set/get a descriptor's multipath alias name(s). + */ +extern int get_aliases(dm_descriptor_t desc, dlist_t **aliases); +extern int set_alias(dm_descriptor_t desc, char *alias); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_DISCOVERY_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c new file mode 100644 index 0000000000..4ced855104 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c @@ -0,0 +1,454 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define _LAYOUT_DLIST_UTIL_C + +#include <assert.h> +#include <string.h> + +#include <libintl.h> +#include <libdiskmgt.h> + +#include "volume_devconfig.h" +#include "volume_dlist.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_dlist_util.h" +#include "layout_request.h" + +#include "layout_slice.h" /* destroy_new_slice */ +#include "layout_svm_util.h" + +/* + * FUNCTION: compare_strings(void *str1, void *str2) + * + * INPUT: str1 - opaque pointer to a char * + * str2 - opaque pointer to a char * + * + * RETURNS: int - <0 - if str1 < str2 + * 0 - if str1 == str2 + * >0 - if str1 > str2 + * + * PURPOSE: dlist_t helper which compares the two input strings. + * + * Comparison is done with string_case_compare() + */ +int +compare_strings( + void *str1, + void *str2) +{ + assert(str1 != NULL); + assert(str2 != NULL); + + return (string_case_compare((char *)str1, (char *)str2)); +} + +/* + * FUNCTION: compare_devconfig_sizes(void *devconf1, void *devconf2) + * + * INPUT: devconf1 - opaque pointer + * devconf2 - opaque pointer + * + * RETURNS: int - <0 - if devconf1.size_in_blks < devconf2.size_in_blks + * 0 - if devconf1.size_in_blks == devconf2.size_in_blks + * >0 - if devconf1.size.in_blks > devconf2.size_in_blks + * + * PURPOSE: dlist_t helper which compares the sizes of two devconfig_t + * structs. + * + * Both input objects are assumed to be devconfig_t pointers. + */ +int +compare_devconfig_sizes( + void *devconf1, + void *devconf2) +{ + uint64_t size1 = 0; + uint64_t size2 = 0; + + assert(devconf1 != NULL); + assert(devconf2 != NULL); + + (void) devconfig_get_size_in_blocks((devconfig_t *)devconf1, &size1); + (void) devconfig_get_size_in_blocks((devconfig_t *)devconf2, &size2); + + return (size1 - size2); +} + +/* + * FUNCTION: compare_slice_sizes(void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer to a dm_descriptor_t slice handle + * desc2 - opaque pointer to a dm_descriptor_t slice handle + * + * RETURNS: int - <0 - if desc1.slicesize < desc2.slicesize + * 0 - if desc1.slicesize == desc2.slicesize + * >0 - if desc1.slicesize > desc2.slicesize + * + * PURPOSE: dlist_t helper which compares the sizes of two slices + * represented as dm_descriptor_t handles. + */ +int +compare_slice_sizes( + void *desc1, + void *desc2) +{ + uint64_t size1 = 0; + uint64_t size2 = 0; + + assert(desc1 != NULL); + assert(desc2 != NULL); + + (void) slice_get_size((uintptr_t)desc1, &size1); + (void) slice_get_size((uintptr_t)desc2, &size2); + + return (size1 - size2); +} + +/* + * FUNCTION: compare_devconfig_and_descriptor_names(void *devconf, + * void *desc) + * + * INPUT: devconf - opaque pointer to a devconfig_t + * desc - opaque pointer to a dm_descriptor_t + * + * RETURNS: int - <0 - if devconf name is "less than" descr name + * 0 - if devconf name is "equal to" descr name + * >0 - if devconf name is "greater than" desc name + * + * PURPOSE: dlist_t helper which compares the name of a devconfig_t + * struct to the name for a dm_descriptor_t. + * + * Note that the order of the arguments is important. + * This function is intended to be passed into the various + * dlist_* functions which take a comparison function. + */ +int +compare_devconfig_and_descriptor_names( + void *devconf, + void *desc) +{ + char *volname = NULL; + char *descname = NULL; + + assert(devconf != NULL); + assert(desc != NULL); + + (void) devconfig_get_name((devconfig_t *)devconf, &volname); + (void) get_display_name((uintptr_t)desc, &descname); + + return (string_case_compare(volname, descname)); +} + +/* + * FUNCTION: compare_string_to_devconfig_name(void *str, void *devconf) + * + * INPUT: str - opaque pointer to a char *str + * devconf - opaque pointer to a devconfig_t + * + * RETURNS: int - <0 - if devconf name is "less than" str + * 0 - if devconf name is "equal to" str + * >0 - if devconf name is "greater than" str + * + * PURPOSE: dlist_t helper which compares a string to the name of + * a devconfig_t struct. + */ +int +compare_string_to_devconfig_name( + void *str, + void *devconf) +{ + char *volname = NULL; + + assert(str != NULL); + assert(devconf != NULL); + + (void) devconfig_get_name((devconfig_t *)devconf, &volname); + if (volname == NULL) { + /* no memory for new string(s) */ + return (-1); + } + + return (string_case_compare(volname, (char *)str)); +} + +/* + * FUNCTION: free_devconfig_object(void *obj) + * + * INPUT: obj - an opaque pointer + * + * RETURNS: void + * + * PURPOSE: helper which decomposes a devconfig_t struct after a + * failed layout attempt. + * + * reclaims allocated space. + * releases reserved volume/HSP names + * undoes slicing + */ +void +free_devconfig_object( + void *obj) +{ + devconfig_t *dev = NULL; + char *name = NULL; + dlist_t *iter = NULL; + component_type_t type = TYPE_UNKNOWN; + + if (obj == NULL) { + return; + } + + dev = (devconfig_t *)obj; + + (void) devconfig_get_type(dev, &type); + (void) devconfig_get_name(dev, &name); + + oprintf(OUTPUT_DEBUG, + gettext(" -->decomposing %s\n"), name); + + switch (type) { + case TYPE_MIRROR: + case TYPE_CONCAT: + case TYPE_RAID5: + case TYPE_HSP: + case TYPE_STRIPE: + + /* release name */ + if (devconfig_isA(dev, TYPE_HSP)) { + release_hsp_name(name); + } else { + release_volume_name(name); + } + + /* decompose volume's components */ + iter = devconfig_get_components(dev); + dlist_free_items(iter, free_devconfig_object); + + (void) devconfig_set_components(dev, NULL); + + break; + + case TYPE_SLICE: + + (void) destroy_new_slice(dev); + + break; + + default: + break; + + } + + free_devconfig(dev); +} + +/* + * FUNCTION: compare_device_names( + * void *str1, void *str2) + * + * INPUT: str1 - opaque pointer + * str2 - opaque pointer + * + * RETURNS: int - <0 - if str1 < str2 + * 0 - if str1 == str2 + * >0 - if str1 > str2 + * + * PURPOSE: dlist_t helper which compares two device name strings. + * + * Both names are assumed to be in CTD form. + * + * Either name may be fully qualified by an absolute + * path. If only one name is fully qualified, the + * leading path with be stripped off prior to the + * comparison. + * + * Uses string_case_compare() to compare the names. + */ +int +compare_device_names( + void *str1, + void *str2) +{ + char *name1 = (char *)str1; + char *name2 = (char *)str2; + + int val = 0; + + assert(str1 != NULL); + assert(str2 != NULL); + + /* if one doesn't start with '/', just compare device names */ + if (*name1 != '/' || *name2 != '/') { + + char *short1 = strrchr(name1, '/'); + char *short2 = strrchr(name2, '/'); + + if (short1 == NULL) { + short1 = name1; + } else { + ++short1; + } + + if (short2 == NULL) { + short2 = name2; + } else { + ++short2; + } + + val = string_case_compare(short2, short1); + + } else { + + /* if they both start with '/', assume they're full paths */ + val = string_case_compare(name2, name1); + } + + return (val); +} + +/* + * FUNCTION: compare_descriptors( + * void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer + * desc2 - opaque pointer + * + * RETURNS: int - <0 - if desc1 < desc2 + * 0 - if desc1 == desc2 + * >0 - if desc1 > desc2 + * + * PURPOSE: dlist_t helper which compares two dm_descriptor_t handles. + */ +int +compare_descriptors( + void *desc1, + void *desc2) +{ + assert(desc1 != NULL); + assert(desc2 != NULL); + + return ((uintptr_t)desc1 - (uintptr_t)desc2); +} + +/* + * FUNCTION: compare_descriptor_names( + * void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer + * desc2 - opaque pointer + * + * RETURNS: int - <0 - if desc1.name < desc2.name + * 0 - if desc1.name == desc2.name + * >0 - if desc1.name > desc2.name + * + * PURPOSE: dlist_t helper which compares the names associated + * with the input dm_descriptor_t handles. + * + * Retrieves the names associated with both descriptors + * and compares them using string_case_compare. + */ +int +compare_descriptor_names( + void *desc1, + void *desc2) +{ + char *name1 = NULL; + char *name2 = NULL; + + assert(desc1 != NULL); + assert(desc2 != NULL); + + (void) get_name((uintptr_t)desc1, &name1); + (void) get_name((uintptr_t)desc2, &name2); + + return (string_case_compare(name1, name2)); +} + +/* + * FUNCTION: compare_slices_on_same_hba( + * void *slice1, void *slice2) + * + * INPUT: slice1 - opaque pointer + * slice2 - opaque pointer + * + * RETURNS: int - 0 - if slice1 is on the same hba as slice2 + * !0 - otherwise + * + * PURPOSE: dlist_t helper which checks whether slice1 is on the + * same hba as slice2 + */ +int +compare_slices_on_same_hba( + void *slice1, + void *slice2) +{ + char *name1, *name2; + + /* Retrieve the names of the slices */ + if (devconfig_get_name((devconfig_t *)slice1, &name1) == 0 && + devconfig_get_name((devconfig_t *)slice2, &name2) == 0) { + + dm_descriptor_t desc1, desc2; + + /* Retrieve the disk descriptors for the slices */ + if (get_disk_for_named_slice(name1, &desc1) == 0 && + get_disk_for_named_slice(name2, &desc2) == 0) { + + dlist_t *hbas1 = NULL; + dlist_t *hbas2 = NULL; + + assert(desc1 != (dm_descriptor_t)0); + assert(desc2 != (dm_descriptor_t)0); + + /* Retrieve list of HBA descriptors for the slices */ + if (disk_get_hbas(desc1, &hbas1) == 0 && + disk_get_hbas(desc2, &hbas2) == 0) { + + dlist_t *itr1; + + for (itr1 = hbas1; itr1 != NULL; itr1 = itr1->next) { + dm_descriptor_t hba1 = (uintptr_t)itr1->obj; + dlist_t *itr2; + + for (itr2 = hbas2; itr2 != NULL; itr2 = itr2->next) { + dm_descriptor_t hba2 = (uintptr_t)itr2->obj; + + if (hba1 == hba2) { + return (0); + } + } + } + } + } + } + + return (1); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h new file mode 100644 index 0000000000..ff65497c99 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h @@ -0,0 +1,216 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_DLIST_UTIL_H +#define _LAYOUT_DLIST_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A collection of utility functions for manipulating and traversing + * dlist_t linked lists. + */ + +/* + * FUNCTION: compare_strings(void *str1, void *str2) + * + * INPUT: str1 - opaque pointer to a char * + * str2 - opaque pointer to a char * + * + * RETURNS: int - <0 - if str1 < str2 + * 0 - if str1 == str2 + * >0 - if str1 > str2 + * + * PURPOSE: dlist_t helper which compares the two input strings. + * + * Comparison is done with string_compare() + */ +extern int compare_strings(void *str1, void *str2); + +/* + * FUNCTION: compare_device_names( + * void *str1, void *str2) + * + * INPUT: str1 - opaque pointer + * str2 - opaque pointer + * + * RETURNS: int - <0 - if str1 < str2 + * 0 - if str1 == str2 + * >0 - if str1 > str2 + * + * PURPOSE: dlist_t helper which compares two device name strings. + * + * Both names are assumed to be in CTD form. + * + * Either name may be fully qualified by an absolute + * path. If only one name is fully qualified, the + * leading path with be stripped off prior to the + * comparison. + * + * Uses string_compare() to compare the names. + */ +extern int compare_device_names(void *str1, void *str2); + +/* + * FUNCTION: compare_devconfig_sizes(void *devconf1, void *devconf2) + * + * INPUT: devconf1 - opaque pointer + * devconf2 - opaque pointer + * + * RETURNS: int - <0 - if devconf1.size_in_blks < devconf2.size_in_blks + * 0 - if devconf1.size_in_blks == devconf2.size_in_blks + * >0 - if devconf1.size_in_blks > devconf2.size_in_blks + * + * PURPOSE: dlist_t helper which compares the sizes of two devconfig_t + * structs. + * + * Both input objects are assumed to be devconfig_t pointers. + */ +extern int compare_devconfig_sizes(void *devconf1, void *devconf2); + +/* + * FUNCTION: compare_slice_sizes(void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer to a dm_descriptor_t slice handle + * desc2 - opaque pointer to a dm_descriptor_t slice handle + * + * RETURNS: int - <0 - if desc1.slicesize < desc2.slicesize + * 0 - if desc1.slicesize == desc2.slicesize + * >0 - if desc1.slicesize > desc2.slicesize + * + * PURPOSE: dlist_t helper which compares the sizes of two slices + * represented as dm_descriptor_t handles. + */ +extern int compare_slice_sizes(void *obj1, void *obj2); + +/* + * FUNCTION: compare_descriptors( + * void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer + * desc2 - opaque pointer + * + * RETURNS: int - <0 - if desc1 < desc2 + * 0 - if desc1 == desc2 + * >0 - if desc1 > desc2 + * + * PURPOSE: dlist_t helper which compares two dm_descriptor_t handles. + */ +extern int compare_descriptors(void *desc1, void *desc2); + +/* + * FUNCTION: compare_descriptor_names( + * void *desc1, void *desc2) + * + * INPUT: desc1 - opaque pointer + * desc2 - opaque pointer + * + * RETURNS: int - <0 - if desc1.name < desc2.name + * 0 - if desc1.name == desc2.name + * >0 - if desc1.name > desc2.name + * + * PURPOSE: dlist_t helper which compares the names associated + * with the input dm_descriptor_t handles. + * + * Retrieves the names associated with both descriptors + * and compares them using string_compare. + */ +extern int compare_descriptor_names(void *desc1, void *desc2); + +/* + * FUNCTION: compare_devconfig_and_descriptor_names(void *devconf, + * void *desc) + * + * INPUT: devconf - opaque pointer to a devconfig_t + * desc - opaque pointer to a dm_descriptor_t + * + * RETURNS: int - <0 - if devconf name is "less than" descr name + * 0 - if devconf name is "equal to" descr name + * >0 - if devconf name is "greater than" desc name + * + * PURPOSE: dlist_t helper which compares the name of a devconfig_t + * struct to the name for a dm_descriptor_t. + * + * Note that the order of the arguments is important. + * This function is intended to be passed into the various + * dlist_* functions which take a comparison function. + */ +extern int compare_devconfig_and_descriptor_names(void *devconf, void *desc); + +/* + * FUNCTION: compare_string_to_devconfig_name(void *str, void *devconf) + * INPUT: str - opaque pointer to a char *str + * devconf - opaque pointer to a devconfig_t + * + * RETURNS: int - <0 - if devconf name is "less than" str + * 0 - if devconf name is "equal to" str + * >0 - if devconf name is "greater than" str + * + * PURPOSE: dlist_t helper which compares a string to the name of + * a devconfig_t struct. + */ +extern int compare_string_to_devconfig_name(void *str, void *devconf); + +/* + * FUNCTION: compare_slices_on_same_hba( + * void *slice1, void *slice2) + * + * INPUT: slice1 - opaque pointer + * slice2 - opaque pointer + * + * RETURNS: int - 0 - if slice1 is on the same hba as slice2 + * !0 - otherwise + * + * PURPOSE: dlist_t helper which checks whether slice1 is on the + * same hba as slice2 + */ +extern int compare_slices_on_same_hba(void *slice1, void *slice2); + +/* + * FUNCTION: free_devconfig_object(void *obj) + * + * INPUT: obj - an opaque pointer + * + * RETURNS: void + * + * PURPOSE: helper which decomposes a devconfig_t struct after a + * failed layout attempt. + * + * reclaims allocated space. + * releases reserved volume/HSP names + * undoes slicing + */ +extern void free_devconfig_object(void *obj); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_DLIST_UTIL_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_hsp.c b/usr/src/cmd/lvm/metassist/layout/layout_hsp.c new file mode 100644 index 0000000000..289f87f1f7 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_hsp.c @@ -0,0 +1,963 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> + +#include <libintl.h> + +#include "volume_error.h" +#include "volume_dlist.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_messages.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_svm_util.h" + +#define _LAYOUT_HSP_C + +static int layout_explicit_hsp( + devconfig_t *hsprequest, + dlist_t *devices, + devconfig_t **hsp); + +static int layout_default_hsp( + devconfig_t *request, + dlist_t *devices, + devconfig_t **hsp); + +static int populate_hsp( + devconfig_t *request, + devconfig_t *hsp, + dlist_t *devices); + +static int assemble_hsp( + devconfig_t *hsp, + dlist_t *newspares, + dlist_t *devices); + +static int get_uniquely_sized_slices( + dlist_t *devices, + dlist_t **unique); + +static int remove_undersized_slices( + dlist_t *unique, + dlist_t **avail); + +static int find_spare_for_component( + devconfig_t *component, + dlist_t *all_spares, + dlist_t *hbas, + dlist_t *disks, + boolean_t *found); + +static int choose_spare_for_component( + devconfig_t *comp, + dlist_t **all_spares, + dlist_t **new_spares, + dlist_t **avail, + dlist_t *used_hbas, + dlist_t *used_disks, + uint16_t npaths); + +/* + * FUNCTION: layout_hsp(devconfig_t *request, devconfig_t hsprequest, + * dlist_t *devices, dlist_t **results) + * + * INPUT: request - pointer to the toplevel request devconfig_t + * hsp - pointer to the optional HSP request devconfig_t + * devices - pointer to a list of devices to be served by the HSP + * + * OUTPUT: results - pointer to a list result devconfig_t, if the HSP + * to service the input list of devices needs to be + * created or modified, it will be appended to the list. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main layout driver for HSP, attempts to build/populate a + * single HSP to service the list of devices. + * + * If the input hsprequest is NULL, use the default HSP scheme: + * a. use the first HSP in the diskset + * b. create an HSP if the diskset has none + * + * If the hsprequest is not NULL: + * a. if the request names an HSP and it already exists, use it + * b. if the request names an HSP and it does not exist, create it + * c. if the request specifies components, use them + */ +int +layout_hsp( + devconfig_t *request, + devconfig_t *hsprequest, + dlist_t *devices, + dlist_t **results) +{ + int error = 0; + devconfig_t *hsp = NULL; + + oprintf(OUTPUT_TERSE, + gettext(" ->Layout a %s\n"), + devconfig_type_to_str(TYPE_HSP)); + + if (hsprequest == NULL) { + error = layout_default_hsp(request, devices, &hsp); + } else { + error = layout_explicit_hsp(hsprequest, devices, &hsp); + } + + if (error != 0) { + print_debug_failure_msg(devconfig_type_to_str(TYPE_HSP), + get_error_string(error)); + } else if (hsp != NULL) { + + if (devconfig_get_components(hsp) == NULL) { + /* HSP is usable as it is */ + free_devconfig(hsp); + hsp = NULL; + } else { + dlist_t *item = NULL; + if ((item = dlist_new_item(hsp)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + print_layout_success_msg(); + } + } + } + + return (error); +} + +/* + * FUNCTION: layout_default_hsp(devconfig_t *request, + * dlist_t *devices, devconfig_t **hsp) + * + * INPUT: request - pointer to the toplevel request devconfig_t + * devices - pointer to a list of devices to be served by the HSP + * + * OUTPUT: hsp - pointer to a devconfig_t to hold the resulting HSP + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout driver for default HSP construction. + * + * a. use the first HSP in the diskset + * b. create an HSP if the diskset has none + * c. add spares to the HSP to service the list of input devices. + */ +static int +layout_default_hsp( + devconfig_t *request, + dlist_t *devices, + devconfig_t **hsp) +{ + char *dsname = get_request_diskset(); + char *hspname = NULL; + boolean_t free_hspname = B_FALSE; + devconfig_t *default_hsp = NULL; + int error = 0; + + oprintf(OUTPUT_TERSE, + gettext(" -->Using default HSP scheme...\n")); + + if ((error = get_default_hsp_name(request, &hspname)) != 0) { + volume_set_error( + gettext("error getting HSP name from defaults\n")); + return (error); + } + + if (hspname != NULL) { + if ((error = hsp_get_by_name(dsname, hspname, &default_hsp)) != 0) { + volume_set_error( + gettext("error getting default HSP by name\n")); + return (error); + } + } else { + /* no default HSP name, get diskset's default HSP */ + if ((error = hsp_get_default_for_diskset(dsname, + &default_hsp)) != 0) { + volume_set_error( + gettext("error getting default HSP\n")); + return (error); + } + + if (default_hsp == NULL) { + /* no default HSP name, no default HSP, make one */ + if ((error = get_next_hsp_name(&hspname)) != 0) { + volume_set_error( + gettext("error making default HSP name\n")); + return (error); + } + free_hspname = B_TRUE; + } + } + + if (default_hsp != NULL) { + + /* Found existing default HSP, copy it */ + dlist_t *spares = devconfig_get_components(default_hsp); + + ((error = devconfig_get_name(default_hsp, &hspname)) != 0) || + (error = new_devconfig(hsp, TYPE_HSP)) || + (error = devconfig_set_name(*hsp, hspname)); + + if (error == 0) { + devconfig_set_components(*hsp, spares); + devconfig_set_components(default_hsp, NULL); + + oprintf(OUTPUT_TERSE, + gettext(" --->Using %s from disk set %s...\n"), + hspname, dsname); + } else { + free_devconfig(*hsp); + *hsp = NULL; + } + + } else { + + /* no existing default HSP, make it */ + ((error = new_devconfig(hsp, TYPE_HSP)) != 0) || + (error = devconfig_set_name(*hsp, hspname)); + if (error == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" --->Created %s for disk set %s...\n "), + hspname, dsname); + } else { + free_devconfig(*hsp); + *hsp = NULL; + } + + if (free_hspname == B_TRUE) { + free(hspname); + } + } + + if (error == 0) { + error = populate_hsp(request, *hsp, devices); + } + + return (error); +} + +/* + * FUNCTION: layout_explicit_hsp(devconfig_t *hsprequest, + * dlist_t *devices, devconfig_t **hsp) + * + * INPUT: hsprequest - pointer to the explicit HSP request devconfig_t + * devices - pointer to a list of devices to be served by the HSP + * + * OUTPUT: hsp - pointer to a HSP devconfig_t to hold resulting HSP + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout driver for an explicit HSP request. + * + * a. if the request names an HSP and it already exists, use it + * b. if the request names an HSP and it does not exist, create it + * c. if the request specifies components, use them + * otherwise, add new spares to handle the input list + * of devices. + */ +static int +layout_explicit_hsp( + devconfig_t *hsprequest, + dlist_t *devices, + devconfig_t **hsp) +{ + char *dsname = get_request_diskset(); + char *hspname = NULL; + dlist_t *rspares = NULL; + int error = 0; + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Explicit HSP request...\n")); + + (void) devconfig_get_name(hsprequest, &hspname); + if (hspname != NULL) { + + (void) hsp_get_by_name(dsname, hspname, hsp); + if (*hsp != NULL) { + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Using %s...\n"), + hspname); + } else { + + /* named HSP doesn't exist, create it */ + ((error = new_devconfig(hsp, TYPE_HSP)) != 0) || + (error = devconfig_set_name(*hsp, hspname)); + if (error == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" --->%s does not exist, " + "created...\n"), hspname); + } else { + free_devconfig(*hsp); + *hsp = NULL; + } + free(hspname); + } + } + + if (error == 0) { + + /* does the hsprequest specify spares? */ + rspares = devconfig_get_components(hsprequest); + if (rspares != NULL) { + + /* put requested spares into HSP */ + dlist_t *list = NULL; + dlist_t *iter = NULL; + + for (iter = rspares; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dlist_t *item = NULL; + if ((dlist_new_item(iter->obj)) == NULL) { + error = ENOMEM; + } else { + list = dlist_append(item, list, AT_TAIL); + } + } + + if (error == 0) { + error = assemble_hsp(*hsp, rspares, devices); + } + + } else { + + /* select new spares */ + error = populate_hsp(hsprequest, *hsp, devices); + } + } + + return (error); +} + +/* + * FUNCTION: populate_hsp(devconfig_t *request, devconfig_t *hsp, + * dlist_t *devices) + * + * INPUT: request - pointer to a request devconfig_t + * hsp - pointer to a HSP devconfig_t + * devices - pointer to a list of devices to be served by the HSP + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Processes the input HSP request and add spares sufficient + * to service the input list of devices. + * + * Determine the available HBAs, disks, and slices. + * Sort thru the input list of devices and determine + * the unique component sizes which need to be spared. + * Filter the available slices and remove those that are + * too small to serve as spares. + * + * Iterate each device and its components and see if the + * HSP currently has a sufficient spare, if not, try + * to select one from the available slices. + * + * If a spare cannot be found for any device component, + * the HSP layout process stops. + * + * If spares are found for all device components, add + * any required new ones to the HSP. + */ +static int +populate_hsp( + devconfig_t *request, + devconfig_t *hsp, + dlist_t *devices) +{ + int error = 0; + uint16_t npaths = 0; + + dlist_t *usable_hbas = NULL; + dlist_t *sel_hbas = NULL; + dlist_t *disks = NULL; + dlist_t *iter = NULL; + + dlist_t *avail = NULL; /* available slices */ + dlist_t *slices = NULL; /* avail slices of sufficient size */ + dlist_t *unique = NULL; /* volume slices that need spares */ + dlist_t *curspares = NULL; /* current spares in the HSP */ + dlist_t *newspares = NULL; /* slices to add to HSP */ + dlist_t *allspares = NULL; /* current and new spares */ + + ((error = get_usable_hbas(&usable_hbas)) != 0) || + (error = select_hbas_with_n_disks(request, usable_hbas, 1, &sel_hbas, + &disks)) || + (error = disks_get_avail_slices(request, disks, &avail)) || + (error = get_volume_npaths(request, &npaths)); + if (error != 0) { + dlist_free_items(sel_hbas, NULL); + dlist_free_items(disks, NULL); + dlist_free_items(avail, NULL); + return (error); + } + + if (disks == NULL || dlist_length(disks) == 0) { + /* all disks have been consumed by the devices */ + volume_set_error( + gettext(" no available disks to populate HSP\n")); + dlist_free_items(sel_hbas, NULL); + dlist_free_items(avail, NULL); + return (-1); + } + + if (avail == NULL || dlist_length(avail) == 0) { + /* all slices have been consumed by the devices */ + volume_set_error( + gettext(" no available slices to populate HSP\n")); + dlist_free_items(sel_hbas, NULL); + dlist_free_items(disks, NULL); + return (-1); + } + + dlist_free_items(sel_hbas, NULL); + dlist_free_items(disks, NULL); + + /* build list of slices needing to be spared */ + ((error = get_uniquely_sized_slices(devices, &unique)) != 0) || + + /* and list of slices of sufficient size to spare for them */ + (error = remove_undersized_slices(unique, &avail)); + + if (error != 0) { + dlist_free_items(avail, NULL); + dlist_free_items(unique, NULL); + dlist_free_items(slices, NULL); + return (error); + } + + /* get spares currently in the HSP */ + curspares = devconfig_get_components(hsp); + + /* clone current spares list */ + for (iter = curspares; + (iter != NULL) && (error == 0); + iter = iter->next) { + dlist_t *item = dlist_new_item(iter->obj); + if (item == NULL) { + error = ENOMEM; + } else { + allspares = dlist_append(item, allspares, AT_TAIL); + } + } + + if (error != 0) { + dlist_free_items(avail, NULL); + dlist_free_items(unique, NULL); + dlist_free_items(slices, NULL); + dlist_free_items(allspares, NULL); + return (error); + } + + /* + * examine device component slices and see if the HSP already + * has a suitable spare. If not, select the best available + * of the same (or larger) size + */ + for (iter = devices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *device = (devconfig_t *)iter->obj; + dlist_t *components = devconfig_get_components(device); + dlist_t *hbas = NULL; + dlist_t *disks = NULL; + dlist_t *iter1; + + error = get_hbas_and_disks_used_by_volume(device, &hbas, &disks); + for (iter1 = components; (iter1 != NULL) && (error == 0); + iter1 = iter1->next) { + + devconfig_t *comp = (devconfig_t *)iter1->obj; + boolean_t found = B_FALSE; + + if ((error = find_spare_for_component( + comp, allspares, hbas, disks, &found)) == 0) { + if (found != B_TRUE) { + error = choose_spare_for_component( + comp, &allspares, &newspares, + &avail, hbas, disks, npaths); + } + } + } + dlist_free_items(disks, NULL); + dlist_free_items(hbas, NULL); + } + + if (error == 0) { + /* existing spares are no longer needed */ + dlist_free_items(curspares, free_devconfig_object); + curspares = NULL; + + error = assemble_hsp(hsp, newspares, devices); + } else { + dlist_free_items(newspares, free_devconfig_object); + newspares = NULL; + } + + dlist_free_items(avail, NULL); + dlist_free_items(slices, NULL); + dlist_free_items(unique, NULL); + dlist_free_items(allspares, NULL); + + return (error); +} + +/* + * FUNCTION: assemble_hsp(devconfig_t *hsp, dlist_t *newspares, + * dlist_t *devices) + * + * INPUT: request - pointer to a HSP devconfig_t + * newspare - pointer to a list of new spares for the HSP + * devices - pointer to a list of devices to be served by the HSP + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Final assembly of an HSP. Attach new spare components + * and associate the HSP with each device in the input list. + */ +static int +assemble_hsp( + devconfig_t *hsp, + dlist_t *newspares, + dlist_t *devices) +{ + dlist_t *iter; + char *hspname = NULL; + int error = 0; + + /* add new spares to HSP */ + (void) devconfig_set_components(hsp, newspares); + (void) devconfig_get_name(hsp, &hspname); + + /* associate HSP with each of the devices */ + for (iter = devices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = iter->obj; + devconfig_t *hspcomp = NULL; + dlist_t *item = NULL; + char *devname = NULL; + + ((error = devconfig_get_name(dev, &devname)) != 0) || + (error = new_devconfig(&hspcomp, TYPE_HSP)) || + (error = devconfig_set_name(hspcomp, hspname)); + + if (error != 0) { + + free_devconfig(hspcomp); + + } else if ((item = dlist_new_item(hspcomp)) == NULL) { + + free_devconfig(hspcomp); + error = ENOMEM; + + } else { + + dlist_t *comps = devconfig_get_components(dev); + comps = dlist_append(comps, item, AT_TAIL); + (void) devconfig_set_components(dev, comps); + + oprintf(OUTPUT_VERBOSE, + gettext(" --->volume %s will use HSP %s\n"), + devname, hspname); + } + } + + return (error); +} + +/* + * FUNCTION: get_uniquely_sized_slices(dlist_t *devices, + * dlist_t **unique) + * + * INPUT: devices - pointer to a list of devconfig_t devices + * + * OUTPUT: unique - pointer to a list of uniquely size slices + * from the input list of devices. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Examine each device's slice components and build a list + * of uniquely sized slices. + */ +static int +get_uniquely_sized_slices( + dlist_t *devices, + dlist_t **unique) +{ + int error = 0; + dlist_t *iter = NULL; + + for (iter = devices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dlist_t *iter1; + for (iter1 = devconfig_get_components((devconfig_t *)iter->obj); + (iter1 != NULL) && (error == 0); + iter1 = iter1->next) { + + devconfig_t *comp = (devconfig_t *)iter1->obj; + if (dlist_contains(*unique, comp, + compare_devconfig_sizes) != B_TRUE) { + + dlist_t *item = NULL; + if ((item = dlist_new_item(comp)) == NULL) { + error = ENOMEM; + } else { + *unique = dlist_insert_ordered(item, *unique, + ASCENDING, compare_devconfig_sizes); + } + } + } + } + + return (error); +} + +/* + * FUNCTION: remove_undersized_slices(dlist_t *unique, + * dlist_t **avail) + * + * INPUT: avail - pointer to a list of available slices + * unique - pointer to a list of uniquely size slices + * + * OUTPUT: avail - pointer to an updated list of available slices + * that are at least as large as slices in the + * unique list. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: filter available slices and remove those that aren't + * large enough for the device components which need spares. + * + * For each uniquely sized slice, find all available slices + * that are larger and add them to the filtered list. + */ +static int +remove_undersized_slices( + dlist_t *unique, + dlist_t **avail) +{ + dlist_t *filtered = NULL; + dlist_t *iter = NULL; + int error = 0; + + for (iter = unique; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *uslice = (devconfig_t *)iter->obj; + uint64_t usize = 0; + dlist_t *iter2 = NULL; + + error = devconfig_get_size(uslice, &usize); + + for (iter2 = *avail; + (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + + dm_descriptor_t aslice = (uintptr_t)iter2->obj; + uint64_t asize = 0; + + error = slice_get_size(aslice, &asize); + if (asize >= usize) { + + /* this slice is large enough */ + dlist_t *item = NULL; + if ((item = dlist_new_item((void *)aslice)) == NULL) { + error = ENOMEM; + } else { + filtered = dlist_insert_ordered(item, filtered, + ASCENDING, compare_slice_sizes); + } + + } + } + } + + if (error == 0) { + dlist_free_items(*avail, NULL); + *avail = filtered; + } else { + dlist_free_items(filtered, NULL); + } + + return (error); +} + +/* + * FUNCTION: find_spare_for_component(devconfig_t *component, + * dlist_t *all_spares, dlist_t *hbas, dlist_t *disks, + * boolean_t *found) + * + * INPUT: comp - pointer to a devconfig_t slice compenent that + * needs to be spared + * all_spares - pointer to a list of spares currently + * in the pool or that will be added + * hbas - pointer to a list of HBAs the component's + * parent device utilizes + * disks - pointer to a list of disks the component's + * parent device utilizes + * + * OUTPUT: found - pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Find a spare for the input component. + * + * Searches the input list of spares to see if one is + * sufficient. + * + * A suffcient spare is one that is large enough to spare + * for the input component and not on the same disk as any + * of the components in the parent device. + * + * The optimal spare would be on a different controller/HBA + * as the component and any of the components in the parent + * device. We settle for sufficient. + */ +static int +find_spare_for_component( + devconfig_t *component, + dlist_t *all_spares, + dlist_t *hbas, + dlist_t *disks, + boolean_t *found) +{ + dlist_t *iter = NULL; + uint64_t csize = 0; + int error = 0; + + *found = B_FALSE; + + (void) devconfig_get_size(component, &csize); + + for (iter = all_spares; + (iter != NULL) && (*found == B_FALSE) && (error == 0); + iter = iter->next) { + + devconfig_t *spare = (devconfig_t *)iter->obj; + char *spname = NULL; + uint64_t spsize = 0; + + if (((error = devconfig_get_name(spare, &spname)) != 0) || + ((error = devconfig_get_size(spare, &spsize)) != 0)) { + continue; + } + + if (spsize >= csize) { + + dm_descriptor_t disk = NULL; + + /* see if spare's disk is independent of the volume */ + error = get_disk_for_named_slice(spname, &disk); + if ((error == 0) && (dlist_contains(disks, (void *)disk, + compare_descriptor_names) == B_FALSE)) { + *found = B_TRUE; + } + } + } + + if ((*found == B_TRUE) && (get_max_verbosity() >= OUTPUT_DEBUG)) { + char *cname = NULL; + (void) devconfig_get_name(component, &cname); + oprintf(OUTPUT_DEBUG, + gettext(" found existing spare for: %s (%llu)\n"), + cname, csize); + } + + return (error); +} + +/* + * FUNCTION: choose_spare_for_component(devconfig_t *component, + * dlist_t *all_spares, dlist_t **new_spares, + * dlist_t avail, uint16_t npaths, dlist_t *used_hbas, + * dlist_t *used_disks) + * + * INPUT: comp - pointer to a devconfig_t slice compenent that + * needs to be spared + * all_spares - pointer to a list of spares currently + * in the pool and those to be added + * new_spares - pointer to a list of spares that need to + * be added to the pool + * avail - list of available slices + * npaths - required number of paths for the spare + * used_hbas - list of HBAs used by the component's parent + * used_disks - list of disks used by the component's parent + * + * OUTPUT: all_spares - the possibly updated list of all spares + * new_spares - the possibly updated list of spares which + * need to be added to the pool. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Find a new spare for the input component. + * + * Select a spare from the available slice list and add + * it to the new_spares list. + * + * The spare slice chosen should be on a unique HBA and + * disk relative to the input lists of used HBAs and disks + * and any spares in the pool. + */ +static int +choose_spare_for_component( + devconfig_t *component, + dlist_t **all_spares, + dlist_t **new_spares, + dlist_t **avail, + dlist_t *used_hbas, + dlist_t *used_disks, + uint16_t npaths) +{ + devconfig_t *spare = NULL; + uint64_t csize = 0; + int error = 0; + + (void) devconfig_get_size(component, &csize); + + if (get_max_verbosity() >= OUTPUT_DEBUG) { + char *cname = NULL; + (void) devconfig_get_name(component, &cname); + oprintf(OUTPUT_DEBUG, + gettext(" select new spare for: %s (%llu)\n"), + cname, csize); + } + + /* + * find a spare for the input component. + * select the best one from the available list that + * is on a unique disk. + */ + + /* + * 1st B_TRUE: require a different disk than those used by + * all spares and devices + * 2nd B_TRUE: requested size is the minimum acceptable + * 1st B_FALSE: do not add an extra cylinder when resizing slice, + * this is only necessary for Stripe components whose + * sizes get rounded down to an interlace multiple and + * then down to a cylinder boundary. + */ + error = choose_slice(csize, npaths, *avail, *all_spares, + used_hbas, used_disks, B_TRUE, B_TRUE, B_FALSE, &spare); + + if ((error == 0) && (spare == NULL)) { + /* can't find one on a unique disk, try again on any disk */ + + /* BEGIN CSTYLED */ + /* + * 1st B_FALSE: don't require a different disk than those used + * by all spares and devices + * 2nd B_TRUE: requested size is still the minimum acceptable + * 2nd B_FALSE: do not add an extra cylinder when resizing slice + * this is only necessary for Stripe components whose + * sizes get rounded down to an interlace multiple and + * then down to a cylinder boundary. + */ + /* END CSTYLED */ + error = choose_slice( + csize, npaths, *avail, *all_spares, used_hbas, + used_disks, B_FALSE, B_TRUE, B_FALSE, &spare); + } + + if ((error == 0) && (spare != NULL)) { + + dlist_t *rmvd = NULL; + dlist_t *item = NULL; + char *spname = NULL; + + if ((item = dlist_new_item(spare)) == NULL) { + error = ENOMEM; + } else { + + /* add spare to the all spares list */ + *all_spares = dlist_append(item, *all_spares, AT_HEAD); + + if ((item = dlist_new_item(spare)) == NULL) { + error = ENOMEM; + } else { + + /* add spare to the new spares list */ + *new_spares = dlist_insert_ordered( + item, *new_spares, ASCENDING, + compare_devconfig_sizes); + + /* remove it from the available list */ + *avail = dlist_remove_equivalent_item(*avail, spare, + compare_devconfig_and_descriptor_names, + &rmvd); + + if (rmvd != NULL) { + free(rmvd); + } + + /* add the spare to the used slice list */ + error = devconfig_get_name(spare, &spname); + if (error == 0) { + error = add_used_slice_by_name(spname); + } + } + } + + } else { + + /* no spare, give up on layout */ + oprintf(OUTPUT_TERSE, + gettext(" <---Failed: insufficient suitable spares\n")); + + volume_set_error( + gettext("failed to find sufficient spares for HSP\n")); + + error = -1; + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_hsp.h b/usr/src/cmd/lvm/metassist/layout/layout_hsp.h new file mode 100644 index 0000000000..7c50cc9213 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_hsp.h @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_HSP_H +#define _LAYOUT_HSP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" +#include "volume_dlist.h" + +extern int layout_hsp( + devconfig_t *request, + devconfig_t *hsprequest, + dlist_t *devices, + dlist_t **results); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_HSP_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_messages.c b/usr/src/cmd/lvm/metassist/layout/layout_messages.c new file mode 100644 index 0000000000..c652972b7b --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_messages.c @@ -0,0 +1,407 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libintl.h> +#include <stdlib.h> + +#include "volume_error.h" +#include "volume_output.h" +#include "volume_string.h" + +#include "layout_messages.h" + +/* + * FUNCTION: print_layout_volume_msg(char *type, uint64_t nbytes) + * + * PURPOSE: Prints a generic message indicating the start of the + * layout process for a volume of the indicated type and + * capacity. + */ +void +print_layout_volume_msg( + char *type, + uint64_t nbytes) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_VERBOSE, + gettext(" ->Layout a %s with capacity %s\n"), + type, spstr); + + free(spstr); +} + +/* + * FUNCTION: print_layout_explicit_msg(char *type) + * + * PURPOSE: Prints a generic message indicating the start of the + * layout population process using explicit components + * for a volume of the indicated type. + */ +void +print_layout_explicit_msg( + char *type) +{ + oprintf(OUTPUT_TERSE, + gettext(" ->Layout a %s with explicitly specified " + "components\n"), + type); +} + +/* + * FUNCTION: print_layout_explicit_added_msg(char *comp) + * + * PURPOSE: Prints a generic message indicating the named component + * was added to a volume. + */ +void +print_layout_explicit_added_msg( + char *comp) +{ + oprintf(OUTPUT_TERSE, gettext(" ---->added '%s'\n"), comp); +} + +/* + * FUNCTION: print_success_msg() + * + * PURPOSE: Prints a generic layout success message. + */ +void +print_layout_success_msg() +{ + oprintf(OUTPUT_TERSE, gettext(" <-Success!\n")); +} + +/* + * FUNCTION: print_insufficient_resources_msg(char *type) + * + * PURPOSE: Prints a message indicating that there are insufficient + * resources. + * + * Also sets the metassist error string indicating why + * the metassist command failed. The volume type is included + * for context in this message. + */ +void +print_insufficient_resources_msg( + char *type) +{ + oprintf(OUTPUT_TERSE, + gettext(" <-Failed: insufficient resources available\n")); + + volume_set_error( + gettext("insufficient resources available to complete " + "requested %s\n"), + type); +} + +/* + * FUNCTION: print_insufficient_hbas_msg(int n) + * + * PURPOSE: Prints a status message indicating that there are insufficient + * HBAs and that only 'n' are available. + * + * Used to indicate strategy selection during layouts. + */ +void +print_insufficient_hbas_msg( + int n) +{ + if (n == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: no HBA has sufficient disks\n")); + } else if (n == 1) { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: only 1 HBA has sufficient disks\n")); + } else { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: only %d HBAs have sufficient disks\n"), + n); + } +} + +/* + * FUNCTION: print_insufficient_disks_msg(int n) + * + * PURPOSE: Prints a status message indicating that there are insufficient + * disks and that only 'n' are available. + * + * Used to indicate strategy selection during layouts. + */ +void +print_insufficient_disks_msg( + int n) +{ + if (n == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: no disks available\n"), + n); + } else if (n == 1) { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: only 1 disk available\n"), + n); + } else { + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: only %d disks available\n"), + n); + } +} + +/* + * FUNCTION: print_no_hbas_msg() + * + * PURPOSE: Prints a layout failure due to no usable HBAs message. + */ +void +print_no_hbas_msg() +{ + oprintf(OUTPUT_TERSE, + gettext(" There are no usable HBAs.\n")); +} + +/* + * FUNCTION: print_debug_failure_msg(char *type, char *err) + * + * PURPOSE: Prints a generic message for unexpected failures + * during layout. + */ +void +print_debug_failure_msg( + char *type, + char *err) +{ + oprintf(OUTPUT_DEBUG, + gettext(" layout of %s failed: %s\n"), + type, err); +} + +/* + * FUNCTION: print_insufficient_components_msg(int ncomp) + * + * INPUT: ncomp - number of available components + * + * PURPOSE: Helper to print out a message indicating that there + * are insufficient components for a volume, only ncomps + * are actually available. + */ +void +print_insufficient_components_msg( + int ncomp) +{ + oprintf(OUTPUT_VERBOSE, + gettext(" <---Failed: only found %d components\n"), ncomp); +} + +/* + * FUNCTION: print_hba_insufficient_space_msg(char *name, uint64_t nbytes) + * + * INPUT: name - a char * HBA name + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper to print out a message indicating the the HBA has + * insufficient space for use by the mirror layout strategy. + */ +void +print_hba_insufficient_space_msg( + char *name, + uint64_t nbytes) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_VERBOSE, + gettext(" <--Failed: '%s' only has %s available\n"), + name, spstr); + + free(spstr); +} + +/* + * FUNCTION: print_insufficient_capacity_msg(uint64_t nbytes) + * + * INPUT: nbytes - available capacity in bytes + * + * PURPOSE: Helper to print out a message indicating that there + * is insufficient space for a volume, only nbytes are + * actually available. + */ +void +print_insufficient_capacity_msg( + uint64_t nbytes) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_VERBOSE, + gettext(" <---Failed: only found %s capacity\n"), spstr); + + free(spstr); +} + +/* + * FUNCTION: print_layout_submirrors_msg(char *type, uint64_t nbytes, + * int nsubs) + * + * PURPOSE: Prints a generic status message indicating that layout of + * nsub submirrors of the indicated type and size has begun. + */ +void +print_layout_submirrors_msg( + char *type, + uint64_t nbytes, + int nsubs) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_TERSE, + gettext(" -->Layout %d %s submirrors with capacity %s\n"), + nsubs, type, spstr); + + free(spstr); +} + +/* + * FUNCTION: print_layout_submirrors_failed_msg(char *type, int count, + * int nsubs) + * + * PURPOSE: Prints a generic status message indicating that only count + * submirrors (out of nsubs) of the indicated type could be + * composed. + */ +void +print_layout_submirrors_failed_msg( + char *type, + int count, + int nsubs) +{ + if (count == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" <---Failed, no %s submirrors could " + "be composed.\n"), + type); + } else { + oprintf(OUTPUT_VERBOSE, + gettext(" <---Failed, only %d of %d %s submirror(s) " + "could be composed.\n"), + count, nsubs, type); + } +} + +/* + * FUNCTION: print_populate_volume_msg(char *type, uint64_t nbytes) + * + * PURPOSE: Prints a generic message indicating a population process + * for a volume of the indicated type and size is beginning. + */ +void +print_populate_volume_msg( + char *type, + uint64_t nbytes) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_TERSE, + gettext(" --->Populate a %s of capacity %s\n"), + type, spstr); + + free(spstr); +} + +/* + * FUNCTION: print_populate_volume_ncomps_msg(char *type, uint64_t nbytes, + * int ncomps) + * + * PURPOSE: Prints a generic message indicating a population process + * for a volume of the indicated type, size and number of + * components is beginning. + */ +void +print_populate_volume_ncomps_msg( + char *type, + uint64_t nbytes, + int ncomps) +{ + char *spstr = NULL; + + (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE); + + oprintf(OUTPUT_TERSE, + gettext(" --->Populate a %s of capacity %s (%d components)\n"), + type, spstr, ncomps); + + free(spstr); +} + +/* + * FUNCTION: print_populate_success_msg() + * + * PURPOSE: Prints a generic message indicating a population process + * completed successfully. + */ +void +print_populate_success_msg() +{ + oprintf(OUTPUT_TERSE, + gettext(" <---Success!\n")); +} + +/* + * FUNCTION: print_populate_choose_slices_msg() + * + * PURPOSE: Prints a generic message indicating a population process + * is beginning to choose slices. + */ +void +print_populate_choose_slices_msg() +{ + oprintf(OUTPUT_VERBOSE, + gettext(" choosing \"best\" slices from " + "those available...\n")); +} + +/* + * FUNCTION: print_populate_no_slices_msg() + * + * PURPOSE: Prints a layout failure due to no available slices message. + */ +void +print_populate_no_slices_msg() +{ + oprintf(OUTPUT_VERBOSE, + gettext(" <---Failed: there are no slices available.\n")); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_messages.h b/usr/src/cmd/lvm/metassist/layout/layout_messages.h new file mode 100644 index 0000000000..d9c7531b21 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_messages.h @@ -0,0 +1,72 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_MESSAGES_H +#define _LAYOUT_MESSAGES_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* + * Functions to print out progress, status and error messages that + * are shared by layout_concat.c, layout_hsp.c, layout_mirror.c, + * layout_stripe.c + */ +extern void print_layout_success_msg(); +extern void print_layout_volume_msg(char *type, uint64_t nbytes); +extern void print_layout_explicit_msg(char *type); +extern void print_layout_explicit_added_msg(char *comp); +extern void print_layout_submirrors_msg(char *type, uint64_t nbytes, int nsubs); +extern void print_layout_submirrors_failed_msg(char *type, int count, + int nsubs); + +extern void print_populate_volume_msg(char *type, uint64_t nbytes); +extern void print_populate_volume_ncomps_msg(char *type, uint64_t nbytes, + int ncomps); +extern void print_populate_success_msg(); +extern void print_populate_choose_slices_msg(); +extern void print_populate_no_slices_msg(); + +extern void print_no_hbas_msg(); +extern void print_debug_failure_msg(); + +extern void print_insufficient_resources_msg(char *type); +extern void print_insufficient_hbas_msg(int n); +extern void print_insufficient_disks_msg(int n); +extern void print_hba_insufficient_space_msg(char *name, uint64_t nbytes); +extern void print_insufficient_capacity_msg(uint64_t nbytes); +extern void print_insufficient_components_msg(int ncomp); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_MESSAGES_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_mirror.c b/usr/src/cmd/lvm/metassist/layout/layout_mirror.c new file mode 100644 index 0000000000..6daf1ab7d5 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_mirror.c @@ -0,0 +1,2413 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libintl.h> + +#include "volume_error.h" +#include "volume_dlist.h" +#include "volume_output.h" + +#include "layout_concat.h" +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_messages.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_stripe.h" +#include "layout_svm_util.h" + +#define _LAYOUT_MIRROR_C + +static int layout_stripe_submirrors( + devconfig_t *request, + dlist_t *cursubs, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results); + +static int layout_concat_submirrors( + devconfig_t *request, + dlist_t *cursubs, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results); + +static int compose_stripe_per_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + uint16_t ncomp, + uint16_t mincomp, + dlist_t **results); + +static int compose_stripes_across_hbas( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + dlist_t *disks, + uint64_t nbytes, + uint16_t nsubs, + uint16_t ncomp, + uint16_t mincomp, + dlist_t **results); + +static int compose_stripes_within_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + uint16_t ncomp, + uint16_t mincomp, + dlist_t **results); + +static int compose_concat_per_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results); + +static int compose_concats_across_hbas( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + dlist_t *disks, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results); + +static int compose_concats_within_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hba, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results); + +static int assemble_mirror( + devconfig_t *request, + dlist_t *subs, + devconfig_t **mirror); + +static int remove_used_disks( + dlist_t **disks, + devconfig_t *volume); + +static int volume_shares_disk( + dm_descriptor_t disk, + devconfig_t *volume, + boolean_t *bool); + +static int select_mpxio_hbas( + dlist_t *hbas, + dlist_t **mpxio_hbas); + +static int set_explicit_submirror_names( + dlist_t *reqs, + dlist_t *subs); + +static int set_explicit_submirror_name( + devconfig_t *req, + devconfig_t *sub); + +/* + * FUNCTION: layout_mirror(devconfig_t *request, nbytes, dlist_t **results) + * + * INPUT: request - pointer to a request devconfig_t + * nsubs - number of submirrors + * nbytes - desired mirror size + * + * OUTPUT: results - pointer to a list of volume devconfig_t results + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main driver to handle a mirror request that does not specify + * subcomponents. + * + * Striped submirrors are tried first, then concats. + */ +int +layout_mirror( + devconfig_t *request, + uint16_t nsubs, + uint64_t nbytes, + dlist_t **results) +{ + dlist_t *subs = NULL; + dlist_t *item = NULL; + boolean_t usehsp = B_FALSE; + int error = 0; + + if ((error = get_volume_faultrecov(request, &usehsp)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + error = 0; + } + + print_layout_volume_msg(devconfig_type_to_str(TYPE_MIRROR), nbytes); + + /* prefer stripe submirrors */ + if ((error = layout_stripe_submirrors( + request, NULL, nbytes, nsubs, &subs)) != 0) { + return (error); + } + + if (subs == NULL) { + /* second chance: mirrored concats */ + if ((error = layout_concat_submirrors( + request, NULL, nbytes, nsubs, &subs)) != 0) { + return (error); + } + } + + if (subs != NULL) { + + devconfig_t *mirror = NULL; + dlist_t *iter = NULL; + + /* unset submirror names prior to final assembly */ + for (iter = subs; iter != NULL; iter = iter->next) { + devconfig_t *sub = (devconfig_t *)iter->obj; + char *name = NULL; + + (void) devconfig_get_name(sub, &name); + release_volume_name(name); + (void) devconfig_set_name(sub, ""); + } + + error = assemble_mirror(request, subs, &mirror); + if (error == 0) { + + if ((item = dlist_new_item(mirror)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + + /* remember submirrors that need HSPs */ + if (usehsp == B_TRUE) { + error = add_to_hsp_list( + devconfig_get_components(mirror)); + } + + print_layout_success_msg(); + } + } else { + /* cleanup submirrors */ + dlist_free_items(subs, free_devconfig_object); + subs = NULL; + } + + } else if (error != 0) { + + print_debug_failure_msg(devconfig_type_to_str(TYPE_MIRROR), + get_error_string(error)); + + } else { + + print_insufficient_resources_msg( + devconfig_type_to_str(TYPE_MIRROR)); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: populate_explicit_mirror(devconfig_t *request, + * dlist_t **results) + * + * INPUT: request - pointer to a request devconfig_t + * + * OUTPUT: results - pointer to a list of volume devconfig_t results + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Processes the input mirror request specifying explicit layout + * constraints on the submirrors. + * + * Primary submirror constraint is explicit type, either + * stripe or concat. Submirror types may be mixed. + * + * Submirror sizes or components may be specified explicitly. + * + * If the mirror does not specify a size, assume the first explicit + * submirror size is the desired size. If a submirror does not + * specify a size or components, use the mirror size. + * + * Scan the submirror requests: those with specific components + * get assembled as encountered. The remainder are grouped by + * type and handled by layout_stripe_submirrors() or + * layout_concat_submirrors(). + * + * If all specified submirrors can be assembled, the final mirror + * is assembled and appended to the results list. + */ +int +populate_explicit_mirror( + devconfig_t *request, + dlist_t **results) +{ + dlist_t *composed = NULL; + dlist_t *list = NULL; + dlist_t *iter = NULL; + dlist_t *concats_by_size = NULL; + dlist_t *stripes_by_size = NULL; + int nsubs = 0; + int error = 0; + uint64_t msize = 0; + boolean_t usehsp = B_FALSE; + + list = devconfig_get_components(request); + nsubs = dlist_length(list); + + if ((error = get_volume_faultrecov(request, &usehsp)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + error = 0; + } + + if ((error = devconfig_get_size(request, &msize)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + msize = 0; + } else { + return (error); + } + } + + print_layout_explicit_msg(devconfig_type_to_str(TYPE_MIRROR)); + + /* + * Scan the list of specified submirrors, collect those that only + * specify size (or no size). Process those with explicit components + * immediately. + */ + composed = NULL; + for (iter = list; (iter != NULL) && (error == 0); iter = iter->next) { + + devconfig_t *comp = (devconfig_t *)iter->obj; + component_type_t ctype = TYPE_UNKNOWN; + dlist_t *clist = NULL; + uint64_t csize = 0; + dlist_t *item = NULL; + + (void) devconfig_get_type(comp, &ctype); + (void) devconfig_get_size(comp, &csize); + clist = devconfig_get_components(comp); + + if (clist != NULL) { + + /* components specified */ + + if (ctype == TYPE_STRIPE) { + error = populate_explicit_stripe(comp, &item); + } else { + error = populate_explicit_concat(comp, &item); + } + + if (error == 0) { + set_explicit_submirror_name( + comp, (devconfig_t *)item->obj); + composed = dlist_append(item, composed, AT_TAIL); + } + + } else { + + /* no components specified */ + + /* if no size is specified, it needs to be inferred */ + + if (msize == 0) { + /* mirror specified no size, first explicit submirror */ + /* size is assumed to be the desired mirror size */ + msize = csize; + } + if (csize == 0) { + /* this submirror specified no size, use mirror size */ + devconfig_set_size(comp, msize); + } + + if ((item = dlist_new_item(comp)) == NULL) { + error = ENOMEM; + break; + } + + if (ctype == TYPE_STRIPE) { + stripes_by_size = dlist_append( + item, stripes_by_size, AT_TAIL); + } else { + concats_by_size = dlist_append( + item, concats_by_size, AT_TAIL); + } + + } + } + + /* compose stripes specified by size */ + if ((error == 0) && (stripes_by_size != NULL)) { + uint16_t n = dlist_length(stripes_by_size); + dlist_t *stripes = NULL; + if ((error = layout_stripe_submirrors( + request, composed, msize, n, &stripes)) == 0) { + + /* adjust stripe names */ + set_explicit_submirror_names(stripes_by_size, stripes); + composed = dlist_append(stripes, composed, AT_TAIL); + + } else { + /* these stripes failed, skip concats_by_size */ + dlist_free_items(stripes, free_devconfig_object); + dlist_free_items(concats_by_size, NULL); + concats_by_size = NULL; + } + dlist_free_items(stripes_by_size, NULL); + } + + /* compose concats specified by size */ + if ((error == 0) && (concats_by_size != NULL)) { + uint16_t n = dlist_length(concats_by_size); + dlist_t *concats = NULL; + if ((error = layout_concat_submirrors( + request, composed, msize, n, &concats)) == 0) { + + /* adjust concat names */ + set_explicit_submirror_names(concats_by_size, concats); + composed = dlist_append(concats, composed, AT_TAIL); + + } else { + + /* these concats failed */ + dlist_free_items(concats, free_devconfig_object); + } + + dlist_free_items(concats_by_size, NULL); + } + + if ((composed != NULL) && ((dlist_length(composed) == nsubs))) { + + /* assemble final mirror */ + + devconfig_t *mirror = NULL; + dlist_t *item = NULL; + + if ((error = assemble_mirror(request, composed, &mirror)) == 0) { + + if ((item = dlist_new_item(mirror)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + if (usehsp == B_TRUE) { + error = add_to_hsp_list( + devconfig_get_components(mirror)); + } + print_layout_success_msg(); + } + } + + } else if (error != 0) { + + print_debug_failure_msg( + devconfig_type_to_str(TYPE_MIRROR), + get_error_string(error)); + + } else { + + dlist_free_items(composed, free_devconfig_object); + print_insufficient_resources_msg( + devconfig_type_to_str(TYPE_MIRROR)); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: assemble_mirror(devconfig_t *request, dlist_t *subs, + * devconfig_t **mirror) + * + * INPUT: request - pointer to a devconfig_t of the current request + * subs - pointer to a list of composed submirrors + * + * OUPUT: mirror - pointer to a devconfig_t to hold final mirror + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which creates and populates a mirror devconfig_t + * struct using information from the input request and the + * list of submirror components. + * + * Determines the name of the mirror either from the request + * or from the default naming scheme and assigns names to + * unnamed submirrors according to the default naming scheme. + * + * Sets the read and write strategies, and the resync pass + * number for the mirror if values are specified in the request. + * + * Attaches the input list of submirrors to the devconfig. + */ +static int +assemble_mirror( + devconfig_t *request, + dlist_t *subs, + devconfig_t **mirror) +{ + dlist_t *iter = NULL; + char *name = NULL; + int error = 0; + + if ((error = new_devconfig(mirror, TYPE_MIRROR)) == 0) { + /* set stripe name, use requested name if specified */ + if ((error = devconfig_get_name(request, &name)) != 0) { + if (error != ERR_ATTR_UNSET) { + volume_set_error(gettext("error getting requested name\n")); + } else { + error = 0; + } + } + + if (error == 0) { + if (name == NULL) { + if ((error = get_next_volume_name(&name, + TYPE_MIRROR)) == 0) { + error = devconfig_set_name(*mirror, name); + free(name); + /* get name for generating submirror names below */ + error = devconfig_get_name(*mirror, &name); + } + } else { + error = devconfig_set_name(*mirror, name); + } + } + } + + /* assign name to any unnamed submirror */ + for (iter = subs; + (error == 0) && (iter != NULL); + iter = iter->next) { + + devconfig_t *sub = (devconfig_t *)iter->obj; + char *subname = NULL; + + error = devconfig_get_name(sub, &subname); + if ((error == ERR_ATTR_UNSET) || (subname == NULL) || + (*subname == '\0')) { + ((error = get_next_submirror_name(name, &subname)) != 0) || + (error = devconfig_set_name(sub, subname)); + free(subname); + } + } + + if (error == 0) { + mirror_read_strategy_t read = 0; + if ((error = get_mirror_read_strategy(request, &read)) == 0) { + error = devconfig_set_mirror_read(*mirror, read); + } else if (error == ERR_ATTR_UNSET) { + error = 0; + } + } + + if (error == 0) { + mirror_write_strategy_t write = 0; + if ((error = get_mirror_write_strategy(request, &write)) == 0) { + error = devconfig_set_mirror_write(*mirror, write); + } else if (error == ERR_ATTR_UNSET) { + error = 0; + } + } + + if (error == 0) { + uint16_t pass = 0; + if ((error = get_mirror_pass(request, &pass)) == 0) { + error = devconfig_set_mirror_pass(*mirror, pass); + } else if (error == ERR_ATTR_UNSET) { + error = 0; + } + } + + /* arrange submirrors in ascending size order */ + if (error == 0) { + dlist_t *sorted = NULL; + dlist_t *next = NULL; + + iter = subs; + while (iter != NULL) { + + next = iter->next; + iter->next = NULL; + iter->prev = NULL; + + sorted = dlist_insert_ordered(iter, + sorted, ASCENDING, compare_devconfig_sizes); + + iter = next; + } + subs = sorted; + } + + if (error == 0) { + devconfig_set_components(*mirror, subs); + } else { + free_devconfig(*mirror); + *mirror = NULL; + } + + return (error); +} + +/* + * FUNCTION: layout_stripe_submirrors(devconfig_t *request, dlist_t *cursubs, + * uint64_t nbytes, uint16_t nsubs, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * these may affect disk and HBA choices for new + * submirrors being composed and are passed along + * into the component selection functions. + * nbytes - the desired capacity for the stripes + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main layout driver for composing stripe submirrors. + * + * Attempts to construct nsub submirrors of size nbytes. + * + * Several different layout strategies are tried in order + * of preference until one succeeds or there are none left. + * + * 1 - mirror with all stripes on the MPXIO "controller" + * . requires MPXIO to be enabled + * . requires nsubs * mincomp available disks on the + * MPXIO HBA + * + * 2 - mirror with stripes within separate HBAs of same type + * . requires nsubs HBAs with mincomp disks + * . stripe width is driven by number of disks on HBA + * + * 3 - mirror with stripes across HBAs of same type + * . requires mincomp HBAs with nsubs disks + * (each stripe has a disk per HBA) + * . stripe width is driven by number of HBAs + * + * 4 - mirror with stripes within separate HBAs of mixed type + * . requires nsubs HBAs with mincomp disks + * . stripe width is driven by number of disks on HBA + * + * 5 - mirror with stripes across HBAs of mixed type + * . requires mincomp HBAs with nsubs disks + * (each stripe has a disk per HBA) + * . stripe width is driven by number of HBAs + * + * 6 - mirror with all stripes within the same HBA + * . requires an HBA with mincomp * nsubs disks + * + * get available HBAs + * + * group HBAs by characteristics + * for (each HBA grouping) and (nsub stripes not composed) { + * select next HBA group + * for (strategy[1,2,3]) and (nsub stripes not composed) { + * compose nsub stripes using HBAs in group + * } + * } + * + * if (nsub stripes not composed) { + * for (strategy[4,5,6]) and (nsub stripes not composed) { + * compose nsub stripes using all HBAs + * } + * } + * + * if (all stripes composed) { + * append composed stripes to results + * } + * + */ +static int +layout_stripe_submirrors( + devconfig_t *request, + dlist_t *cursubs, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results) +{ + /* + * these enums define the # of strategies and the preference order + * in which they are tried + */ + typedef enum { + ALL_STRIPES_ON_MPXIO = 0, + STRIPE_PER_SIMILAR_HBA, + STRIPE_ACROSS_SIMILAR_HBAS, + N_SIMILAR_HBA_STRATEGIES + } similar_hba_strategy_order_t; + + typedef enum { + STRIPE_PER_ANY_HBA = 0, + STRIPE_ACROSS_ANY_HBAS, + STRIPE_WITHIN_ANY_HBA, + N_ANY_HBA_STRATEGIES + } any_hba_strategy_order_t; + + dlist_t *usable_hbas = NULL; + dlist_t *similar_hba_groups = NULL; + dlist_t *iter = NULL; + dlist_t *subs = NULL; + + boolean_t usehsp = B_FALSE; + uint16_t mincomp = 0; + uint16_t maxcomp = 0; + + int error = 0; + + (error = get_usable_hbas(&usable_hbas)); + if (error != 0) { + return (error); + } + + print_layout_submirrors_msg(devconfig_type_to_str(TYPE_STRIPE), + nbytes, nsubs); + + if (dlist_length(usable_hbas) == 0) { + print_no_hbas_msg(); + volume_set_error(gettext("There are no usable HBAs.")); + return (-1); + } + + similar_hba_groups = NULL; + ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) || + + /* + * determine the min/max number of stripe components + * based on the request, the diskset defaults or the + * global defaults. These are absolute limits, the + * actual values are determined by the number of HBAs + * and/or disks available. + */ + (error = get_stripe_min_comp(request, &mincomp)) || + (error = get_stripe_max_comp(request, &maxcomp)) || + (error = get_volume_faultrecov(request, &usehsp)); + if (error != 0) { + return (error); + } + + for (iter = similar_hba_groups; + (error == 0) && (subs == NULL) && (iter != NULL); + iter = iter->next) { + + dlist_t *hbas = (dlist_t *)iter->obj; + + similar_hba_strategy_order_t order; + + for (order = ALL_STRIPES_ON_MPXIO; + (order < N_SIMILAR_HBA_STRATEGIES) && + (subs == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case ALL_STRIPES_ON_MPXIO: + + if (is_mpxio_enabled() == B_TRUE) { + dlist_t *mpxio_hbas = NULL; + + /* see if any HBA supports MPXIO */ + error = select_mpxio_hbas(hbas, &mpxio_hbas); + if ((error == 0) && (mpxio_hbas != NULL)) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 1: use %d-%d MPXIO disks\n"), + mincomp * nsubs, maxcomp * nsubs); +/* END CSTYLED */ + + /* see if MPXIO HBA has enough disks */ + error = select_hbas_with_n_disks( + request, mpxio_hbas, (mincomp * nsubs), + &selhbas, &disks); + + if ((error == 0) && (dlist_length(selhbas) > 0)) { + error = compose_stripes_within_hba( + request, cursubs, mpxio_hbas, nbytes, + nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + dlist_free_items(mpxio_hbas, NULL); + } + + break; + + case STRIPE_PER_SIMILAR_HBA: + + error = select_hbas_with_n_disks( + request, hbas, mincomp, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 2: use %d-%d disks from %d similar HBAs - stripe per HBA\n"), + mincomp, maxcomp, nsubs); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= nsubs) { + error = compose_stripe_per_hba( + request, cursubs, selhbas, nbytes, + nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case STRIPE_ACROSS_SIMILAR_HBAS: + + error = select_hbas_with_n_disks( + request, hbas, nsubs, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 3: use %d disks from %d-%d similar HBAs - stripe across HBAs \n"), + nsubs, mincomp, maxcomp); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= mincomp) { + error = compose_stripes_across_hbas( + request, cursubs, selhbas, disks, + nbytes, nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + default: + break; + } + + dlist_free_items(selhbas, NULL); + dlist_free_items(disks, NULL); + } + } + + for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { + dlist_free_items((dlist_t *)iter->obj, NULL); + } + dlist_free_items(similar_hba_groups, NULL); + + /* retry using all available HBAs */ + if (subs == NULL) { + + any_hba_strategy_order_t order; + + for (order = STRIPE_PER_ANY_HBA; + (order < N_ANY_HBA_STRATEGIES) && + (subs == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case STRIPE_PER_ANY_HBA: + + error = select_hbas_with_n_disks( + request, usable_hbas, nsubs, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 4: use %d-%d disks from any %d HBAs - stripe per HBA\n"), + mincomp, maxcomp, nsubs); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= nsubs) { + error = compose_stripe_per_hba( + request, cursubs, selhbas, nbytes, + nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case STRIPE_ACROSS_ANY_HBAS: + + error = select_hbas_with_n_disks( + request, usable_hbas, nsubs, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 5: use %d disks from %d-%d HBAs - stripe across HBAs \n"), + nsubs, mincomp, maxcomp); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= mincomp) { + error = compose_stripes_across_hbas( + request, cursubs, selhbas, disks, + nbytes, nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case STRIPE_WITHIN_ANY_HBA: + + error = select_hbas_with_n_disks( + request, usable_hbas, (mincomp * nsubs), + &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 6: use %d-%d disks from any single HBA - %d stripes within HBA\n"), + mincomp * nsubs, maxcomp * nsubs, nsubs); +/* END CSTYLED */ + if ((n = dlist_length(selhbas)) > 0) { + error = compose_stripes_within_hba( + request, cursubs, selhbas, nbytes, + nsubs, maxcomp, mincomp, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + default: + break; + } + + dlist_free_items(selhbas, NULL); + dlist_free_items(disks, NULL); + } + } + + if (error == 0) { + *results = dlist_append(subs, *results, AT_TAIL); + } + return (error); +} + +/* + * FUNCTION: layout_concat_submirrors(devconfig_t *request, dlist_t *cursubs, + * uint64_t nbytes, uint16_t nsubs, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * nbytes - the desired capacity for the concats + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main layout driver for composing concat submirrors. + * + * Attempts to construct nsub submirrors of size nbytes. + * + * Several different layout strategies are tried in order + * of preference until one succeeds or there are none left. + * + * 1 - mirror with all concats on the MPXIO "controller" + * . requires MPXIO to be enabled + * . requires nsubs available disks on the MPXIO HBA + * + * 2 - mirror with concats on separate HBAs of same type + * . requires nsubs HBAs with available disks + * + * 3 - mirror with concats across HBAs of same type + * . requires an HBA with at least 1 available disk + * + * 4 - mirror with concats on separate HBAs of mixed type + * . requires nsubs HBAs with available disks + * + * 5 - mirror with concats across HBAs of mixed type + * . requires an HBA with at least 1 available disk + * + * 6 - mirror with all concats on the same HBA + * . requires an HBA with at least nsubs available disks + * + * get available HBAs + * + * group HBAs by characteristics + * for (each HBA grouping) and (nsub concats not composed) { + * select next HBA group + * for (strategy[1,2,3]) and (nsub concats not composed) { + * compose nsub concats, nbytes in size + * } + * } + * + * if (nsub concats not composed) { + * for (strategy[4,5,6]) and (nsub concats not composed) { + * compose nsub concats, nbytes in size + * } + * } + * + * if (all concats composed) { + * append composed concats to results + * } + * + */ +static int +layout_concat_submirrors( + devconfig_t *request, + dlist_t *cursubs, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results) +{ + /* + * these enums define the # of strategies and the preference order + * in which they are tried + */ + typedef enum { + ALL_CONCATS_ON_MPXIO = 0, + CONCAT_PER_SIMILAR_HBA, + CONCAT_ACROSS_SIMILAR_HBAS, + N_SIMILAR_HBA_STRATEGIES + } similar_hba_strategy_order_t; + + typedef enum { + CONCAT_PER_ANY_HBA = 0, + CONCAT_ACROSS_ANY_HBAS, + CONCAT_WITHIN_ANY_HBA, + N_ANY_HBA_STRATEGIES + } any_hba_strategy_order_t; + + dlist_t *usable_hbas = NULL; + dlist_t *similar_hba_groups = NULL; + dlist_t *iter = NULL; + dlist_t *subs = NULL; + + boolean_t usehsp = B_FALSE; + + int error = 0; + + (error = get_usable_hbas(&usable_hbas)); + if (error != 0) { + return (error); + } + + print_layout_submirrors_msg(devconfig_type_to_str(TYPE_CONCAT), + nbytes, nsubs); + + if (dlist_length(usable_hbas) == 0) { + print_no_hbas_msg(); + volume_set_error(gettext("There are no usable HBAs.")); + return (-1); + } + + similar_hba_groups = NULL; + ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) || + (error = get_volume_faultrecov(request, &usehsp)); + if (error != 0) { + return (error); + } + + for (iter = similar_hba_groups; + (error == 0) && (subs == NULL) && (iter != NULL); + iter = iter->next) { + + dlist_t *hbas = (dlist_t *)iter->obj; + + similar_hba_strategy_order_t order; + + for (order = ALL_CONCATS_ON_MPXIO; + (order < N_SIMILAR_HBA_STRATEGIES) && + (subs == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case ALL_CONCATS_ON_MPXIO: + + if (is_mpxio_enabled() == B_TRUE) { + dlist_t *mpxio_hbas = NULL; + + /* see if any HBA supports MPXIO */ + error = select_mpxio_hbas(hbas, &mpxio_hbas); + if ((error == 0) && (mpxio_hbas != NULL)) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 1: use at least %d MPXIO disks\n"), + nsubs); +/* END CSTYLED */ + + /* see if MPXIO HBA has enough disks */ + error = select_hbas_with_n_disks( + request, hbas, nsubs, &selhbas, &disks); + + if ((error == 0) && + ((n = dlist_length(selhbas)) > 0)) { + error = compose_concats_within_hba( + request, cursubs, mpxio_hbas, nbytes, + nsubs, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + dlist_free_items(mpxio_hbas, NULL); + } + + break; + + case CONCAT_PER_SIMILAR_HBA: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 2: use any disks from %d similar HBAs - concat per HBA\n"), + nsubs); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= nsubs) { + error = compose_concat_per_hba( + request, cursubs, selhbas, + nbytes, nsubs, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case CONCAT_ACROSS_SIMILAR_HBAS: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 3: use any disks from any similar HBAs - " + "%d concats across HBAs\n"), + nsubs); +/* END CSTYLED */ + error = compose_concats_across_hbas( + request, cursubs, selhbas, disks, + nbytes, nsubs, &subs); + } + + break; + + default: + break; + } + + dlist_free_items(selhbas, NULL); + dlist_free_items(disks, NULL); + } + } + + for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { + dlist_free_items((dlist_t *)iter->obj, NULL); + } + dlist_free_items(similar_hba_groups, NULL); + + /* retry using all available HBAs */ + if (subs == NULL) { + + any_hba_strategy_order_t order; + + for (order = CONCAT_PER_ANY_HBA; + (order < N_ANY_HBA_STRATEGIES) && + (subs == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case CONCAT_PER_ANY_HBA: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 4: use any disks from %d HBAs - concat per HBA\n"), + nsubs); +/* END CSTYLED */ + if ((n = dlist_length(selhbas)) >= nsubs) { + error = compose_concat_per_hba( + request, cursubs, selhbas, + nbytes, nsubs, &subs); + } else { + print_insufficient_hbas_msg(n); + } + } + break; + + case CONCAT_ACROSS_ANY_HBAS: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, + gettext(" -->Strategy 5: use any disks from any HBA - %d concats across HBAs\n"), + nsubs); +/* END CSTYLED */ + error = compose_concats_across_hbas( + request, cursubs, selhbas, disks, + nbytes, nsubs, &subs); + } + + break; + + case CONCAT_WITHIN_ANY_HBA: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 6: use any disks from any single HBA - %d concats within an HBA\n"), + nsubs); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) > 0) { + error = compose_concats_within_hba( + request, cursubs, selhbas, + nbytes, nsubs, &subs); + } else { + print_insufficient_hbas_msg(n); + } + + } + break; + + default: + break; + } + + dlist_free_items(selhbas, NULL); + dlist_free_items(disks, NULL); + } + } + + if (error == 0) { + *results = dlist_append(subs, *results, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: compose_stripe_per_hba(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes, + * uint16_t nsubs, int maxcomp, int mincomp, + * dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * nbytes - the desired capacity for the stripes + * nsubs - the desired number of stripes + * maxcomp - the maximum number of stripe components + * mincomp - the minimum number of stripe components + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of stripes + * of the desired size using available disks on any of the HBAs + * from the input list. + * + * The number of components within the composed stripes will be + * in the range of mincomp to ncomp, preferring more components + * over fewer. All stripes composed by a single call to this + * function will have the same number of components. + * + * Each stripe will use disks from a single HBA. + * + * All input HBAs are expected to have at least mincomp available + * disks. + * + * If the stripes can be composed, they are appended to the list + * of result volumes. + * + * while (more HBAs and more stripes to compose) { + * select next HBA + * get available space for this HBA + * get available disks for this HBA + * if (not enough space or disks) { + * continue + * } + * + * use # disks as # of stripe components - limit to maxcomp + * for ((ncomps downto mincomp) && (more stripes to compose)) { + * while (more stripes to compose) { + * if a stripe can be composed using disks { + * save stripe + * increment stripe count + * } + * while (more HBAs and more stripes to compose) { + * select next HBA + * get available space for this HBA + * get available disks for this HBA + * if (not enough space or disks) { + * continue + * } + * if a stripe can be composed using disks { + * save stripe + * increment stripe count + * } + * } + * + * if (not all stripes composed) { + * delete any compose stripes + * } + * } + * } + * + * if (not all stripes composed) { + * delete any stripes composed + * } + * } + * + * if (not all stripes composed) { + * delete any stripes composed + * } + * + * append composed stripes to results + */ +static int +compose_stripe_per_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + uint16_t maxcomp, + uint16_t mincomp, + dlist_t **results) +{ + int error = 0; + dlist_t *list = NULL; + dlist_t *iter = NULL; + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Trying to compose %d Stripes with " + "%d-%d components on separate HBAs.\n"), + nsubs, mincomp, maxcomp); + + for (iter = hbas; + (list == NULL) && (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + dlist_t *disks = NULL; + uint64_t space = 0; + int ncomp = 0; + char *name; + + ((error = get_display_name(hba, &name)) != 0) || + (error = hba_get_avail_disks_and_space(request, + hba, &disks, &space)); + if (error != 0) { + continue; + } + + /* check for sufficient space and minimum # of disks */ + if (space < nbytes) { + (void) print_hba_insufficient_space_msg(name, space); + dlist_free_items(disks, NULL); + continue; + } + + if ((ncomp = dlist_length(disks)) < mincomp) { + print_insufficient_disks_msg(ncomp); + dlist_free_items(disks, NULL); + continue; + } + + /* make the stripe as wide as possible, up to maxcomp */ + for (ncomp = ((ncomp > maxcomp) ? maxcomp : ncomp); + (list == NULL) && (ncomp >= mincomp) && (error == 0); + ncomp--) { + + int count = 0; + + /* try composing nsubs stripes with ncomp components */ + while (count < nsubs) { + + devconfig_t *stripe = NULL; + dlist_t *item = NULL; + dlist_t *iter1 = NULL; + + /* build first stripe using disks on this HBA */ + if (((error = populate_stripe(request, nbytes, + disks, ncomp, cursubs, &stripe)) != 0) || + (stripe == NULL)) { + /* first stripe failed at the current width */ + /* break while loop and try a different width */ + break; + } + + /* composed a stripe */ + if ((item = dlist_new_item((void*)stripe)) == NULL) { + error = ENOMEM; + break; + } + ++count; + list = dlist_append(item, list, AT_TAIL); + + /* compose stripes on remaining HBAs */ + for (iter1 = iter->next; + (count < nsubs) && (iter1 != NULL) && (error == 0); + iter1 = iter1->next) { + + dm_descriptor_t hba1 = (uintptr_t)iter1->obj; + uint64_t space1 = 0; + dlist_t *disks1 = NULL; + + error = hba_get_avail_disks_and_space(request, + hba1, &disks1, &space1); + if (error != 0) { + continue; + } + + /* enough space/disks on this HBA? */ + if ((dlist_length(disks1) < ncomp) || + (space1 < nbytes)) { + dlist_free_items(disks1, NULL); + continue; + } + + stripe = NULL; + error = populate_stripe( + request, nbytes, disks1, + ncomp, cursubs, &stripe); + + if (stripe != NULL) { + /* prepare to compose another */ + if ((item = dlist_new_item( + (void *)stripe)) == NULL) { + error = ENOMEM; + break; + } + list = dlist_append(item, list, AT_TAIL); + ++count; + } + + dlist_free_items(disks1, NULL); + disks1 = NULL; + } + + if ((iter1 == NULL) && (count < nsubs)) { + /* + * no HBAs remain and haven't composed + * enough stripes at the current width. + * break while loop and try another width. + */ + break; + } + } + + if (count < nsubs) { + /* + * stripe composition at current width failed... + * prepare to try a narrower width. + * NB: narrower widths may work since some HBA(s) + * may have fewer available disks + */ + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_STRIPE), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + } + } + + dlist_free_items(disks, NULL); + disks = NULL; + } + + if (error == 0) { + *results = dlist_append(list, *results, AT_TAIL); + } else { + dlist_free_items(list, free_devconfig_object); + } + + return (error); +} + +/* + * FUNCTION: compose_stripes_across_hbas(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, dlist_t *disks, + * uint64_t nbytes, uint16_t nsubs, int maxcomp, + * int mincomp, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * disks - pointer to a list of available disks on the HBAs + * nbytes - the desired capacity for the stripes + * nsubs - the desired number of stripes + * ncomp - the maximum number of stripe components + * mincomp - the minimum number of stripe components + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of stripes + * of the desired size using available disks on any of the HBAs + * from the input list. + * + * The number of components within the composed stripes will be + * in the range of mincomp to ncomp, preferring more components + * over fewer. All stripes composed by a single call to this + * function will have the same number of components. + * + * Each stripe will use a disk from several different HBAs. + * + * All input HBAs are expected to have at least nsubs available + * disks. + * + * If the stripes can be composed, they are appended to the list + * of result volumes. + * + * for (ncomps downto mincomp) { + * + * copy the input disk list + * while (more stripes to compose) { + * if a stripe can be composed using disks { + * save stripe + * remove used disks from disk list + * increment stripe count + * } else + * end while loop + * } + * + * free copied disk list + * if (not all stripes composed) { + * delete any stripes composed + * decrement ncomps + * } + * } + * + * if (not all stripes composed) { + * delete any stripes composed + * } + * + * append composed stripes to results + */ +static int +compose_stripes_across_hbas( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + dlist_t *disks, + uint64_t nbytes, + uint16_t nsubs, + uint16_t ncomp, + uint16_t mincomp, + dlist_t **results) +{ + int error = 0; + int count = 0; + + dlist_t *list = NULL; + + while ((ncomp >= mincomp) && (count < nsubs) && (error == 0)) { + + dlist_t *iter; + dlist_t *item; + dlist_t *disks_copy = NULL; + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Trying to compose %d Stripes with " + "%d components across %d HBAs.\n"), + nsubs, ncomp, dlist_length(hbas)); + + /* copy disk list, it is modified by the while loop */ + for (iter = disks; iter != NULL; iter = iter->next) { + if ((item = dlist_new_item(iter->obj)) == NULL) { + error = ENOMEM; + } else { + disks_copy = dlist_append(item, disks_copy, AT_HEAD); + } + } + + /* compose nsubs stripe submirrors of ncomp components */ + while ((count < nsubs) && (error == 0)) { + + devconfig_t *stripe = NULL; + dlist_t *item = NULL; + + error = populate_stripe( + request, nbytes, disks_copy, ncomp, cursubs, &stripe); + + if ((error == 0) && (stripe != NULL)) { + if ((item = dlist_new_item((void *)stripe)) == NULL) { + error = ENOMEM; + } else { + ++count; + list = dlist_append(item, list, AT_TAIL); + error = remove_used_disks(&disks_copy, stripe); + } + } else if (stripe == NULL) { + break; + } + } + + /* free copy of disk list */ + dlist_free_items(disks_copy, NULL); + disks_copy = NULL; + + if ((error == 0) && (count < nsubs)) { + /* failed to compose enough stripes at this width, */ + /* prepare to try again with the next narrower width. */ + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_STRIPE), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + count = 0; + --ncomp; + } + } + + if (count < nsubs) { + dlist_free_items(list, free_devconfig_object); + list = NULL; + } else { + *results = dlist_append(list, *results, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: compose_stripes_within_hba(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes, + * uint16_t nsubs, int maxcomp, int mincomp, + * dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * nbytes - the desired capacity for the stripes + * nsubs - the desired number of stripes + * maxcomp - the maximum number of stripe components + * mincomp - the minimum number of stripe components + * nsubs - the number of stripes to be composed + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of stripes + * of the desired size using available disks within any single + * HBA from the input list. + * + * The number of components within the composed stripes will be + * in the range of mincomp to maxcomp, preferring more components + * over fewer. All stripes composed by a single call to this + * function will have the same number of components. + * + * All stripes will use disks from a single HBA. + * + * All input HBAs are expected to have at least nsubs * mincomp + * available disks and total space sufficient for subs stripes. + * + * If the stripes can be composed, they are appended to the list + * of result volumes. + * + * while (more HBAs and more stripes need to be composed) { + * select next HBA + * if (not enough available space on this HBA) { + * continue; + * } + * get available disks for HBA + * use # disks as # of stripe components - limit to maxcomp + * for (ncomps downto mincomp) { + * if ((ncomps * nsubs) > ndisks) { + * continue; + * } + * while (more stripes need to be composed) { + * if a stripe can be composed using disks { + * save stripe + * remove used disks from disk list + * } else + * end while loop + * } + * if (not all stripes composed) { + * delete any stripes composed + * } + * } + * } + * + * if (not all stripes composed) { + * delete any stripes composed + * } + * + * append composed stripes to results + */ +static int +compose_stripes_within_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + uint16_t maxcomp, + uint16_t mincomp, + dlist_t **results) +{ + int error = 0; + int count = 0; + + dlist_t *list = NULL; + dlist_t *iter = NULL; + + for (iter = hbas; + (count < nsubs) && (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + uint64_t space = 0; + dlist_t *disks = NULL; + int ndisks = 0; + int ncomp = 0; + char *name = NULL; + + ((error = get_display_name(hba, &name)) != 0) || + (error = hba_get_avail_disks_and_space(request, + hba, &disks, &space)); + if (error != 0) { + dlist_free_items(disks, NULL); + continue; + } + + if (space < (nsubs * nbytes)) { + (void) print_hba_insufficient_space_msg(name, space); + dlist_free_items(disks, NULL); + continue; + } + + ndisks = dlist_length(disks); + + /* + * try composing stripes from ncomp down to mincomp. + * stop when nsubs stripes have been composed, or when the + * minimum stripe width has been tried + */ + for (ncomp = maxcomp; + (ncomp >= mincomp) && (count != nsubs) && (error == 0); + ncomp--) { + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Trying to compose %d Stripes with " + "%d components on a single HBA.\n"), + nsubs, ncomp); + + if (ndisks < (ncomp * nsubs)) { + print_insufficient_disks_msg(ndisks); + continue; + } + + /* try composing nsubs stripes, each ncomp wide */ + for (count = 0; (count < nsubs) && (error == 0); count++) { + + devconfig_t *stripe = NULL; + + error = populate_stripe( + request, nbytes, disks, ncomp, cursubs, &stripe); + + if ((error == 0) && (stripe != NULL)) { + + dlist_t *item = dlist_new_item((void *)stripe); + if (item == NULL) { + error = ENOMEM; + } else { + list = dlist_append(item, list, AT_TAIL); + error = remove_used_disks(&disks, stripe); + } + } else if (stripe == NULL) { + break; + } + } + + if (count < nsubs) { + /* failed to compose enough stripes at this width, */ + /* prepare to try again with fewer components */ + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_STRIPE), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + } + } + + dlist_free_items(disks, NULL); + } + + if (count < nsubs) { + dlist_free_items(list, free_devconfig_object); + list = NULL; + } + + *results = list; + + return (error); +} + +/* + * FUNCTION: compose_concats_per_hba(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes, + * uint16_t nsubs, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * nbytes - the desired capacity for the concats + * nsubs - the number of concats to be composed + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of concats + * of the desired size using available disks within HBAs from the + * input list. Each concat will be composed using disks from a + * single HBA. + * + * If the concats can be composed, they are appended to the list + * of result volumes. + * + * while (more HBAs AND more concats need to be composed) { + * if (not enough available space on this HBA) { + * continue; + * } + * + * get available disks for HBA + * if (concat can be composed) { + * save concat + * increment count + * } + * } + * + * if (not all stripes composed) { + * delete any concats composed + * } + * + * append composed concats to results + */ +static int +compose_concat_per_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results) +{ + int error = 0; + int count = 0; + + dlist_t *list = NULL; + dlist_t *iter = NULL; + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Trying to compose %d Concats on " + "separate HBAs.\n"), nsubs); + + for (iter = hbas; + (iter != NULL) && (error == 0) && (count < nsubs); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + uint64_t space = 0; + devconfig_t *concat = NULL; + dlist_t *disks = NULL; + + error = hba_get_avail_disks_and_space(request, hba, &disks, &space); + if ((error == 0) && (space >= nbytes)) { + error = populate_concat( + request, nbytes, disks, cursubs, &concat); + } + + if ((error == 0) && (concat != NULL)) { + dlist_t *item = dlist_new_item((void *)concat); + if (item == NULL) { + error = ENOMEM; + } else { + ++count; + list = dlist_append(item, list, AT_TAIL); + } + } + + dlist_free_items(disks, NULL); + } + + if (count != nsubs) { + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_CONCAT), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + } else { + *results = dlist_append(list, *results, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: compose_concats_across_hbas(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, dlist_t *disks, + * uint64_t nbytes, uint16_t nsubs, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * disks - pointer to a list of available disks on the HBAs + * nbytes - the desired capacity for the concats + * nsubs - the number of concats to be composed + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of concats + * of the desired size using any available disks from the input + * list of available HBAs. + * + * If the concats can be composed, they are appended to the list + * of result volumes. + * + * copy the input disk list + * while (more concats need to be composed) { + * if (a concat can be composed using remaining disks) { + * save concat + * remove used disks from disk list + * increment count + * } else { + * end while loop + * } + * } + * + * if (not all concats composed) { + * delete any concats composed + * } + * + * append composed concats to results + */ +static int +compose_concats_across_hbas( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + dlist_t *disks, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results) +{ + int error = 0; + int count = 0; + + dlist_t *list = NULL; + dlist_t *item = NULL; + dlist_t *iter = NULL; + dlist_t *disks_copy = NULL; + + /* copy disk list, it is modified by the while loop */ + for (iter = disks; iter != NULL; iter = iter->next) { + if ((item = dlist_new_item(iter->obj)) == NULL) { + error = ENOMEM; + } else { + disks_copy = dlist_append(item, disks_copy, AT_HEAD); + } + } + + while ((count < nsubs) && (error == 0)) { + + devconfig_t *concat = NULL; + + error = populate_concat( + request, nbytes, disks_copy, cursubs, &concat); + + if ((error == 0) && (concat != NULL)) { + + item = dlist_new_item((void *)concat); + if (item == NULL) { + error = ENOMEM; + } else { + count++; + list = dlist_append(item, list, AT_TAIL); + error = remove_used_disks(&disks_copy, concat); + } + } else if (concat == NULL) { + break; + } + } + + /* free copy of disk list */ + dlist_free_items(disks_copy, NULL); + disks_copy = NULL; + + if (count != nsubs) { + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_CONCAT), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + } else { + *results = dlist_append(list, *results, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: compose_concats_within_hba(devconfig_t *request, + * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes, + * uint16_t nsubs, dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * cursubs - pointer to a list of already composed submirrors + * hbas - pointer to a list of available HBAs + * nbytes - the desired capacity for the concats + * nsubs - the number of concats to be composed + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which composes the requested number of concats + * of the desired size using available disks within any single + * HBA from the input list. + * + * + * HBAs in the list are expected to have at least 2 available + * disks and total space sufficient for the submirrors. + * + * If the concats can be composed, they are appended to the list + * of result volumes. + * + * while (more HBAs) { + * if (not enough available space on this HBA) { + * continue; + * } + * + * get available disks for HBA + * while (more concats need to be composed) { + * if a concat can be composed using disks { + * save concat + * remove used disks from disk list + * increment count + * } else { + * delete any concats composed + * end while loop + * } + * } + * } + * + * if (not all concats composed) { + * delete any concats composed + * } + * + * append composed concats to results + */ +static int +compose_concats_within_hba( + devconfig_t *request, + dlist_t *cursubs, + dlist_t *hbas, + uint64_t nbytes, + uint16_t nsubs, + dlist_t **results) +{ + int error = 0; + + dlist_t *iter = NULL; + dlist_t *list = NULL; + int count = 0; + + oprintf(OUTPUT_VERBOSE, + gettext(" --->Trying to compose %d Concats within " + "a single HBA.\n"), nsubs); + + for (iter = hbas; + (count < nsubs) && (error == 0) && (iter != NULL); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + dlist_t *disks = NULL; + uint64_t space = 0; + + error = hba_get_avail_disks_and_space(request, hba, &disks, &space); + if ((error == 0) && (space >= (nsubs * nbytes))) { + + /* try composing nsubs concats all on this HBA */ + count = 0; + while ((count < nsubs) && (error == 0)) { + devconfig_t *concat = NULL; + dlist_t *item = NULL; + + error = populate_concat( + request, nbytes, disks, cursubs, &concat); + + if ((error == 0) && (concat != NULL)) { + item = dlist_new_item((void*)concat); + if (item == NULL) { + error = ENOMEM; + } else { + count++; + list = dlist_append(item, list, AT_TAIL); + error = remove_used_disks(&disks, concat); + } + } else if (concat == NULL) { + dlist_free_items(list, free_devconfig_object); + list = NULL; + break; + } + } + } + + dlist_free_items(disks, NULL); + } + + if (count < nsubs) { + print_layout_submirrors_failed_msg( + devconfig_type_to_str(TYPE_CONCAT), + count, nsubs); + + dlist_free_items(list, free_devconfig_object); + list = NULL; + } else { + *results = dlist_append(list, *results, AT_TAIL); + } + + return (error); +} + +/* + * FUNCTION: remove_used_disks(dlist_t **disks, devconfig_t *volume) + * + * INPUT: disks - pointer to a list of disks + * volume - pointer to a devconfig_t volume + * + * OUPUT: disks - pointer to new list of disks + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which updates the input list of disks by removing + * those which have slices used by the input volume. + * + * Constructs a new list containing only disks not used by + * the volume. + * + * The original list is freed. + */ +static int +remove_used_disks( + dlist_t **disks, + devconfig_t *volume) +{ + dlist_t *list = NULL; + dlist_t *iter = NULL; + dlist_t *item = NULL; + int error = 0; + + for (iter = *disks; (iter != NULL) && (error == 0); iter = iter->next) { + + dm_descriptor_t diskp = (uintptr_t)iter->obj; + boolean_t shares = B_FALSE; + + error = volume_shares_disk(diskp, volume, &shares); + if ((error == 0) && (shares != B_TRUE)) { + /* disk is unused */ + if ((item = dlist_new_item((void*)diskp)) == NULL) { + error = ENOMEM; + } else { + list = dlist_append(item, list, AT_TAIL); + } + } + } + + if (error != 0) { + dlist_free_items(list, NULL); + } else { + + /* free original disk list, return new list */ + dlist_free_items(*disks, NULL); + + *disks = list; + } + + return (error); +} + +/* + * FUNCTION: volume_shares_disk(dm_descriptor_t disk, + * devconfig_t *volume, boolean_t *shares) + * + * INPUT: disk - a dm_descriptor_t handle for the disk of interest + * volume - a devconfig_t pointer to a volume + * bool - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input disk has a slice that is used + * as a component by the input volume. + * + * If the disk contributes a slice component, bool is set + * to B_TRUE, B_FALSE otherwise. + */ +static int +volume_shares_disk( + dm_descriptor_t disk, + devconfig_t *volume, + boolean_t *shares) +{ + dlist_t *iter = NULL; + int error = 0; + + *shares = B_FALSE; + + /* look at all slices in the volume */ + for (iter = devconfig_get_components(volume); + (iter != NULL) && (*shares == B_FALSE) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + + if (devconfig_isA(dev, TYPE_SLICE)) { + + /* get disk for volume's slice */ + dm_descriptor_t odisk = NULL; + char *oname = NULL; + + ((error = devconfig_get_name(dev, &oname)) != 0) || + (error = get_disk_for_named_slice(oname, &odisk)); + + if (error == 0) { + if (compare_descriptor_names( + (void*)disk, (void*)odisk) == 0) { + /* otherslice is on same disk, stop */ + *shares = B_TRUE; + } + } + } + } + + return (error); +} + +/* + * FUNCTION: select_mpxio_hbas(dlist_t *hbas, dlist_t **mpxio_hbas) + * + * INPUT: hbas - pointer to a list of dm_descriptor_t HBA handles + * + * OUTPUT: mpxio_hbas - pointer to a new list of containing HBAs that + * are multiplex enabled. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Iterates the input list of HBAs and builds a new list + * containing those that are multiplex enabled. + * + * The output list should be passed to dlist_free_items() + * when no longer needed. + */ +static int +select_mpxio_hbas( + dlist_t *hbas, + dlist_t **mpxio_hbas) +{ + dlist_t *iter; + int error = 0; + + for (iter = hbas; (iter != NULL) && (error == 0); iter = iter->next) { + dm_descriptor_t hba = (uintptr_t)iter->obj; + boolean_t ismpxio = B_FALSE; + if ((error = hba_is_multiplex(hba, &ismpxio)) == 0) { + if (ismpxio == B_TRUE) { + dlist_t *item = dlist_new_item((void *)hba); + if (item != NULL) { + *mpxio_hbas = + dlist_append(item, *mpxio_hbas, AT_TAIL); + } else { + error = ENOMEM; + } + } + } + } + + if (error != 0) { + dlist_free_items(*mpxio_hbas, NULL); + *mpxio_hbas = NULL; + } + + return (error); +} + +/* + * FUNCTION: set_explicit_submirror_names(dlist_t *reqs, dlist_t *subs) + * + * INPUT: reqs - pointer to a list of request devconfig_ts + * subs - pointer to a list of volume devconfig_ts + * + * SIDEEFFECT: Modifies the volume names. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Iterates the lists of volumes and requests and calls + * set_explicit_mirror_name for each pair. + */ +static int +set_explicit_submirror_names( + dlist_t *reqs, + dlist_t *subs) +{ + int error = 0; + + while ((reqs != NULL) && (subs != NULL) && (error == 0)) { + + error = set_explicit_submirror_name( + (devconfig_t *)reqs->obj, + (devconfig_t *)subs->obj); + + reqs = reqs->next; + subs = subs->next; + } + + return (error); +} + +/* + * FUNCTION: set_explicit_submirror_name(dlist_t *req, dlist_t *sub) + * + * INPUT: req - pointer to a request devconfig_t + * sub - pointer to a volume devconfig_t + * + * SIDEEFFECT: Modifies the volume name. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Clears the volume's current name and returns the name + * to the available pool. + * + * If a name is specified in the request, the name is used + * as the volume's name. + * + * (Unnamed submirrors will have default names assigned + * during final mirror assembly.) + */ +static int +set_explicit_submirror_name( + devconfig_t *req, + devconfig_t *sub) +{ + char *name = NULL; + int error = 0; + + /* unset current submirror name */ + (void) devconfig_get_name(sub, &name); + release_volume_name(name); + (void) devconfig_set_name(sub, ""); + + if (devconfig_get_name(req, &name) != ERR_ATTR_UNSET) { + (void) devconfig_set_name(sub, name); + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_mirror.h b/usr/src/cmd/lvm/metassist/layout/layout_mirror.h new file mode 100644 index 0000000000..36c5b5a8bf --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_mirror.h @@ -0,0 +1,53 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_MIRROR_H +#define _LAYOUT_MIRROR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" +#include "volume_dlist.h" + +extern int layout_mirror( + devconfig_t *request, + uint16_t nsubs, + uint64_t nbytes, + dlist_t **results); + +extern int populate_explicit_mirror( + devconfig_t *req, + dlist_t **results); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_MIRROR_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_request.c b/usr/src/cmd/lvm/metassist/layout/layout_request.c new file mode 100644 index 0000000000..bebd3c8be9 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_request.c @@ -0,0 +1,3418 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <string.h> +#include <libintl.h> + +#include "volume_error.h" +#include "volume_defaults.h" +#include "volume_dlist.h" +#include "volume_output.h" +#include "volume_request.h" + +#include "layout_device_cache.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_validate.h" + +#define _LAYOUT_REQUEST_C + +static char *_request_diskset = NULL; +static devconfig_t *_toplevel_request = NULL; +static defaults_t *_defaults = NULL; + +/* + * This file contains code which handles various aspects of the + * request and defaults devconfig_t structs passed to the layout + * module. + * + * Functions are provided which determine what devices are available + * for use by the various volume layout mechanisms. These are based + * on the user specified available/unavailable devices included in + * a request or in the defaults associated with the destination diskset. + */ + +/* + * A struct to hold device "specifications" extracted from a user + * specified device name. This struct is used to compare the user's + * available and unavailable device specifications against physical + * devices attached to the system. + * + * The spec struct holds one of two different specifications: if the + * user supplied device name is parsable as a CTD name, it is parsed + * into the component ids. Otherwise, it is stored as is. + * + * The CTD name space implies a device hierarchy and metassist + * supports an implied wildcarding scheme for the CTD name space. + * A CTD specification from the user is of the form cX, cXdX, + * cXdXsX, cXtX, cXtXdX, or cXtXdXsX, so it may or may nor + * correspond to an individual physical device depending on + * the context. + * + * For example, "c1" can mean the controller/HBA with the + * name "c1" or it can mean all devices attached to the + * controller named "c1". + * + * The ctd specs make matching physical devices against a + * user specification easier since the matching is based on + * the numeric values extracted from the cXtXdXsX string + * and not on the strings themselves. The strings are + * troublesome because of situations like "c1" being + * compared to "c11t1d0s0" and getting false matches. + * + * The ID_UNSPECIFIED value is used to flag components + * that were not in the CTD name: + * + * "c3" -> { ctrl=3, target=ID_UNSPECIFIED, + * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } + * + * "c3t2" -> { ctrl=3, target=2, + * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } + */ + +#define ID_UNSPECIFIED -1 +typedef struct { + int ctrl; + int target; + int lun; + int slice; + boolean_t is_ide; +} ctd_spec_t; + +typedef enum { + SPEC_TYPE_CTD = 0, + SPEC_TYPE_RAW, + SPEC_TYPE_OTHER +} spec_type_t; + +typedef struct { + spec_type_t type; + union { + ctd_spec_t *ctd; + char *raw; + } data; +} device_spec_t; + +static int get_spec_for_name( + char *name, + device_spec_t **id); + +static int create_device_spec( + char *name, + device_spec_t **spec); + +static int create_device_ctd_spec( + char *name, + device_spec_t **spec); + +static int create_device_raw_spec( + char *name, + device_spec_t **spec); + +static void destroy_device_spec( + device_spec_t *spec); + +static boolean_t ctd_spec_includes_device( + device_spec_t *spec, + device_spec_t *device); + +static boolean_t raw_spec_includes_device( + device_spec_t *spec, + device_spec_t *device); + +/* + * get_spec_for_name builds up a cached mapping of device + * names to the corresponding device_spec_t structs. + * + * This saves repeatedly converting the device names, which + * could get expensive since devices are checked against the + * user specified available/unavailable devices a lot. + * + * The cache is implemented as a list of these structs: + */ +typedef struct { + + char *name; + device_spec_t *device_spec; + +} spec_cache_t; + +static dlist_t *_spec_cache = NULL; + +static int destroy_spec_cache(); +static int compare_name_to_spec_cache_name( + void *name, void *list_item); + +/* + * The user specified available/unavailable devices are + * accessed frequently during layout. To make this more + * efficient, the char *arrays of available/unavailable + * specifications for a request or defaults devconfig_t + * object are converted to device_spec_ts the first time + * they're accessed and then cached using this struct: + */ +typedef struct { + + devconfig_t *request; + + /* + * avail_specs_list is a list of device spec_t + * corresponding to available devices specified + * in the request object + */ + dlist_t *avail_specs_list; + + /* + * unavail_specs_list is a list of device spec_t + * corresponding to unavailable devices specified + * in the request object + */ + dlist_t *unavail_specs_list; + +} request_spec_list_t; + +dlist_t *_request_spec_list_cache = NULL; + +static int destroy_request_spec_list_cache(); +static void destroy_request_spec_list_entry(void *obj); + +static int compare_request_to_request_spec_list_request( + void *object, + void *list_item); + +static int convert_usernames_to_specs( + char **specs, + dlist_t **list); + +/* other private functions */ +static int is_device_avail( + dm_descriptor_t desc, + devconfig_t *request, + boolean_t *avail); + +static int is_named_device_avail( + devconfig_t *request, + char *device_name, + boolean_t check_aliases, + boolean_t *avail); + +static int avail_list_includes_device_name( + dlist_t *list, + char *device_name, + boolean_t check_aliases, + boolean_t *includes); + +static int unavail_list_includes_device_name( + dlist_t *list, + char *device_name, + boolean_t check_aliases, + boolean_t *includes); + +static int spec_includes_device_name( + device_spec_t *spec, + char *device_name, + boolean_t check_aliases, + boolean_t *includes); + +static boolean_t spec_includes_device( + device_spec_t *spec, + device_spec_t *device); + +static int disk_get_avail_space( + devconfig_t *request, + dm_descriptor_t disk, + uint64_t *avail); + +static int compare_hba_n_avail_disks( + void *obj1, + void *obj2); + +/* + * FUNCTION: release_request_caches() + * + * RETURNS: 0 + * + * PURPOSE: cleanup the module private caches. + */ +int +release_request_caches() +{ + (void) destroy_request_spec_list_cache(); + (void) destroy_spec_cache(); + + return (0); +} +/* + * FUNCTION: int set_request_diskset(char *) + * + * INPUT: char * - pointer to the diskset name + * OUTPUT: 0 - success + * !0 - validation failure + * RETURNS: + * + * PURPOSE: set the module global diskset name. + */ +int +set_request_diskset( + char *dsname) +{ + _request_diskset = dsname; + + if (dsname == NULL || dsname[0] == '\0') { + volume_set_error( + gettext("No disk set specified in request\n")); + return (-1); + } + + return (0); +} + +/* + * FUNCTION: char *get_request_diskset() + * + * INPUT: none - + * OUTPUT: none - + * RETURNS: char * - pointer to the currently set diskset name + * + * PURPOSE: get the global name of the current diskset. + */ +char * +get_request_diskset() +{ + return (_request_diskset); +} + +/* + * FUNCTION: void unset_request_diskset() + * + * PURPOSE: unset the module global diskset name. + */ +void +unset_request_diskset( + char *dsname) +{ + _request_diskset = NULL; +} + +/* + * FUNCTION: int set_toplevel_request(devconfig_t *) + * + * INPUT: devconfig_t * - pointer to the diskset request + * OUTPUT: 0 - success + * !0 - validation failure + * RETURNS: + * + * PURPOSE: set the module global toplevel request struct. + * this will be set within the only public entry + * point to the module -- get_layout() + * + * SIDEEFFECT: The devconfig_t's list of available and unavailable + * devices will be validated. + */ +int +set_toplevel_request( + devconfig_t *req) +{ + _toplevel_request = req; + + return (validate_request_avail_unavail(req)); +} + +/* + * FUNCTION: void unset_toplevel_request() + * + * PURPOSE: unset the layout module global toplevel request struct. + * + */ +void +unset_toplevel_request() +{ + _toplevel_request = NULL; +} + +/* + * FUNCTION: int set_defaults(devconfig_t *) + * + * INPUT: devconfig_t * - pointer to the global defaults devconfig_t + * OUTPUT: 0 - success + * !0 - validation failure + * RETURNS: + * + * PURPOSE: set the module global defaults struct. + * this will be set within the only public entry + * point to the module -- get_layout() + * + * SIDEEFFECT: The devconfig_t's list of available and unavailable + * devices will be validated. + */ +int +set_request_defaults( + defaults_t *defaults) +{ + int error = 0; + devconfig_t *diskset = NULL; + + _defaults = defaults; + + if ((error = defaults_get_diskset_by_name( + _defaults, get_request_diskset(), &diskset)) == 0) { + + error = validate_request_avail_unavail(diskset); + + } else if (error == ENOENT) { + /* no defaults to verify */ + error = 0; + } + + return (error); +} + +/* + * FUNCTION: void unset_request_defaults() + * + * PURPOSE: unset the layout module global defaults struct. + * + */ +void +unset_request_defaults() +{ + _defaults = NULL; +} + +/* + * FUNCTION: get_stripe_min_comp(devconfig_t *req, uint16_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint64_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the minimum of components + * for striped volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_stripe_min_comp( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_stripe_mincomp( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_stripe_max_comp(devconfig_t *req, uint16_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint64_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the maximum number of components + * for striped volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_stripe_max_comp( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_stripe_maxcomp( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_stripe_interlace(devconfig_t *req, uint64_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint64_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the interlace value for striped + * volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + * + * If no value is explictly specified, ERR_ATTR_UNSET is + * returned. + */ +int +get_stripe_interlace( + devconfig_t *req, + uint64_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_stripe_interlace(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + error = 0; + } + + if (*val == 0) { + if ((error = defaults_get_stripe_interlace( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_mirror_read_strategy(devconfig_t *req, + * mirror_read_strategy_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a mirror_read_strategy_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the write strategy mirrored volumes + * should have for volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + * + * If no value is explictly specified, ERR_ATTR_UNSET is + * returned. + */ +int +get_mirror_read_strategy( + devconfig_t *req, + mirror_read_strategy_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_mirror_read(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_mirror_read( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_mirror_write_strategy(devconfig_t *req, + * mirror_write_strategy_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a mirror_write_strategy_t to hold result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the write strategy mirrored volumes + * should have for volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + * + * If no value is explictly specified, ERR_ATTR_UNSET is + * returned. + */ +int +get_mirror_write_strategy( + devconfig_t *req, + mirror_write_strategy_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_mirror_write(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_mirror_write( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_mirror_pass(devconfig_t *req, uint16_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint16_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the resync pass mirrored volumes + * should have for volumes satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + * + * If no value is explictly specified, ERR_ATTR_UNSET is + * returned. + */ +int +get_mirror_pass( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_mirror_pass(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_mirror_pass( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_mirror_nsubs(devconfig_t *req, uint16_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint16_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines how many submirrors mirrored + * volumes should have for volumes satisfying the input + * request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_mirror_nsubs( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_mirror_nsubs( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_volume_faultrecov(devconfig_t *req, boolean_t *val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a boolean_t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines whether data redundant volumes + * should also have fault recovery (e.g., HSPs) for volumes + * satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_volume_faultrecov( + devconfig_t *req, + boolean_t *val) +{ + int error = 0; + + *val = B_FALSE; + + if ((error = devconfig_get_volume_usehsp(req, val)) != 0) { + if (error == ERR_ATTR_UNSET) { + component_type_t type = TYPE_UNKNOWN; + (void) devconfig_get_type(req, &type); + + switch (type) { + case TYPE_MIRROR: + error = defaults_get_mirror_usehsp( + _defaults, get_request_diskset(), val); + break; + + case TYPE_STRIPE: + error = defaults_get_stripe_usehsp( + _defaults, get_request_diskset(), val); + break; + + case TYPE_CONCAT: + error = defaults_get_concat_usehsp( + _defaults, get_request_diskset(), val); + break; + + case TYPE_VOLUME: + error = defaults_get_volume_usehsp( + _defaults, get_request_diskset(), val); + break; + } + } + } + + return (error); +} + +/* + * FUNCTION: get_volume_redundancy_level(devconfig_t *req, uint16_t val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint16-t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the appropriate level of data + * redundancy a volume should have for volumes satisfying + * the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_volume_redundancy_level( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_volume_redundancy_level( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_volume_npaths(devconfig_t *req, uint16_t val) + * INPUT: req - a devconfig_t pointer to the current request + * val - pointer to a uint16-t to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the appropriate level of datapath + * redundancy a slice component should have for volumes + * satisfying the input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_volume_npaths( + devconfig_t *req, + uint16_t *val) +{ + int error = 0; + + *val = 0; + + if ((error = devconfig_get_volume_npaths(req, val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + + if (*val == 0) { + if ((error = defaults_get_volume_npaths( + _defaults, get_request_diskset(), val)) != 0) { + if (error != ERR_ATTR_UNSET) { + return (error); + } + } + } + + return (error); +} + +/* + * FUNCTION: get_default_hsp_name(devconfig_t *req, char **hspname) + * INPUT: req - a devconfig_t pointer to the current request + * hspname - pointer to a char * to hold the result, if any + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which determines the default HSP name for the + * input request. + * + * The value to use is taken from the input request, the + * toplevel diskset request, the diskset defaults or the + * global defaults. + */ +int +get_default_hsp_name( + devconfig_t *req, + char **name) +{ + int error = 0; + + *name = NULL; + + if ((error = defaults_get_hsp_name(_defaults, + get_request_diskset(), name)) != 0) { + if (error != ENOENT) { + return (error); + } + error = 0; + } + + return (error); +} + +/* + * FUNCTION: slice_is_available(char *sname, devconfig_t *request, + * boolean_t bool) + * INPUT: sname - a slice name + * request - pointer to a devconfig_t struct representing + * the current layout request being processed + * bool - pointer to a boolean to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: Validation helper which determines if the named slice can + * be used as a volume component when satisfying the input + * request. + * + * Check if the slice appears in the known slice list, + * then check the request's available and unavailable + * device specifications. + */ +int +slice_is_available( + char *sname, + devconfig_t *request, + boolean_t *bool) +{ + dm_descriptor_t slice = (dm_descriptor_t)0; + int error = 0; + + *bool = B_FALSE; + + if ((error = slice_get_by_name(sname, &slice)) != 0) { + return (error); + } + + if (slice == (dm_descriptor_t)0) { + /* no slice found */ + return (ENODEV); + } + + if (error == 0) { + error = is_named_device_avail(request, sname, B_TRUE, bool); + } + + return (error); +} + +/* + * FUNCTION: get_disks_for_target(char *name, dlist_t **disks) + * + * INPUT: name - a char* device CTD name + * + * OUTPUT: disks - disks matching the input target name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Validation helper function which finds all disks "on" the + * input target. + * + * The input name is assumed to be a target name, cXtX, and + * the list of known disks is searched to find any disk that + * looks to be "on" that target. + * + * "On" is determined by comparing a disk's name and + * aliases to the target to see if they match. + */ +int +get_disks_for_target( + char *name, + dlist_t **disks) +{ + int error = 0; + device_spec_t *targetid = NULL; + + error = get_spec_for_name(name, &targetid); + if (error == 0) { + dlist_t *known_disks = NULL; + dlist_t *iter = NULL; + + get_known_disks(&known_disks); + for (iter = known_disks; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + device_spec_t *diskid = NULL; + char *diskname = NULL; + dlist_t *diskaliases = NULL; + dlist_t *item; + + ((error = get_display_name(disk, &diskname)) != 0) || + (error = get_aliases(disk, &diskaliases)) || + (error = get_spec_for_name(diskname, &diskid)); + + if (error == 0) { + if (spec_includes_device(targetid, diskid) == B_TRUE) { + /* add disk */ + if ((item = dlist_new_item((void *)disk)) == NULL) { + error = ENOMEM; + } else { + *disks = dlist_append(item, *disks, AT_HEAD); + } + } else { + /* check disk's aliases */ + dlist_t *iter2; + for (iter2 = diskaliases; + (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + + char *aliasname = NULL; + device_spec_t *aliasid = NULL; + error = get_display_name(disk, &aliasname); + error = get_spec_for_name(aliasname, &aliasid); + + if (spec_includes_device( + targetid, aliasid) == B_TRUE) { + + /* alias matched, add disk */ + item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + *disks = + dlist_append(item, *disks, AT_HEAD); + } + } + } + } + } + } + } + + return (error); +} + +/* + * FUNCTION: select_hbas_with_n_disks(devconfig_t *request, + * dlist_t *hbas, int mindisks, dlist_t **selhbas, + * dlist_t **seldisks) + * + * INPUT: request - pointer to a devconfig_t struct representing + * the current layout request being processed + * hbas - pointer to a list of HBAs + * mindisks - minimum number of disks required on the HBAs + * + * OUTPUT: selhbas - pointer to a list containing the HBAs with at + * least mindisks available disks. + * seldisks - pointer to a list containing the available disks + * for the HBAs in selhbas + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which counts the number of available disks associated + * with each of the input HBAs and adds those that have at + * least mindisks to the output list. + * + * Only available disks that have available space are counted. + * + * Disks connected thru multiple HBAs are only counted for + * the first HBA they're accessed through. + * + * The list of HBAs returned will be in descending order, + * i.e., HBAs with more disks come before those with fewer. + * + * The returned lists of HBAs and disks must be passed to + * dlist_free_items() to recover the space allocated to hold + * each list item. + * + * for (each HBA) { + * + * select HBA + * get available disks on HBA + * + * for (each disk) { + * if (disk is not in selected disk list) + * add it to the list + * else + * count it as a distinct disk on this HBA + * } + * + * if (this HBA has >= mindisks distinct disks) + * add this HBA to the list of returned HBAs + * + * } + */ +int +select_hbas_with_n_disks( + devconfig_t *request, + dlist_t *hbas, + int mindisks, + dlist_t **selhbas, + dlist_t **seldisks) +{ + dlist_t *iter = NULL; + int error = 0; + + *selhbas = NULL; + *seldisks = NULL; + + /* for each input HBA */ + for (iter = hbas; (error == 0) && (iter != NULL); iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + dlist_t *iter2 = NULL; + dlist_t *disks = NULL; + uint64_t space = 0; + uint16_t ndistinct = 0; + + error = hba_get_avail_disks_and_space(request, hba, &disks, &space); + + /* for each of this HBA's disks */ + for (iter2 = disks; + (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + + dm_descriptor_t disk = (uintptr_t)iter2->obj; + + /* unique disk? has it been seen thru some other HBA? */ + if (dlist_contains(*seldisks, (void *)disk, + compare_descriptor_names) != B_TRUE) { + + /* distinct, add to list of all_distinct */ + dlist_t *item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + + *seldisks = + dlist_append(item, *seldisks, AT_HEAD); + + /* increment this HBA's distinct disk count */ + ++ndistinct; + } + } + } + + if (ndistinct >= mindisks) { + + /* this HBA has minimum # of disks, add to output list */ + dlist_t *item = dlist_new_item((void *)hba); + if (item == NULL) { + error = ENOMEM; + } else { + *selhbas = + dlist_insert_ordered( + item, *selhbas, DESCENDING, + compare_hba_n_avail_disks); + + /* save # of disks for ordering the list */ + hba_set_n_avail_disks(hba, ndistinct); + } + } + + dlist_free_items(disks, NULL); + } + + if (error != 0) { + oprintf(OUTPUT_TERSE, + gettext("failed selecting HBAs with n disks: %d\n"), + error); + + dlist_free_items(*selhbas, NULL); + *selhbas = NULL; + dlist_free_items(*seldisks, NULL); + *seldisks = NULL; + } + + return (error); +} + +/* + * FUNCTION: hba_get_avail_disks_and_space(devconfig_t *request, + * dm_descriptor_t hba, dlist_t **disks, uint64_t *space) + * + * INPUT: request - pointer to a devconfig_t struct representing + * the current layout request being processed + * hba - dm_descriptor_t handle for an HBA + * + * OUTPUT: disks - pointer to a list to hold the computed available + * disks + * avail - pointer to a uint64_t to hold the aggregate + * available space on the available disks + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which examines the disks associated with the + * input HBA and assembles a list of those that are available. + * + * Available is defined as being in the usable list, having + * unused space and not specifically excluded by the request's + * list of unavailable devices. + * + * The returned list must be passed to dlist_free_items() + * to recover the memory allocated to hold each list item. + */ +int +hba_get_avail_disks_and_space( + devconfig_t *request, + dm_descriptor_t hba, + dlist_t **disks, + uint64_t *space) +{ + dlist_t *usable_disks = NULL; + dlist_t *iter = NULL; + int error = 0; + + *disks = NULL; + + /* for each usable disk */ + error = get_usable_disks(&usable_disks); + for (iter = usable_disks; + (error == 0) && (iter != NULL); + iter = iter->next) { + + dm_descriptor_t disk = (uintptr_t)iter->obj; + boolean_t avail = B_FALSE; + dlist_t *hbas = NULL; + + /* is disk attached to HBA in question? */ + error = disk_get_hbas(disk, &hbas); + if (error != 0) { + continue; + } + + if (dlist_contains(hbas, (void *)hba, + compare_descriptor_names) == B_TRUE) { + + /* is disk available? */ + error = is_device_avail(disk, request, &avail); + if ((error == 0) && (avail == B_TRUE)) { + uint64_t disk_space = 0; + + /* does disk have available space? */ + error = disk_get_avail_space(request, disk, &disk_space); + if ((error == 0) && (disk_space > 0)) { + + dlist_t *item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + *disks = dlist_append(item, *disks, AT_HEAD); + } + + *space += disk_space; + } + } + } + + dlist_free_items(hbas, NULL); + } + + if (error != 0) { + dlist_free_items(*disks, NULL); + *disks = NULL; + } + + return (error); +} + +/* + * FUNCTION: disk_get_avail_space(devconfig_t *request, + * dlist_t *disks, uint64_t space) + * + * INPUT: request - pointer to a devconfig_t struct representing + * the current layout request being processed + * disks - pointer to a list of disks + * space - pointer to a uint64_t to hold the computed available + * space + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which iterates the input list of disks and determines + * the aggregate amount of available space they represent. + * + * Only disk slices that are in the usable slice list and not + * specifically excluded by the request's list of unavailable + * devices will contribute to the aggregate space computation. + */ +static int +disk_get_avail_space( + devconfig_t *request, + dm_descriptor_t disk, + uint64_t *space) +{ + dlist_t *usable_slices = NULL; + dlist_t *iter = NULL; + int error = 0; + + *space = 0; + + /* for each usable slice */ + error = get_usable_slices(&usable_slices); + for (iter = usable_slices; + (error == 0) && (iter != NULL); + iter = iter->next) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + dm_descriptor_t slice_disk; + boolean_t avail = B_FALSE; + boolean_t reserved = B_FALSE; + boolean_t used = B_FALSE; + + /* is slice on disk in question? */ + if (((error = slice_get_disk(slice, &slice_disk)) != 0) || + (compare_descriptor_names((void *)slice_disk, + (void *)disk) != 0)) { + continue; + } + + /* is slice reserved by an explicit layout request? */ + if (((error = is_reserved_slice(slice, &reserved)) != 0) || + (reserved == B_TRUE)) { + continue; + } + + /* is slice used by a pending layout request? */ + if (((error = is_used_slice(slice, &used)) != 0) || + (used == B_TRUE)) { + continue; + } + + /* is slice available? */ + if (((error = is_device_avail(slice, request, &avail)) == 0) && + (avail == B_TRUE)) { + + /* does slice have usable space? */ + uint64_t size = 0; + if ((error = slice_get_size(slice, &size)) == 0) { + *space += size; + } + } + } + + return (error); +} + +/* + * FUNCTION: disks_get_avail_slices(devconfig_t *request, + * dlist_t *disks, dlist_t **slices) + * + * INPUT: request - pointer to a devconfig_t struct representing + * the current layout request being processed + * disks - pointer to a list of disks + * slices - pointer to an output list of disks + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: helper which iterates the input list of disks and builds a + * new list which contains disks that are determined to be + * available for satisfying the input request. + * + * A disk must contain at least one slice in the available + * slice list as well as have available space in order + * to be available. + */ +int +disks_get_avail_slices( + devconfig_t *request, + dlist_t *disks, + dlist_t **slices) +{ + dlist_t *usable_slices = NULL; + dlist_t *iter = NULL; + int error = 0; + + *slices = NULL; + + /* for each usable slice */ + error = get_usable_slices(&usable_slices); + for (iter = usable_slices; + (error == 0) && (iter != NULL); + iter = iter->next) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + dm_descriptor_t disk = (dm_descriptor_t)0; + boolean_t avail = B_FALSE; + boolean_t reserved = B_FALSE; + boolean_t used = B_FALSE; + + /* is slice on a disk in the input list? */ + if (((error = slice_get_disk(slice, &disk)) != 0) || + (dlist_contains(disks, (void *)disk, + compare_descriptor_names) != B_TRUE)) { + continue; + } + + /* is slice reserved by an explicit layout request? */ + if (((error = is_reserved_slice(slice, &reserved)) != 0) || + (reserved == B_TRUE)) { + continue; + } + + /* is slice used by a pending layout request? */ + if (((error = is_used_slice(slice, &used)) != 0) || + (used == B_TRUE)) { + continue; + } + + /* is slice available? */ + if (((error = is_device_avail(slice, request, &avail)) == 0) && + (avail == B_TRUE)) { + + /* does slice have available space? */ + uint64_t size = 0; + error = slice_get_size(slice, &size); + if ((error == 0) && (size > 0)) { + dlist_t *item = dlist_new_item((void *)slice); + if (item == NULL) { + error = ENOMEM; + } else { + *slices = dlist_append(item, *slices, AT_TAIL); + } + } + } + } + + if (error != 0) { + dlist_free_items(*slices, NULL); + *slices = NULL; + } + + return (error); +} + + +/* + * FUNCTION: get_hbas_and_disks_used_by_volumes(dlist_t *volumes, + * dlist_t **hbas, dlist_t **disks) + * + * INPUT: volumes - pointer to a list of devconfig_t volumes + * + * OUTPUT: hbas - a list of HBAs utilized by the input volumes + * disks - a list of disks utilized by the input volumes + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: An aggregate list of HBAs and disks used by the input volumes + * is built up by iterating the list of volumes and calling + * get_hbas_disks_used_by_volume() to determine the HBAs and disk + * used by each volume. + * + * The returned lists of HBAs and disks may contain duplicates. + */ +int +get_hbas_and_disks_used_by_volumes( + dlist_t *volumes, + dlist_t **hbas, + dlist_t **disks) +{ + dlist_t *iter = NULL; + int error = 0; + + for (iter = volumes; + (iter != NULL) && (error == 0); + iter = iter->next) { + error = get_hbas_and_disks_used_by_volume( + (devconfig_t *)iter->obj, hbas, disks); + } + + return (error); +} + +/* + * FUNCTION: get_hbas_and_disks_used_by_volume(devconfig_t *volume, + * dlist_t **hbas, dlist_t **disks) + * + * INPUT: volume - pointer to a devconfig_t volume + * + * OUTPUT: hbas - a list of HBAs updated to include those utilized + * by the input volume + * disks - a list of disks updated to inlclude those utilized + * by the input volume + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: The volume's components are iterated and the disks and HBAs + * for each component are determined and appended to the input + * lists of HBAs and disks. + * + * The returned lists of HBAs and disks may contain duplicates. + */ +int +get_hbas_and_disks_used_by_volume( + devconfig_t *volume, + dlist_t **hbas, + dlist_t **disks) +{ + dlist_t *iter = NULL; + int error = 0; + + for (iter = devconfig_get_components(volume); + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + if (devconfig_isA(dev, TYPE_SLICE)) { + + dm_descriptor_t disk = NULL; + char *name = NULL; + + /* get disk for component slice */ + ((error = devconfig_get_name(dev, &name)) != 0) || + (error = get_disk_for_named_slice(name, &disk)); + if (error == 0) { + dlist_t *item = dlist_new_item((void *)disk); + if (item == NULL) { + error = ENOMEM; + } else { + *disks = dlist_append(item, *disks, AT_HEAD); + } + } + + /* get HBAs for disk */ + if (error == 0) { + dlist_t *disk_hbas = NULL; + if ((error = disk_get_hbas(disk, &disk_hbas)) == 0) { + /* the hba list may contain dups, but that's ok */ + *hbas = dlist_append(disk_hbas, *hbas, AT_HEAD); + } + } + + } else if (devconfig_isA(dev, TYPE_MIRROR)) { + + /* collect info for submirrors */ + dlist_t *iter1; + for (iter1 = devconfig_get_components(dev); + (iter1 != NULL) && (error == 0); + iter1 = iter1->next) { + error = get_hbas_and_disks_used_by_volume( + (devconfig_t *)iter1->obj, hbas, disks); + } + + } + } + + return (error); +} + +/* + * FUNCTION: compare_hba_n_avail_disks(void *obj1, void *obj2) + * + * INPUT: obj1 - opaque pointer + * obj2 - opaque pointer + * + * RETURNS: int - <0 - if obj1 has fewer available disks than obj2 + * 0 - if obj1 has the same # of available disks as obj2 + * >0 - if obj1 has more available disks than obj2 + * + * PURPOSE: dlist_t helper which compares the number of available disks + * for two HBAs represented as dm_descriptor_t handles. + * + * Both input objects are assumed to be dm_descriptor_t handles. + * + * The number of available disks associated with the HBAs was + * computed and saved in select_hbas_with_n_disks(), this + * function just checks the saved values. + */ +static int +compare_hba_n_avail_disks( + void *obj1, + void *obj2) +{ + uint16_t n1 = 0; + uint16_t n2 = 0; + + assert(obj1 != NULL); + assert(obj2 != NULL); + + (void) hba_get_n_avail_disks((uintptr_t)obj1, &n1); + (void) hba_get_n_avail_disks((uintptr_t)obj2, &n2); + + return ((int)n1 - n2); +} + +/* + * FUNCTION: is_device_avail(dm_descriptor_t desc, + * devconfig_t *request, boolean_t *avail) + * + * INPUT: desc - a dm_descriptor_t device handle + * request - pointer to a devconfig_t struct representing + * the current layout request being processed + * avail - pointer to a boolean to hold the result + * + * RETURNS: int - 0 - on success + * !0 - otherwise + * + * PURPOSE: Internal helper which determines if the input device can + * be used as a volume component when satisfying the input + * request. + * + * The device is assumed to be a known valid device. + * + * The function checks if the device passes the request's + * available and unavailable device specifications. + * + * The input device name may be either a DID name or a CTD + * name. All name comparisons are done using the CTD name. + */ +static int +is_device_avail( + dm_descriptor_t desc, + devconfig_t *request, + boolean_t *avail) +{ + char *name = NULL; + int error = 0; + + *avail = B_FALSE; + + if ((error = get_display_name(desc, &name)) == 0) { + error = is_named_device_avail(request, name, B_TRUE, avail); + } + + return (error); +} + +/* + * FUNCTION: compare_request_to_request_spec_list_request( + * void *request, void *list_item) + * + * INPUT: request - opaque pointer to a devconfig_t + * list_item - opaque pointer to a request_spec_list_t + * + * RETURNS: int - 0 - if request is the same as list_item->request + * !0 - otherwise + * + * PURPOSE: dlist_t helper which compares the input request pointer + * to the list_item's request pointer for equality. + * + * This function is the lookup mechanism for the lists of + * cached device_spec_ts representing available/unavailable + * devices for a given defaults_t request/defaults struct. + * + * The defaults_t struct pointer is the lookup key. + */ +static int +compare_request_to_request_spec_list_request( + void *request, + void *list_item) +{ + request_spec_list_t *entry = + (request_spec_list_t *)list_item; + + assert(request != NULL); + assert(entry != NULL); + + /* compare two devconfig_t pointers, if identical, return 0 */ + return ((devconfig_t *)request != entry->request); +} + +/* + * FUNCTION: compare_device_spec_specificity(void *spec1, void *spec2) + * + * INPUT: spec1 - opaque pointer to a device_spec_t + * spec2 - opaque pointer to a device_spec_t + * + * RETURNS: int - <0 - if spec1 is less specific than spec2 + * 0 - if spec1 is as specific than spec2 + * >0 - if spec1 is more specific than spec2 + * + * PURPOSE: dlist_t helper which compares the level of specificity + * in the two input device_spec_t structs. The one + * which specifies more "components" of a cXtXdXsX device + * name is considered more specific. + */ +static int +compare_device_spec_specificity( + void *spec1, + void *spec2) +{ + if (spec1 == NULL || spec2 == NULL) { + return (-1); + } + + if ((((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED) && + (((device_spec_t *)spec2)->data.ctd->slice == ID_UNSPECIFIED)) { + /* spec1 has slice, spec2 does not, spec1 more specific */ + return (1); + } + + if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->slice == ID_UNSPECIFIED)) { + /* spec2 has slice, spec1 does not, spec2 more specific */ + return (-1); + } + + if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED)) { + /* both spec1 and spec2 have slice */ + return (0); + } + + if ((((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED) && + (((device_spec_t *)spec2)->data.ctd->lun == ID_UNSPECIFIED)) { + /* spec1 has lun, spec2 does not, spec1 more specific */ + return (1); + } + + if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->lun == ID_UNSPECIFIED)) { + /* spec2 has lun, spec1 does not, spec2 more specific */ + return (-1); + } + + if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED)) { + /* both spec1 and spec2 have lun */ + return (0); + } + + if ((((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED) && + (((device_spec_t *)spec2)->data.ctd->target == ID_UNSPECIFIED)) { + /* spec1 has target, spec2 does not, spec1 more specific */ + return (1); + } + + if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->target == ID_UNSPECIFIED)) { + /* spec2 has target, spec1 does not, spec2 more specific */ + return (-1); + } + + if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && + (((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED)) { + /* both spec1 and spec2 have target */ + return (0); + } + + /* both specify just ctrl */ + return (0); +} + +/* + * FUNCTION: find_request_spec_list_entry(devconfig_t *request) + * + * INPUT: request - pointer to a devconfig_t struct + * + * RETURNS: request_spec_list_entry - pointer to a + * request_spec_list_entry struct + * + * PURPOSE: Lookup function which encapsulates the details of locating + * the device_spec_list_t cache entry for the input request. + */ +static request_spec_list_t * +find_request_spec_list_entry( + devconfig_t *request) +{ + dlist_t *list_item = NULL; + request_spec_list_t *entry = NULL; + + list_item = dlist_find( + _request_spec_list_cache, + (void *)request, + compare_request_to_request_spec_list_request); + + if (list_item != NULL) { + entry = (request_spec_list_t *)list_item->obj; + } + + return (entry); +} + +/* + * FUNCTION: add_request_spec_list_entry(devconfig_t *request, + * char **avail_device_specs, char **unavail_device_specs, + * request_spec_list_entry_t **entry) + * + * INPUT: entry - pointer to the request_spec_list_entry struct to be + * added to the cache. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which encapsulates the details of adding a + * device_spec_list_t cache entry. + */ +static int +add_request_spec_list_entry( + request_spec_list_t *entry) +{ + dlist_t *list_item = dlist_new_item((void *)entry); + + if (list_item == NULL) { + return (ENOMEM); + } + + _request_spec_list_cache = dlist_append(list_item, + _request_spec_list_cache, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: make_request_spec_list_entry(devconfig_t *request, + * char **avail_device_specs, char **unavail_device_specs, + * request_spec_list_entry_t **entry) + * + * INPUT: request - pointer to a devconfig_t struct + * avail_device_specs - char * array of user specified available + * devices associated with the input request + * unavail_device_specs - char * array of user specified + * unavailable devices associated with the input + * request + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which encapsulates the details of generating a new + * the device_spec_list_t cache entry for the input request + * and its lists of avail/unavail devices. + * + * Converts the input arrays of (un)available device names into + * equivalent lists of device_spec_t structs. + * + * Creates a new cache entry, populates it and adds it to the + * cache. + */ +static int +make_request_spec_list_entry( + devconfig_t *request, + char **avail_device_specs, + char **unavail_device_specs, + request_spec_list_t **entry) +{ + int error = 0; + dlist_t *list = NULL; + + *entry = calloc(1, sizeof (request_spec_list_t)); + if (*entry == NULL) { + return (ENOMEM); + } + + (*entry)->request = request; + + /* + * map the avail_device_name array into a list of device_spec_t + * and save the list as the entry's available list + */ + error = convert_usernames_to_specs( + avail_device_specs, &list); + + if (error == 0) { + (*entry)->avail_specs_list = list; + } + + /* + * map the unavail_device_name array into a list of device_spec_t + * and save the list as the entry's unavailable list + */ + list = NULL; + error = convert_usernames_to_specs( + unavail_device_specs, &list); + + if (error == 0) { + (*entry)->unavail_specs_list = list; + } + + if (error != 0) { + /* delete the partial entry */ + destroy_request_spec_list_entry((void *)*entry); + *entry = NULL; + } + + return (error); +} + +/* + * FUNCTION: convert_usernames_to_specs(char **specs, dlist_t **list) + * + * INPUT: specs - char * array of device CTD names + * + * OUTPUT: list - pointer to a list of device_spec_t corresponding + * to each name in the input array + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which converts the input CTD device names to the + * equivalent device_spec_t structs. + * + * Iterates the input array and converts each CTD name to a + * device_spec_t using get_spec_for_name(). + */ +static int +convert_usernames_to_specs( + char **specs, + dlist_t **list) +{ + int i = 0; + int error = 0; + + /* + * For each spec in the array, get the corresponding + * device_spec_t and add it to the list. + * + * Any spec in the array that looks to be a DID name + * is first converted to its equivalent CTD name. + */ + for (i = 0; + (specs != NULL) && (specs[i] != NULL) && (error == 0); + i++) { + + device_spec_t *spec = NULL; + char *userspec = specs[i]; + + error = get_spec_for_name(userspec, &spec); + if ((error == 0) && (spec != NULL)) { + dlist_t *list_item = dlist_new_item((void *)spec); + if (spec == NULL) { + error = ENOMEM; + } else { + *list = dlist_insert_ordered + (list_item, *list, DESCENDING, + compare_device_spec_specificity); + } + } + } + + if (error != 0) { + /* the device_spec_t in the list items are maintained */ + /* in a cache elsewhere, so don't free them here. */ + dlist_free_items(*list, NULL); + *list = NULL; + } + + return (error); +} + +/* + * FUNCTION: destroy_request_spec_list_entry(void *entry) + * + * INPUT: entry - opaque pointer to a request_spec_list_t + * + * RETURNS: nothing + * + * PURPOSE: Function which reclaims memory allocated to a + * request_spec_list_t. + * + * Frees memory allocated to the avail_spec_list and + * unavail_spec_list. Entries in the list are not freed, + * since they are owned by the device_spec cache. + */ +static void +destroy_request_spec_list_entry( + void *obj) +{ + request_spec_list_t *entry = (request_spec_list_t *)obj; + + if (entry != NULL) { + /* items in the list are in the spec_cache and will */ + /* be cleaned up when it is destroyed. */ + dlist_free_items(entry->avail_specs_list, NULL); + dlist_free_items(entry->unavail_specs_list, NULL); + free(entry); + } +} + +/* + * FUNCTION: destroy_request_spec_list_cache() + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which destroys all entries in the request_spec_list + * cache. + */ +static int +destroy_request_spec_list_cache() +{ + dlist_free_items(_request_spec_list_cache, + destroy_request_spec_list_entry); + _request_spec_list_cache = NULL; + + return (0); +} + +/* + * FUNCTION: get_request_avail_spec_list(devconfig_t *request, + * dlist_t **list) + * + * INPUT: request - a pointer to a devconfig_t + * + * OUTPUT: list - pointer to a list of device_spec_t corresponding + * to the devices specified as available by the + * input request. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which locates or builds the list of device_spec_t + * for the available devices specified in the input request. + * + * Looks up the input request in the request_spec_list cache. + * If there is currently no entry in the cache for the request, + * an entry is built and added. + * + * The entry's list of available device_spec_t is returned. + */ +static int +get_request_avail_spec_list( + devconfig_t *request, + dlist_t **list) +{ + request_spec_list_t *entry = NULL; + int error = 0; + + if ((entry = find_request_spec_list_entry(request)) == NULL) { + + /* create cache entry for this request */ + error = make_request_spec_list_entry( + request, + devconfig_get_available(request), + devconfig_get_unavailable(request), + &entry); + + if ((error == 0) && (entry != NULL)) { + if ((error = add_request_spec_list_entry(entry)) != 0) { + destroy_request_spec_list_entry(entry); + entry = NULL; + } + } + } + + if ((error == 0) && (entry != NULL)) { + *list = entry->avail_specs_list; + } + + return (error); +} + +/* + * FUNCTION: get_request_unavail_spec_list(devconfig_t *request, + * dlist_t **list) + * + * INPUT: request - a pointer to a devconfig_t + * + * OUTPUT: list - pointer to a list of device_spec_t corresponding + * to the devices specified as unavailable by the + * input request. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which locates or builds the list of device_spec_t + * for the unavailable devices specified in the input request. + * + * Looks up the input request in the request_spec_list cache. + * If there is currently no entry in the cache for the request, + * an entry is built and added. + * + * The entry's list of unavailable device_spec_t is returned. + */ +static int +get_request_unavail_spec_list( + devconfig_t *request, + dlist_t **list) +{ + request_spec_list_t *entry = NULL; + int error = 0; + + if ((entry = find_request_spec_list_entry(request)) == NULL) { + + /* create new entry for this request */ + error = make_request_spec_list_entry( + request, + devconfig_get_available(request), + devconfig_get_unavailable(request), + &entry); + + if ((error == 0) && (entry != NULL)) { + if ((error = add_request_spec_list_entry(entry)) != 0) { + destroy_request_spec_list_entry(entry); + entry = NULL; + } + } + } + + if ((error == 0) && (entry != NULL)) { + *list = entry->unavail_specs_list; + } + + return (error); +} + +/* + * FUNCTION: get_default_avail_spec_list(defaults_t *defaults, + * char *dsname, dlist_t **list) + * + * INPUT: defaults - a pointer to a defaults_t struct + * dsname - the name of the diskset whose defaults should be used + * + * OUTPUT: list - pointer to a list of device_spec_t corresponding + * to the devices specified as available by the + * defaults for the named diskset, or the global + * defaults for all disksets. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which locates or builds the list of device_spec_t + * for the available devices for the named diskset. + * + * Locates the defaults for the named diskset, if there are none, + * locates the global defaults for all disksets. + * + * The defaults devconfig_t struct is then used to look up the + * the corresponding entry in the request_spec_list cache. + * + * If there is currently no entry in the cache for the defaults, + * an entry is built and added. + * + * The entry's list of available device_spec_t is returned. + */ +static int +get_default_avail_spec_list( + defaults_t *alldefaults, + char *dsname, + dlist_t **list) +{ + request_spec_list_t *entry = NULL; + devconfig_t *defaults = NULL; + int error = 0; + + /* Get diskset defaults, or global if none for diskset */ + error = defaults_get_diskset_by_name( + alldefaults, dsname, &defaults); + + if (error != 0) { + if (error == ENOENT) { + /* to get global defaults, pass a NULL diskset name */ + error = defaults_get_diskset_by_name( + alldefaults, NULL, &defaults); + } + + if (error != 0) { + if (error != ENOENT) { + oprintf(OUTPUT_DEBUG, + gettext("get defaults for %s returned %d\n"), + dsname, error); + } else { + error = 0; + } + } + } + + if ((entry = find_request_spec_list_entry(defaults)) == NULL) { + + /* create new entry for these defaults */ + error = make_request_spec_list_entry( + defaults, + devconfig_get_available(defaults), + devconfig_get_unavailable(defaults), + &entry); + + if ((error == 0) && (entry != NULL)) { + if ((error = add_request_spec_list_entry(entry)) != 0) { + destroy_request_spec_list_entry(entry); + entry = NULL; + } + } + } + + if ((error == 0) && (entry != NULL)) { + *list = entry->avail_specs_list; + } + + return (error); +} + +/* + * FUNCTION: get_default_unavail_spec_list(defaults_t *defaults, + * char *dsname, dlist_t **list) + * + * INPUT: defaults - a pointer to a defaults_t struct + * dsname - the name of the diskset whose defaults should be used + * + * OUTPUT: list - pointer to a list of device_spec_t corresponding + * to the devices specified as unavailable by the + * defaults for the named diskset, or the global + * defaults for all disksets. + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which locates or builds the list of device_spec_t + * for the unavailable devices for the named diskset. + * + * Locates the defaults for the named diskset, if there are none, + * locates the global defaults for all disksets. + * + * The defaults devconfig_t struct is then used to look up the + * the corresponding entry in the request_spec_list cache. + * + * If there is currently no entry in the cache for the defaults, + * an entry is built and added. + * + * The entry's list of unavailable device_spec_t is returned. + */ +static int +get_default_unavail_spec_list( + defaults_t *alldefaults, + char *dsname, + dlist_t **list) +{ + request_spec_list_t *entry = NULL; + devconfig_t *defaults = NULL; + int error = 0; + + /* Get diskset defaults, or global if none for diskset */ + error = defaults_get_diskset_by_name( + alldefaults, dsname, &defaults); + + if (error != 0) { + + if (error == ENOENT) { + /* to get global defaults, pass a NULL diskset name */ + error = defaults_get_diskset_by_name( + alldefaults, NULL, &defaults); + } + + if (error != 0) { + if (error != ENOENT) { + oprintf(OUTPUT_DEBUG, + gettext("get defaults for %s returned %d\n"), + dsname, error); + } else { + error = 0; + } + } + } + + if ((entry = find_request_spec_list_entry(defaults)) == NULL) { + + /* create new entry for these defaults */ + error = make_request_spec_list_entry( + defaults, + devconfig_get_available(defaults), + devconfig_get_unavailable(defaults), + &entry); + + if ((error == 0) && (entry != NULL)) { + if ((error = add_request_spec_list_entry(entry)) != 0) { + destroy_request_spec_list_entry(entry); + entry = NULL; + } + } + } + + if ((error == 0) && (entry != NULL)) { + *list = entry->unavail_specs_list; + } + + return (error); +} + +/* + * FUNCTION: is_named_device_avail(devconfig_t *request, char *device_name, + * boolean_t check_aliases, boolean_t *avail) + * + * INPUT: request - the current request devconfig_t + * device_name - char * device name + * check_aliases - boolean_t which indicates whether the device's + * aliases should be considered by the availability checks. + * + * OUTPUT: avail - a boolean_t * to hold the result + * + * RETURNS: int - !0 on error + * + * avail is set to B_TRUE if the named device is available for + * the input request, B_FALSE otherwise. + * + * PURPOSE: Determine if the named device can be used to satisfy the + * input request. + * + * There are several levels at which device availabiity or + * unavailability may be specifed: + * + * 1. the volume subrequest, + * 2. the toplevel (diskset) request, + * 3. the diskset-specific defaults + * 4. the global defaults + * + * If the diskset-specific defaults exist, only they are checked. + * + * The precedence ordering that is enforced: + * + * 1. if request has an avail list, the name must be in it + * and not in the request's unavail list. + * 2. if request has an unavail list, the name must not be in it. + * 3. if toplevel request has an avail list, the name must be + * in it and not in the toplevel request's unavailable + * list. + * 4. if toplevel request has an unavail list, the name must + * not be in it. + * 5. if defaults have an avail list, the name must be in it + * and not in the defaults unavailable list. + * 6. if defaults have an unavail list, the name must not be + * in it. + */ +static int +is_named_device_avail( + devconfig_t *request, + char *device_name, + boolean_t check_aliases, + boolean_t *avail) +{ + typedef enum check_types { + DEVICE_REQUEST = 0, + DISKSET_REQUEST, + DEFAULTS, + N_CHECKS + } check_type_t; + + check_type_t check_type; + + typedef enum list_types { + AVAIL = 0, + UNAVAIL, + N_LISTS + } list_type_t; + + dlist_t *lists[N_CHECKS][N_LISTS]; + boolean_t includes; + int error = 0; + + memset(lists, 0, (N_CHECKS * N_LISTS) * sizeof (dlist_t *)); + + if (request != NULL) { + /* get avail/unavail specs for request */ + ((error = get_request_avail_spec_list( + request, &lists[DEVICE_REQUEST][AVAIL])) != 0) || + (error = get_request_unavail_spec_list( + request, &lists[DEVICE_REQUEST][UNAVAIL])); + } + + if ((error == 0) && (_toplevel_request != NULL)) { + /* diskset request */ + ((error = get_request_avail_spec_list( + _toplevel_request, &lists[DISKSET_REQUEST][AVAIL])) != 0) || + (error = get_request_unavail_spec_list( + _toplevel_request, &lists[DISKSET_REQUEST][UNAVAIL])); + } + + if ((error == 0) && (_defaults != NULL)) { + /* and diskset/global defaults */ + ((error = get_default_avail_spec_list(_defaults, + get_request_diskset(), &lists[DEFAULTS][AVAIL])) != 0) || + (error = get_default_unavail_spec_list(_defaults, + get_request_diskset(), &lists[DEFAULTS][UNAVAIL])); + } + + if (error != 0) { + return (error); + } + + *avail = B_TRUE; + + for (check_type = DEVICE_REQUEST; + (check_type < N_CHECKS) && (error == 0); + check_type++) { + + if (lists[check_type][AVAIL] != NULL) { + + /* does avail spec list include named device? */ + if ((error = avail_list_includes_device_name( + lists[check_type][AVAIL], device_name, check_aliases, + &includes)) == 0) { + + if (includes != B_TRUE) { + *avail = B_FALSE; + } + + if ((includes == B_TRUE) && + (lists[check_type][UNAVAIL] != NULL)) { + + /* device is available, is it in the unavail list? */ + if ((error = unavail_list_includes_device_name( + lists[check_type][UNAVAIL], device_name, + check_aliases, &includes)) == 0) { + + if (includes == B_TRUE) { + *avail = B_FALSE; + } + } + } + } + + /* lists at this level checked, skip remainder */ + break; + + } else if (lists[check_type][UNAVAIL] != NULL) { + + /* does unavail spec list include named device? */ + if ((error = unavail_list_includes_device_name( + lists[check_type][UNAVAIL], device_name, + check_aliases, &includes)) == 0) { + + if (includes == B_TRUE) { + *avail = B_FALSE; + } + } + + /* list at this level checked, skip remainder */ + break; + } + } + + return (error); +} + +/* + * FUNCTION: avail_list_includes_device_name(dlist_t *list, + * char *device_name, boolean_t check_aliases, + * boolean_t *includes) + * + * INPUT: list - a dlist_t list of available device_spec_t + * device_name - a char * device CTD name + * check_aliases - boolean_t which indicates if the device's + * aliases should be considered in the availability + * checking. + * + * OUTPUT: includes - B_TRUE - if named device is "included" by any + * specification in the input list + * B_FALSE - otherwise + * + * RETURNS: int - 0 on success + * - !0 otherwise + * + * PURPOSE: Helper used by is_named_device_avail that determines + * if the input list of device specifications "includes" + * a specific device. + * + * Iterates the elements of the input array and searches + * for a match using spec_includes_device_name(). + */ +static int +avail_list_includes_device_name( + dlist_t *list, + char *device_name, + boolean_t check_aliases, + boolean_t *includes) +{ + dlist_t *iter = NULL; + int error = 0; + + *includes = B_FALSE; + + for (iter = list; + (*includes == B_FALSE) && (iter != NULL) && (error == 0); + iter = iter->next) { + + device_spec_t *spec = (device_spec_t *)iter->obj; + error = spec_includes_device_name(spec, device_name, + check_aliases, includes); + } + + return (0); +} + +/* + * FUNCTION: unavail_list_includes_device_name(dlist_t *list, + * char *device_name, boolean_t check_aliases, + * boolean_t *includes) + * + * INPUT: list - a dlist_t list of unavailable device_spec_t + * device_name - a char * device CTD name + * check_aliases - boolean_t which indicates if the device's + * aliases should be considered in the availability + * checking. + * + * OUTPUT: includes - B_TRUE - if named device is "included" by any + * specification in the input list + * B_FALSE - otherwise + * + * RETURNS: int - 0 on success + * - !0 otherwise + * + * PURPOSE: Helper used by is_named_device_avail that determines + * if the input list of device specifications "includes" + * a specific device. + * + * Iterates the elements of the input array and searches + * for a match using spec_includes_device_name_or_alias(). + */ +static int +unavail_list_includes_device_name( + dlist_t *list, + char *device_name, + boolean_t check_aliases, + boolean_t *includes) +{ + dlist_t *iter = NULL; + int error = 0; + device_spec_t *unavail_spec; + boolean_t check_for_alternate_hba = B_FALSE; + + *includes = B_FALSE; + + /* + * the specs in the list are in descending order of specificity. + * so a more exact spec will rule the device out before a less + * exact spec. + * + * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the + * input device name is "c3t0d0s0", it will match "c3t0d0" + * before "c3". + * + * This is important for the multi-path alias checking below. + * If the input device name is ruled out by a non-controller + * specification, it is really unavailable. + */ + for (iter = list; + (*includes == B_FALSE) && (iter != NULL); + iter = iter->next) { + + unavail_spec = (device_spec_t *)iter->obj; + error = spec_includes_device_name( + unavail_spec, device_name, check_aliases, includes); + + } + + if ((error == 0) && (*includes == B_TRUE)) { + + /* matched an unavailable spec, was it a controller/HBA? */ + oprintf(OUTPUT_DEBUG, + "device \"%s\" is unavailable, " + "it matched \"c(%d)t(%d)d(%d)s(%d)\"\n", + device_name, + unavail_spec->data.ctd->ctrl, + unavail_spec->data.ctd->target, + unavail_spec->data.ctd->lun, + unavail_spec->data.ctd->slice); + + if ((unavail_spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (unavail_spec->data.ctd->target == ID_UNSPECIFIED) && + (unavail_spec->data.ctd->lun == ID_UNSPECIFIED) && + (unavail_spec->data.ctd->slice == ID_UNSPECIFIED)) { + + /* + * Need to see if the named device is a disk or slice, + * and if so check to see if the it is multipathed + * and possibly accessible thru another controller/HBA. + */ + check_for_alternate_hba = B_TRUE; + } + } + + if ((error == 0) && (check_for_alternate_hba == B_TRUE)) { + + dm_descriptor_t slice = (dm_descriptor_t)0; + dm_descriptor_t disk = (dm_descriptor_t)0; + + ((error = slice_get_by_name(device_name, &slice)) != 0) || + (error = disk_get_by_name(device_name, &disk)); + if (error != 0) { + return (error); + } + + /* if it is a slice, get its disk */ + if ((error == 0) && (slice != (dm_descriptor_t)0)) { + error = slice_get_disk(slice, &disk); + } + + if ((error == 0) && (disk != (dm_descriptor_t)0)) { + + /* see if all the disk's HBAs are unavailable */ + dlist_t *hbas = NULL; + dlist_t *iter = NULL; + + error = disk_get_hbas(disk, &hbas); + + if (hbas != NULL) { + oprintf(OUTPUT_DEBUG, + gettext(" checking alternate paths for %s\n"), + device_name); + } else { + oprintf(OUTPUT_DEBUG, + gettext(" no alternate paths for %s\n"), + device_name); + } + + /* for each of the disk's HBAs */ + for (iter = hbas; + (iter != NULL) && (*includes == B_TRUE) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + device_spec_t *hbaspec; + char *hbaname = NULL; + dlist_t *iter2 = NULL; + + *includes = B_FALSE; + + ((error = get_display_name(hba, &hbaname)) != 0) || + (error = get_spec_for_name(hbaname, &hbaspec)); + + /* is HBA unavailable? */ + for (iter2 = list; + (iter2 != NULL) && (error == 0) && + (*includes == B_FALSE); + iter2 = iter2->next) { + + device_spec_t *spec = + (device_spec_t *)iter2->obj; + + *includes = spec_includes_device(spec, hbaspec); + } + } + dlist_free_items(hbas, NULL); + + /* if *includes==B_TRUE here, all HBAs are unavailable */ + } + } + + return (error); +} + +/* + * FUNCTION: spec_includes_device_name(device_spec_t *spec, + * char *device_name, boolean_t check_aliases, + * boolean_t *includes) + * + * INPUT: spec - a device_spec_t CTD specification. + * device_name - a char * device CTD name + * check_aliases - boolean_t which indicates if the device's + * aliases should be considered in the checking. + * + * OUTPUT: includes - B_TRUE - if device is "included" by the input + * specification + * B_FALSE - otherwise + * + * RETURNS: int - 0 on success + * - !0 otherwise + * + * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that + * determines if the input device specification "includes" + * the named device. + * + * If check_aliases is true and the named device is a slice or + * a disk drive, its multi-pathed aliases are also checked + * against the spec. + */ +static int +spec_includes_device_name( + device_spec_t *spec, + char *device_name, + boolean_t check_aliases, + boolean_t *includes) +{ + device_spec_t *device_spec; + int error = 0; + + error = get_spec_for_name(device_name, &device_spec); + if (error == 0) { + + *includes = spec_includes_device(spec, device_spec); + + if ((*includes == B_FALSE) && (check_aliases == B_TRUE)) { + + /* spec doesn't include name, check aliases */ + + dm_descriptor_t device = (dm_descriptor_t)0; + dlist_t *aliases = NULL; + + /* only slices and disks have aliases */ + error = slice_get_by_name(device_name, &device); + if (device != (dm_descriptor_t)0) { + error = get_aliases(device, &aliases); + } else if (error == 0) { + error = disk_get_by_name(device_name, &device); + if (device != (dm_descriptor_t)0) { + error = get_aliases(device, &aliases); + } + } + + if ((error == 0) && (aliases != NULL)) { + + dlist_t *iter; + for (iter = aliases; + (iter != NULL) && (*includes == B_FALSE) && + (error == 0); + iter = iter->next) { + + char *alias = (char *)iter->obj; + device_spec_t *alias_spec; + + error = get_spec_for_name(alias, &alias_spec); + if (error == 0) { + /* does spec include alias? */ + *includes = spec_includes_device(spec, alias_spec); + } + } + } + dlist_free_items(aliases, free); + } + } + + return (error); +} + +/* + * FUNCTION: destroy_device_spec(device_spec_t *spec) + * + * INPUT: spec - pointer to a device_spec_t + * + * RETURNS: nothing + * + * PURPOSE: Function which reclaims memory allocated to a device_spec_t. + * + * Frees memory allocated to hold the specific data in the spec. + */ +static void +destroy_device_spec( + device_spec_t *spec) +{ + if (spec != NULL) { + if (spec->type == SPEC_TYPE_CTD) { + free(spec->data.ctd); + } else if (spec->type == SPEC_TYPE_RAW) { + free(spec->data.raw); + } + free(spec); + } +} + +/* + * FUNCTION: create_device_spec(char *name, device_spec_t **spec); + * + * INPUT: name - pointer to a char* device name + * + * OUTPUT: spec - pointer to a device_spec_t to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Function which creates a device_spec_t for the input + * device name. + * + */ +static int +create_device_spec( + char *name, + device_spec_t **spec) +{ + int error = 0; + + /* allocate the device spec and try various parsing schemes */ + *spec = (device_spec_t *)calloc(1, sizeof (device_spec_t)); + if (*spec == NULL) { + error = ENOMEM; + } else { + if (((error = create_device_ctd_spec(name, spec)) != 0) && + (error != ENOMEM)) { + /* CTD failed, try other parsing schemes */ + error = create_device_raw_spec(name, spec); + } + } + + return (error); +} + +/* + * FUNCTION: create_device_ctd_spec(char *name, device_spec_t **spec); + * + * INPUT: name - pointer to a char* device name + * + * OUTPUT: spec - pointer to a device_spec_t updated with the parsed + * CTD spec, if successful + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Function which atttempts to parse the input device name into + * cXtXdXsX component ids. The ids are the integer values of each + * specified segment of the input name. + * + * If the name doesn't contain a segment, the id is set to + * ID_UNSPECIFIED. + * + * The input name must be well-formed. + * + * These are the acceptable forms: + * + * cXtXdXsX + * cXtXdX + * cXtX + * cXdXsX + * cXdX + * cX + */ +static int +create_device_ctd_spec( + char *name, + device_spec_t **spec) +{ + uint_t ctrl; + uint_t target; + uint_t lun; + uint_t slice; + + uint_t nscan; + uint_t nchars; + + char *device_str; + char *target_str; + char *ctd_str; + char *t_ptr; + char *d_ptr; + char *s_ptr; + + boolean_t is_ide = B_FALSE; + boolean_t got_slice = B_FALSE; + boolean_t got_lun = B_FALSE; + boolean_t got_target = B_FALSE; + boolean_t got_ctrl = B_FALSE; + + int error = 0; + + ctd_str = strdup(name); + if (ctd_str == NULL) { + return (ENOMEM); + } + + /* trim any leading path (/dev/dsk/cXtXdXsX) */ + if ((device_str = strrchr(ctd_str, '/')) != NULL) { + ++device_str; + } else { + device_str = ctd_str; + } + + /* find each segment start position */ + t_ptr = strrchr(device_str, 't'); + d_ptr = strrchr(device_str, 'd'); + s_ptr = strrchr(device_str, 's'); + + /* + * scan ids from each existing segment working backwards + * so as to leave the device_str in the correct state + * for the next expected segment + */ + if (s_ptr != NULL) { + + /* found 's', try to get slice */ + nchars = strlen(s_ptr); + if ((sscanf(s_ptr, "s%u%n", &slice, &nscan) != 1) || + (nscan != nchars)) { + + error = -1; + oprintf(OUTPUT_DEBUG, + gettext("no slice component in device " + "name \"%s\".\n"), + name); + + } else { + got_slice = B_TRUE; + *s_ptr = '\0'; + } + } + + if ((error == 0) && (d_ptr != NULL)) { + + /* found 'd', try to get disk/lun */ + nchars = strlen(d_ptr); + if ((sscanf(d_ptr, "d%u%n", &lun, &nscan) != 1) || + (nscan != nchars)) { + + error = -1; + oprintf(OUTPUT_DEBUG, + gettext("no disk/lun component " + "in device name \"%s\".\n"), + name); + + } else { + got_lun = B_TRUE; + *d_ptr = '\0'; + } + } + + if ((error == 0) && (t_ptr != NULL)) { + + /* found 't', try to get target, it may be a hex WWN id */ + + /* skip leading 't' and add two for the 'OX' */ + nchars = strlen(t_ptr + 1) + 2; + if ((target_str = (char *)malloc(nchars+1)) == NULL) { + + error = ENOMEM; + + } else { + + strcpy(target_str, "0X"); + strcpy(target_str+2, t_ptr + 1); + target_str[nchars] = '\0'; + + if ((sscanf(target_str, "%x%n", &target, &nscan) != 1) || + (nscan != nchars)) { + + error = -1; + oprintf(OUTPUT_DEBUG, + gettext("no target/WWN component " + "in device name \"%s\".\n"), + name); + + } else { + got_target = B_TRUE; + *t_ptr = '\0'; + } + + free(target_str); + } + + } else { + is_ide = B_TRUE; + } + + if ((error == 0) && (device_str != NULL)) { + + /* get controller/hba/channel */ + nchars = strlen(device_str); + if ((sscanf(device_str, "c%u%n", &ctrl, &nscan) != 1) || + (nscan != nchars)) { + + error = -1; + oprintf(OUTPUT_DEBUG, + gettext("no channel/HBA component " + "in device name \"%s\".\n"), + name); + + } else { + got_ctrl = B_TRUE; + } + } + + free(ctd_str); + + if (error == 0) { + + /* allocate the ctd_spec_t struct and store the ids */ + (*spec)->type = SPEC_TYPE_CTD; + (*spec)->data.ctd = (ctd_spec_t *)calloc(1, sizeof (ctd_spec_t)); + + if ((*spec)->data.ctd == NULL) { + error = ENOMEM; + } + + (*spec)->data.ctd->slice = ID_UNSPECIFIED; + (*spec)->data.ctd->lun = ID_UNSPECIFIED; + (*spec)->data.ctd->target = ID_UNSPECIFIED; + (*spec)->data.ctd->ctrl = ID_UNSPECIFIED; + + if (got_slice == B_TRUE) { + (*spec)->data.ctd->slice = slice; + } + + if (got_lun == B_TRUE) { + (*spec)->data.ctd->lun = lun; + } + + if (got_target == B_TRUE) { + (*spec)->data.ctd->target = target; + } + + if (got_ctrl == B_TRUE) { + (*spec)->data.ctd->ctrl = ctrl; + } + + (*spec)->data.ctd->is_ide = is_ide; + } + + return (error); +} + +/* + * FUNCTION: create_device_raw_spec(char *name, device_spec_t **spec); + * + * INPUT: name - pointer to a char* device name + * + * OUTPUT: spec - pointer to a device_spec_t updated with the raw spec + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Function which creates a "raw" spec for the input name. + * + * This is a last resort if all other spec parsing schemes failed, + * the "raw" spec is just the input device name. + */ +static int +create_device_raw_spec( + char *name, + device_spec_t **spec) +{ + int error = 0; + char *ctd_str = strdup(name); + + if (ctd_str == NULL) { + return (ENOMEM); + } + + (*spec)->type = SPEC_TYPE_RAW; + (*spec)->data.raw = ctd_str; + + oprintf(OUTPUT_DEBUG, + gettext("made raw device spec for \"%s\"\n"), ctd_str); + + return (error); +} + +/* + * FUNCTION: get_spec_for_name(char *name, device_spec_t **id); + * + * INPUT: name - pointer to a char* device name + * + * OUTPUT: id - pointer to a device_spec_t to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Function which finds the device_spec_t that already + * exists for the input name or creates it. + * + * The returned struct should not be freed, it is maintained + * in a cache that will be purged when the layout process + * is complete. + */ +int +get_spec_for_name( + char *name, + device_spec_t **id) +{ + dlist_t *item; + int error = 0; + + item = dlist_find(_spec_cache, (void *)name, + compare_name_to_spec_cache_name); + + if (item == NULL) { + if ((error = create_device_spec(name, id)) == 0) { + + spec_cache_t *entry = (spec_cache_t *) + calloc(1, sizeof (spec_cache_t)); + + if (entry == NULL) { + destroy_device_spec(*id); + error = ENOMEM; + } else { + char *dup = strdup(name); + if (dup == NULL) { + free(entry); + destroy_device_spec(*id); + *id = NULL; + error = ENOMEM; + } else { + entry->name = dup; + entry->device_spec = *id; + } + + if (error == 0) { + dlist_t *item = dlist_new_item((void *)entry); + if (item == NULL) { + free(entry); + destroy_device_spec(*id); + *id = NULL; + error = ENOMEM; + } else { + _spec_cache = + dlist_append(item, _spec_cache, AT_HEAD); + } + } + } + } + } else { + *id = ((spec_cache_t *)item->obj)->device_spec; + } + + return (error); +} + +/* + * FUNCTION: spec_includes_device(device_spec_t *spec, + * device_spec_t *device) + * + * INPUT: spec - pointer to a device_spec struct + * device - pointer to a device_spec struct + * + * RETURNS: boolean_t - B_TRUE if the device is included in the spec + * B_FALSE otherwise + * + * PURPOSE: Function which determines if the input device matches the + * input spec. + * + * If both specs are of the same type, the appropriate + * comparison function is called. + * + * If the two specs are of different types, no comparison + * is done and B_FALSE is returned. + */ +boolean_t +spec_includes_device( + device_spec_t *spec, + device_spec_t *device) +{ + if ((spec->type == SPEC_TYPE_CTD) && (device->type == SPEC_TYPE_CTD)) { + return (ctd_spec_includes_device(spec, device)); + } else if ((spec->type == SPEC_TYPE_RAW) && + (device->type == SPEC_TYPE_RAW)) { + return (raw_spec_includes_device(spec, device)); + } + + return (B_FALSE); +} + +/* + * FUNCTION: ctd_spec_includes_device(device_spec_t *spec, + * device_spec_t *device) + * + * INPUT: spec - pointer to a device_spec struct + * device - pointer to a device_spec struct + * + * RETURNS: boolean_t - B_TRUE if the device is included in the spec + * B_FALSE otherwise + * + * PURPOSE: Function which determines if the input CTD device spec + * matches the input CTD spec. + * + * The device_spec_t structs contain component "ids" for + * both the specification and the device. + * + * The device must match each of the ids in the spec that + * are specified. + * + * spec devices matched + * -------------------------------------------------------- + * cX cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX + * cXtX cXtX, cXtXdX, cXtXdXsX + * cXtXdX cXtXdX, cXtXdXsX + * cXtXdXsX cXtXdXsX + * cXdX cXdX, cXdXsX + * cXdXsX cXdXsX + */ +static boolean_t +ctd_spec_includes_device( + device_spec_t *spec, + device_spec_t *device) +{ + boolean_t match = B_FALSE; + + if (spec->data.ctd->is_ide) { + + /* valid IDE names are cX, cXdX, cXdXsX, no target */ + + if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (spec->data.ctd->lun != ID_UNSPECIFIED) && + (spec->data.ctd->slice != ID_UNSPECIFIED)) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && + (spec->data.ctd->lun == device->data.ctd->lun) && + (spec->data.ctd->slice == device->data.ctd->slice); + + } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (spec->data.ctd->lun != ID_UNSPECIFIED)) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && + (spec->data.ctd->lun == device->data.ctd->lun); + + } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); + + } + + } else { + + /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */ + + if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (spec->data.ctd->target != ID_UNSPECIFIED) && + (spec->data.ctd->lun != ID_UNSPECIFIED) && + (spec->data.ctd->slice != ID_UNSPECIFIED)) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && + (spec->data.ctd->target == device->data.ctd->target) && + (spec->data.ctd->lun == device->data.ctd->lun) && + (spec->data.ctd->slice == device->data.ctd->slice); + + } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (spec->data.ctd->target != ID_UNSPECIFIED) && + (spec->data.ctd->lun != ID_UNSPECIFIED)) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && + (spec->data.ctd->target == device->data.ctd->target) && + (spec->data.ctd->lun == device->data.ctd->lun); + + } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && + (spec->data.ctd->target != ID_UNSPECIFIED)) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && + (spec->data.ctd->target == device->data.ctd->target); + + } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { + + match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); + + } + } + + oprintf(OUTPUT_DEBUG, + gettext("spec: c(%d) t(%d) d(%d) s(%d) " + "%s: c(%d) t(%d) d(%d) s(%d)\n"), + spec->data.ctd->ctrl, spec->data.ctd->target, + spec->data.ctd->lun, spec->data.ctd->slice, + (match ? gettext("includes") : gettext("does not include")), + device->data.ctd->ctrl, device->data.ctd->target, + device->data.ctd->lun, device->data.ctd->slice); + + return (match); +} + +/* + * FUNCTION: raw_spec_includes_device(device_spec_t *spec, + * device_spec_t *device) + * + * INPUT: spec - pointer to a device_spec struct + * device - pointer to a device_spec struct + * + * RETURNS: boolean_t - B_TRUE if the device is included in the spec + * B_FALSE otherwise + * + * PURPOSE: Function which determines if the input raw device spec + * matches the input spec. + * + * The device_spec_t raw elements are checked. + * + * If the spec's raw device name is exactly contained at the + * beginning of the device spec's raw name, then the function + * evaluates to true. + */ +static boolean_t +raw_spec_includes_device( + device_spec_t *spec, + device_spec_t *device) +{ + return (strncasecmp(spec->data.raw, + device->data.raw, strlen(spec->data.raw)) == 0); +} + +/* + * FUNCTION: compare_name_to_spec_cache_name(void *name, void *list_item) + * + * INPUT: name - opaque pointer to a char * device name + * list_item - opaque pointer to a spec_cache_t entry + * + * RETURNS: int - 0 - if request is the same as list_item->request + * !0 - otherwise + * + * PURPOSE: dlist_t helper which compares the input device name + * to the list_item's device name for equality. + * + * This function is the lookup mechanism for the device_spec + * associated with the name. + */ +static int +compare_name_to_spec_cache_name( + void *name, + void *list_item) +{ + spec_cache_t *entry = (spec_cache_t *)list_item; + + assert(name != NULL); + assert(entry != NULL); + + return (string_case_compare((char *)name, entry->name)); +} + +/* + * FUNCTION: destroy_spec_cache_entry(void *entry) + * + * INPUT: entry - opaque pointer to a spec_cache_t + * + * RETURNS: nothing + * + * PURPOSE: Function which reclaims memory allocated to a + * spec_cache_t entry. + * + * Frees memory allocated to hold the CTD name and the + * corresponding device_spec_t. + */ +static void +destroy_spec_cache_entry( + void *obj) +{ + spec_cache_t *entry = (spec_cache_t *)obj; + + if (entry != NULL) { + free(entry->name); + destroy_device_spec(entry->device_spec); + free(entry); + } +} + +/* + * FUNCTION: destroy_spec_cache() + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Function which destroys all entries in the device_spec + * cache. + */ +static int +destroy_spec_cache() +{ + dlist_free_items(_spec_cache, destroy_spec_cache_entry); + _spec_cache = NULL; + + return (0); +} + +/* + * FUNCTION: get_device_access_name(devconfig_t *request, + * dm_descriptor_t desc, char **name) + * + * INPUT: request - a devconfig_t request + * desc - a dm_descriptor_t device handle + * + * OUTPUT: name - a char * pointer to hold the preferred name + * + * RETURNS: int - 0 - if request is the same as list_item->request + * !0 - otherwise + * + * PURPOSE: Utility function to determine which of the possible device + * names should be used to access a known available device. + * + * Devices handled are slices and disks. + * + * If the input device is a multipathed disk or slice, it + * can have several possible names. Determine which of the + * names should be used based on the input request's available + * or unavailable device specifications. + * + */ +int +get_device_access_name( + devconfig_t *request, + dm_descriptor_t desc, + char **name) +{ + int error = 0; + boolean_t avail = B_FALSE; + dlist_t *aliases = NULL; + + assert(desc != (dm_descriptor_t)0); + + *name = NULL; + + if ((error = get_display_name(desc, name)) != 0) { + return (error); + } + + if (is_did_name(*name) == B_TRUE) { + oprintf(OUTPUT_DEBUG, + gettext("device DID name %s is preferred\n"), + *name); + return (0); + } + + error = is_named_device_avail(request, *name, B_FALSE, &avail); + if (error != 0) { + return (error); + } + + if (avail == B_TRUE) { + oprintf(OUTPUT_DEBUG, + gettext("device name %s is accessible\n"), + *name); + return (0); + } + + /* search aliases for an 'available' name, prefer DID names */ + if ((error = get_aliases(desc, &aliases)) == 0) { + + dlist_t *iter = aliases; + char *availname = NULL; + char *didname = NULL; + + for (; (iter != NULL) && (error == 0); iter = iter->next) { + + char *alias = (char *)iter->obj; + error = is_named_device_avail(request, alias, B_FALSE, &avail); + + if ((error == 0) && (avail == B_TRUE)) { + oprintf(OUTPUT_DEBUG, + gettext("device alias %s is accessible for %s\n"), + alias, *name); + + availname = alias; + + if (is_did_name(availname) == B_TRUE) { + didname = alias; + break; + } + } + } + + if (error == 0) { + if (didname != NULL) { + *name = didname; + } else if (availname != NULL) { + *name = availname; + } + } + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_request.h b/usr/src/cmd/lvm/metassist/layout/layout_request.h new file mode 100644 index 0000000000..0388e59752 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_request.h @@ -0,0 +1,161 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_REQUEST_H +#define _LAYOUT_REQUEST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libdiskmgt.h" + +#include "volume_dlist.h" +#include "volume_defaults.h" +#include "volume_devconfig.h" + +/* XXX these are really in layout.c */ +extern int string_case_compare(char *str1, char *str2); +extern int add_modified_disk(devconfig_t *request, dm_descriptor_t diskx); +extern int add_to_hsp_list(dlist_t *devices); + +extern int release_request_caches(); + +extern int set_request_diskset(char *disksset); +extern char *get_request_diskset(); +extern void unset_request_diskset(); + +extern int set_toplevel_request(devconfig_t *request); +extern void unset_toplevel_request(); + +extern int set_request_defaults(defaults_t *defaults); +extern void unset_request_defaults(); + +extern int get_device_access_name( + devconfig_t *request, + dm_descriptor_t desc, + char **name); + +/* + * get list of HBAs, disks or slices that are available + * to satisfy the given request + */ +extern int slice_is_available( + char *name, + devconfig_t *request, + boolean_t *bool); + +extern int disks_get_avail_slices( + devconfig_t *request, + dlist_t *disks, + dlist_t **slices); + +extern int select_hbas_with_n_disks( + devconfig_t *request, + dlist_t *hbas, + int mindisks, + dlist_t **selhbas, + dlist_t **seldisks); + +extern int hba_get_avail_disks_and_space( + devconfig_t *request, + dm_descriptor_t hba, + dlist_t **list, + uint64_t *space); + +/* + * get lists of HBAs and disks that are used by volumes + */ +extern int get_hbas_and_disks_used_by_volumes( + dlist_t *volumes, + dlist_t **hbas, + dlist_t **disks); + +extern int get_hbas_and_disks_used_by_volume( + devconfig_t *volume, + dlist_t **hbas, + dlist_t **disks); + +/* + * accessors to get user-settable device parameters, + * values come from either the request or the diskset + * or global defaults + */ +extern int get_stripe_min_comp( + devconfig_t *request, + uint16_t *val); + +extern int get_stripe_max_comp( + devconfig_t *request, + uint16_t *val); + +extern int get_stripe_interlace( + devconfig_t *request, + uint64_t *val); + +extern int get_mirror_read_strategy( + devconfig_t *request, + mirror_read_strategy_t *val); + +extern int get_mirror_write_strategy( + devconfig_t *request, + mirror_write_strategy_t *val); + +extern int get_mirror_pass( + devconfig_t *request, + uint16_t *val); + +extern int get_mirror_nsubs( + devconfig_t *request, + uint16_t *val); + +extern int get_volume_faultrecov( + devconfig_t *request, + boolean_t *val); + +extern int get_volume_redundancy_level( + devconfig_t *request, + uint16_t *val); + +extern int get_volume_npaths( + devconfig_t *request, + uint16_t *val); + +extern int get_default_hsp_name( + devconfig_t *req, + char **name); + +extern int get_disks_for_target( + char *name, + dlist_t **disks); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_REQUEST_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_slice.c b/usr/src/cmd/lvm/metassist/layout/layout_slice.c new file mode 100644 index 0000000000..e1792b2b94 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_slice.c @@ -0,0 +1,2335 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <meta.h> + +#include "volume_string.h" + +#include "volume_devconfig.h" +#include "volume_error.h" +#include "volume_dlist.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_messages.h" +#include "layout_request.h" +#include "layout_slice.h" + +#define _LAYOUT_SLICE_C + +static int pick_from_best_hba_and_disk( + dlist_t *list, + dlist_t *used, + dm_descriptor_t *chosen); + +static int slice_has_same_disk_geom( + dm_descriptor_t slice, + dlist_t *used, + boolean_t *bool); + +static int slice_on_unique_disk( + dm_descriptor_t slice, + dlist_t *used, + dlist_t *othervols, + boolean_t *bool); + +static int slice_on_unique_hba( + dm_descriptor_t slice, + dlist_t *used, + dlist_t *othervols, + boolean_t *bool); + +static int slice_on_similar_bus( + dm_descriptor_t slice, + dlist_t *used, + boolean_t *bool); + +static int slice_has_n_paths( + dm_descriptor_t slice, + uint16_t npaths, + boolean_t *bool); + +static int compare_modslice_names( + void *obj1, + void *obj2); + +static int compare_string_to_modslice_name( + void *str, + void *modslice); + +static int create_new_slice( + dm_descriptor_t oslice, + uint64_t nbytes, + boolean_t add_extra_cyl, + devconfig_t **nslice); + +static int create_modified_slice( + dm_descriptor_t oslice, + char *oname, + uint32_t oindex, + uint64_t ostart, + uint64_t osize, + uint64_t bps, + char *nname, + uint32_t nindex, + uint64_t nsize, + devconfig_t **nslice); + +/* + * list to track resized slices + */ +static dlist_t *_modified_slices = NULL; + +/* + * struct to track used slices and their disks... + */ +typedef struct { + char *slicename; + dm_descriptor_t disk; +} usedslice_t; + +/* + * list to of usedslice_t to track slices that have been + * used for any reason. + */ +static dlist_t *_used_slices = NULL; + +static int add_used_slice_list_entry(char *slicename, dm_descriptor_t disk); +static int compare_usedslice_name_to_string(void *obj1, void *obj2); +static void free_used_slice(void *obj); + +/* + * list of slices reserved to be used for explicit + * volume requests + */ +static dlist_t *_rsvd_slices = NULL; + +/* + * list of slices needing to be removed (zeroed out) prior to + * applying any metassist modifications to the system. + */ +static dlist_t *_rmvd_slices = NULL; + +/* + * FUNCTION: choose_slice( + * uint64_t nbytes, + * uint16_t npaths, + * dlist_t *slices, + * dlist_t *used, + * dlist_t *used_hbas, + * dlist_t *used_disks, + * boolean_t unused_disk, + * boolean_t nbytes_is_min, + * boolean_t add_extra_cyl, + * devconfig_t **chosen) + * + * INPUT: nbytes - required size + * npaths - minimum required data paths + * *slices - slices from which to choose + * *used - slices used by the volume under construction + * *used_hbas - hbas used by other volumes relevant to + * the volume under construction + * *used_disks - disks used by other volumes relevant to + * the volume under construction + * unused_disk - if true, the chosen slice must be from an + * unused disk + * nbytes_is_min - if true, the chosen slice may be larger than + * nbytes. + * add_extra_cyl - passed to create_new_slice, see comment there. + * **chosen - pointer to hold the chosen slice + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Choosen a slice from the list of those available. + * + * Of those available, choose in order of preference: + * + * - one on a unique HBA and disk that is of the exact size + * - one on a unique HBA and disk that is of sufficient size + * - one on unique HBA that is of the exact size + * - one on unique HBA that is of sufficient size + * - one on unique disk that is of the exact size + * - one on unique disk that is of sufficient size + * - one on any HBA that is of exact size + * - one on any HBA that is of sufficient size + * - one on a unique HBA that is the largest size + * - one on a unique disk that is the largest size + * - one on any HBA that is the largest size + * + * The function scans the available slices and builds lists of + * those meeting the criteria above. After the scan is complete, + * the lists are examined in order, the first non-empty list is + * chosen. If there are several possibilities in the chosen list, + * see if it is possible select the slice from the least used HBA + * and/or disk. + * + * If nbytes_is_min is true, the returned slice will be + * at least nbytes in capacity. + * + * If unused_disk is true, the returned slice will be from + * a disk with no other known uses. + */ +int +choose_slice( + uint64_t nbytes, + uint16_t npaths, + dlist_t *slices, + dlist_t *used, + dlist_t *used_hbas, + dlist_t *used_disks, + boolean_t unused_disk, + boolean_t nbytes_is_min, + boolean_t add_extra_cyl, + devconfig_t **chosen) +{ + dlist_t *iter = NULL; + + dm_descriptor_t slice = NULL; + boolean_t resize = B_FALSE; + boolean_t verbose = (get_max_verbosity() == OUTPUT_VERBOSE); + + int error = 0; + + /* + * indexes into the list array: + * i -> unique controller 0 = yes, 1 = no + * j -> same bus type 0 = yes, 1 = no + * k -> unique disk 0 = yes, 1 = no + * l -> same disk geom 0 = yes, 1 = no + * m -> size 0 == exact, 1 = larger, 2 = any + */ + int i, j, k, l, m; + dlist_t *list[2][2][2][2][3]; + + /* output string arrays for each array dimension and index */ + char *uniqhba[2]; + char *samebus[2]; + char *uniqdisk[2]; + char *samegeom[2]; + char *sizes[3]; + + /* other output strings */ + char *look_msg = NULL; + char *npaths_msg = NULL; + char *samegeom_msg = NULL; + char *samebus_msg = NULL; + char *uniqhba_msg = NULL; + char *uniqdisk_msg = NULL; + char *exact_msg = NULL; + char *larger_msg = NULL; + char *smaller_msg = NULL; + char *insuff_paths = NULL; + char *too_small = NULL; + char *useddisk_msg = NULL; + + if (verbose == B_TRUE) { + /* only initialize the output strings if needed */ + + /* BEGIN CSTYLED */ + look_msg = gettext( + "\tlooking at slice: %s (%s)\n"); + npaths_msg = gettext( + "\t has the requested number of data paths (%d)\n"); + samegeom_msg = gettext( + "\t has the same disk geometry relative to used slices\n"); + samebus_msg = gettext( + "\t on a similar I/O bus/HBA relative to used slices\n"); + uniqhba_msg = gettext( + "\t on a unique HBA relative to used slices\n"); + uniqdisk_msg = gettext( + "\t on a unique disk relative to used slices\n"); + exact_msg = gettext( + "\t the exact size necessary\n"); + larger_msg = gettext( + "\t larger than necessary\n"); + smaller_msg = gettext( + "\t smaller than necessary\n"); + insuff_paths = gettext( + "\t rejected: not enough paths (%d requested)\n"); + too_small = gettext( + "\t rejected: too small\n"); + useddisk_msg = gettext( + "\t rejected: on a disk with other volume component(s)\n"); + + uniqhba[0] = gettext("unique HBA"); + uniqhba[1] = gettext("non unique HBA"); + samebus[0] = gettext("same bus type"); + samebus[1] = gettext("different bus type"); + uniqdisk[0] = gettext("unique disk"); + uniqdisk[1] = gettext("non unique disk"); + samegeom[0] = gettext("same geometry"); + samegeom[1] = gettext("different geometry"); + sizes[0] = gettext("an exact size slice"); + sizes[1] = gettext("a larger slice"); + sizes[2] = gettext("a smaller slice"); + + /* END CSTYLED */ + } + + /* init list array pointers */ + (void) memset(list, 0, 2*2*2*2*3 * sizeof (dlist_t *)); + + for (iter = slices; + (iter != NULL) && (error == 0); iter = iter->next) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + uint64_t snbytes = 0; + boolean_t uniqdisk = B_FALSE; + boolean_t uniqhba = B_FALSE; + boolean_t samegeom = B_FALSE; + boolean_t samebus = B_FALSE; + boolean_t paths = B_FALSE; + dlist_t *item = NULL; + + ((error = slice_get_size(slice, &snbytes)) != 0) || + (error = slice_has_n_paths(slice, npaths, &paths)) || + (error = slice_on_unique_hba(slice, used, used_hbas, &uniqhba)) || + (error = slice_on_unique_disk(slice, used, used_disks, + &uniqdisk)) || + (error = slice_on_similar_bus(slice, used, &samebus)) || + (error = slice_has_same_disk_geom(slice, used, &samegeom)); + if (error != 0) { + continue; + } + + if (verbose == B_TRUE) { + char *sname = NULL; + char *sizestr = NULL; + (void) get_display_name(slice, &sname); + if (bytes_to_sizestr(snbytes, &sizestr, + universal_units, B_FALSE) == 0) { + oprintf(OUTPUT_VERBOSE, look_msg, sname, sizestr); + free(sizestr); + } + } + + if (npaths > 1) { + if (paths && verbose) { + /* specifically asked for more paths, ... */ + oprintf(OUTPUT_VERBOSE, npaths_msg); + } + } else if (npaths == 1) { + /* every disk has at least 1 path */ + paths = B_TRUE; + } + + if (verbose == B_TRUE) { + if (uniqhba) { + oprintf(OUTPUT_VERBOSE, uniqhba_msg); + } + if (uniqdisk) { + oprintf(OUTPUT_VERBOSE, uniqdisk_msg); + } + + if (used != NULL) { + if (samebus) { + oprintf(OUTPUT_VERBOSE, samebus_msg); + } + if (samegeom) { + oprintf(OUTPUT_VERBOSE, samegeom_msg); + } + } + + if (snbytes > nbytes) { + oprintf(OUTPUT_VERBOSE, larger_msg); + } else if (snbytes == nbytes) { + oprintf(OUTPUT_VERBOSE, exact_msg); + } else { + oprintf(OUTPUT_VERBOSE, smaller_msg); + } + } + + /* filter slices not meeting minimum criteria */ + if (nbytes_is_min && (snbytes < nbytes)) { + /* not large enough */ + if (verbose == B_TRUE) { + oprintf(OUTPUT_VERBOSE, too_small); + } + continue; + } + + if (paths == B_FALSE) { + /* not connected thru enough paths */ + if (verbose == B_TRUE) { + oprintf(OUTPUT_VERBOSE, insuff_paths, npaths); + } + continue; + } + + if (uniqdisk != B_TRUE && unused_disk == TRUE) { + /* not on a unique disk */ + if (verbose == B_TRUE) { + oprintf(OUTPUT_VERBOSE, useddisk_msg); + } + continue; + } + + /* map slice properties into array indices */ + i = (uniqhba ? 0 : 1); + j = (samebus ? 0 : 1); + k = (uniqdisk ? 0 : 1); + l = (samegeom ? 0 : 1); + m = (snbytes == nbytes ? 0 : (snbytes > nbytes ? 1 : 2)); + + /* + * insert slice into the list array using derived indices. + * NB: lists of slices larger than necessary are kept in + * ascending order (results in best fit, not worst fit) + */ + if ((item = dlist_new_item((void*)slice)) == NULL) { + error = ENOMEM; + } else { + list[i][j][k][l][m] = + dlist_insert_ordered( + item, + list[i][j][k][l][m], + (m == 1 ? ASCENDING : DESCENDING), + compare_slice_sizes); + } + } + + /* + * Select a slice from one of the lists. + * + * The list with the combination of lowest indices + * is the most preferred list... in rough order: + * + * one on a unique HBA and disk that is of the exact size + * one on a unique HBA and disk that is of sufficient size (resize) + * one on unique HBA that is of the exact size + * one on unique HBA that is of sufficient size (resize) + * one on unique disk that is of the exact size + * one on unique disk that is of sufficient size (resize) + * one on any HBA that is of exact size + * one on any HBA that is of sufficient size (resize) + * one on a unique HBA that is the largest size + * one on a unique disk that is the largest size + * one on any HBA that is the largest size + */ + slice = NULL; + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < 2; k++) { + for (l = 0; l < 2; l++) { + for (m = 0; m < 3; m++) { + if (list[i][j][k][l][m] != NULL) { + + /* pick least used slice from this list */ + error = pick_from_best_hba_and_disk( + list[i][j][k][l][m], + used, &slice); + + resize = (m == 1); + + /* terminate all loops */ + goto stop; + } + } + } + } + } + } +stop: + + /* + * Slice chosen, is a resize necessary? + */ + if ((error == 0) && (slice != NULL)) { + + if (error == 0) { + if (verbose == B_TRUE) { + uint64_t snbytes = 0; + char *sname = NULL; + char *sizestr = NULL; + + (void) get_display_name(slice, &sname); + (void) slice_get_size(slice, &snbytes); + + if (bytes_to_sizestr(snbytes, &sizestr, + universal_units, B_FALSE) == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" selected %s (%s)\n" + " it is %s on a\n" + " %s (%s) and a\n" + " %s (%s)\n"), + sname, sizestr, + sizes[m], + uniqhba[i], samebus[j], + uniqdisk[k], samegeom[l]); + free(sizestr); + } + } + + if (resize) { + if (verbose == B_TRUE) { + oprintf(OUTPUT_VERBOSE, + gettext(" it has excess space, " + "resizing...\n")); + } + + error = create_new_slice(slice, nbytes, add_extra_cyl, + chosen); + if ((error == 0) && (*chosen != NULL) && verbose) { + oprintf(OUTPUT_VERBOSE, + gettext(" exactly resized\n")); + } + } + + if (error == 0) { + /* either no resize was necessary or the resize failed */ + if (*chosen == NULL) { + /* + * use the original slice as it is. + * Make a devconfig_t for it. + */ + error = create_devconfig_for_slice(slice, chosen); + } + } + } + } else if (slice == NULL) { + oprintf(OUTPUT_DEBUG, + gettext(" no possible slice\n")); + } + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < 2; k++) { + for (l = 0; l < 2; l++) { + for (m = 0; m < 3; m++) { + if (list[i][j][k][l][m] != NULL) { + dlist_free_items(list[i][j][k][l][m], NULL); + } + } + } + } + } + } + + return (error); +} + +/* + * FUNCTION: create_devconfig_for_slice(dm_descriptor_t slice, + * devconfig_t **nslice) + * + * INPUT: slice - dm_descriptor_t handle to an existing slice + * nslice - devconfig_t pointer to hold the new slice + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Creates a devconfig_t struct representation of the input + * slice dm_descriptor. + */ +int +create_devconfig_for_slice( + dm_descriptor_t slice, + devconfig_t **nslice) +{ + uint64_t nbytes = 0; + uint64_t nblks = 0; + uint64_t stblk = 0; + uint32_t index = 0; + char *name = NULL; + int error = 0; + + ((error = get_display_name(slice, &name)) != 0) || + (error = slice_get_size(slice, &nbytes)) || + (error = slice_get_size_in_blocks(slice, &nblks)) || + (error = slice_get_start_block(slice, &stblk)) || + (error = slice_get_index(slice, &index)); + if (error != 0) { + return (error); + } + + ((error = new_devconfig(nslice, TYPE_SLICE)) != 0) || + (error = devconfig_set_name(*nslice, name)) || + (error = devconfig_set_slice_index(*nslice, index)) || + (error = devconfig_set_slice_start_block(*nslice, stblk)) || + (error = devconfig_set_size_in_blocks(*nslice, nblks)) || + (error = devconfig_set_size(*nslice, nbytes)); + if (error != 0) { + free_devconfig(*nslice); + } + + return (error); +} + +/* + * FUNCTION: make_slicename_for_disk_and_index(dm_descriptor_t disk, + * uint32_t index, char **slicename) + * + * INPUT: disk - a dm_descriptor_t disk handle + * index - a slice index + * + * OUTPUT slicename - a char * pointer to hold the resulting slicename + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Utility function to manufacture a new slice name given the + * "parent" disk and an available slice index. + * + * The caller should free the returned name when done with it. + */ +static int +make_slicename_for_disk_and_index( + dm_descriptor_t disk, + uint16_t index, + char **slicename) +{ + char *dname; + int error = 0; + + if ((error = get_display_name(disk, &dname)) == 0) { + error = make_slicename_for_diskname_and_index(dname, + index, slicename); + } + + return (error); +} + +/* + * FUNCTION: make_slicename_for_diskname_and_index(char *diskname, + * uint32_t index, char **slicename) + * + * INPUT: diskname - a char * disk name + * index - a slice index + * + * OUTPUT slicename - a char * pointer to hold the resulting slicename + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Utility function to manufacture a new slice name given the + * name of a disk and an available slice index. + * + * The caller should free the returned name when done with it. + */ +int +make_slicename_for_diskname_and_index( + char *diskname, + uint16_t index, + char **slicename) +{ + int error = 0; + char buf[MAXNAMELEN+1]; + + (void) snprintf(buf, sizeof (buf), "%ss%u", diskname, index); + if ((*slicename = strdup(buf)) == NULL) { + *slicename = NULL; + error = ENOMEM; + } + + return (error); +} + +/* + * FUNCTION: create_new_slice(dm_descriptor_t oslice, uint64_t nbytes, + * boolean_t add_extra_cyl, devconfig_t **nslice) + * + * INPUT: oslice - dm_descriptor_t handle to an existing slice + * nbytes - desired minimum size of the new slice + * add_extra_cyl - boolean indicating whether the resized slice + * needs to be oversized by 1 cylinder to account for + * interlace rounding done for stripe components. + * nslice - devconfig_t pointer to hold the new slice + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Creates a new slice object using space from the input slice. + * + * If there is an open slice slot in the disk VTOC, it will be + * reserved for the new slice. Space for the new slice will be + * taken from the original slice. + * + * If there is no open slice slot, the original slice will be + * returned as the usable new slice. + * + * The new slice will be of at least 'nbytes' bytes and possibly + * larger due to sector and cylinder boundary alignment. + * + * For EFI labeled disks, nbytes is rounded up to the next block + * boundary. + * + * For VTOC labeled disks, nbytes is rounded up to the next + * cylinder boundary. + * + * Additionally, if add_extra_cyl is true, the new slice will be + * made 1 cylinder larger than necessary. This accounts for the + * interlace rounding done within libmeta when computing the + * usable size of stripe components on disks with VTOC labels. + * Rounding the size up to the next cylinder boundary is not + * sufficient because libmeta will round this size down to an + * integral multiple of the stripe interlace and then round that + * result down to a cylinder boundary. This makes the usable + * size of the slice one cylinder smaller and possibly less than + * nbytes. Adding an extra cylinder ensures the usable size is + * greater than nbytes despite the rounding. + * + * If the resize is successful a pointer to the devconfig_t + * representing the new slice will be returned in "newslice". + * + * If the resize cannot be done, the newslice pointer will + * be NULL. + */ +static int +create_new_slice( + dm_descriptor_t oslice, + uint64_t nbytes, + boolean_t add_extra_cyl, + devconfig_t **nslice) +{ + dm_descriptor_t odisk = NULL; + boolean_t efi = B_FALSE; + + char *oname = NULL; + uint64_t osize = 0; /* orig size (bytes) */ + uint64_t ostart = 0; /* orig start (byte) */ + uint64_t ostblk = 0; /* orig start (blk) */ + uint64_t nsize = 0; /* new size (bytes) */ + uint64_t bytes_per_sect = 0; + + uint32_t oindex = 0; + uint32_t nindex = oindex; + + int error = 0; + + *nslice = NULL; + + ((error = slice_get_disk(oslice, &odisk)) != 0) || + (error = slice_get_index(oslice, &oindex)); + if (error != 0) { + return (error); + } + + /* find an unused slice number, default to oindex */ + nindex = oindex; + if ((error = disk_get_available_slice_index(odisk, &nindex)) != 0) { + return (error); + } + + ((error = get_display_name(oslice, &oname)) != 0) || + (error = slice_get_size(oslice, &osize)) || + (error = slice_get_start(oslice, &ostart)) || + (error = slice_get_start_block(oslice, &ostblk)) || + (error = disk_get_is_efi(odisk, &efi)) || + (error = disk_get_blocksize(odisk, &bytes_per_sect)); + if (error != 0) { + return (error); + } + + if (efi) { + + /* EFI: round size to an integral number of blocks (sectors) */ + nsize = bytes_per_sect * + ((nbytes + (bytes_per_sect - 1)) / bytes_per_sect); + + oprintf(OUTPUT_DEBUG, + gettext(" " + "rounded up to %10.2f blocks\n"), + (double)(nsize/bytes_per_sect)); + + } else { + + /* VTOC: round size to an integral number of cylinders */ + uint64_t nhead = 0; + uint64_t nsect = 0; + uint64_t ncyls = 0; + + ((error = disk_get_ncylinders(odisk, &ncyls)) != 0) || + (error = disk_get_nheads(odisk, &nhead)) || + (error = disk_get_nsectors(odisk, &nsect)); + if (error == 0) { + uint64_t bytes_per_cyl = nhead * nsect * bytes_per_sect; + nsize = bytes_per_cyl * + ((nbytes + (bytes_per_cyl - 1)) / bytes_per_cyl); + + if (add_extra_cyl == TRUE) { + nsize += bytes_per_cyl; + } + + oprintf(OUTPUT_DEBUG, + gettext(" " + "rounded VTOC slice to %10.2f cylinders " + "(out of %llu)\n"), + (double)(nsize/bytes_per_cyl), ncyls); + } + } + + /* is sufficient space still available? */ + if (error == 0) { + if (osize == nsize) { + /* use existing slice as is */ + ((error = create_devconfig_for_slice(oslice, nslice)) != 0) || + (error = disk_reserve_index(odisk, (uint16_t)nindex)); + } else if (osize > nsize) { + + if (nindex == oindex) { + /* no more slices, resize existing slice */ + ((error = create_devconfig_for_slice(oslice, + nslice)) != 0) || + (error = devconfig_set_size(*nslice, nsize)) || + (error = devconfig_set_size_in_blocks(*nslice, + nsize/bytes_per_sect)); + (error = disk_reserve_index(odisk, (uint16_t)nindex)); + + } else { + /* make a new slice */ + char *nname = NULL; + + ((error = make_slicename_for_disk_and_index(odisk, + nindex, &nname)) != 0) || + (error = create_modified_slice(oslice, oname, oindex, + ostart, osize, bytes_per_sect, nname, nindex, nsize, + nslice)) || + /* mark the new slice's index as used */ + (error = disk_reserve_index(odisk, (uint16_t)nindex)); + + if ((error != 0) && (*nslice == NULL)) { + free(nname); + } + } + } + } + + return (error); +} + +/* + * FUNCTION: create_modified_slice(dm_descriptor_t oslice, char *oname, + * uint32_t oindex, uint64_t ostart, uint64_t osize, + * uint64_t bytes_per_sect, uint64_t nsize, + * char *nname, uint32_t nindex, devconfig_t **nslice) + * + * INPUT: oslice - dm_descriptor_t handle for the original slice + * oname - existing source slice name + * oindex - existing source slice VTOC index + * ostart - existing source slice start byte + * osize - existing source slice size in bytes + * bytes_per_sect - bytes per block (sector) for the disk + * nname - new slice name + * nindex - new slice VTOC index + * nsize - new slice size in bytes (cylinder and block aligned) + * + * SIDEEFFECTS: updates the module private list of modified slices + * + * OUTPUT: nslice - pointer to a devconfig_t to hold the new slice + * + * PURPOSE: create a new VTOC slice by taking space from an + * existing slice. + * + * The input size for the new slice is expected to be + * cylinder aligned. + */ +static int +create_modified_slice( + dm_descriptor_t oslice, + char *oname, + uint32_t oindex, + uint64_t ostart, + uint64_t osize, + uint64_t bytes_per_sect, + char *nname, + uint32_t nindex, + uint64_t nsize, + devconfig_t **nslice) +{ + int error = 0; + + /* compute start sector and size in sectors for the new slice */ + + /* subtract nsize from original slice to get starting byte */ + uint64_t nstart = (ostart + osize) - nsize; + + /* convert starting byte to a sector */ + uint64_t nstblk = (uint64_t)(nstart / bytes_per_sect); + + /* convert nsize to an integral number of blocks (sectors) */ + uint64_t nblks = (uint64_t)(nsize / bytes_per_sect); + + /* create a modified slice record for the new slice */ + error = assemble_modified_slice(oslice, nname, nindex, + nstblk, nblks, nsize, nslice); + if (error != 0) { + free(nname); + return (error); + } + + /* update the existing source slice's new size */ + osize = osize - nsize; + (void) slice_set_size(oslice, osize); + + /* update/create the modified slice record gfor the source slice */ + error = assemble_modified_slice((dm_descriptor_t)0, + oname, oindex, (uint64_t)(ostart / bytes_per_sect), + (uint64_t)(osize / bytes_per_sect), + osize, NULL); + + return (error); +} + +/* + * FUNCTION: assemble_modified_slice(dm_descriptor_t src_slice, + * char *mod_name, uint32_t mod_index, + * uint64_t mod_stblk, uint64_t mod_nblks, + * uint64_t mod_size, devconfig_t **modslice) + * + * INPUT: src_slice - dm_descriptor_t handle of the slice space + * was taken from to create the modified slice + * mod_name - name of the modified slice + * mod_index - name of the modified slice + * mod_stblk - start block of the modified slice + * mod_nblks - size in blocks of the modified slice + * mod_size - size in bytes of the modified slice + * + * OUTPUT: mod_slice - if non-NULL, will be populated with a + * devconfig_t representing the modified slice. + * + * SIDEEFFECTS: adds or updates an entry in the modified slice list + * tracking the slices that have been explicitly modified + * by the layout code. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Utility function to which updates or creates a devconfig_t + * representing a slice that needs to be modified. + * + * If a modified slice record does not exist for the named + * slice, a new devconfig_t struct is allocated and added + * to the modified slice list. + * + * The existing or created devconfig_t struct is updated with + * the input values. + * + * The information about the slices in the modified slice list + * will eventually be handed to fmthard. + */ +int +assemble_modified_slice( + dm_descriptor_t src_slice, + char *mod_name, + uint32_t mod_index, + uint64_t mod_stblk, + uint64_t mod_nblks, + uint64_t mod_size, + devconfig_t **mod_slice) +{ + devconfig_t *slice = NULL; + modslice_t *mstp = NULL; + dlist_t *item = NULL; + int error = 0; + + /* see if the slice has been modified before */ + if ((item = dlist_find(_modified_slices, mod_name, + compare_string_to_modslice_name)) != NULL) { + + /* yes, update the resize count and attributes */ + mstp = (modslice_t *)item->obj; + slice = mstp->slice_devcfg; + + mstp->times_modified += 1; + mstp->src_slice_desc = src_slice; + + ((error = devconfig_set_slice_start_block(slice, + mod_stblk)) != 0) || + (error = devconfig_set_size(slice, mod_size)) || + (error = devconfig_set_size_in_blocks(slice, mod_nblks)); + + } else { + + /* no, first modification... */ + /* create a devconfig_t representing the new slice */ + ((error = new_devconfig(&slice, TYPE_SLICE)) != 0) || + (error = devconfig_set_name(slice, mod_name)) || + (error = devconfig_set_slice_index(slice, mod_index)) || + (error = devconfig_set_slice_start_block(slice, mod_stblk)) || + (error = devconfig_set_size_in_blocks(slice, mod_nblks)) || + (error = devconfig_set_size(slice, mod_size)); + if (error == 0) { + /* add to list of modified slices */ + if ((mstp = (modslice_t *) + calloc(1, sizeof (modslice_t))) != NULL) { + + /* count # of times source slice has been modified */ + if (src_slice != (dm_descriptor_t)0) { + mstp->times_modified = 0; + } else { + mstp->times_modified = 1; + } + mstp->src_slice_desc = src_slice; + mstp->slice_devcfg = slice; + + if ((item = dlist_new_item(mstp)) != NULL) { + _modified_slices = + dlist_insert_ordered( + item, + _modified_slices, + ASCENDING, + compare_modslice_names); + } else { + error = ENOMEM; + } + } else { + error = ENOMEM; + } + } + + if (error != 0) { + free_devconfig(mstp); + free_devconfig(slice); + } + } + + if (error == 0) { + oprintf(OUTPUT_DEBUG, + " " + "modified %s (start blk: %9llu, nblks: %9llu)\n", + mod_name, mod_stblk, mod_nblks); + + /* return devconfig_t for modified slice */ + if (mod_slice != NULL) { + *mod_slice = slice; + mstp->volume_component = B_TRUE; + } + } + + return (error); +} + +/* + * FUNCTION: dlist_t *get_modified_slices() + * + * RETURNS: pointer to the list of modslice_t structs representing + * modified slices + * + * PURPOSE: public accessor to the list of slices modified while + * processing a request. + */ +dlist_t * +get_modified_slices() +{ + return (_modified_slices); +} + +/* + * FUNCTION: free_modslice_object(void *obj) + * + * INPUT: obj - opaque pointer + * + * PURPOSE: Frees memory associated with a modslice_t struct. + */ +static void +free_modslice_object( + void *obj) +{ + assert(obj != (modslice_t *)NULL); + + if (((modslice_t *)obj)->slice_devcfg != NULL) { + if (((modslice_t *)obj)->volume_component != B_TRUE) { + free_devconfig(((modslice_t *)obj)->slice_devcfg); + } + } + + free(obj); +} + +/* + * FUNCTION: void release_modified_slices() + * + * INPUT: none - + * OUTPUT: none - + * + * PURPOSE: cleanup the module global list of slices modified + * while processing a request. + */ +int +release_modified_slices() +{ + dlist_free_items(_modified_slices, free_modslice_object); + _modified_slices = NULL; + + return (0); +} + +/* + * FUNCTION: destroy_new_slice(devconfig_t *dev) + * + * INPUT: dev - a devconfig_t pointer to a slice object + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Undoes slice creation done by create_new_slice(): + * + * release index + * remove from used_slices + * remove from modified_slices + * return space to source slice + * free memory + */ +int +destroy_new_slice( + devconfig_t *dev) +{ + dm_descriptor_t disk = NULL; + uint64_t size = 0; + uint16_t index = 0; + modslice_t *modified = NULL; + dlist_t *item = NULL; + char *name = NULL; + int error = 0; + + ((error = devconfig_get_name(dev, &name)) != 0) || + (error = devconfig_get_slice_index(dev, &index)) || + (error = devconfig_get_size(dev, &size)) || + (error = get_disk_for_named_slice(name, &disk)) || + (error = disk_release_index(disk, index)) || + (error = remove_used_slice_by_name(name)); + if (error != 0) { + return (error); + } + + /* remove from the modified_slices list */ + _modified_slices = + dlist_remove_equivalent_item( + _modified_slices, name, + compare_string_to_modslice_name, &item); + + if (item != NULL) { + modified = (modslice_t *)item->obj; + free((void*) item); + } + + /* space from an existing slice? if so reclaim it. */ + if (modified != NULL) { + + dm_descriptor_t src = modified->src_slice_desc; + char *srcname = NULL; + dlist_t *srcitem = NULL; + + if (src != (dm_descriptor_t)0) { + if ((error = get_display_name(src, &srcname)) == 0) { + srcitem = + dlist_find( + _modified_slices, + srcname, + compare_string_to_modslice_name); + } + } + + if ((error == 0) && (srcitem != NULL)) { + + modslice_t *source = (modslice_t *)srcitem->obj; + devconfig_t *srcdevcfg = NULL; + uint64_t srcsize = NULL; + uint64_t srcsizeblks = NULL; + uint64_t inblks = NULL; + + srcdevcfg = source->slice_devcfg; + source->times_modified -= 1; + + ((error = devconfig_get_size(srcdevcfg, &srcsize)) != 0) || + (error = devconfig_set_size(srcdevcfg, srcsize + size)) || + (error = slice_set_size(src, srcsize + size)) || + (error = slice_get_size_in_blocks(src, &srcsizeblks)) || + (error = devconfig_get_size_in_blocks(srcdevcfg, &inblks)); + (error = devconfig_set_size_in_blocks(srcdevcfg, srcsizeblks)); + + if (error == 0) { + + /* was only modification undone? */ + if (source->times_modified == 0) { + + _modified_slices = + dlist_remove_equivalent_item( + _modified_slices, srcname, + compare_string_to_modslice_name, + &srcitem); + + free_modslice_object((modslice_t *)srcitem->obj); + free((void *)srcitem); + } + } + } + + free_modslice_object(modified); + } + + return (error); +} + +/* + * FUNCTION: pick_from_best_hba_and_disk(dlist_t *slices, + * dlist_t *used, dm_descriptor_t *chosen) + * + * INPUT: slices - a dlist_t poitner to a list of slices + * used - a dlist_t pointer to a list of used slices + * chosen - a dm_descriptor_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Examines the input list of slices and chooses the one + * that is on the least used HBA and disk. + * + * HBA and disk usage is determined by examining the input + * list of used slices and counting the number of slices + * each HBA and disk contributes. + * + * The HBA which contributes the fewest is selected, and + * then the disk on that HBA which contributes the fewest + * is selected. + * + * The largest slice from that disk is then returned. + */ +static int +pick_from_best_hba_and_disk( + dlist_t *slices, + dlist_t *used, + dm_descriptor_t *chosen) +{ + dlist_t *iter = NULL; + dlist_t *iter1 = NULL; + dlist_t *iter2 = NULL; + dlist_t *item = NULL; + + dlist_t *used_slice_hbas = NULL; + + int maxuses = 128; + int maxslices = VTOC_SIZE; /* meta.h */ + + int i = 0; + int error = 0; + + /* + * allocate an array to hold lists of slices grouped by + * HBA contribution... the list indexed by N is the list + * of slices that are on HBAs contributing N slices + */ + dlist_t **prefhbas = (dlist_t **)calloc(maxuses, sizeof (dlist_t *)); + + /* + * allocate an array to hold lists of slices grouped by + * disk contribution... the list indexed by N is the list + * of slices that are on disks contributing N slices + */ + dlist_t **prefdisks = (dlist_t **)calloc(maxslices, sizeof (dlist_t *)); + + *chosen = (dm_descriptor_t)0; + + if (prefhbas == NULL || prefdisks == NULL) { + free(prefhbas); + free(prefdisks); + return (ENOMEM); + } + + /* + * precompute the used slices' lists of HBAS: iterate the list + * of used slices and determine the HBA(s) each is connected thru. + * construct a list of lists containing the HBAs. + */ + for (iter = used; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *uslice = (devconfig_t *)iter->obj; + dm_descriptor_t udisk = NULL; + char *uname = NULL; + dlist_t *uhbas = NULL; + + /* need to use disk to get to HBAs because */ + /* the slice doesn't exist yet */ + ((error = devconfig_get_name(uslice, &uname)) != 0) || + (error = get_disk_for_named_slice(uname, &udisk)) || + (error = disk_get_hbas(udisk, &uhbas)); + if (error == 0) { + if ((item = dlist_new_item((void *)uhbas)) == NULL) { + error = ENOMEM; + } else { + used_slice_hbas = dlist_append( + item, used_slice_hbas, AT_HEAD); + } + } + } + + /* + * iterate the list of chosen slices and for each, + * determine how many other slices from its HBA(s) + * are already being used... + * + * iter steps thru the list of slices + * iter1 steps thru each of the slice's HBAs + * iter2 steps thru the precomputed list of used slice's HBAs + * dlist_contains then searches each used slice's HBAs + * to see if it contains iter1's HBA + * + * If it does, increment the count for that HBA. + */ + for (iter = slices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + dlist_t *hbas = NULL; + int n = 0; /* # slices each HBA contributes */ + + if ((error = slice_get_hbas(slice, &hbas)) != 0) { + continue; + } + + for (iter1 = hbas; iter1 != NULL; iter1 = iter1->next) { + for (iter2 = used_slice_hbas; iter2 != NULL; + iter2 = iter2->next) { + + dlist_t *uhbas = (dlist_t *)iter2->obj; + if (dlist_contains(uhbas, iter1->obj, + compare_descriptor_names) == B_TRUE) { + n++; + } + } + } + + dlist_free_items(hbas, NULL); + + /* group slices from HBAs contributing more than maxuses */ + if (n >= maxuses) { + n = maxuses - 1; + } + + /* add slice to list in descending size order */ + if ((item = dlist_new_item((void*)slice)) == NULL) { + error = ENOMEM; + } else { + prefhbas[n] = + dlist_insert_ordered( + item, + prefhbas[n], + DESCENDING, + compare_slice_sizes); + } + } + + /* free list of lists of used slices HBAs */ + for (iter = used_slice_hbas; iter != NULL; iter = iter->next) { + dlist_free_items((dlist_t *)iter->obj, NULL); + } + dlist_free_items(used_slice_hbas, NULL); + + /* + * Select the list of slices that are on the HBA(s) contributing + * the fewest slices... iterate these slices and for each, detemmine + * how many other slices from its disk are already being used... + */ + for (i = 0; (i < maxuses) && (error == 0); i++) { + + for (iter = (dlist_t *)prefhbas[i]; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + dm_descriptor_t disk; + int n = 0; + + (void) slice_get_disk(slice, &disk); + + /* + * count how many slices this slice's disk is contributing + * by comparing it to the list of used slices + */ + for (iter1 = _used_slices; iter1 != NULL; iter1 = iter1->next) { + usedslice_t *used = (usedslice_t *)iter1->obj; + if (compare_descriptors( + (void *)disk, (void *)used->disk) == 0) { + n++; + } + } + + /* add slice to list in descending size order */ + if ((item = dlist_new_item((void *)slice)) == NULL) { + error = ENOMEM; + } else { + prefdisks[n] = + dlist_insert_ordered( + item, + prefdisks[n], + DESCENDING, + compare_slice_sizes); + } + } + } + + if (error == 0) { + /* select largest slice from least used disk */ + for (i = 0; (i < maxslices) && (*chosen == NULL); i++) { + if (prefdisks[i] != NULL) { + *chosen = (uintptr_t)prefdisks[i]->obj; + } + } + } + + for (i = 0; i < maxuses; i++) { + dlist_free_items(prefhbas[i], NULL); + } + for (i = 0; i < maxslices; i++) { + dlist_free_items(prefdisks[i], NULL); + } + + free((void*)prefhbas); + free((void*)prefdisks); + + return (error); +} + +/* + * FUNCTION: slice_on_unique_hba(dm_descriptor_t slice, + * dlist_t *used, dlist_t *used_hbas, + * boolean_t *unique) + * + * INPUT: slice - a dm_descriptor_t handle for the slice of interest + * used - a dlist_t pointer to a list of used slices + * used_hbas - a dlist_t pointer to a list of used_hbas + * unique - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input slice is connected thru the same HBA + * as a slice in the used list. + * + * Also checks to see if the input slice is connected thru any + * HBA in the used_hbas list. + * + * If the slice is found to be on a unique HBA, bool is set + * to B_TRUE, B_FALSE otherwise. + */ +static int +slice_on_unique_hba( + dm_descriptor_t slice, + dlist_t *used, + dlist_t *used_hbas, + boolean_t *unique) +{ + dlist_t *iter = NULL; + dlist_t *iter1 = NULL; + + dlist_t *hbas = NULL; + + int error = 0; + + *unique = B_TRUE; + + if ((error = slice_get_hbas(slice, &hbas)) != 0) { + return (error); + } + + /* + * check to see if any of slice's HBAs is the same + * as the HBA for any of the used + */ + for (iter = used; + (iter != NULL) && (*unique == B_TRUE) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + if (devconfig_isA(dev, TYPE_SLICE)) { + + dm_descriptor_t odisk = NULL; + char *oname = NULL; + dlist_t *ohbas = NULL; + + /* get HBAs for other slice using its disk */ + /* because the slice doesn't exist yet. */ + ((error = devconfig_get_name(dev, &oname)) != 0) || + (error = get_disk_for_named_slice(oname, &odisk)) || + (error = disk_get_hbas(odisk, &ohbas)); + + /* any HBA overlap? */ + for (iter1 = hbas; + (iter1 != NULL) && (*unique == B_TRUE) && (error == 0); + iter1 = iter1->next) { + + if (dlist_contains(ohbas, iter1->obj, + compare_descriptor_names) == B_TRUE) { + *unique = B_FALSE; + } + } + dlist_free_items(ohbas, NULL); + } + } + + /* + * check to see if any of slice's HBAs is the contained + * in the list of used hbas + */ + for (iter = hbas; + (iter != NULL) && (*unique == B_TRUE) && (error == 0); + iter = iter->next) { + if (dlist_contains(used_hbas, + iter->obj, compare_descriptor_names) == B_TRUE) { + *unique = B_FALSE; + } + } + + dlist_free_items(hbas, NULL); + + return (error); +} + +/* + * FUNCTION: slice_on_unique_disk(dm_descriptor_t slice, + * dlist_t *used, dlist_t *used_disks, + * boolean_t *unique) + * + * INPUT: slice - a dm_descriptor_t handle for the slice of interest + * used - a dlist_t pointer to a list of used slices + * othervols - a dlist_t pointer to a list of other volumes + * bool - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input slice is on a drive that is not + * part of any volume in the othervols list, or on the same + * drive as any slice in the used list. + * + * If the slice is found to be on a unique disk, bool is set + * to B_TRUE, B_FALSE otherwise. + */ +static int +slice_on_unique_disk( + dm_descriptor_t slice, + dlist_t *used, + dlist_t *used_disks, + boolean_t *unique) +{ + dm_descriptor_t disk = NULL; + dlist_t *iter = NULL; + int error = 0; + + *unique = B_TRUE; + + if ((error = slice_get_disk(slice, &disk)) != 0) { + return (error); + } + + /* + * check to see if this disk is the same as the + * disk for any of the used + */ + for (iter = used; + (iter != NULL) && (*unique == B_TRUE) && (error == 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + + if (devconfig_isA(dev, TYPE_SLICE)) { + + /* get disk for otherslice */ + dm_descriptor_t odisk = NULL; + char *oname = NULL; + + ((error = devconfig_get_name(dev, &oname)) != 0) || + (error = get_disk_for_named_slice(oname, &odisk)); + + if ((error == 0) && + (compare_descriptor_names( + (void*)disk, (void*)odisk) == 0)) { + /* origslice is on same disk, stop */ + *unique = B_FALSE; + } + } + } + + /* check disk against the used disks */ + if ((error == 0) && (*unique == B_TRUE) && + dlist_contains(used_disks, (void *)disk, + compare_descriptor_names) == B_TRUE) { + *unique = B_FALSE; + } + + return (error); +} + +/* + * FUNCTION: slice_has_same_disk_geom(dm_descriptor_t slice, + * dlist_t *used, boolean_t *has_same_geom) + * + * INPUT: slice - a dm_descriptor_t handle for the slice of interest + * used - a dlist_t pointer to a list of used slices + * bool - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input slice is on a drive with similar + * hardware geometry as the slices in the used list. + * + * If the slice is found to be on a disk with similar geometry, + * bool is set to B_TRUE, B_FALSE otherwise. + * + * The comparison is based on the available disk geometry + * information which may not be relevant or accurate for + * EFI labeled disks, so the disk drive type needs to be + * checked as well. + */ +static int +slice_has_same_disk_geom( + dm_descriptor_t slice, + dlist_t *used, + boolean_t *has_same_geom) +{ + dm_descriptor_t disk = NULL; + boolean_t efi = B_FALSE; + uint64_t bsize = 0; + uint64_t ncyls = 0; + uint64_t nsects = 0; + uint64_t nheads = 0; + dlist_t *iter = NULL; + int error = 0; + + *has_same_geom = B_TRUE; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = disk_get_is_efi(disk, &efi)) || + (error = disk_get_blocksize(disk, &bsize)); + + if ((error == 0) && (efi == B_FALSE)) { + ((error = disk_get_ncylinders(disk, &ncyls)) != 0) || + (error = disk_get_nheads(disk, &nheads)) || + (error = disk_get_nsectors(disk, &nsects)); + } + + if (error != 0) { + return (error); + } + + /* + * check to see if slice's disk has the same geometry + * as the disks for the slices in the used list + */ + for (iter = used; + (iter != NULL) && (*has_same_geom == B_TRUE) && (error = 0); + iter = iter->next) { + + devconfig_t *dev = (devconfig_t *)iter->obj; + + if (devconfig_isA(dev, TYPE_SLICE)) { + + /* get disk info for otherslice */ + dm_descriptor_t odisk = NULL; + char *oname = NULL; + boolean_t oefi = B_FALSE; + uint64_t obsize = 0; + uint64_t oncyls = 0; + uint64_t onsects = 0; + uint64_t onheads = 0; + + ((error = devconfig_get_name(dev, &oname)) != 0) || + (error = get_disk_for_named_slice(oname, &odisk)) || + (error = disk_get_is_efi(odisk, &oefi)) || + (error = disk_get_blocksize(odisk, &obsize)); + + if ((error == 0) && (oefi == B_FALSE)) { + ((error = disk_get_ncylinders(odisk, &oncyls)) != 0) || + (error = disk_get_nheads(odisk, &onheads)) || + (error = disk_get_nsectors(odisk, &onsects)); + } + + if (error == 0) { + if ((bsize != obsize) || (ncyls != oncyls) || + (nsects != onsects) || (nheads != onheads)) { + /* this disk has a different geometry */ + *has_same_geom = B_FALSE; + } + } + } + } + + return (error); +} + +/* + * FUNCTION: slice_on_similar_bus(dm_descriptor_t slice, + * dlist_t *used, boolean_t *on_smlr_bus) + * + * INPUT: slice - a dm_descriptor_t handle for the slice of interest + * used - a dlist_t pointer to a list of used slices + * bool - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input slice is connected thru a bus with + * characteristics similar to the slices in the used list. + * + * If the slice is found to be on a similar bus, bool is set + * to B_TRUE, B_FALSE otherwise. + * + * The comparison is actually between any of the HBA/controllers + * thru which the slices are connected to the system. + * If any are of similar type (e.g., fibre, SCSI) and + * protocol (SCSI-2, -3, fast/wide), then the slices are + * considered to be on similar busses. + */ +static int +slice_on_similar_bus( + dm_descriptor_t slice, + dlist_t *used, + boolean_t *on_smlr_bus) +{ + dlist_t *iter = NULL; + dlist_t *iter1 = NULL; + dlist_t *hbas = NULL; + int error = 0; + + /* if there are no used slices, then the bus is similar */ + *on_smlr_bus = B_TRUE; + if (dlist_length(used) == 0) { + return (0); + } + + (error = slice_get_hbas(slice, &hbas)); + if (error != 0) { + return (error); + } + + /* if there are used slices, then make sure the bus is similar */ + *on_smlr_bus = B_FALSE; + for (iter = hbas; + (iter != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + char *type = NULL; + boolean_t fast80 = B_FALSE; + boolean_t fast40 = B_FALSE; + boolean_t fast20 = B_FALSE; + boolean_t wide = B_FALSE; + + ((error = hba_get_type(hba, &type)) != 0) || + (error = hba_is_fast_80(hba, &fast80)) || + (error = hba_is_fast_40(hba, &fast40)) || + (error = hba_is_fast_20(hba, &fast20)) || + (error = hba_supports_wide(hba, &wide)); + if (error != 0) { + continue; + } + + /* check against the HBAs for the used slices */ + for (iter1 = used; + (iter1 != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0); + iter1 = iter1->next) { + + devconfig_t *used = (devconfig_t *)iter1->obj; + + /* get HBAs for otherslice */ + dm_descriptor_t udisk = NULL; + char *uname = NULL; + dlist_t *uhbas = NULL; + dlist_t *iter2 = NULL; + + ((error = devconfig_get_name(used, &uname)) != 0) || + (error = get_disk_for_named_slice(uname, &udisk)) || + (error = disk_get_hbas(udisk, &uhbas)); + + for (iter2 = uhbas; + (iter2 != NULL) && (*on_smlr_bus == B_FALSE) && + (error == 0); + iter2 = iter2 ->next) { + + dm_descriptor_t uhba = (uintptr_t)iter2->obj; + char *utype = NULL; + boolean_t ufast80 = B_FALSE; + boolean_t ufast40 = B_FALSE; + boolean_t ufast20 = B_FALSE; + boolean_t uwide = B_FALSE; + + ((error = hba_get_type(uhba, &utype)) != 0) || + (error = hba_is_fast_80(uhba, &ufast80)) || + (error = hba_is_fast_40(uhba, &ufast40)) || + (error = hba_is_fast_20(uhba, &ufast20)) || + (error = hba_supports_wide(uhba, &uwide)); + + if (error == 0) { + /* check sync speed ? */ + if ((fast80 == ufast80) && (fast40 == ufast40) && + (fast20 == ufast20) && (wide == uwide) && + (type == utype)) { + *on_smlr_bus = B_TRUE; + } + } + } + dlist_free_items(uhbas, NULL); + } + } + + dlist_free_items(hbas, NULL); + + return (error); +} + +/* + * FUNCTION: slice_has_n_paths(dm_descriptor_t slice, + * uint16_t npaths, boolean_t *has_n_paths) + * INPUT: slice - a dm_descriptor_t handle for the slice of interest + * npaths - the number of paths desired + * has_n_paths - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determines if the input slice is connected via npaths. + * has_n_paths is set to B_TRUE if so, B_FALSE otherwise. + * + * In order for a disk to have multiple paths, MPXIO must + * be enabled and these conditions should hold: + * + * Slice will have one drive object. + * Drive will have one HBA (scsi_vhci) + * Drive will have one alias. + * Drive will have possibly > 1 paths. + * + * Getting the HBAs and aliases for the disk is relatively + * expensive, so they aren't checked. The actual number of + * paths is only checked if MPXIO is known to be enabled on + * the system and the input npaths is > 1. + */ +static int +slice_has_n_paths( + dm_descriptor_t slice, + uint16_t npaths, + boolean_t *has_n_paths) +{ + int error = 0; + + *has_n_paths = B_FALSE; + + if ((npaths > 1) && (is_mpxio_enabled() == B_TRUE)) { + + dm_descriptor_t disk = NULL; + dlist_t *paths = NULL; + + ((error = slice_get_disk(slice, &disk)) != 0) || + (error = disk_get_paths(disk, &paths)); + + if ((error == 0) && (dlist_length(paths) == npaths)) { + *has_n_paths = B_TRUE; + } + dlist_free_items(paths, NULL); + } + + return (error); +} + +/* + * FUNCTION: compare_string_to_modslice_name(void *str, void *modslice) + * + * INPUT: str - opaque char * pointer + * modslice - opaque modslice_t pointer + * + * RETURNS: int - <0 - if str < modslice->slice_devcfg.name + * 0 - if str == modslice->slice_devcfg.name + * >0 - if str > modslice->slice_devcfg.name + * + * PURPOSE: dlist_t helper which compares the input string to + * the name of a slice represented as modslice_t struct. + * + * Comparison is done via string_case_compare. + */ +static int +compare_string_to_modslice_name( + void *str, + void *modslice) +{ + char *name = NULL; + + assert(str != NULL); + assert(modslice != NULL); + + (void) devconfig_get_name( + ((modslice_t *)modslice)->slice_devcfg, &name); + + return (string_case_compare((char *)str, name)); +} + +/* + * FUNCTION: compare_modslice_names(void *obj1, void *obj2) + * + * INPUT: obj1 - opaque pointer + * obj2 - opaque pointer + * + * RETURNS: int - <0 - if obj1 name < obj2 name + * 0 - if obj1 name == obj2 name + * >0 - if obj1 name > obj2 name + * + * PURPOSE: dlist_t helper which compares the names of two slices + * represented as modslice_t structs. + * + * Comparison is done by string_case_compare + */ +static int +compare_modslice_names( + void *obj1, + void *obj2) +{ + char *name1 = NULL; + char *name2 = NULL; + + assert(obj1 != NULL); + assert(obj2 != NULL); + + (void) devconfig_get_name( + ((modslice_t *)obj1)->slice_devcfg, &name1); + (void) devconfig_get_name( + ((modslice_t *)obj2)->slice_devcfg, &name2); + + return (string_case_compare(name1, name2)); +} + +/* + * FUNCTION: release_used_slices() + * + * PURPOSE: Helper which cleans up the module private list of used + * slices. + */ +void +release_used_slices() +{ + dlist_free_items(_used_slices, free_used_slice); + _used_slices = NULL; +} + +static void +free_used_slice( + void *obj) +{ + if (obj != NULL) { + usedslice_t *used = (usedslice_t *)obj; + free(used->slicename); + free(used); + } +} + +/* + * FUNCTION: is_used_slice(dm_descriptor_t slice, boolean_t *is_used) + * + * INPUT: slice - a dm_descriptor_t slice handle + * + * OUTPUT: is_reserved - pointer to a boolean_t to hold the + * return result. + * + * PURPOSE: Helper which checks to see if the input slice + * is in the used_slice list. + * + * Check the input name against any used slice name or alias. + * is_used is set to B_TRUE if the input slice is already used, + * B_FALSE otherwise. + */ +int +is_used_slice( + dm_descriptor_t slice, + boolean_t *is_used) +{ + char *name; + int error = 0; + + if ((error = get_display_name(slice, &name)) == 0) { + *is_used = dlist_contains(_used_slices, (void *)name, + compare_usedslice_name_to_string); + } + + return (error); +} + +/* + * FUNCTIONS: add_used_slice(dm_descriptor_t slice) + * add_used_slice_by_name(char *slicename) + * add_used_slice_list_entry(char *slice) + * remove_used_slice_by_name(char *slicename) + * + * INPUT: diskset - a char * diskset name. + * slice - a dm_descriptor_t slice handle + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Access or maintain the list of used slices. + */ +int +add_used_slice( + dm_descriptor_t slice) +{ + dm_descriptor_t disk; + char *name; + int error = 0; + + assert(slice != (dm_descriptor_t)0); + + ((error = get_display_name(slice, &name)) != 0) || + (error = slice_get_disk(slice, &disk)) || + (error = add_used_slice_list_entry(name, disk)); + + return (error); +} + +int +add_used_slice_by_name( + char *slicename) +{ + dm_descriptor_t disk = (dm_descriptor_t)0; + int error = 0; + + assert(slicename != NULL); + + /* find disk for slice */ + error = get_disk_for_named_slice(slicename, &disk); + if (error == 0) { + error = add_used_slice_list_entry(slicename, disk); + } + + return (error); +} + +static int +add_used_slice_list_entry( + char *slicename, + dm_descriptor_t disk) +{ + usedslice_t *used = NULL; + int error = 0; + + assert(slicename != NULL); + assert(disk != (dm_descriptor_t)0); + + used = (usedslice_t *)calloc(1, sizeof (usedslice_t)); + if (used == NULL) { + error = ENOMEM; + } else { + + used->disk = disk; + if ((used->slicename = strdup(slicename)) == NULL) { + free(used); + error = ENOMEM; + } else { + dlist_t *item = dlist_new_item((void *) used); + if (item == NULL) { + free(used->slicename); + free(used); + error = ENOMEM; + } else { + _used_slices = + dlist_append(item, _used_slices, AT_HEAD); + } + } + } + return (error); +} + +int +remove_used_slice_by_name( + char *slice) +{ + dlist_t *removed = NULL; + + _used_slices = + dlist_remove_equivalent_item(_used_slices, (void *)slice, + compare_usedslice_name_to_string, &removed); + + if (removed != NULL) { + free_used_slice(removed->obj); + removed->obj = NULL; + free(removed); + } + + return (0); +} + +/* + * FUNCTION: compare_usedslice_name_to_string(void *obj1, void *obj2) + * INPUT: obj1 - opaque pointer + * obj2 - opaque pointer + * + * RETURNS: int - <0 - if obj1 name < obj2 name + * 0 - if obj1 name == obj2 name + * >0 - if obj1 name > obj2 name + * + * PURPOSE: dlist_t helper which compares the names of a slice + * represented as modslice_t struct to a string. + * + * obj1 is assumed to be a char * + * obj2 is assumed to be a usedslice_t * + * + * Comparison is done via string_case_compare. + */ +static int +compare_usedslice_name_to_string( + void *obj1, + void *obj2) +{ + assert(obj1 != NULL); + assert(obj2 != NULL); + + return (string_case_compare((char *)obj1, + ((usedslice_t *)obj2)->slicename)); +} + +/* + * FUNCTION: disk_has_used_slice(dm_descriptor_t disk, boolean_t *hasused) + * + * INPUT: disk - a dm_descriptor_t disk handle. + * inuse - a boolean_t pointer to hold the result + * + * RETURNS: int - 0 on success + * !0 othersize. + * + * PURPOSE: Determines if any of the known used slices is on the + * input disk. + */ +int +disk_has_used_slice( + dm_descriptor_t disk, + boolean_t *hasused) +{ + dlist_t *iter; + int error = 0; + + *hasused = B_FALSE; + for (iter = _used_slices; + (iter != NULL) && (*hasused == B_FALSE); + iter = iter->next) { + + usedslice_t *used = (usedslice_t *)iter->obj; + + /* compare used slice's disk to disk */ + if (compare_descriptors((void *)disk, (void *)used->disk) == 0) { + *hasused = B_TRUE; + } + } + + return (error); +} + +/* + * FUNCTION: add_reserved_slice(dm_descriptor_t slice) + * + * INPUT: slice - a dm_descriptor_t slice handle + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which remembers specfically requested slices + * in a private list to ensure that the same slice isn't + * requested more than once. + * + * Does not check to see if the slice already exists + * in the list of reserved slices. Assumes that the + * caller has checked using is_reserved_slice(). + * + * The reserved slice list is used by several functions: + * + * 1. layout_validate.validate_slice_components() adds user + * requested slices to the list. + * + * 2. After all potentially usable slices have been scanned, + * layout_validate.validate_reserved_slices() checks the + * slices in the reserved and ensures that each slice is + * actually usable as a volume component. + * + * 3. layout.disk_get_avail_space(), layout.disk_get_avail_slices() + * exclude slices in the reserved list from being considered + * available for general layout use. + */ +int +add_reserved_slice( + dm_descriptor_t slice) +{ + dlist_t *item = NULL; + + if ((item = dlist_new_item((void *)slice)) == NULL) { + return (ENOMEM); + } + + _rsvd_slices = dlist_append(item, _rsvd_slices, AT_HEAD); + + return (0); +} + +/* + * FUNCTION: is_reserved_slice(dm_descriptor_t slice, + * boolean_t *is_reserved) + * + * INPUT: slice - a dm_descriptor_t slice handle + * + * OUTPUT: is_reserved - pointer to a boolean_t to hold the + * return result. + * + * PURPOSE: Helper which checks to see if the input slice + * was previously reserved. + * + * Check the input name against any reserved slice + * name or alias. is_reserved is set to B_TRUE if the + * input slice is already reserved, B_FALSE otherwise. + */ +int +is_reserved_slice( + dm_descriptor_t slice, + boolean_t *is_reserved) +{ + *is_reserved = dlist_contains( + _rsvd_slices, (void *)slice, compare_descriptor_names); + + return (0); +} + +/* + * FUNCTION: release_reserved_slice() + * + * PURPOSE: Helper which cleans up the module private list of reserved + * slices. + */ +void +release_reserved_slices() +{ + dlist_free_items(_rsvd_slices, free); + _rsvd_slices = NULL; +} + +/* + * FUNCTION: get_reserved_slices(dlist_t **list) + * + * OUTPUT: list - a dlist_t pointer to hold the returned list of + * reserverd slices. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Accessor to retrieve the current list of reserved slice + * dm_descriptor_t handles. + */ +int +get_reserved_slices( + dlist_t **list) +{ + *list = _rsvd_slices; + + return (0); +} + +/* + * FUNCTION: add_slice_to_remove(char *name, uint32_t index) + * + * INPUT: name - name of a slice + * index - index for the slice + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Utility function to add the named slice to the list of + * those that need to be "removed" by having their sizes + * set to 0. + */ +int +add_slice_to_remove( + char *name, + uint32_t index) +{ + rmvdslice_t *rmvd = NULL; + int error = 0; + + assert(name != NULL); + + rmvd = (rmvdslice_t *)calloc(1, sizeof (rmvdslice_t)); + if (rmvd == NULL) { + error = ENOMEM; + } else { + rmvd->slice_index = index; + if ((rmvd->slice_name = strdup(name)) == NULL) { + free(rmvd); + error = ENOMEM; + } else { + dlist_t *item = dlist_new_item((void *) rmvd); + if (item == NULL) { + free(rmvd->slice_name); + free(rmvd); + error = ENOMEM; + } else { + _rmvd_slices = + dlist_append(item, _rmvd_slices, AT_HEAD); + } + } + } + return (error); +} + +/* + * FUNCTION: get_removed_slices() + * + * RETURNS: dlist_t * - pointer to a list of rmvdslice_t structs + * + * PURPOSE: Accessor to retrieve the current list of names of slices + * to be removed. + */ +dlist_t * +get_slices_to_remove( + dlist_t **list) +{ + return (_rmvd_slices); +} + +static void +free_rmvd_slice( + void *obj) +{ + if (obj != NULL) { + rmvdslice_t *rmvd = (rmvdslice_t *)obj; + free(rmvd->slice_name); + free(rmvd); + } +} + +/* + * FUNCTION: release_removed_slices() + * + * PURPOSE: Helper which cleans up the module private list of removed + * slices. + */ +void +release_slices_to_remove() +{ + dlist_free_items(_rmvd_slices, free_rmvd_slice); + _rmvd_slices = NULL; +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_slice.h b/usr/src/cmd/lvm/metassist/layout/layout_slice.h new file mode 100644 index 0000000000..5a3febdf65 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_slice.h @@ -0,0 +1,135 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_SLICE_H +#define _LAYOUT_SLICE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libdiskmgt.h" +#include "volume_devconfig.h" +#include "volume_dlist.h" + +/* + * struct to track which slices need to be explicitly "removed" from + * the system before applying any metassist updates/changes. + */ +typedef struct { + char *slice_name; + uint32_t slice_index; +} rmvdslice_t; + +extern void release_slices_to_remove(); +extern dlist_t *get_slices_to_remove(); +extern int add_slice_to_remove(char *name, uint32_t index); + +/* + * struct to track which slices have been explicitly modified + * during the layout process... + * + * src_slice_desc is the dm_descriptor_t of the slice which provided the + * space (this is only relevant to slices that have been created by + * taking space from some other "source" slice). + * slice_devconfig is the devconfig_t struct with the modified slice properties. + * times_modified is the number of times the slice has been modified + * (this is only relevant to slices that have been resized to + * provide space for new slices) + * volume_component is used to control when the slice_devcfg is freed. + * if volume_component is B_TRUE, the devconfig is returned as part + * of the result of layout and so cannot be freed by + * release_modified_slices. + */ +typedef struct { + dm_descriptor_t src_slice_desc; + devconfig_t *slice_devcfg; + int times_modified; + boolean_t volume_component; +} modslice_t; + +extern dlist_t *get_modified_slices(); +extern int release_modified_slices(); + +extern int make_slicename_for_diskname_and_index( + char *diskname, + uint16_t index, + char **slicename); + +extern int assemble_modified_slice( + dm_descriptor_t src_slice_desc, + char *mod_name, + uint32_t mod_index, + uint64_t mod_stblk, + uint64_t mod_nblks, + uint64_t mod_size, + devconfig_t **mod_slice); + +extern int choose_slice( + uint64_t nbytes, + uint16_t npaths, + dlist_t *slices, + dlist_t *used, + dlist_t *used_hbas, + dlist_t *used_disks, + boolean_t unused_disk, + boolean_t nbytes_is_min, + boolean_t add_extra_cyl, + devconfig_t **chosen); + +extern int create_devconfig_for_slice( + dm_descriptor_t slice, + devconfig_t **newslice); + +extern int destroy_new_slice( + devconfig_t *vol); + +/* + * accessors for the list of used slice names for named diskset. + */ +extern int is_used_slice(dm_descriptor_t slice, boolean_t *is_used); +extern int add_used_slice_by_name(char *slicename); +extern int remove_used_slice_by_name(char *slicename); +extern int add_used_slice(dm_descriptor_t slice); +extern void release_used_slices(); +extern int disk_has_used_slice(dm_descriptor_t disk, boolean_t *inuse); + +/* + * accessors to track slices reserved for use in explicit + * volume requests + */ +extern int add_reserved_slice(dm_descriptor_t slice); +extern int is_reserved_slice(dm_descriptor_t slice, boolean_t *is_rsvd); +extern int get_reserved_slices(dlist_t **list); +extern void release_reserved_slices(); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_SLICE_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_stripe.c b/usr/src/cmd/lvm/metassist/layout/layout_stripe.c new file mode 100644 index 0000000000..cfb4ed160e --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_stripe.c @@ -0,0 +1,1034 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> + +#include <libintl.h> + +#include "volume_error.h" +#include "volume_devconfig.h" +#include "volume_dlist.h" +#include "volume_output.h" + +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_messages.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_svm_util.h" + +#define _LAYOUT_STRIPE_C + +static int compose_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + int max, + int min, + dlist_t *othervols, + devconfig_t **stripe); + +static int compose_stripe_within_hba( + devconfig_t *request, + dlist_t *hbas, + uint64_t nbytes, + uint16_t min, + uint16_t max, + devconfig_t **stripe); + +static int assemble_stripe( + devconfig_t *request, + dlist_t *comps, + devconfig_t **stripe); + +static dlist_t * +order_stripe_components_alternate_hbas( + dlist_t *comps); + +static int compute_usable_stripe_capacity( + dlist_t *comps, + uint64_t ilace, + uint64_t *nbytes); + +/* + * FUNCTION: layout_stripe(devconfig_t *request, uint64_t nbytes, + * dlist_t **results) + * + * INPUT: request - pointer to a devconfig_t of the current request + * nbytes - the desired capacity of the stripe + * + * OUPUT: results - pointer to a list of composed volumes + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Main layout driver for composing stripe volumes. + * + * Attempts to construct a stripe of size nbytes. + * + * Basic goal of all strategies is to build wide-thin stripes: + * build widest stripe possible across as many HBAs as possible. + * + * Several different layout strategies are tried in order + * of preference until one succeeds or there are none left. + * + * 1 - stripe across similar HBAs + * . number of components is driven by # of HBAs + * . requires mincomp available HBAs + * + * 2 - stripe within a single HBA + * . number of components is driven by # of disks + * . requires at least 1 HBA with mincomp disks + * + * 3 - stripe across all available disks on similar HBAs + * . number of components is driven by # of disk + * . requires at least mincomp disks + * + * 4 - stripe across all available HBAs + * . number of components is driven by # of HBAs + * . requires at least mincomp HBAs + * + * 5 - stripe across all available disks on all HBAs + * . number of components is driven by # of disks + * . requires at least mincomp disks + * + * Each strategy tries to compose a stripe with the + * maximum number of components first then reduces the + * number of components down to mincomp. + * + * get allowed minimum number of stripe components + * get allowed maximum number of stripe components + * get available HBAs + * + * group HBAs by characteristics + * for (each HBA grouping) and (stripe not composed) { + * select next HBA group + * for (strategy[1,2,3]) and (stripe not composed) { + * compose stripe using HBAs in group + * } + * } + * + * if (stripe not composed) { + * for (strategy[4,5]) and (stripe not composed) { + * compose stripe using all HBAs + * } + * } + * + * if (stripe composed) { + * append composed stripe to results + * } + * + */ +int +layout_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t **results) +{ + /* + * these enums define the # of strategies and the preference order + * in which they are tried + */ + typedef enum { + STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER = 0, + STRIPE_WITHIN_SIMILAR_HBA, + STRIPE_ACROSS_SIMILAR_HBAS, + N_SIMILAR_HBA_STRATEGIES + } similar_hba_strategy_order_t; + + typedef enum { + STRIPE_ACROSS_ANY_HBAS_DISK_PER = 0, + STRIPE_ACROSS_ANY_HBAS, + N_ANY_HBA_STRATEGIES + } any_hba_strategy_order_t; + + + dlist_t *usable_hbas = NULL; + dlist_t *similar_hba_groups = NULL; + dlist_t *iter = NULL; + devconfig_t *stripe = NULL; + + uint16_t mincomp = 0; + uint16_t maxcomp = 0; + + int error = 0; + + (error = get_usable_hbas(&usable_hbas)); + if (error != 0) { + return (error); + } + + print_layout_volume_msg(devconfig_type_to_str(TYPE_STRIPE), nbytes); + + if (dlist_length(usable_hbas) == 0) { + print_no_hbas_msg(); + volume_set_error(gettext("There are no usable HBAs.")); + return (-1); + } + + ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) || + + /* + * determine the min/max number of stripe components + * based on the request, the diskset defaults or the + * global defaults. These are absolute limits, the + * actual values are determined by the number of HBAs + * and/or disks available. + */ + (error = get_stripe_min_comp(request, &mincomp)) || + (error = get_stripe_max_comp(request, &maxcomp)); + if (error != 0) { + return (error); + } + + for (iter = similar_hba_groups; + (error == 0) && (stripe == NULL) && (iter != NULL); + iter = iter->next) { + + dlist_t *hbas = (dlist_t *)iter->obj; + + similar_hba_strategy_order_t order; + + for (order = STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER; + (order < N_SIMILAR_HBA_STRATEGIES) && + (stripe == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 1: use 1 disk from %d-%d similar HBAs - stripe across HBAs\n"), + mincomp, maxcomp); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= mincomp) { + n = ((n > maxcomp) ? maxcomp : n); + error = compose_stripe( + request, nbytes, disks, n, + mincomp, NULL, &stripe); + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case STRIPE_WITHIN_SIMILAR_HBA: + + error = select_hbas_with_n_disks( + request, hbas, mincomp, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 2: use %d-%d disks from any single HBA - stripe within HBA\n"), + mincomp, maxcomp); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) > 0) { + error = compose_stripe_within_hba( + request, selhbas, nbytes, + mincomp, maxcomp, &stripe); + } else { + print_insufficient_disks_msg(n); + } + } + + break; + + case STRIPE_ACROSS_SIMILAR_HBAS: + + error = select_hbas_with_n_disks( + request, hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 3: use %d-%d disks from %d similar HBAs - stripe across HBAs\n"), + mincomp, maxcomp, dlist_length(hbas)); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) > 0) { + if ((n = dlist_length(disks)) >= mincomp) { + n = ((n > maxcomp) ? maxcomp : n); + error = compose_stripe( + request, nbytes, disks, n, + mincomp, NULL, &stripe); + } else { + print_insufficient_disks_msg(n); + } + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + default: + break; + } + + dlist_free_items(disks, NULL); + dlist_free_items(selhbas, NULL); + } + } + + for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { + dlist_free_items((dlist_t *)iter->obj, NULL); + } + dlist_free_items(similar_hba_groups, NULL); + + /* + * if striping within similar HBA groups failed, + * try across all available HBAs + */ + if ((stripe == NULL) && (error == 0)) { + + any_hba_strategy_order_t order; + + for (order = STRIPE_ACROSS_ANY_HBAS_DISK_PER; + (order < N_ANY_HBA_STRATEGIES) && + (stripe == NULL) && (error == 0); + order++) { + + dlist_t *selhbas = NULL; + dlist_t *disks = NULL; + int n = 0; + + switch (order) { + + case STRIPE_ACROSS_ANY_HBAS_DISK_PER: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 4: use 1 disk from %d-%d available HBAs - stripe across any HBAs\n"), + mincomp, maxcomp); +/* END CSTYLED */ + + if ((n = dlist_length(selhbas)) >= mincomp) { + + n = ((n > maxcomp) ? maxcomp : n); + error = compose_stripe( + request, nbytes, disks, n, + mincomp, NULL, &stripe); + + } else { + print_insufficient_hbas_msg(n); + } + } + + break; + + case STRIPE_ACROSS_ANY_HBAS: + + error = select_hbas_with_n_disks( + request, usable_hbas, 1, &selhbas, &disks); + + if (error == 0) { + +/* BEGIN CSTYLED */ +oprintf(OUTPUT_TERSE, +gettext(" -->Strategy 5: use %d-%d disks from %d available HBA - stripe across any HBAs\n"), + mincomp, maxcomp, dlist_length(selhbas)); +/* END CSTYLED */ + + if ((n = dlist_length(disks)) >= mincomp) { + + n = ((n > maxcomp) ? maxcomp : n); + error = compose_stripe( + request, nbytes, disks, n, + mincomp, NULL, &stripe); + + } else { + print_insufficient_disks_msg(n); + } + } + + break; + } + + dlist_free_items(disks, NULL); + dlist_free_items(selhbas, NULL); + } + } + + if (stripe != NULL) { + + dlist_t *item = NULL; + if ((item = dlist_new_item(stripe)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + print_layout_success_msg(); + } + + } else if (error != 0) { + + print_debug_failure_msg( + devconfig_type_to_str(TYPE_STRIPE), + get_error_string(error)); + + } else { + + print_insufficient_resources_msg( + devconfig_type_to_str(TYPE_STRIPE)); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: populate_stripe(devconfig_t *request, uint64_t nbytes, + * dlist_t *disks, uint16_t ncomp, dlist_t *othervols, + * devconfig_t **stripe) + * + * INPUT: request - pointer to a request devconfig_t + * nbytes - desired stripe size + * disks - pointer to a list of availalb disks + * ncomp - number of components desired + * othervols - pointer to a list of other volumes whose + * composition may affect this stripe + * (e.g., submirrors of the same mirror) + * + * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper to populate a stripe with the specified number of + * components and aggregate capacity using slices on disks + * in the input list. + * + * If the othervols list is not empty, the slice components + * chosen for the stripe must not on the same disks as any + * of the other volumes. + * + * If sufficient slice components can be found, the stripe + * is assembled and returned. + */ +int +populate_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + uint16_t ncomp, + dlist_t *othervols, + devconfig_t **stripe) +{ + uint16_t npaths = 0; + uint16_t ncomps = 0; /* number of components found */ + uint64_t rsize = 0; /* reqd component size */ + + dlist_t *other_hbas = NULL; + dlist_t *other_disks = NULL; + + dlist_t *slices = NULL; + dlist_t *comps = NULL; + + int error = 0; + + *stripe = NULL; + + ((error = disks_get_avail_slices(request, disks, &slices)) != 0) || + (error = get_volume_npaths(request, &npaths)); + if (error != 0) { + return (error); + } + + print_populate_volume_ncomps_msg( + devconfig_type_to_str(TYPE_STRIPE), nbytes, ncomp); + + if (slices == NULL) { + print_populate_no_slices_msg(); + return (0); + } + + /* determine HBAs and disks used by othervols */ + error = get_hbas_and_disks_used_by_volumes(othervols, + &other_hbas, &other_disks); + if (error != 0) { + dlist_free_items(other_hbas, NULL); + dlist_free_items(other_disks, NULL); + return (error); + } + + print_populate_choose_slices_msg(); + + /* + * each stripe component needs to be this size. + * Note that the stripe interlace doesn't need to be + * taken into account in this computation because any + * slice selected as a stripe component will be oversized + * to account for interlace and cylinder rounding done + * by libmeta. + */ + rsize = nbytes / ncomp; + + /* + * need to select 'ncomp' slices that are at least 'rsize' + * large in order to reach the desired capacity. + */ + ncomps = 0; + while ((ncomps < ncomp) && (error == 0)) { + + devconfig_t *comp = NULL; + dlist_t *item = NULL; + dlist_t *rmvd = NULL; + char *cname = NULL; + + /* BEGIN CSTYLED */ + /* + * 1st B_TRUE: require a different disk than those used by + * comps and othervols + * 2nd B_TRUE: requested size is minimum acceptable + * 3rd B_TRUE: add an extra cylinder to the resulting slice, this is + * necessary for Stripe components whose sizes get rounded + * down to an interlace multiple and then down to a cylinder + * boundary. + */ + /* END CSTYLED */ + error = choose_slice(rsize, npaths, slices, comps, + other_hbas, other_disks, B_TRUE, B_TRUE, B_TRUE, &comp); + + if ((error == 0) && (comp != NULL)) { + + ++ncomps; + + item = dlist_new_item(comp); + if (item == NULL) { + error = ENOMEM; + } else { + + /* add selected component to comp list */ + comps = dlist_insert_ordered( + item, + comps, + ASCENDING, + compare_devconfig_sizes); + + /* remove it from the available list */ + slices = dlist_remove_equivalent_item(slices, (void *) comp, + compare_devconfig_and_descriptor_names, &rmvd); + + if (rmvd != NULL) { + free(rmvd); + } + + /* add the component slice to the used list */ + if ((error = devconfig_get_name(comp, &cname)) == 0) { + error = add_used_slice_by_name(cname); + } + } + } else if (comp == NULL) { + /* no possible slice */ + break; + } + } + + dlist_free_items(slices, NULL); + dlist_free_items(other_hbas, NULL); + dlist_free_items(other_disks, NULL); + + if (ncomps == ncomp) { + + if ((error = assemble_stripe(request, comps, stripe)) == 0) { + print_populate_success_msg(); + } else { + dlist_free_items(comps, free_devconfig_object); + } + + } else if (error == 0) { + + if (ncomps > 0) { + print_insufficient_components_msg(ncomps); + dlist_free_items(comps, free_devconfig_object); + } else { + print_populate_no_slices_msg(); + } + + } + return (error); +} + +/* + * FUNCTION: populate_explicit_stripe(devconfig_t *request, + * dlist_t **results) + * + * INPUT: request - pointer to a request devconfig_t + * + * OUTPUT: results - pointer to a list of volume devconfig_t results + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Processes the input stripe request that specifies explicit + * slice components. + * + * The components have already been validated and reserved, + * all that is required is to create devconfig_t structs + * for each requested slice. + * + * The net size of the stripe is determined by the slice + * components. + * + * The stripe devconfig_t is assembled and appended to the + * results list. + * + * This function is also called from + * layout_mirror.populate_explicit_mirror() + */ +int +populate_explicit_stripe( + devconfig_t *request, + dlist_t **results) +{ + devconfig_t *stripe = NULL; + int error = 0; + + dlist_t *comps = NULL; + dlist_t *iter = NULL; + dlist_t *item = NULL; + + print_layout_explicit_msg(devconfig_type_to_str(TYPE_STRIPE)); + + /* assemble components */ + iter = devconfig_get_components(request); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + devconfig_t *rqst = (devconfig_t *)iter->obj; + dm_descriptor_t rqst_slice = NULL; + char *rqst_name = NULL; + devconfig_t *comp = NULL; + + /* slice components have been validated */ + /* turn each into a devconfig_t */ + ((error = devconfig_get_name(rqst, &rqst_name)) != 0) || + (error = slice_get_by_name(rqst_name, &rqst_slice)) || + (error = create_devconfig_for_slice(rqst_slice, &comp)); + + if (error == 0) { + + print_layout_explicit_added_msg(rqst_name); + + item = dlist_new_item((void *)comp); + if (item == NULL) { + error = ENOMEM; + } else { + comps = dlist_append(item, comps, AT_TAIL); + } + } + } + + if (error == 0) { + error = assemble_stripe(request, comps, &stripe); + } + + if (error == 0) { + if ((item = dlist_new_item(stripe)) == NULL) { + error = ENOMEM; + } else { + *results = dlist_append(item, *results, AT_TAIL); + print_populate_success_msg(); + } + } else { + dlist_free_items(comps, free_devconfig); + } + + return (error); +} + +/* + * FUNCTION: compose_stripe(devconfig_t *request, uint64_t nbytes, + * dlist_t *disks, uint16_t max, uint16_t min, + * dlist_t *othervols, devconfig_t **stripe) + * + * INPUT: request - pointer to a request devconfig_t + * nbytes - desired stripe size + * disks - pointer to a list of availalb disks + * max - maximum number of components allowed + * min - minimum number of components allowed + * othervols - pointer to a list of other volumes whose + * composition may affect this stripe + * (e.g., submirrors of the same mirror) + * + * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Attempt to compose a stripe of capacity nbytes, with + * component slices chosen from the input list of disks. + * The number of components in the stripe should be in the + * range min <= N <= max, more components are preferred. + * + * If a stripe can be composed, a pointer to it will be + * returned in the stripe devconfig_t. + * + * This is a loop wrapped around populate_stripe which + * varies the number of components between 'max' and 'min'. + */ +static int +compose_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + int max, + int min, + dlist_t *othervols, + devconfig_t **stripe) +{ + int error = 0; + + *stripe = NULL; + + for (; (error == 0) && (*stripe == NULL) && (max >= min); max--) { + error = populate_stripe( + request, nbytes, disks, max, othervols, stripe); + } + + return (error); +} + +/* + * FUNCTION: compose_stripe_within_hba(devconfig_t *request, + * dlist_t *hbas, uint64_t nbytes, + * int maxcomp, int mincomp, dlist_t **stripe) + * + * INPUT: request - pointer to a devconfig_t of the current request + * hbas - pointer to a list of available HBAs + * nbytes - the desired capacity for the stripe + * maxcomp - the maximum number of stripe components + * mincomp - the minimum number of stripe components + * + * OUTPUT: stripe - pointer to a stripe devconfig_t result + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Layout function which compose a stripe of the desired size + * using available disks within any single HBA from the input list. + * + * The number of components within the composed stripe will be + * in the range of min to max, preferring more components + * over fewer. + * + * All input HBAs are expected to have at least mincomp + * available disks and total space sufficient for the stripe. + * + * If the stripe can be composed, a pointer to it is returned in + * the stripe devconfig_t *. + * + * + * while (more hbas and stripe not composed) { + * select HBA + * if (not enough available space on this HBA) { + * continue; + * } + * get available disks for HBA + * use # disks as max # of stripe components + * try to compose stripe + * } + * + */ +static int +compose_stripe_within_hba( + devconfig_t *request, + dlist_t *hbas, + uint64_t nbytes, + uint16_t min, + uint16_t max, + devconfig_t **stripe) +{ + int error = 0; + + dlist_t *iter = NULL; + + *stripe = NULL; + + for (iter = hbas; + (iter != NULL) && (error == 0) && (*stripe == NULL); + iter = iter->next) { + + dm_descriptor_t hba = (uintptr_t)iter->obj; + dlist_t *disks = NULL; + uint64_t space = 0; + uint16_t ncomp = 0; + char *name; + + ((error = get_display_name(hba, &name)) != 0) || + (error = hba_get_avail_disks_and_space(request, + hba, &disks, &space)); + + if (error == 0) { + if (space >= nbytes) { + ncomp = dlist_length(disks); + ncomp = ((ncomp > max) ? max : ncomp); + error = compose_stripe( + request, nbytes, disks, ncomp, + min, NULL, stripe); + } else { + print_hba_insufficient_space_msg(name, space); + } + } + + dlist_free_items(disks, NULL); + } + + return (error); +} + +/* + * FUNCTION: assemble_stripe(devconfig_t *request, dlist_t *comps, + * devconfig_t **stripe) + * + * INPUT: request - pointer to a devconfig_t of the current request + * comps - pointer to a list of slice components + * + * OUPUT: stripe - pointer to a devconfig_t to hold final stripe + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which creates and populates a stripe devconfig_t + * struct using information from the input request and the + * list of slice components. + * + * Determines the name of the stripe either from the request + * or from the default naming scheme. + * + * Sets the interlace for the stripe if a value is specified + * in the request. + * + * Attaches the input list of components to the devconfig. + */ +static int +assemble_stripe( + devconfig_t *request, + dlist_t *comps, + devconfig_t **stripe) +{ + uint64_t ilace = 0; + char *name = NULL; + int error = 0; + + if ((error = new_devconfig(stripe, TYPE_STRIPE)) == 0) { + /* set stripe name, use requested name if specified */ + if ((error = devconfig_get_name(request, &name)) != 0) { + if (error != ERR_ATTR_UNSET) { + volume_set_error(gettext("error getting requested name\n")); + } else { + error = 0; + } + } + + if (error == 0) { + if (name == NULL) { + if ((error = get_next_volume_name(&name, + TYPE_STRIPE)) == 0) { + error = devconfig_set_name(*stripe, name); + free(name); + } + } else { + error = devconfig_set_name(*stripe, name); + } + } + } + + if (error == 0) { + if ((error = get_stripe_interlace(request, &ilace)) == 0) { + error = devconfig_set_stripe_interlace(*stripe, ilace); + } else if (error == ENOENT) { + ilace = get_default_stripe_interlace(); + error = 0; + } + } + + if (error == 0) { + uint64_t nbytes = 0; + if ((error = compute_usable_stripe_capacity(comps, + ilace, &nbytes)) == 0) { + error = devconfig_set_size_in_blocks(*stripe, nbytes/DEV_BSIZE); + } + } + + if (error == 0) { + comps = order_stripe_components_alternate_hbas(comps); + devconfig_set_components(*stripe, comps); + } else { + free_devconfig(*stripe); + *stripe = NULL; + } + + return (error); +} + +/* + * Order the given stripe component list such that the number of + * slices on the same hba adjacent to each other in the list are + * minimized. + * + * @param comps + * the slice component list to order + * + * @return the first element of the resulting list + */ +static dlist_t * +order_stripe_components_alternate_hbas( + dlist_t *comps) +{ + dlist_t *iter; + + oprintf(OUTPUT_DEBUG, + gettext("Stripe components before ordering to alternate HBAs:\n")); + + for (iter = comps; iter != NULL; iter = iter->next) { + devconfig_t *slice = (devconfig_t *)(iter->obj); + char *name; + devconfig_get_name(slice, &name); + oprintf(OUTPUT_DEBUG, " %s\n", name); + } + + return (dlist_separate_similar_elements( + comps, compare_slices_on_same_hba)); +} + +/* + * FUNCTION: compute_usable_stripe_capacity(dlist_t *comps, uint64_t ilace, + * uint64_t *nbytes) + * + * INPUT: comps - pointer to a list of stripe components + * ilace - the expected stripe interlace in bytes + * + * OUPUT: nbytes - pointer to hold the computed capacity + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which computes the usable size of a stripe taking + * into account the interlace and cylinder rounding that + * libmeta uses: a stripe component's size is rounded down to + * an integral multiple of the interlace and then rounded down + * to a cylinder boundary on VTOC labeled disks. + * + * (These libmeta computations are in the meta_stripe_attach() + * function of .../lib/lvm/libmeta/common/meta_stripe.c and + * meta_adjust_geom() in .../lib/lvm/libmeta/common/meta_init.c) + * + * This function's implementation iterates the input list of + * stripe component slices and determines the smallest usable + * component capacity. + * + * The usable stripe capacity is then that component capacity + * times the number of components. + */ +static int +compute_usable_stripe_capacity( + dlist_t *comps, + uint64_t ilace, + uint64_t *nbytes) +{ + uint64_t bytes_per_component = 0; + dlist_t *iter; + int ncomps = 0; + int error = 0; + + for (iter = comps; (iter != NULL) && (error == 0); iter = iter->next) { + + devconfig_t *comp = (devconfig_t *)iter->obj; + char *comp_name = NULL; + uint64_t comp_nbytes = 0; + dm_descriptor_t comp_disk; + boolean_t comp_disk_efi = B_FALSE; + uint64_t comp_disk_bps = 0; /* disk bytes per sector */ + + ((error = devconfig_get_size(comp, &comp_nbytes)) != 0) || + (error = devconfig_get_name(comp, &comp_name)) || + (error = get_disk_for_named_slice(comp_name, &comp_disk)) || + (error = disk_get_blocksize(comp_disk, &comp_disk_bps)) || + (error = disk_get_is_efi(comp_disk, &comp_disk_efi)); + if (error == 0) { + + if (comp_disk_efi == B_FALSE) { + uint64_t nhead = 0; + uint64_t nsect = 0; + uint64_t ncyls = 0; + + /* do cylinder and interlace rounding for non-EFI disks */ + ((error = disk_get_ncylinders(comp_disk, &ncyls)) != 0) || + (error = disk_get_nheads(comp_disk, &nhead)) || + (error = disk_get_nsectors(comp_disk, &nsect)); + if (error == 0) { + /* compute bytes per cyl */ + uint64_t bpc = nhead * nsect * comp_disk_bps; + + /* round nbytes down to a multiple of interlace */ + comp_nbytes = (comp_nbytes / ilace) * ilace; + + /* round nbytes down to a cylinder boundary */ + comp_nbytes = (comp_nbytes / bpc) * bpc; + } + } + + /* save smallest component size */ + if ((bytes_per_component == 0) || + (comp_nbytes < bytes_per_component)) { + bytes_per_component = comp_nbytes; + } + + ++ncomps; + } + } + + if (error == 0) { + /* size of stripe = smallest component size * n components */ + *nbytes = (bytes_per_component * ncomps); + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_stripe.h b/usr/src/cmd/lvm/metassist/layout/layout_stripe.h new file mode 100644 index 0000000000..b41c9c3ecf --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_stripe.h @@ -0,0 +1,60 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_STRIPE_H +#define _LAYOUT_STRIPE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" +#include "volume_dlist.h" + +extern int layout_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t **results); + +extern int populate_stripe( + devconfig_t *request, + uint64_t nbytes, + dlist_t *disks, + uint16_t ncomps, + dlist_t *othervols, + devconfig_t **stripe); + +extern int populate_explicit_stripe( + devconfig_t *request, + dlist_t **results); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_STRIPE_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c new file mode 100644 index 0000000000..add5b9fdfa --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c @@ -0,0 +1,2087 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <meta.h> +#include <sdssc.h> +#include <mdiox.h> +#include <meta_repartition.h> + +#include "volume_dlist.h" +#include "volume_error.h" +#include "volume_output.h" + +#include "layout_device_util.h" +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_request.h" +#include "layout_svm_util.h" + +static int _max_hsps = 1000; /* # of HSPs (arbitrary limit) */ +static int _max_devs = 8192; /* # of SVM volumes allowed */ +static int _max_devs_cfg = 128; /* # of SVM volumes configured */ +static int _max_sets = 4; /* # of SVM disk sets */ + +/* volume name prefixes for generating new names */ +static const char *_hsp_prefix = "hsp"; +static const char *_dev_prefix = "d"; + +/* + * dynamically allocated arrays to track used HSP (hspXXX) and volume + * names (dXXX) by number + */ +static boolean_t *hsps_by_number = NULL; +static boolean_t *devs_by_number = NULL; + +/* + * This struct remembers a diskset and the names of + * the disks in the set + */ +typedef struct { + char *name; + dlist_t *disknames; + dlist_t *hsps; +} diskset_t; + +/* + * list of diskset_t for known disksets + */ +static dlist_t *_disksets = NULL; + +static int add_diskset( + char *diskset); + +static int add_diskset_diskname( + char *diskset, + char *diskname); + +static int add_diskset_hsp( + char *diskset, + char *hspname); + +static int add_diskset_hsp_spare( + char *diskset, + char *hspname, + char *spare); + +static int is_disk_in_local_diskset( + dm_descriptor_t disk, + boolean_t *bool); + +static int is_disk_in_named_diskset( + dm_descriptor_t disk, + char *dsname, + boolean_t *bool); + +/* SVM snapshot stuff */ +typedef enum { + SVM_DISKSET = 0, + SVM_MDB, + SVM_STRIPE, + SVM_MIRROR, + SVM_RAID, + SVM_TRANS, + SVM_SP, + SVM_HSP, + SVM_HS, + SVM_DRIVE +} svm_type_t; + +typedef struct svm_snap_entry { + struct svm_snap_entry *next; + char *diskset; + svm_type_t type; + char *name; + char *slice; +} svm_snap_t; + +static svm_snap_t *svm_snapshot(int *errp); +static void free_svm_snapshot(svm_snap_t *listp); + +static char *type_name(svm_type_t type); +static int add_record( + svm_snap_t **listp, + char *setname, + svm_type_t type, + char *mname, + char *slice_name); +static int diskset_info(svm_snap_t **listp, mdsetname_t *sp); +static void free_names(mdnamelist_t *nlp); +static int load_svm(svm_snap_t **listp); +static int new_entry( + svm_snap_t **listp, + char *sname, + svm_type_t type, + char *mname, + mdsetname_t *sp); + +/* + * FUNCTION: scan_svm_names(char *diskset) + * + * INPUT: diskset - a char * disk set name + * + * PURPOSE: Take a snapshot of the current SVM config. + * + * Scan it and remember: + * 1. all known disk sets + * s. the disks in the named disk set + * 3. the used device and HSP names in the named disk set + * 4. the HSPs in the disk set + * 5. the spares in the HSPs + */ +int +scan_svm_names( + char *diskset) +{ + int ndisks = 0; + int nhsps = 0; + int ndevices = 0; + int nsets = 0; + + int number = 0; + int error = 0; + svm_snap_t *headp = NULL; + svm_snap_t *listp = NULL; + char *tablefmt = " %-20s %-10s %-20s %-10s\n"; + + oprintf(OUTPUT_TERSE, + gettext("\nScanning system SVM configuration...\n")); + + headp = svm_snapshot(&error); + if (error != 0) { + oprintf(OUTPUT_TERSE, + gettext("failed to scan SVM devices\n")); + return (error); + } + + if (error == 0) { + if ((error = get_max_number_of_devices(&_max_devs_cfg)) == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" configured maximum number of " + "volumes: %d\n"), + _max_devs_cfg); + } + } + + if (error == 0) { + if ((error = get_max_number_of_disksets(&_max_sets)) == 0) { + oprintf(OUTPUT_VERBOSE, + gettext(" configured maximum number of " + "disk sets: %d\n"), + _max_sets); + } + } + + if (error == 0) { + /* array is realloc'ed as necessary */ + if ((hsps_by_number = + (boolean_t *)calloc(_max_hsps, sizeof (boolean_t))) == NULL) { + oprintf(OUTPUT_TERSE, + gettext("failed to allocate HSP name array\n")); + error = ENOMEM; + } + } + + if (error == 0) { + /* array is realloc'ed as necessary */ + if ((devs_by_number = + (boolean_t *)calloc(_max_devs, sizeof (boolean_t))) == NULL) { + oprintf(OUTPUT_TERSE, + gettext("failed to allocate volume name array\n")); + error = ENOMEM; + } + } + + if ((error == 0) && (get_max_verbosity() >= OUTPUT_DEBUG)) { + (void) oprintf(OUTPUT_DEBUG, "\n"); + (void) oprintf(OUTPUT_DEBUG, + tablefmt, + gettext("disk set"), + gettext("dev type"), + gettext("name"), + gettext("slice")); + (void) oprintf(OUTPUT_DEBUG, + " -----------------------------------" + "-----------------------------------\n"); + } + + for (listp = headp; listp != NULL && error == 0; listp = listp->next) { + + oprintf(OUTPUT_DEBUG, + tablefmt, + listp->diskset, + type_name(listp->type), + listp->name, + listp->slice); + + switch (listp->type) { + case SVM_DISKSET: + + error = add_diskset(listp->name); + ++nsets; + break; + + case SVM_DRIVE: + + error = add_diskset_diskname(listp->diskset, listp->name); + + /* is this drive in the requested diskset? */ + if (string_case_compare(diskset, listp->diskset) == 0) { + ++ndisks; + } + break; + + case SVM_MIRROR: + case SVM_RAID: + case SVM_TRANS: + case SVM_SP: + case SVM_STRIPE: + + /* is this SVM volume in the requested diskset? */ + if (string_case_compare(diskset, listp->diskset) == 0) { + + /* isolate device name from "poolname/dXXXX" */ + char *cp = strrchr(listp->name, '/'); + if (cp != NULL) { + ++cp; + } else { + cp = listp->name; + } + + /* BEGIN CSTYLED */ + /* + * names for requested devices and HSPs are remembered + * so that the default name generation scheme knows + * which names are already being used + */ + /* END CSTYLED */ + /* extract device number from name "dXXXX" */ + if (sscanf(cp, "d%d", &number) != EOF) { + oprintf(OUTPUT_DEBUG, + gettext(" device: %6s number: %3d\n"), + cp, number); + + if (number > _max_devs) { + /* hit current limit, expand it */ + boolean_t *tmp = + (boolean_t *)realloc((void *)_max_devs, + (number * sizeof (boolean_t))); + + if (tmp == NULL) { + error = ENOMEM; + } else { + _max_devs = number; + devs_by_number = tmp; + } + } + + if ((error == 0) && + (devs_by_number[number] == B_FALSE)) { + devs_by_number[number] = B_TRUE; + ++ndevices; + } + } + } + break; + + case SVM_HSP: + + /* is this HSP in the requested diskset? */ + if (string_case_compare(diskset, listp->diskset) == 0) { + + /* isolate HSP name from "poolname/hspXXX" */ + char *cp = strrchr(listp->name, '/'); + if (cp != NULL) { + ++cp; + } else { + cp = listp->name; + } + + /* extract pool number from name "hspXXX" */ + if (sscanf(cp, "hsp%03d", &number) != EOF) { + oprintf(OUTPUT_DEBUG, + gettext(" HSP: %6s number: %3d\n"), + cp, number); + + if (number > _max_hsps) { + /* hit our arbitrary limit, double it */ + boolean_t *tmp = + (boolean_t *)realloc((void *)hsps_by_number, + 2 * _max_hsps * sizeof (boolean_t)); + + if (tmp != NULL) { + _max_hsps *= 2; + hsps_by_number = tmp; + } else { + error = ENOMEM; + } + } + + if ((error == 0) && + (hsps_by_number[number] == B_FALSE)) { + hsps_by_number[number] = B_TRUE; + error = add_diskset_hsp(diskset, cp); + ++nhsps; + } + } + } + + break; + + case SVM_HS: + + /* is this hot spare in the requested disk set? */ + if (string_case_compare(diskset, listp->diskset) == 0) { + + /* isolate HSP name from "poolname/hspXXXX" */ + char *cp = strrchr(listp->name, '/'); + if (cp != NULL) { + ++cp; + } else { + cp = listp->name; + } + + error = add_diskset_hsp_spare(diskset, cp, listp->slice); + } + break; + + case SVM_MDB: + default: + break; + } + } + + free_svm_snapshot(headp); + + if (error == 0) { + /* available diskset? subtract 1 for the local set */ + if ((diskset_exists(diskset) != B_TRUE) && + (nsets >= _max_sets)) { + volume_set_error( + gettext("Disk set \"%s\" cannot be created, the " + "maximum number of disk sets (%d) already " + "exists.\n"), + diskset, _max_sets); + error = -1; + } + } + + if (error == 0) { + oprintf(OUTPUT_VERBOSE, + gettext("\n Disk set \"%s\" has:\n\n"), diskset); + oprintf(OUTPUT_VERBOSE, + gettext(" %d drives\n"), ndisks); + oprintf(OUTPUT_VERBOSE, + gettext(" %d volumes\n"), ndevices); + oprintf(OUTPUT_VERBOSE, + gettext(" %d HSPs\n"), nhsps); + } else { + free(hsps_by_number); + free(devs_by_number); + hsps_by_number = (boolean_t *)NULL; + devs_by_number = (boolean_t *)NULL; + } + + return (error); +} + +/* + * FUNCTION: release_svm_names() + * + * PURPOSE: Release snapshot of the current SVM config. + * + * Free memory allocated by scan_svm_names() + */ +void +release_svm_names() +{ + dlist_t *iter; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + diskset_t *diskset = (diskset_t *)iter->obj; + dlist_free_items(diskset->disknames, free); + dlist_free_items(diskset->hsps, free_devconfig); + free(diskset->name); + } + dlist_free_items(_disksets, free); + _disksets = NULL; + + if (hsps_by_number != NULL) + free(hsps_by_number); + if (devs_by_number != NULL) + free(devs_by_number); + + hsps_by_number = (boolean_t *)NULL; + devs_by_number = (boolean_t *)NULL; +} + +/* + * FUNCTION: diskset_exists(char *diskset) + * + * INPUT: dsname - a char * diskset name + * + * RETURNS: boolean_t - B_TRUE if the named diskset exists + * B_FALSE otherwise + * + * PURPOSE: Checks the list of known disk sets and determines + * if the input name is in that list. + */ +boolean_t +diskset_exists( + char *dsname) +{ + dlist_t *iter; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + diskset_t *diskset = (diskset_t *)iter->obj; + if (string_case_compare(dsname, diskset->name) == 0) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* + * FUNCTION: add_diskset(char *dsname) + * + * INPUT: dsname - a char * disk set name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Add the named disk set to the list of known disk sets. + */ +static int +add_diskset( + char *dsname) +{ + dlist_t *iter; + int error = 0; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + diskset_t *diskset = (diskset_t *)iter->obj; + if (string_case_compare(diskset->name, dsname) == 0) { + break; + } + } + + if (iter == NULL) { + + dlist_t *item = NULL; + diskset_t *diskset = (diskset_t *)calloc(1, sizeof (diskset_t)); + + if (diskset == NULL) { + error = ENOMEM; + } else { + diskset->hsps = NULL; + diskset->name = strdup(dsname); + if (diskset->name == NULL) { + free(diskset); + error = ENOMEM; + } else { + if ((item = dlist_new_item(diskset)) == NULL) { + free(diskset->name); + free(diskset); + error = ENOMEM; + } else { + _disksets = dlist_append(item, _disksets, AT_HEAD); + oprintf(OUTPUT_DEBUG, + gettext(" added disk set %s \n"), dsname); + } + } + } + } + + return (error); +} + +/* + * FUNCTION: add_diskset_diskname(char *diskset, char *diskname) + * + * INPUT: dsname - a char * disk set name + * diskname - a char * disk name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Add the disk name to the named disk set's list of disks. + * + * The input diskname is fully qualified with the path + * to the raw disk device (/dev/rdsk/cXtXdXsX) which is + * not relevant, so it is removed. + */ +static int +add_diskset_diskname( + char *dsname, + char *diskname) +{ + dlist_t *iter; + int error = 0; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + if (string_case_compare(diskset->name, dsname) == 0) { + + dlist_t *item = NULL; + char *name = NULL; + char *cp = NULL; + + /* trim leading path */ + if ((cp = strrchr(diskname, '/')) != 0) { + if ((name = strdup(cp+1)) == NULL) { + error = ENOMEM; + } + } else if ((name = strdup(diskname)) == NULL) { + error = ENOMEM; + } + + if ((item = dlist_new_item(name)) == NULL) { + free(name); + error = ENOMEM; + } else { + diskset->disknames = + dlist_append(item, diskset->disknames, AT_HEAD); + } + + break; + } + } + + if ((error == 0) && (iter == NULL)) { + /* new disk set */ + if ((error = add_diskset(dsname)) == 0) { + return (add_diskset_diskname(dsname, diskname)); + } + } + + return (error); +} + +/* + * FUNCTION: add_diskset_hsp(char *dsname, char *hspname) + * + * INPUT: dsname - a char * disk set name + * hspname - a char * HSP name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Model a new HSP for the named disk set. + * + * Metassist can use existing HSPs to service new volumes. + * + * It is necessary to have a model of what HSPs currently + * exist for each disk set. + * + * This function takes information found during discovery + * and turns it into a form usable by the HSP layout code. + */ +static int +add_diskset_hsp( + char *dsname, + char *hspname) +{ + dlist_t *iter; + int error = 0; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + + if (string_case_compare(diskset->name, dsname) == 0) { + + dlist_t *item = NULL; + devconfig_t *hsp = NULL; + + if (((error = new_devconfig(&hsp, TYPE_HSP)) != 0) || + (error = devconfig_set_name(hsp, hspname))) { + free_devconfig(hsp); + } else { + if ((item = dlist_new_item(hsp)) == NULL) { + free_devconfig(hsp); + error = ENOMEM; + } else { + diskset->hsps = + dlist_append(item, diskset->hsps, AT_TAIL); + + oprintf(OUTPUT_DEBUG, + gettext(" added %s to disk set %s\n"), + hspname, dsname); + } + } + break; + } + } + + if ((error == 0) && (iter == NULL)) { + if ((error = add_diskset(dsname)) == 0) { + return (add_diskset_hsp(dsname, hspname)); + } + } + + return (error); +} + +/* + * FUNCTION: add_diskset_hsp_spare(char *dsname, char *hspname, + * char *sparename) + * + * INPUT: dsname - a char * diskset name + * hspname - a char * HSP name + * sparename - a char * hot spare (slice) name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Locate the named hot spare pool in the named disk set and + * add the named spare slice to its list of spares. + * + * Metassist can use existing HSPs to service new volumes. + * + * It is necessary to have a model of what HSPs currently + * exist for each disk set. + * + * This function takes information found during discovery + * and turns it into a form usable by the HSP layout code. + */ +static int +add_diskset_hsp_spare( + char *dsname, + char *hspname, + char *sparename) +{ + dlist_t *iter; + int error = 0; + + for (iter = _disksets; iter != NULL; iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + + if (string_case_compare(diskset->name, dsname) == 0) { + + dlist_t *item = + dlist_find( + diskset->hsps, hspname, + compare_string_to_devconfig_name); + + if (item != NULL) { + + /* add spare to HSP */ + devconfig_t *hsp = (devconfig_t *)item->obj; + dm_descriptor_t slice = (dm_descriptor_t)0; + + (void) slice_get_by_name(sparename, &slice); + if (slice == (dm_descriptor_t)0) { + oprintf(OUTPUT_TERSE, + gettext("warning: ignoring nonexistent " + "slice %s defined in %s\n"), + sparename, hspname); + } else { + + uint64_t nbytes = 0; + uint32_t index = 0; + devconfig_t *spare = NULL; + + /* build a devconfig_t model of the slice */ + if (((error = slice_get_size(slice, &nbytes)) != 0) || + (error = slice_get_index(slice, &index)) || + (error = new_devconfig(&spare, TYPE_SLICE)) || + (error = devconfig_set_name(spare, sparename)) || + (error = devconfig_set_size(spare, nbytes)) || + (error = devconfig_set_slice_index(spare, index))) { + free_devconfig(spare); + } else { + + if ((item = dlist_new_item(spare)) == NULL) { + error = ENOMEM; + free_devconfig(spare); + } else { + dlist_t *spares; + spares = devconfig_get_components(hsp); + spares = dlist_append(item, spares, AT_TAIL); + devconfig_set_components(hsp, spares); + + oprintf(OUTPUT_DEBUG, + gettext(" added %s to %s in " + "disk set %s\n"), + sparename, hspname, dsname); + } + } + } + + break; + + } else { + if ((error = add_diskset_hsp(dsname, hspname)) == 0) { + return (add_diskset_hsp_spare( + dsname, hspname, sparename)); + } + } + } + } + + return (error); +} + +/* + * Return a list of disks in the given diskset. + * + * @param dsname + * The name of the named disk set, or "" for the local + * set. + * + * @param disks + * RETURN: pointer to the list of disks in the given disk + * set + * + * @return 0 if succesful, non-zero otherwise + */ +int +get_disks_in_diskset( + char *dsname, + dlist_t **disks) +{ + dlist_t *known_disks; + int error = 0; + + *disks = NULL; + + if ((error = get_known_disks(&known_disks)) == 0) { + dlist_t *iter; + + /* For each known disk... */ + for (iter = known_disks; + iter != NULL && error == 0; + iter = iter->next) { + dm_descriptor_t disk = (uintptr_t)iter->obj; + boolean_t in_diskset = B_FALSE; + + /* If this disk is in the given set... */ + error = is_disk_in_diskset(disk, dsname, &in_diskset); + if (error == 0 && in_diskset == B_TRUE) { + dlist_t *item = dlist_new_item((void *)disk); + *disks = dlist_append(item, *disks, AT_TAIL); + } + } + } + + return (error); +} + +/* + * FUNCTION: is_disk_in_diskset(dm_descriptor_t disk, char *dsname, + * boolean_t *bool) + * + * INPUT: disk - dm_descriptor_t disk handle + * dsname - char * diskset name, or MD_LOCAL_NAME for + * the local set. + * + * OUTPUT: bool - pointer to a boolean_t to hold the result + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determine if the input disk is known to be in the + * given diskset. + */ +int +is_disk_in_diskset( + dm_descriptor_t disk, + char *dsname, + boolean_t *bool) +{ + if (string_case_compare(dsname, MD_LOCAL_NAME) == 0) { + return (is_disk_in_local_diskset(disk, bool)); + } + + return (is_disk_in_named_diskset(disk, dsname, bool)); +} + +static int +is_disk_in_local_diskset( + dm_descriptor_t disk, + boolean_t *bool) +{ + dlist_t *iter; + dlist_t *aliases = NULL; + boolean_t in_named_diskset = B_FALSE; + char *name = NULL; + int error = 0; + + *bool = B_FALSE; + + error = get_display_name(disk, &name); + if (error == 0) { + + error = get_aliases(disk, &aliases); + if (error == 0) { + + /* For each known disk set... */ + for (iter = _disksets; + iter != NULL && in_named_diskset == B_FALSE; + iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + dlist_t *names = diskset->disknames; + + /* Check disk name */ + in_named_diskset = dlist_contains( + names, name, compare_device_names); + + /* Check disk aliases */ + if (in_named_diskset == B_FALSE) { + dlist_t *iter2; + for (iter2 = aliases; + iter2 != NULL && in_named_diskset == B_FALSE; + iter2 = iter2->next) { + in_named_diskset = dlist_contains(names, + (char *)iter2->obj, compare_device_names); + } + } + } + } + } + + if (error == 0) { + *bool = (in_named_diskset == B_TRUE ? B_FALSE : B_TRUE); + } + + return (error); +} + +static int +is_disk_in_named_diskset( + dm_descriptor_t disk, + char *dsname, + boolean_t *bool) +{ + dlist_t *iter; + int error = 0; + boolean_t in_diskset = B_FALSE; + + *bool = B_FALSE; + + for (iter = _disksets; + (iter != NULL) && (in_diskset == B_FALSE); + iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + + if (string_case_compare(diskset->name, dsname) == 0) { + + dlist_t *names = diskset->disknames; + dlist_t *aliases = NULL; + char *name = NULL; + + ((error = get_display_name(disk, &name)) != 0) || + (error = get_aliases(disk, &aliases)); + if (error != 0) { + break; + } + + /* check disk name */ + in_diskset = dlist_contains(names, name, compare_device_names); + + /* check disk aliases */ + if (in_diskset == B_FALSE) { + dlist_t *iter2; + for (iter2 = aliases; + (iter2 != NULL) && (in_diskset == B_FALSE); + iter2 = iter2->next) { + in_diskset = dlist_contains(names, + (char *)iter2->obj, compare_device_names); + } + } + } + } + + *bool = in_diskset; + + return (error); +} + +/* + * FUNCTION: is_disk_in_other_diskset(dm_descriptor_t disk, char *dsname, + * boolean_t *bool) + * + * INPUT: disk - dm_descriptor_t disk handle + * dsname - char * disk set name + * + * OUTPUT: bool - pointer to a boolean_t to hold the result. + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determine if the named disk is known to be in a disk set + * other than the named disk set. + */ +int +is_disk_in_other_diskset( + dm_descriptor_t disk, + char *dsname, + boolean_t *bool) +{ + boolean_t in_other = B_FALSE; + dlist_t *iter; + dlist_t *aliases = NULL; + char *name = NULL; + char *cp = NULL; + int error = 0; + + ((error = get_display_name(disk, &name)) != 0) || + (error = get_aliases(disk, &aliases)); + if (error != 0) { + return (error); + } + + /* + * discard the leading path, it is probably /dev/dsk + * and the disk set disk names are all /dev/rdsk/... + * + * aliases do not have leading paths + */ + cp = strrchr(name, '/'); + if (cp != NULL) { + ++cp; + } else { + cp = name; + } + name = cp; + + for (iter = _disksets; + (iter != NULL) && (in_other == B_FALSE); + iter = iter->next) { + + diskset_t *diskset = (diskset_t *)iter->obj; + dlist_t *names = diskset->disknames; + + if (string_case_compare(diskset->name, dsname) == 0) { + /* skip named disk set */ + continue; + } + + /* see if disk's name is in disk set's name list */ + in_other = dlist_contains(names, name, compare_device_names); + + /* see if any of the disk's aliases is in name list */ + if (in_other == B_FALSE) { + dlist_t *iter2; + for (iter2 = aliases; + (iter2 != NULL) && (in_other == B_FALSE); + iter2 = iter2->next) { + + in_other = dlist_contains(names, + (char *)iter2->obj, compare_device_names); + } + } + } + + *bool = in_other; + + return (error); +} + +/* + * FUNCTION: hsp_get_default_for_diskset(char *diskset, + * devconfig_t **hsp) + * + * INPUT: diskset - char * disk set name + * + * RETURNS: devconfig_t * - pointer to the first HSP in the disk set + * NULL if none found + * + * PURPOSE: Locate the first HSP in the named disk set. + */ +int +hsp_get_default_for_diskset( + char *diskset, + devconfig_t **hsp) +{ + dlist_t *iter = _disksets; + + *hsp = NULL; + + for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) { + diskset_t *set = (diskset_t *)iter->obj; + if (string_case_compare(set->name, diskset) == 0) { + dlist_t *item = set->hsps; + if (item != NULL) { + *hsp = item->obj; + } + } + } + + return (0); +} + +/* + * FUNCTION: get_n_metadb_replicas(int *nreplicas) + * + * OUTPUT: nreplicas - pointer to int to hold the result + * + * RETURNS: int - 0 on success + * !0 on failure + * + * PURPOSE: Check the number of replicas configured for the local set. + */ +int +get_n_metadb_replicas( + int *nreplicas) +{ + mdsetname_t *sp; + md_replicalist_t *rlp = NULL; + md_error_t mderror = mdnullerror; + int error = 0; + + *nreplicas = 0; + + sp = metasetname(MD_LOCAL_NAME, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + error = -1; + } else { + *nreplicas = metareplicalist(sp, MD_BASICNAME_OK, &rlp, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + error = -1; + } else if (rlp != NULL) { + metafreereplicalist(rlp); + rlp = NULL; + } + + if (*nreplicas < 0) { + *nreplicas = 0; + } + } + + return (error); +} + +/* + * FUNCTION: hsp_get_by_name(char *diskset, char *name, + * devconfig_t **hsp) + * + * INPUT: diskset - char * disk set name + * name - char * HSP name + * + * OUTPUT: hsp - a devconfig_t * - pointer to hold + * the named HSP if none found + * + * PURPOSE: Locate the named HSP in the named disk set. + */ +int +hsp_get_by_name( + char *diskset, + char *name, + devconfig_t **hsp) +{ + dlist_t *iter = _disksets; + + *hsp = NULL; + + for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) { + diskset_t *set = (diskset_t *)iter->obj; + if (string_case_compare(set->name, diskset) == 0) { + dlist_t *item = dlist_find( + set->hsps, name, compare_string_to_devconfig_name); + if (item != NULL) { + *hsp = item->obj; + } + } + } + + return (0); +} + +/* + * FUNCTION: is_volume_name_valid(char *name) + * + * OUTPUT: name - pointer to a char * volume name + * + * RETURNS: boolean_t - B_TRUE if the input name is valid + * B_FALSE otherwise + * + * PURPOSE: Wrapper around libmeta volume name validation method. + */ +boolean_t +is_volume_name_valid( + char *name) +{ + return (is_metaname(name)); +} + +/* + * FUNCTION: is_hsp_name_valid(char *name) + * + * INPUT: name - char * HSP name + * + * RETURNS: boolean_t - B_TRUE if the input name is valid + * B_FALSE otherwise + * + * PURPOSE: Wrapper around libmeta HSP name validation method. + */ +boolean_t +is_hsp_name_valid( + char *name) +{ + return (is_hspname(name)); +} + +/* + * FUNCTION: extract_index(char *name, char *prefix, char *num_fmt, + * int *index) + * + * INPUT: name - const char * volume name + * prefix - const char * fixed part of format string + * num_fmt - const char * format of number to extract (e.g. %d) + * + * OUTPUT: index - pointer to int to hold numeric part of name + * + * RETURNS: boolean_t - B_TRUE if the input name is parsed correctly + * B_FALSE otherwise + * + * PURPOSE: Extract the numeric portion of a device name for use + * by higher-level functions. + */ +static boolean_t +extract_index( + const char *name, + const char *prefix, + const char *num_fmt, + int *index) +{ + char buf[MAXNAMELEN]; + const char *cp; + const char *fmt = buf; + + if ((cp = strrchr(name, '/')) != NULL) { + ++cp; + } else { + cp = name; + } + + (void) snprintf(buf, sizeof (buf), "%s%s", prefix, num_fmt); + if (sscanf(cp, fmt, index) == 1) + return (B_TRUE); + else + return (B_FALSE); +} + +/* + * FUNCTION: is_volume_name_in_range(char *name) + * + * INPUT: name - char * volume name + * + * RETURNS: boolean_t - B_TRUE if the input name is in the allowed + * range of names + * B_FALSE otherwise + * + * PURPOSE: Determine if the input volume name is within the allowed + * range of device names (0 <= n < max # of devices configured). + */ +boolean_t +is_volume_name_in_range( + char *name) +{ + int index = -1; + + if (extract_index(name, _dev_prefix, "%d", &index)) { + if (index >= 0 && index < _max_devs_cfg) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* + * FUNCTION: reserve_volume_name(char *name) + * + * INPUT: name - a char * volume name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Mark a volume name/number as used. + * + * Assumes that the input name has been validated. + * + * if the name is not currently available, return -1 + */ +int +reserve_volume_name( + char *name) +{ + int index = -1; + + if (extract_index(name, _dev_prefix, "%d", &index)) { + if (devs_by_number[index] != B_TRUE) { + devs_by_number[index] = B_TRUE; + return (0); + } + } + + return (-1); +} + +/* + * FUNCTION: reserve_hsp_name(char *name) + * + * INPUT: name - a char * hsp name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Mark a HSP name/number as used. + * + * Assumes that the input name has been validated. + * + * if the name is not currently available, return -1 + */ +int +reserve_hsp_name( + char *name) +{ + int index = -1; + + if (extract_index(name, _hsp_prefix, "%03d", &index)) { + if (hsps_by_number[index] != B_TRUE) { + hsps_by_number[index] = B_TRUE; + return (0); + } + } + + return (-1); +} + +/* + * FUNCTION: release_volume_name(char *name) + * + * INPUT: name - a char * volume name + * + * PURPOSE: release the input volume name. + * + * Extract volume number from the input name + * and use it to index into the array of used + * volume numbers. Make that volume number + * available for use again. + */ +void +release_volume_name( + char *name) +{ + int index = -1; + + if (name != NULL && extract_index(name, _dev_prefix, "%d", &index)) { + oprintf(OUTPUT_DEBUG, + gettext("released volume name %s%d\n"), + _dev_prefix, index); + devs_by_number[index] = B_FALSE; + } +} + +/* + * FUNCTION: release_hsp_name(char *name) + * + * INPUT: name - a char * HSP name + * + * PURPOSE: release the input HSP name. + * + * Extract volume number from the input name + * and use it to index into the array of used + * hsp numbers. Make that hsp number available + * for use again. + */ +void +release_hsp_name( + char *name) +{ + int index = -1; + + if (name != NULL && extract_index(name, _hsp_prefix, "%d", &index)) { + oprintf(OUTPUT_DEBUG, + gettext("released hsp name %s%d\n"), + _hsp_prefix, index); + hsps_by_number[index] = B_FALSE; + } +} + +/* + * FUNCTION: get_next_volume_name(char **name) + * + * OUTPUT: name - pointer to a char * to hold volume name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: generate a new volume name using the standard device + * name prefix and the lowest available device number. + * + * if type == MIRROR, determine the next available mirror + * name according to the convention that a mirror name is + * a multiple of 10. + * + * If such a name is unavailable, use the next available name. + */ +int +get_next_volume_name( + char **name, + component_type_t type) +{ + int next = 0; + + for (next = 0; next < _max_devs_cfg; ++next) { + if ((type == TYPE_MIRROR && ((next % 10) != 0)) || + (type != TYPE_MIRROR && ((next % 10) == 0))) { + /* use/save multiples of 10 for mirrors */ + continue; + } + if (devs_by_number[next] != B_TRUE) { + break; + } + } + + if ((next == _max_devs_cfg) && (type == TYPE_MIRROR)) { + /* try next sequentially available name */ + for (next = 0; next < _max_devs_cfg; ++next) { + if (devs_by_number[next] != B_TRUE) { + break; + } + } + } + + if (next == _max_devs_cfg) { + volume_set_error( + gettext("ran out of logical volume names.\n")); + return (-1); + } + + *name = (char *)calloc(MAXNAMELEN, sizeof (char)); + if (*name == NULL) { + return (ENOMEM); + } + + (void) snprintf(*name, MAXNAMELEN-1, "%s%d", _dev_prefix, next); + + devs_by_number[next] = B_TRUE; + return (0); +} + +/* + * FUNCTION: get_next_submirror_name(char *mname, char **subname) + * + * INPUT: mname - pointer to a char * mirror name + * OUTPUT: subname - pointer to a char * to hold submirror name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Determine the next available submirror name according + * to the convention that each submirror name is a sequential + * increment of its mirror's name. + * + * If such a name is unavailable, return the next sequentially + * available volume name. + */ +int +get_next_submirror_name( + char *mname, + char **subname) +{ + char buf[MAXNAMELEN]; + int error = 0; + int next = 0; + int i = 0; + + *subname = NULL; + + /* try next sequential name: mirror + 1... */ + if (extract_index(mname, _dev_prefix, "%d", &next)) { + for (i = next + 1; i < _max_devs_cfg; i++) { + if ((i % 10) == 0) { + /* save for mirrors */ + continue; + } + if (devs_by_number[i] == B_FALSE) { + (void) snprintf(buf, MAXNAMELEN-1, "%s%d", _dev_prefix, i); + if ((*subname = strdup(buf)) != NULL) { + devs_by_number[i] = B_TRUE; + } else { + error = ENOMEM; + } + break; + } + } + } + + if ((error == 0) && (*subname == NULL)) { + /* name adhering to convention isn't available, */ + /* use next sequentially available name */ + error = get_next_volume_name(subname, TYPE_STRIPE); + } + + return (error); +} + +/* + * FUNCTION: get_next_hsp_name(char **name) + * + * OUTPUT: name - pointer to a char * to hold name + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which generates a new hotsparepool name + * using the standard name prefix and the lowest + * available hsp number. + */ +int +get_next_hsp_name( + char **name) +{ + int next = 0; + + for (next = 0; next < _max_hsps; ++next) { + if (hsps_by_number[next] != B_TRUE) { + break; + } + } + + if (next == _max_hsps) { + volume_set_error(gettext("ran out of HSP names")); + return (-1); + } + + *name = (char *)calloc(MAXNAMELEN, sizeof (char)); + if (*name == NULL) { + oprintf(OUTPUT_TERSE, + gettext("failed to allocate volume name string, " + "out of memory")); + return (ENOMEM); + } + + (void) snprintf(*name, MAXNAMELEN-1, "%s%03d", _hsp_prefix, next); + + hsps_by_number[next] = B_TRUE; + + return (0); +} + +static char * +type_name( + svm_type_t type) +{ + switch (type) { + case SVM_DISKSET: return (gettext("disk set")); + case SVM_MDB: return (gettext("metadb")); + case SVM_STRIPE: return (gettext("stripe")); + case SVM_MIRROR: return (gettext("mirror")); + case SVM_RAID: return (gettext("raid")); + case SVM_TRANS: return (gettext("trans")); + case SVM_SP: return (gettext("soft partition")); + case SVM_HSP: return (gettext("hot spare pool")); + case SVM_HS: return (gettext("hot spare")); + case SVM_DRIVE: return (gettext("drive")); + default: return (gettext("unknown")); + } +} + +static svm_snap_t * +svm_snapshot(int *errp) +{ + svm_snap_t *svm_listp = NULL; + + *errp = 0; + + /* initialize the cluster library entry points */ + if (sdssc_bind_library() == SDSSC_ERROR) { + + volume_set_error(gettext("sdssc_bin_library() failed\n")); + *errp = -1; + + } else { + + /* load the SVM cache */ + *errp = load_svm(&svm_listp); + + if (*errp != 0) { + free_svm_snapshot(svm_listp); + svm_listp = NULL; + } + + } + + return (svm_listp); +} + +static void +free_svm_snapshot(svm_snap_t *listp) { + + svm_snap_t *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->diskset); + free((void *)listp->name); + free((void *)listp->slice); + free((void *)listp); + listp = nextp; + } +} + +static int +add_record( + svm_snap_t **listp, + char *setname, + svm_type_t type, + char *mname, + char *slice_name) +{ + svm_snap_t *sp; + + sp = (svm_snap_t *)malloc(sizeof (svm_snap_t)); + if (sp == NULL) { + return (ENOMEM); + } + + if ((sp->diskset = strdup(setname)) == NULL) { + free(sp); + return (ENOMEM); + } + + if ((sp->name = strdup(mname)) == NULL) { + free(sp->diskset); + free(sp); + return (ENOMEM); + } + + sp->type = type; + + if ((sp->slice = strdup(slice_name)) == NULL) { + free(sp->diskset); + free(sp->name); + free(sp); + return (ENOMEM); + } + + sp->next = *listp; + *listp = sp; + + return (0); +} + +static int +diskset_info( + svm_snap_t **listp, + mdsetname_t *sp) +{ + md_error_t error = mdnullerror; + md_replicalist_t *replica_list = NULL; + md_replicalist_t *mdbp; + mdnamelist_t *nlp; + mdnamelist_t *trans_list = NULL; + mdnamelist_t *mirror_list = NULL; + mdnamelist_t *raid_list = NULL; + mdnamelist_t *stripe_list = NULL; + mdnamelist_t *sp_list = NULL; + mdhspnamelist_t *hsp_list = NULL; + + if (metareplicalist(sp, MD_BASICNAME_OK, &replica_list, &error) < 0) { + /* there are no metadb's; that is ok, no need to check the rest */ + mdclrerror(&error); + return (0); + } + mdclrerror(&error); + + for (mdbp = replica_list; mdbp != NULL; mdbp = mdbp->rl_next) { + char size[MAXPATHLEN]; + + (void) snprintf(size, sizeof (size), "%d", + (int)mdbp->rl_repp->r_nblk); + + if (new_entry(listp, mdbp->rl_repp->r_namep->cname, SVM_MDB, size, + sp)) { + metafreereplicalist(replica_list); + return (ENOMEM); + } + } + metafreereplicalist(replica_list); + + if (meta_get_trans_names(sp, &trans_list, 0, &error) >= 0) { + for (nlp = trans_list; nlp != NULL; nlp = nlp->next) { + if (new_entry(listp, nlp->namep->cname, SVM_TRANS, + nlp->namep->cname, sp)) { + free_names(trans_list); + return (ENOMEM); + } + } + + free_names(trans_list); + } + mdclrerror(&error); + + if (meta_get_mirror_names(sp, &mirror_list, 0, &error) >= 0) { + for (nlp = mirror_list; nlp != NULL; nlp = nlp->next) { + if (add_record(listp, sp->setname, SVM_MIRROR, + nlp->namep->cname, "")) { + free_names(mirror_list); + return (ENOMEM); + } + } + + free_names(mirror_list); + } + mdclrerror(&error); + + if (meta_get_raid_names(sp, &raid_list, 0, &error) >= 0) { + for (nlp = raid_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_raid_t *raid; + + mdn = metaname(&sp, nlp->namep->cname, &error); + mdclrerror(&error); + if (mdn == NULL) { + continue; + } + + raid = meta_get_raid(sp, mdn, &error); + mdclrerror(&error); + + if (raid != NULL) { + int i; + + for (i = 0; i < raid->cols.cols_len; i++) { + if (new_entry(listp, + raid->cols.cols_val[i].colnamep->cname, SVM_RAID, + nlp->namep->cname, sp)) { + free_names(raid_list); + return (ENOMEM); + } + } + } + } + + free_names(raid_list); + } + mdclrerror(&error); + + if (meta_get_stripe_names(sp, &stripe_list, 0, &error) >= 0) { + for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_stripe_t *stripe; + + mdn = metaname(&sp, nlp->namep->cname, &error); + mdclrerror(&error); + if (mdn == NULL) { + continue; + } + + stripe = meta_get_stripe(sp, mdn, &error); + mdclrerror(&error); + + if (stripe != NULL) { + int i; + + for (i = 0; i < stripe->rows.rows_len; i++) { + md_row_t *rowp; + int j; + + rowp = &stripe->rows.rows_val[i]; + + for (j = 0; j < rowp->comps.comps_len; j++) { + md_comp_t *component; + + component = &rowp->comps.comps_val[j]; + if (new_entry(listp, component->compnamep->cname, + SVM_STRIPE, nlp->namep->cname, sp)) { + free_names(stripe_list); + return (ENOMEM); + } + } + } + } + } + + free_names(stripe_list); + } + mdclrerror(&error); + + if (meta_get_sp_names(sp, &sp_list, 0, &error) >= 0) { + for (nlp = sp_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_sp_t *soft_part; + + mdn = metaname(&sp, nlp->namep->cname, &error); + mdclrerror(&error); + if (mdn == NULL) { + continue; + } + + soft_part = meta_get_sp(sp, mdn, &error); + mdclrerror(&error); + + if (soft_part != NULL) { + if (new_entry(listp, soft_part->compnamep->cname, SVM_SP, + nlp->namep->cname, sp)) { + free_names(sp_list); + return (ENOMEM); + } + } + } + + free_names(sp_list); + } + mdclrerror(&error); + + if (meta_get_hsp_names(sp, &hsp_list, 0, &error) >= 0) { + mdhspnamelist_t *nlp; + + for (nlp = hsp_list; nlp != NULL; nlp = nlp->next) { + md_hsp_t *hsp; + + hsp = meta_get_hsp(sp, nlp->hspnamep, &error); + mdclrerror(&error); + if (hsp != NULL) { + int i; + + for (i = 0; i < hsp->hotspares.hotspares_len; i++) { + md_hs_t *hs; + + hs = &hsp->hotspares.hotspares_val[i]; + + if (add_record(listp, sp->setname, SVM_HS, + nlp->hspnamep->hspname, hs->hsnamep->bname)) { + metafreehspnamelist(hsp_list); + return (ENOMEM); + } + } + } + + if (add_record(listp, sp->setname, SVM_HSP, + nlp->hspnamep->hspname, "")) { + metafreehspnamelist(hsp_list); + return (ENOMEM); + } + } + + metafreehspnamelist(hsp_list); + } + + mdclrerror(&error); + + return (0); +} + +static void +free_names( + mdnamelist_t *nlp) +{ + mdnamelist_t *p; + + for (p = nlp; p != NULL; p = p->next) { + meta_invalidate_name(p->namep); + } + metafreenamelist(nlp); +} + +/* + * Create a list of SVM devices + */ +static int +load_svm( + svm_snap_t **listp) +{ + int max_sets; + md_error_t error = mdnullerror; + int i; + + if ((max_sets = get_max_sets(&error)) == 0) { + return (0); + } + + if (!mdisok(&error)) { + volume_set_error( + gettext("failed to get maximum number of disk sets.\n")); + mdclrerror(&error); + return (-1); + } + + /* for each possible set number, see if we really have a disk set */ + for (i = 0; i < max_sets; i++) { + mdsetname_t *sp; + + if ((sp = metasetnosetname(i, &error)) == NULL) { + if (!mdisok(&error) && error.info.errclass == MDEC_RPC) { + /* rpc error - no metasets */ + break; + } + + mdclrerror(&error); + continue; + } + + mdclrerror(&error); + + if (add_record(listp, sp->setname, SVM_DISKSET, sp->setname, "")) { + metaflushsetname(sp); + return (ENOMEM); + } + + /* check for drives in disk sets */ + if (sp->setno != 0) { + md_drive_desc *dd; + + dd = metaget_drivedesc(sp, MD_BASICNAME_OK | PRINT_FAST, + &error); + mdclrerror(&error); + for (; dd != NULL; dd = dd->dd_next) { + if (add_record(listp, sp->setname, SVM_DRIVE, + dd->dd_dnp->rname, "")) { + metaflushsetname(sp); + return (ENOMEM); + } + } + } + + if (diskset_info(listp, sp)) { + metaflushsetname(sp); + return (ENOMEM); + } + + metaflushsetname(sp); + } + + mdclrerror(&error); + + return (0); +} + +/* determine if 'sp' is built on a slice */ +static int +new_entry( + svm_snap_t **listp, + char *slice_name, + svm_type_t type, + char *mname, + mdsetname_t *sp) +{ + mdname_t *mdn; + md_error_t error = mdnullerror; + + mdn = metaname(&sp, slice_name, &error); + if (!mdisok(&error)) { + mdn = NULL; + } + mdclrerror(&error); + + if (mdn != NULL && ( + mdn->drivenamep->type == MDT_ACCES || + mdn->drivenamep->type == MDT_COMP || + mdn->drivenamep->type == MDT_FAST_COMP)) { + + return (add_record(listp, sp->setname, type, mname, mdn->bname)); + } else { + return (add_record(listp, sp->setname, type, mname, "")); + } +} + +/* + * FUNCTION: get_default_stripe_interlace() + * + * RETURNS: uint64_t - default stripe interlace value + * + * PURPOSE: Helper which retrieves the default stripe interlace + * from libmeta. + */ +uint64_t +get_default_stripe_interlace() +{ + /* convert back to bytes */ + return ((uint64_t)meta_default_stripe_interlace() * DEV_BSIZE); +} + +/* + * FUNCTION: get_max_number_of_devices(int *max) + * + * OUTPUT: max - pointer to int to hold the configured maximum number + * of SVM devices + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which determines the maximum number of allowed + * SVM devices configured for the system. + * + * Wrapper around libmeta function meta_get_max_nunits(). + */ +int +get_max_number_of_devices( + int *max) +{ + md_error_t mderror = mdnullerror; + + *max = meta_get_nunits(&mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + return (-1); + } + + return (0); +} + +/* + * FUNCTION: get_max_number_of_disksets(int *max) + * + * OUTPUT: max - pointer to in to hold the configured maximum number + * of disk sets + * + * RETURNS: int - 0 on success + * !0 otherwise + * + * PURPOSE: Helper which determines the maximum number of allowed + * disk sets which has been configured for the system. + * + * Wrapper around libmeta function get_max_sets(). + */ +int +get_max_number_of_disksets( + int *max) +{ + md_error_t mderror = mdnullerror; + + *max = get_max_sets(&mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + return (-1); + } + + return (0); +} + +/* + * FUNCTION: is_reserved_replica_slice_index(char *diskset, char *dname, + * uint32_t index, boolean_t *bool) + * + * INPUT: diskset - char * disk set name + * dname - char * disk name + * index - integer index of interest + * + * OUTPUT: bool - pointer to a boolean_t to hold the result + * + * RETURNS: int - 0 - success + * !0 - failure + * + * PURPOSE: Helper which determines if the input slice index on + * the named disk in the named disk set is the replica + * slice that is reserved on disks in disk sets. + * + * The named disk is assumed to be in the named disk set. + * + * Determines if metassist is being run in a simulated + * hardware enironment, if not the libmeta function to + * determine the replica slice index is called. + * + * If simulation is active, then a local implementation + * is used to determine the replica slice index. + */ +int +is_reserved_replica_slice_index( + char *diskset, + char *dname, + uint32_t index, + boolean_t *bool) +{ + int error = 0; + boolean_t sim = B_FALSE; + static char *simfile = "METASSISTSIMFILE"; + + sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0)); + + if (sim != B_TRUE) { + + /* sim disabled: use meta_replicaslice() */ + + md_error_t mderror = mdnullerror; + mdsetname_t *sp; + mddrivename_t *dnp; + uint_t replicaslice; + + /* slice assumed to be on disk in the named disk set */ + sp = metasetname(diskset, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + return (-1); + } + + dnp = metadrivename(&sp, dname, &mderror); + if (!mdisok(&mderror)) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + return (-1); + } + + if (meta_replicaslice(dnp, &replicaslice, &mderror) != 0) { + volume_set_error(mde_sperror(&mderror, NULL)); + mdclrerror(&mderror); + return (-1); + } + + *bool = (replicaslice == (uint_t)index); + + } else { + + dm_descriptor_t disk; + boolean_t efi = B_FALSE; + + /* sim enabled: use same logic as meta_replicaslice() */ + ((error = disk_get_by_name(dname, &disk)) != 0) || + (error = disk_get_is_efi(disk, &efi)); + if (error == 0) { + + if (efi == B_FALSE) { + *bool = (index == MD_SLICE7); + } else { + *bool = (index == MD_SLICE6); + } + } + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h new file mode 100644 index 0000000000..d979144146 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h @@ -0,0 +1,87 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VOLUME_SVM_UTIL_H +#define _VOLUME_SVM_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" + +/* + * scan existing SVM config for the named diskset + * and build lists of device, HSP and diskset names. + */ +extern int scan_svm_names(char *diskset); +extern void release_svm_names(); + +extern int hsp_get_default_for_diskset(char *diskset, + devconfig_t **hsp); +extern int hsp_get_by_name(char *diskset, char *hspname, + devconfig_t **hsp); + +extern int get_next_volume_name(char **name, + component_type_t type); +extern int get_next_hsp_name(char **name); +extern int get_next_submirror_name(char *mname, char **subname); + +extern int reserve_volume_name(char *name); +extern int reserve_hsp_name(char *name); + +extern void release_volume_name(char *name); +extern void release_hsp_name(char *name); + +extern boolean_t is_volume_name_valid(char *name); +extern boolean_t is_hsp_name_valid(char *name); + +extern boolean_t is_volume_name_in_range(char *name); + +extern int get_disks_in_diskset(char *dsname, dlist_t **disks); + +extern int is_disk_in_diskset( + dm_descriptor_t disk, char *diskset, boolean_t *bool); +extern int is_disk_in_other_diskset( + dm_descriptor_t disk, char *diskset, boolean_t *bool); + +extern boolean_t diskset_exists(char *name); +extern uint64_t get_default_stripe_interlace(); + +extern int get_n_metadb_replicas(int *nreplicas); +extern int get_max_number_of_devices(int *max); +extern int get_max_number_of_disksets(int *max); + +extern int is_reserved_replica_slice_index( + char *diskset, char *dname, uint32_t index, boolean_t *bool); + +#ifdef __cplusplus +} +#endif + +#endif /* _VOLUME_SVM_UTIL_H */ diff --git a/usr/src/cmd/lvm/metassist/layout/layout_validate.c b/usr/src/cmd/lvm/metassist/layout/layout_validate.c new file mode 100644 index 0000000000..2e65b5cf71 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_validate.c @@ -0,0 +1,1994 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> + +#include <libintl.h> + +#include "metassist.h" +#include "volume_dlist.h" +#include "volume_error.h" +#include "volume_string.h" +#include "volume_output.h" + +#define _LAYOUT_VALIDATE_C + +#include "layout_discovery.h" +#include "layout_dlist_util.h" +#include "layout_device_cache.h" +#include "layout_device_util.h" +#include "layout_request.h" +#include "layout_slice.h" +#include "layout_svm_util.h" +#include "layout_validate.h" + +/* + * This module contains the majority of the validation code which + * layout applies to input requests. The assumption/agreement with + * the controller implementation is that requests passed into layout + * have undergone syntactic validation and that layout is responsible + * for semantic validation. + * + * The semantic validation that is handled: + * + * 1. For a toplevel diskset request, validate: + * + * - the number of disksets is not exceeded + * - the number of devices is not exceeded + * + * (These items are not directly validated within this module, + * but it is useful to document that they are handled somewhere). + * + * 2. For any devconfig_t representing a volume request, verify that: + * + * - all HSP names are semantically valid. The name should conform + * to the HSP naming convention: hspXXX. + * + * - all concat, stripe, mirror, and volume names refer to + * unused, semantically valid metadevice names. Examples of + * bad data: + * + * - a valid volume name that is already in use (d0, d10) + * + * - a valid volume name that is used two or more times to + * refer to new elements in the request. + * + * - a valid volume name that is out of range (d99877, + * d44356) or exceeds the maximum number of possible + * volumes given the current SVM configuration. + * + * - all available and unavailable device specifications refer + * to existing controllers, disks, or slices on the system. + * Examples of bad data: + * + * - a valid but non-existent controller (c23, c2) + * - a valid but non-existent disk (c0t0d8, c1t0d0) + * - a valid slice on a non-existent disk or controller + * (c0t0d8s7, c1t0d05) + * - a valid slice on an existing disk (c0t0d0s12, + * c0t0d0s9) + * + * - any typed volume request that explicitly specifies components + * requires additional validation to detect syntactically valid + * expressions that are semantically ambiguous: + * + * a concat request that: + * - specifies size and components is invalid + * + * a stripe request that: + * - specifies size and components is invalid + * - specifies mincomp and components but not enough + * components is invalid + * - specifies maxcomp and components but too many + * components is invalid + * + * a HSP request that: + * - specifies components that are not appropriate for + * the volumes the HSP serves is invalid (?) + * + * a stripe, concat or HSP request that: + * - specifies a component that was used in a prior + * request is invalid + * - specifies a component that does not exist in the + * diskset is invalid (e.g., c0t0d0s0, but c0t0d0 is + * not yet in the diskset) + * + * a mirror request that: + * - specifies nsubs and components but not enough + * components is invalid + * - specifies components and the components specify + * different sizes results in a WARNING since the total + * usable capacity of the mirror is determined by the + * smallest of its submirrors. + * - specifies components and the components specify + * components results in a WARNING since the submirrors + * may end up with different sizes + */ +static int validate_request_name( + devconfig_t *req, + component_type_t type); + +static int validate_request_size( + devconfig_t *req, + component_type_t type); + +static int validate_minimum_size( + uint64_t nbytes); + +static uint64_t apply_layout_overhead_factor( + uint64_t req_size); + +static int get_space_available_for_request( + devconfig_t *request, + dlist_t *usable_slices, + uint64_t *avail_space); + +static int do_available_space_check( + uint64_t req_size, + uint64_t raw_avail_space, + devconfig_t *request, + dlist_t *usable_slices); + +static int validate_request_redundancy_level( + devconfig_t *req); + +static int validate_request_npaths( + devconfig_t *req); + +static int validate_request_submirrors( + devconfig_t *req); + +static int validate_submirror_types( + dlist_t *submirrors); + +static int validate_submirror_number( + devconfig_t *req, + dlist_t *submirrors); + +static int validate_submirror_sizes( + devconfig_t *req, + dlist_t *submirrors); + +static int validate_submirror_size_and_components( + devconfig_t *submir, + uint64_t mirror_size, + uint64_t *assumed_size, + dlist_t **submirs_with_size, + dlist_t **submirs_with_comps, + dlist_t **submirs_no_size_or_comps); + +static int validate_slice_components( + devconfig_t *req, + component_type_t type); + +static char *get_device_aliases_string( + dm_descriptor_t desc); + +static int validate_device_array( + char **array, + char *which, + dlist_t **list); + +static int add_reserved_name(char *name); +static boolean_t is_rsvd_name(char *name); +static dlist_t *_rsvd_names = NULL; + +/* + * FUNCTION: release_validatation_caches() + * + * RETURNS: int - 0 + * + * PURPOSE: Cleanup function. + * + * Purges list of reserved volume names. Should be called + * after all layout requests have been processed. + */ +int +release_validation_caches() +{ + dlist_free_items(_rsvd_names, NULL); + _rsvd_names = NULL; + + return (0); +} + +/* + * FUNCTION: validate_basic_svm_config() + * + * RETURNS: int - 0 on success + * !0 on failure + * + * PURPOSE: Check to see if the local set metadb replicas have been created. + * + * Makes sure at least 1 metadb replica exists for the local set. + */ +int +validate_basic_svm_config() +{ + int error = 0; + int nreplicas = 0; + + if ((error = get_n_metadb_replicas(&nreplicas)) == 0) { + if (nreplicas == 0) { + volume_set_error( + gettext("Failed: State database replicas must " + "exist before using %s.\n" + "See metadb(1M) and %s(1M)."), + progname, progname); + error = -1; + } else { + oprintf(OUTPUT_DEBUG, + gettext("%d metadb replicas found.\n"), + nreplicas); + } + } + + return (error); +} + +/* + * FUNCTION: validate_request_sizes(devconfig_t *req) + * + * INPUT: req: a devconfig_t pointer to the toplevel request + * + * RETURNS: int - 0 on success + * !0 on failure + * + * PURPOSE: Check to see if the any of the individual volume request + * sizes exceeds the raw available space on the system or + * the space available to that specific request. + * + * Check to see if the total space for all requests exceeds + * the raw available space. + * + * If any check fails, stop checking, emit an error and + * return -1. + * + * Note: this function must be called after the slice + * usages have been determined and the list of usable + * slices has been generated. + */ +int +validate_request_sizes( + devconfig_t *request) +{ + int error = 0; + dlist_t *usable_slices; + dlist_t *iter; + char bad_rqst_info[BUFSIZ]; + uint64_t bad_rqst_space = 0; + uint64_t total_rqst_space = 0; + uint64_t raw_space = 0; + + (void) get_usable_slices(&usable_slices); + + /* + * calculate raw available space: space on slices that are + * "available" based on the diskset defaults or global defaults + */ + if ((error = get_space_available_for_request(request, + usable_slices, &raw_space)) != 0) { + return (error); + } + + if (raw_space == 0) { + volume_set_error( + gettext("Failed: there is no available space.\n")); + return (-1); + } + + /* deduct sizes of reserved components */ + (void) get_reserved_slices(&iter); + for (; (iter != NULL) && (raw_space != 0) && (error == 0); + iter = iter->next) { + dm_descriptor_t slice = (uintptr_t)iter->obj; + uint64_t nbytes; + if ((error = slice_get_size(slice, &nbytes)) == 0) { + if (raw_space >= nbytes) { + raw_space -= nbytes; + } else { + raw_space = 0; + } + } + } + + /* + * check each volume request's size against raw_space, + * if that looks ok, do a closer check with the request's + * available devices + */ + iter = devconfig_get_components(request); + for (; (iter != NULL) && (error == 0); iter = iter->next) { + + devconfig_t *req = (devconfig_t *)iter->obj; + component_type_t type = TYPE_UNKNOWN; + char *typestr = NULL; + uint64_t nbytes = 0; + + (void) devconfig_get_type(req, &type); + if (type == TYPE_HSP) { + continue; + } + + typestr = devconfig_type_to_str(type); + + if ((error = devconfig_get_size(req, &nbytes)) == 0) { + + /* check specified size */ + + if (type == TYPE_CONCAT || type == TYPE_STRIPE) { + if ((error = do_available_space_check( + apply_layout_overhead_factor(nbytes), + raw_space, req, usable_slices)) == 0) { + total_rqst_space += nbytes; + } else if (error == ENOSPC || error == E2BIG) { + (void) snprintf(bad_rqst_info, BUFSIZ-1, + "%s", typestr); + bad_rqst_space = nbytes; + } + } else if (type == TYPE_MIRROR) { + uint16_t nsubs = 0; + if ((error = get_mirror_nsubs(req, &nsubs)) == 0) { + if ((error = do_available_space_check( + apply_layout_overhead_factor(nbytes * nsubs), + raw_space, req, usable_slices)) == 0) { + total_rqst_space += (nsubs * nbytes); + } else { + (void) snprintf(bad_rqst_info, BUFSIZ-1, + gettext("%s with %d submirrors"), + typestr, nsubs); + bad_rqst_space = nbytes; + } + } + } + + } else if ((error == ERR_ATTR_UNSET) && (type == TYPE_MIRROR)) { + + /* mirror specified no size: find submirror that does */ + + dlist_t *subs = devconfig_get_components(req); + + error = 0; + if (subs != NULL) { + dlist_t *iter2; + int nsubs = dlist_length(subs); + for (iter2 = subs; + (iter2 != NULL) && (error == 0); + iter2 = iter2->next) { + devconfig_t *sub = (devconfig_t *)iter2->obj; + if ((error = devconfig_get_size(sub, &nbytes)) == 0) { + if ((error = do_available_space_check( + apply_layout_overhead_factor(nbytes * nsubs), + raw_space, req, usable_slices)) == 0) { + total_rqst_space += (nbytes * nsubs); + } else { + (void) snprintf(bad_rqst_info, BUFSIZ-1, + gettext("%s with %d submirrors"), + typestr, nsubs); + bad_rqst_space = nbytes; + } + break; + } else if (error == ERR_ATTR_UNSET) { + error = 0; + } + } + } + } + } + + /* + * do_available_space_check may return ENOSPC or E2BIG + */ + if (error == ENOSPC) { + char *sizestr = NULL; + (void) bytes_to_sizestr(bad_rqst_space, + &sizestr, universal_units, B_FALSE); + + volume_set_error( + gettext("Failed: the request for a %s %s " + "exceeds the available space.\n"), + sizestr, bad_rqst_info); + + free(sizestr); + error = -1; + + } else if (error == E2BIG) { + char *sizestr = NULL; + (void) bytes_to_sizestr(bad_rqst_space, + &sizestr, universal_units, B_FALSE); + + volume_set_error( + gettext("Failed: the request for a %s %s " + "exceeds the usable space on the device(s) " + "specified as available.\n"), + sizestr, bad_rqst_info); + + free(sizestr); + error = -1; + + } else if (apply_layout_overhead_factor(total_rqst_space) > raw_space) { + char *sizestr = NULL; + (void) bytes_to_sizestr( + total_rqst_space, &sizestr, universal_units, B_FALSE); + + volume_set_error( + gettext("Failed: the total space requested for the " + "volumes (about %s) exceeds the available " + "space.\n"), + sizestr); + + free(sizestr); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: apply_layout_overhead_factor(uint64_t req_size) + * + * INPUT: req_size: a requested volume size + * + * RETURNS: the requested volume size with an overhead factor applied + * + * PURPOSE: The input size size is inflated by a "fudge" factor + * to account for some of the expected overhead required for + * volumes such as block and cylinder boundary alignment. + */ +static uint64_t +apply_layout_overhead_factor( + uint64_t req_size) +{ + double overhead = 1.15; + double d_size = req_size; + uint64_t result = (uint64_t)(d_size * overhead); + + return (result); +} + +/* + * FUNCTION: get_space_available_for_request(devconfig_t *request, + * dlist_t *usable_slices, uint64_t *avail_space) + * + * INPUT: request: a devconfig_t volume request + * usable_slices: a list of usable slice dm_descriptor_t handles + * + * OUTPUT: avail_space: the total space on slices in the usable_slice + * list that is available for use by the input + * request. + * + * RETURNS: int - 0 on success + * !0 on failure + * + * PURPOSE: Iterate the input list of usable slices, determine which are + * available to the input request and accumulate the total space + * they represent. + * + * The slices in the usable_slice list are those with no apparent + * usage detected. The slice_is_available() check determines + * whether the slice passes the available/unavailable device + * specification associated with the input request. + */ +static int +get_space_available_for_request( + devconfig_t *request, + dlist_t *usable_slices, + uint64_t *avail_space) +{ + dlist_t *iter; + int error = 0; + + *avail_space = 0; + + for (iter = usable_slices; + (iter != NULL) && (error == 0); + iter = iter->next) { + dm_descriptor_t slice = (uintptr_t)iter->obj; + char *sname; + uint64_t nbytes; + boolean_t avail = B_FALSE; + if ((error = get_display_name(slice, &sname)) == 0) { + if ((error = slice_is_available(sname, request, &avail)) == 0) { + if (avail == B_TRUE) { + if ((error = slice_get_size(slice, &nbytes)) == 0) { + *avail_space += nbytes; + } + } + } + } + } + + return (error); +} + +/* + * FUNCTION: do_available_space_check(uint64_t req_size, + * uint64_t raw_avail_space, devconfig_t *request, + * dlist_t *usable_slices) + * + * INPUT: req_size: the requested size of a volume + * raw_avail_space:the total available space for all volumes + * request: a devconfig_t volume request + * usable_slices: a list of usable slice dm_descriptor_t handles + * + * RETURNS: int - ENOSPC if the requested size exceeds the raw + * available space. + * + * E2BIG if the requested size exceeds the space + * available specifically to the input request, + * taking into account its available and + * unavailable device specifications. + * + * 0 otherwise + * + * PURPOSE: Check the input request size against different forms of + * available space. + * + * If the requested size is less than the raw_avail_space, do the + * more expensive check against the space specifically available + * to the input request. + */ +static int +do_available_space_check( + uint64_t req_size, + uint64_t raw_avail_space, + devconfig_t *request, + dlist_t *usable_slices) +{ + int error = 0; + + if (req_size > raw_avail_space) { + error = ENOSPC; + } else { + uint64_t avail_space = 0; + if ((error = get_space_available_for_request(request, + usable_slices, &avail_space)) == 0) { + if (req_size > avail_space) { + error = E2BIG; + } + } + } + + return (error); +} + +/* + * FUNCTION: validate_request(devconfig_t *req) + * + * INPUT: req - a devconfig_t representing a volume layout request. + * + * RETURNS: int - 0 if the request passes validation + * !0 otherwise. + * + * PURPOSE: Main entry point into the layout request semantic + * validatation. + * + * Determines the type of volume requested and invokes the + * appropriate validation functions. + */ +int +validate_request( + devconfig_t *req) +{ + int error = 0; + component_type_t type = TYPE_UNKNOWN; + + ((error = validate_request_avail_unavail(req)) != 0) || + (error = devconfig_get_type(req, &type)); + if (error != 0) { + return (error); + } + + if (type == TYPE_MIRROR) { + + ((error = validate_request_name(req, type)) != 0) || + (error = validate_request_size(req, type)) || + (error = validate_request_submirrors(req)); + + } else if (type == TYPE_CONCAT || type == TYPE_STRIPE) { + + ((error = validate_request_name(req, type)) != 0) || + (error = validate_request_size(req, type)) || + (error = validate_slice_components(req, type)); + + } else if (type == TYPE_HSP) { + + ((error = validate_request_name(req, type)) != 0) || + (error = validate_slice_components(req, type)); + + } else if (type == TYPE_VOLUME) { + + ((error = validate_request_name(req, type)) != 0) || + (error = validate_request_redundancy_level(req)) || + (error = validate_request_npaths(req)); + + } + + return (error); +} + +/* + * FUNCTION: validate_reserved_slices() + * + * RETURNS: int - 0 if all reserved slices are usable in + * new devices. + * !0 otherwise. + * + * PURPOSE: Ensures that each reserved slice is actually usable + * as a volume component. + * + * Retrieves list of reserved slices and list of usable + * slices. Ensures that each reserved slice is in the + * usable list, generates an error if it is not. + * + * This is broken out as a separate function because + * initial validation is using the lists of all known + * devices. Device "usability" is only determined after + * the initial validation has completed successfully. + */ +int +validate_reserved_slices() +{ + dlist_t *reserved_slices; + dlist_t *usable_slices; + int error = 0; + + ((error = get_reserved_slices(&reserved_slices)) != 0) || + (error = get_usable_slices(&usable_slices)); + if (error == 0) { + + dlist_t *iter; + for (iter = reserved_slices; + (iter != NULL) && (error == 0); + iter = iter->next) { + + if (dlist_contains(usable_slices, iter->obj, + compare_descriptor_names) != B_TRUE) { + + dm_descriptor_t slice = (uintptr_t)iter->obj; + char *name = NULL; + + error = get_display_name(slice, &name); + if (error == 0) { + char *aliases = get_device_aliases_string(slice); + if (aliases[0] != NULL) { + volume_set_error( + gettext("A requested volume component " + "is currently in use: \"%s\" " + "(aliases: %s).\n"), + name, aliases); + } else { + volume_set_error( + gettext("A requested volume component " + "is currently in use: \"%s\"\n"), + name); + } + error = -1; + } + } + } + } + + return (error); +} + +/* + * FUNCTION: validate_request_avail_unavail(devconfig_t *req) + * + * INPUT: req - a devconfig_t representing a volume layout request. + * + * RETURNS: int - 0 if the request passes validation + * !0 otherwise. + * + * PURPOSE: validation function for a request's lists of available + * and unavailable devices. + * + * validates that both lists contain names of known devices. + * + * validates that the same name does not appear in both lists. + */ +int +validate_request_avail_unavail( + devconfig_t *req) +{ + dlist_t *avail = NULL; + dlist_t *unavail = NULL; + int error = 0; + + /* check that each array contains valid devices */ + ((error = validate_device_array(devconfig_get_available(req), + gettext("available"), &avail)) != 0) || + (error = validate_device_array(devconfig_get_unavailable(req), + gettext("unavailable"), &unavail)); + + /* check that the arrays don't both contain the same device(s) */ + if (error == 0) { + dlist_t *iter; + for (iter = avail; iter != NULL; iter = iter->next) { + if (dlist_contains(unavail, iter->obj, + compare_descriptor_names) == B_TRUE) { + char *name; + char *aliases = + get_device_aliases_string((uintptr_t)iter->obj); + + (void) get_display_name((uintptr_t)iter->obj, &name); + if (aliases[0] != NULL) { + volume_set_error( + gettext("\"%s\" specified as both available " + "and unavailable.\n" + "It has these aliases: %s\n"), + name, aliases); + } else { + volume_set_error( + gettext("\"%s\" specified as both available " + "and unavailable.\n"), + name); + } + error = -1; + break; + } + } + } + + dlist_free_items(avail, NULL); + dlist_free_items(unavail, NULL); + + return (error); +} + +/* + * FUNCTION: validate_device_array(char **array, char *which, dlist_t **list) + * + * INPUT: array - an array of char * device names + * which - either "available" or "unavailable" + * indicating the array name to use in + * error strings. + * OUTPUT: list - a list of device descriptors corresponding the each + * of the input names. + * + * RETURNS: int - 0 if the array passes validation + * !0 otherwise. + * + * PURPOSE: validation function for a request's list of available + * or unavailable devices. + * + * DID names are converted to CTD names. + * + * The CTD name must be of an available slice, disk or + * HBA, or a known used slice, disk or HBA that was + * discovered when the system's devices were probed. + * + * Any other name is assumed to refer to a device not + * attached to the system and results in a validation + * failure. + * + * Descriptors for validated devices are added to the input + * list. + */ +int +validate_device_array( + char **array, + char *which, + dlist_t **list) +{ + int error = 0; + int i = 0; + + if (array == NULL || *array == NULL) { + return (0); + } + + for (i = 0; (array[i] != NULL) && (error == 0); i++) { + + dm_descriptor_t slice = (dm_descriptor_t)0; + dm_descriptor_t disk = (dm_descriptor_t)0; + dm_descriptor_t hba = (dm_descriptor_t)0; + char *name = array[i]; + + /* name must correspond to a known HBA, disk, or slice */ + if ((error = hba_get_by_name(name, &hba)) == 0) { + if (hba == (dm_descriptor_t)0) { + if ((error = disk_get_by_name(name, &disk)) == 0) { + if (disk == (dm_descriptor_t)0) { + error = slice_get_by_name(name, &slice); + } + } + } + } + + if (error != 0) { + break; + } + + /* 0 sized slices cannot be used as-is, pretend non-existant */ + if (slice != (dm_descriptor_t)0) { + uint64_t size = 0; + if ((error = slice_get_size(slice, &size)) == 0) { + if (size == 0) { + slice = (dm_descriptor_t)0; + } + } + } + + oprintf(OUTPUT_DEBUG, + gettext(" validate %s (%s): s=%llu, d=%llu, c=%llu\n"), + which, array[i], slice, disk, hba); + + if ((error == 0) && ((slice != 0) || (disk != 0) || (hba != 0))) { + + /* name represents an individual "device", add it to the list */ + dm_descriptor_t desc = (dm_descriptor_t)0; + dlist_t *item; + + if (slice != 0) { + desc = slice; + } else if (disk != 0) { + desc = disk; + } else if (hba != 0) { + desc = hba; + } + + if ((item = dlist_new_item((void *)desc)) == NULL) { + error = ENOMEM; + } else { + *list = dlist_append(item, *list, AT_HEAD); + } + + } else if (is_ctd_target_name(name) == B_TRUE) { + + /* expand target to all of its disks */ + dlist_t *disks = NULL; + if ((error = get_disks_for_target(name, &disks)) == 0) { + if ((disks == NULL) || (dlist_length(disks) == 0)) { + volume_set_error( + gettext("nonexistent device specified " + "as %s: \"%s\"."), + which, array[i]); + error = -1; + } else { + dlist_t *iter; + for (iter = disks; + (iter != NULL) && (error == 0); + iter = iter->next) { + + dlist_t *item; + if ((item = dlist_new_item(iter->obj)) == NULL) { + error = ENOMEM; + } else { + *list = dlist_append(item, *list, AT_HEAD); + } + } + } + } + + } else { + + /* not a slice, disk, target or ctrl */ + volume_set_error( + gettext("nonexistent device specified " + "as %s: \"%s\"."), + which, array[i]); + error = -1; + } + } + + return (error); +} + +/* + * FUNCTION: validate_request_name(devconfig_t *req, component_type_t type) + * + * INPUT: req - a devconfig_t volume request + * type - the volume type being requested + * + * SIDEEFFECT: if the request specifies a name and the name is valid and + * not currently in use an attempt is made to reserve it. + * if the name has already been reserved by a prior volume + * request, validation fails. + * + * RETURNS: int - 0 if the requested name passes validation + * (or there is no name request) + * !0 otherwise. + * + * PURPOSE: Validation function for a request's volume name. + * + * a HSP name must be valid and reservable. + * + * a volume name must be valid and reservable. + */ +static int +validate_request_name( + devconfig_t *req, + component_type_t type) +{ + char *name = NULL; + char *typestr = devconfig_type_to_str(type); + int error = 0; + + if ((error = devconfig_get_name(req, &name)) != 0) { + if (error != ERR_ATTR_UNSET) { + volume_set_error( + gettext("error getting requested name.\n")); + return (error); + } + /* no name specified */ + return (0); + } + + if (type == TYPE_HSP) { + if (is_hsp_name_valid(name) == 0) { + volume_set_error( + gettext("requested %s name \"%s\" is not valid.\n"), + typestr, name); + error = -1; + } else if (reserve_hsp_name(name) != 0) { + if (is_rsvd_name(name) == B_TRUE) { + volume_set_error( + gettext("requested %s name \"%s\" used " + "previously in this request.\n"), + typestr, name); + } else { + volume_set_error( + gettext("requested %s name \"%s\" is not " + "available.\n"), + typestr, name); + } + error = -1; + } else { + error = add_reserved_name(name); + } + } else { + if (is_volume_name_valid(name) == 0) { + volume_set_error( + gettext("requested %s name \"%s\" is not valid.\n"), + typestr, name); + error = -1; + } else if (is_volume_name_in_range(name) != B_TRUE) { + int max = 0; + (void) get_max_number_of_devices(&max); + volume_set_error( + gettext("requested %s name \"%s\" is not legal.\n" + "Use a name less than d%d.\n"), + typestr, name, max); + error = -1; + } else if (reserve_volume_name(name) != 0) { + if (is_rsvd_name(name) == B_TRUE) { + volume_set_error( + gettext("requested %s name \"%s\" used " + "previously in this request.\n"), + typestr, name); + } else { + volume_set_error( + gettext("requested %s name \"%s\" is not " + "available, a volume with that name " + "already exists.\n"), + typestr, name); + } + error = -1; + } else { + error = add_reserved_name(name); + } + } + + return (error); +} + +/* + * FUNCTION: add_reserved_name(char *name) + * + * INPUT: name - a char * volume name + * + * RETURNS: int - 0 on success + * !0 otherwise. + * + * PURPOSE: Helper which remembers specfically requested names + * in a private list to ensure that the same name isn't + * requested more than once. + */ +static int +add_reserved_name( + char *name) +{ + dlist_t *item = NULL; + + if ((item = dlist_new_item(name)) == NULL) { + return (ENOMEM); + } + + _rsvd_names = dlist_append(item, _rsvd_names, AT_TAIL); + + return (0); +} + +/* + * FUNCTION: is_rsvd_name(char *name) + * + * INPUT: name - a char * volume name + * + * RETURNS: boolean_t - B_TRUE if the requested name is currently + * reserved, B_FALSE otherwise. + * + * PURPOSE: Helper which checks to see if the input volume + * name was previously reserved. + */ +static boolean_t +is_rsvd_name( + char *name) +{ + dlist_t *iter = NULL; + + for (iter = _rsvd_names; iter != NULL; iter = iter->next) { + if ((string_case_compare(name, (char *)iter->obj)) == 0) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +/* + * FUNCTION: validate_request_size(devconfig_t *req, component_type_t type) + * + * INPUT: req - a devconfig_t volume request + * type - the volume type being requested + * + * RETURNS: int - 0 if the requested size passes validation + * (or there is no size request) + * !0 otherwise. + * + * PURPOSE: Validation function for a request's volume size. + * + * a HSP request can have no size. + * + * a concat, stripe or mirror request may have a size. + * if size is specified, the request cannot also specify + * components. Conversely, if the request does not specify + * a size, it must specify components. + */ +static int +validate_request_size( + devconfig_t *req, + component_type_t type) +{ + uint64_t nbytes = 0; + int error = 0; + + if (type == TYPE_HSP) { + return (0); + } + + if ((error = devconfig_get_size(req, &nbytes)) != 0) { + if (error == ERR_ATTR_UNSET) { + /* nbytes not specified, request must have subcomponents */ + dlist_t *list = devconfig_get_components(req); + if (list != NULL && dlist_length(list) > 0) { + error = 0; + } else { + volume_set_error( + gettext("%s request specifies no size or " + "subcomponents.\n"), + devconfig_type_to_str(type)); + error = -1; + } + } + return (error); + } + + return (error); +} + +/* + * FUNCTION: validate_minimum_size(uint64_t nbytes) + * + * INPUT: nbytes - requested volume size in bytes + * + * RETURNS: int - 0 if the requested size passes validation + * (or there is no size request) + * !0 otherwise. + * + * PURPOSE: Validation function for a request's volume size. + * + * an error is issued if the requested size <= 512K. + */ +static int +validate_minimum_size( + uint64_t nbytes) +{ + static uint64_t min = (512 * 1024) - 1; + int error = 0; + + if (nbytes <= min) { + char *sizestr = NULL; + char *minstr = NULL; + + (void) bytes_to_sizestr( + nbytes, &sizestr, universal_units, B_FALSE); + (void) bytes_to_sizestr( + min, &minstr, universal_units, B_FALSE); + + volume_set_error( + gettext("requested volume size (%s) must be " + "greater than %s.\n"), + sizestr, minstr); + + free(sizestr); + free(minstr); + + error = -1; + } + + return (error); +} + +/* + * FUNCTION: validate_request_redundancy_level(devconfig_t *req) + * + * INPUT: req - a devconfig_t volume request + * + * RETURNS: int - 0 if the requested redundancy level + * passes validation (or none was requested) + * !0 otherwise. + * + * PURPOSE: Validation function for a redundant volume request's + * redundancy level. + * + * If the request specifies redundancy, the value must be + * between 1 and 4. + */ +static int +validate_request_redundancy_level( + devconfig_t *req) +{ + uint16_t rlevel = 0; + int error = 0; + + if ((error = devconfig_get_volume_redundancy_level( + req, &rlevel)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + } + return (error); + } + + if (rlevel > 4) { + volume_set_error(gettext( + "requested redundancy level must be between 0 and 4.\n")); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: validate_request_npaths(devconfig_t *req) + * + * INPUT: req - a devconfig_t volume request + * + * RETURNS: int - 0 if the requested # of redundant data paths + * passes validation (or none was requested) + * !0 otherwise. + * + * PURPOSE: Validation function for a volume request's number of + * redundant data paths. This value controls the number + * of independent data paths slices components selected + * for the volume should have. + * + * If the request specifies npaths, the value must be + * between 1 and 4 (4 is an arbitrary upper limit, there + * is no known physical limit). + */ +static int +validate_request_npaths( + devconfig_t *req) +{ + uint16_t npaths = 0; + uint16_t minpaths = 1; + uint16_t maxpaths = 4; + + int error = 0; + + if ((error = devconfig_get_volume_npaths(req, &npaths)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + } + return (error); + } + + if (npaths < minpaths || npaths > maxpaths) { + volume_set_error( + gettext("requested number of redundant paths must be " + "between %d and %d.\n"), minpaths, maxpaths); + error = -1; + } + + + if ((npaths > 1) && (is_mpxio_enabled() != B_TRUE)) { + volume_set_error( + gettext("requested number of redundant paths (%d) cannot " + "be provided, MPXIO is not enabled on this " + "system."), + npaths); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: validate_request_submirrors(devconfig_t *req) + * + * INPUT: req - a devconfig_t volume request + * + * RETURNS: int - 0 if the requested mirror's submirrors + * pass validation + * !0 otherwise. + * + * PURPOSE: Validation function for a mirror volume request's + * explicitly specified submirror components. + * + * Items to check: + * a. submirror types + * b. submirror number + * c. submirror sizes + */ +static int +validate_request_submirrors( + devconfig_t *req) +{ + dlist_t *submirrors = NULL; + int error = 0; + + submirrors = devconfig_get_components(req); + + ((error = validate_submirror_types(submirrors)) != 0) || + (error = validate_submirror_number(req, submirrors)) || + (error = validate_submirror_sizes(req, submirrors)); + + return (error); +} + +/* + * FUNCTION: validate_submirror_types(dlist_t *subs) + * + * INPUT: subs - a list of submirror requests + * + * RETURNS: int - 0 if the requested submirrors + * pass validation + * !0 otherwise. + * + * PURPOSE: Validation function for a mirror volume request's + * explicitly specified submirror components. + * + * Checks that each requested submirror request + * is for a concat or stripe. + */ +static int +validate_submirror_types( + dlist_t *submirrors) +{ + dlist_t *iter; + int error = 0; + + /* specified submirrors must be stripes or concats */ + for (iter = submirrors; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *submir = (devconfig_t *)iter->obj; + component_type_t submirtype = TYPE_UNKNOWN; + + if ((error = devconfig_get_type(submir, &submirtype)) != 0) { + volume_set_error( + gettext("failed to get requested component type.\n")); + break; + } + + if (submirtype != TYPE_CONCAT && submirtype != TYPE_STRIPE) { + volume_set_error( + gettext("requested submirror type \"%s\" " + "is not valid.\n"), + devconfig_type_to_str(submirtype)); + error = -1; + break; + } + } + + return (error); +} + +/* + * FUNCTION: validate_submirror_number(devconfig_t *req, dlist_t *subs) + * + * INPUT: req - the mirror request + * subs - the list of requested submirrors + * + * RETURNS: int - 0 if the requested submirrors + * pass validation + * !0 otherwise. + * + * PURPOSE: Validation function for a mirror volume request's + * explicitly specified submirror components. + * + * Checks that the number of submirror components + * that have been specified matches the number of + * submirrors specified. + */ +static int +validate_submirror_number( + devconfig_t *req, + dlist_t *submirrors) +{ + uint16_t nsubs = 0; + int error = 0; + + if ((error = devconfig_get_mirror_nsubs(req, &nsubs)) != 0) { + if (error == ERR_ATTR_UNSET) { + /* not specified */ + error = 0; + } + } else if ((submirrors != NULL) && + (dlist_length(submirrors) != nsubs)) { + volume_set_error( + gettext("the requested number of submirrors (%d) differs " + "from the number of specified submirrors (%d).\n"), + nsubs, dlist_length(submirrors)); + error = -1; + } + + return (error); +} + +/* + * FUNCTION: validate_submirror_sizes(devconfig_t *req, + * dlist_t *submirrors) + * + * INPUT: req - the mirror request + * submirrors - the list of requested submirrors + * + * RETURNS: int - 0 if the requested submirrors + * pass validation + * !0 otherwise. + * + * PURPOSE: Validation function for a mirror volume request's + * explicitly specified size. Assumes that the mirror's size + * has been validated by validate_request_size(). + * + * Compares explicitly requested mirror size against specified + * component sizes and checks: + * + * - any submirror request that specifies both size and + * components is invalid + * - any submirror request specifying a size different + * than that explictly requested for the mirror is + * invalid + * - a submirror request specifying a size < 512K is invalid. + * + * Other validation/warnings: + * + * - submirrors that specify components may end up with + * usable capacity that differs from what was specified + * for the mirror. + * + * - submirrors which specify neither size nor components are + * assumed to be the size requested for the mirror. If the + * mirror size is not specified, the first explicit size for + * a submirror is assumed as the size for the mirror. + */ +static int +validate_submirror_sizes( + devconfig_t *req, + dlist_t *submirrors) +{ + dlist_t *submirs_with_size = NULL; + dlist_t *submirs_with_comps = NULL; + dlist_t *submirs_with_nothing = NULL; + + dlist_t *iter = NULL; + uint64_t mirror_size = 0; + uint64_t assumed_size = 0; + int error = 0; + + if (submirrors == NULL || dlist_length(submirrors) == 0) { + return (0); + } + + if ((error = devconfig_get_size(req, &mirror_size)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + } else { + return (error); + } + } + + /* + * check size and component for each submirror, + * collect those that specify size, components or neither + * into separate lists. + */ + for (iter = submirrors; + (iter != NULL) && (error == 0); + iter = iter->next) { + + devconfig_t *submir = (devconfig_t *)iter->obj; + + error = validate_submirror_size_and_components(submir, + mirror_size, &assumed_size, &submirs_with_size, + &submirs_with_comps, &submirs_with_nothing); + + } + + if (error == 0) { + + int n_size = dlist_length(submirs_with_size); + int n_comp = dlist_length(submirs_with_comps); + int n_none = dlist_length(submirs_with_nothing); + + if ((n_size != 0) && (n_comp != 0)) { + /* some submirrors specified size, some components */ + oprintf(OUTPUT_TERSE, + gettext(" *** warning: %d submirrors are specified " + "by size, %d specified by components.\n" + " The resulting mirror capacity will be " + "that of the smallest submirror.\n"), + n_size, n_comp); + } + + if (n_none != 0) { + if (assumed_size != 0) { + /* some submirrors specified neither size or components */ + char *sizestr = NULL; + + (void) bytes_to_sizestr( + assumed_size, &sizestr, universal_units, B_FALSE); + + oprintf(OUTPUT_TERSE, + gettext(" *** warning: %d submirrors specified " + "neither size or components,\n" + " the assumed size is %s.\n"), + n_none, sizestr); + + free(sizestr); + + } else if (mirror_size == 0) { + volume_set_error( + gettext("no size specified for requested " + "mirror and no sizes/components " + "specified for its submirrors.")); + + error = -1; + } + } + + dlist_free_items(submirs_with_size, NULL); + dlist_free_items(submirs_with_comps, NULL); + dlist_free_items(submirs_with_nothing, NULL); + + } + + return (error); +} + +/* + * FUNCTION: validate_submirror_size_and_components( + * devconfig_t *submir, + * uint64_t mirror_size, + * uint64_t *assumed_size, + * dlist_t **submirs_with_size, + * dlist_t **submirs_with_comps, + * dlist_t **submirs_no_size_or_comps) + * + * INPUT: submir - a specific submirror request + * mirror_size, - the size specified for the mirror + * + * OUTPUT: assumed_size - the assumed size of the mirror, + * if none specified. + * submirs_with_size - pointer to a list of submirror + * requests that specify a size + * submirs_with_comps - pointer to a list of submirror + * requests that specify components + * submirs_no_size_or_comps - pointer to a list of + * submirror requests that specify neither + * a size or components + * + * RETURNS: int - 0 if the requested submirrors + * pass validation + * !0 otherwise. + * + * PURPOSE: Validation function which checks a specific submirror + * request's size and components against the parent mirror's + * size. + * + * - any submirror request that specifies both size and + * components is invalid + * - any submirror request specifying a size different + * than that explictly requested for the mirror is + * invalid + * - a submirror request specifying a size < 512K is invalid. + * - any components specified for a submirror are validated. + * + * If the submirror passes the validation checks, it is added + * to the appropriate output list. + * + * If the input mirror_size is 0 and the submirror specifies + * a valid size, the submirror size is returned as the + * assumed_size for the mirror. + */ +static int +validate_submirror_size_and_components( + devconfig_t *submir, + uint64_t mirror_size, + uint64_t *assumed_size, + dlist_t **submirs_with_size, + dlist_t **submirs_with_comps, + dlist_t **submirs_no_size_or_comps) +{ + uint64_t submir_size = 0; + component_type_t submir_type = TYPE_UNKNOWN; + char *submir_typestr = NULL; + dlist_t *submir_comps = NULL; + dlist_t *item = NULL; + int n_submir_comps = 0; + int error = 0; + + submir_comps = devconfig_get_components(submir); + if (submir_comps != NULL) { + n_submir_comps = dlist_length(submir_comps); + } + + if ((error = devconfig_get_size(submir, &submir_size)) != 0) { + if (error == ERR_ATTR_UNSET) { + /* submirror size not specified */ + error = 0; + submir_size = 0; + } + } + + if (error != 0) { + return (error); + } + + /* submirror type previously validated */ + (void) devconfig_get_type(submir, &submir_type); + submir_typestr = devconfig_type_to_str(submir_type); + + if (submir_size == 0) { + + /* submirror has no size, components? */ + if (n_submir_comps > 0) { + + /* validate components */ + error = validate_slice_components(submir, submir_type); + + item = dlist_new_item((void *)submir); + if (item == NULL) { + error = ENOMEM; + } else { + *submirs_with_comps = + dlist_append(item, *submirs_with_comps, AT_TAIL); + } + + } else { + + /* no size or components */ + item = dlist_new_item((void *)submir); + if (item == NULL) { + error = ENOMEM; + } else { + *submirs_no_size_or_comps = + dlist_append(item, *submirs_no_size_or_comps, AT_TAIL); + } + + } + + } else { + + /* submirror has size, check it */ + if (error == 0) { + error = validate_minimum_size(submir_size); + } + + /* check size against mirror's size */ + if ((error == 0) && (submir_size != mirror_size)) { + + if (mirror_size != 0) { + + /* sizes differ */ + char *sizestr = NULL; + char *mstr = NULL; + + (void) bytes_to_sizestr( + submir_size, &sizestr, universal_units, B_FALSE); + (void) bytes_to_sizestr( + mirror_size, &mstr, universal_units, B_FALSE); + + volume_set_error( + gettext("the requested submirror size (%s) " + "differs from the requested mirror " + "size (%s).\n"), + sizestr, mstr); + + error = -1; + + free(sizestr); + free(mstr); + + } else if (*assumed_size == 0) { + + /* first size assumed as mirror size */ + char *sizestr = NULL; + + (void) bytes_to_sizestr( + submir_size, &sizestr, universal_units, B_FALSE); + + oprintf(OUTPUT_TERSE, + gettext(" *** warning, using first " + "explicit submirror size (%s)\n" + " as the mirror size\n"), + sizestr); + + *assumed_size = submir_size; + + free(sizestr); + + } else if (submir_size != *assumed_size) { + + /* submirror sizes differ */ + char *sizestr1 = NULL; + char *sizestr2 = NULL; + + (void) bytes_to_sizestr( + submir_size, &sizestr1, universal_units, B_FALSE); + (void) bytes_to_sizestr( + *assumed_size, &sizestr2, universal_units, B_FALSE); + + volume_set_error( + gettext("submirror specifies different " + "size (%s) than a previous " + "submirror (%s)\n"), + sizestr1, sizestr2); + + free(sizestr1); + free(sizestr2); + + error = -1; + } + } + + if ((error == 0) && (n_submir_comps > 0)) { + + /* size and subcomponents specified */ + char *sizestr = NULL; + + (void) bytes_to_sizestr( + submir_size, &sizestr, universal_units, B_FALSE); + + volume_set_error( + gettext("%s submirror specifies both an " + "explicit size (%s) and components.\n"), + submir_typestr, sizestr); + + free(sizestr); + error = -1; + + } + + if (error == 0) { + item = dlist_new_item((void *)submir); + if (item == NULL) { + error = ENOMEM; + } else { + *submirs_with_size = + dlist_append(item, *submirs_with_size, AT_TAIL); + } + } + } + + return (error); +} + + +/* + * FUNCTION: validate_slice_components(devconfig_t *req, + * component_type_t type) + * + * INPUT: req - the request + * type - the type of volume being requested + * + * SIDEEFFECT: if the slice component is otherwise valid, an attempt is made + * to reserve it. + * + * RETURNS: int - 0 if the request passes slice component validation + * !0 otherwise. + * + * PURPOSE: Validation function for a concat, stripe or HSP request's + * explicitly specified slice components. + * + * Is the component slice a known device + * Is the component slice available + * Is the component slice already reserved + * + * If the request is for a stripe or concat and the + * request specifies an explicit size, it cannot also + * specify component slices. This is a validation failure. + * + * If the request is for a stripe, the number of specified + * slice components must agree with any expilcit specification + * of the minimum or maximum number of components the stripe + * should have. + */ +static int +validate_slice_components( + devconfig_t *req, + component_type_t type) +{ + dlist_t *list = NULL; + dlist_t *iter = NULL; + int error = 0; + int ncomp = 0; + + char *dsname = get_request_diskset(); + char *voltype = devconfig_type_to_str(type); + + list = devconfig_get_components(req); + + for (iter = list; (iter != NULL) && (error == 0); iter = iter->next) { + + devconfig_t *comp = (devconfig_t *)iter->obj; + component_type_t ctype = TYPE_UNKNOWN; + char *cname = NULL; + dm_descriptor_t slice = (dm_descriptor_t)0; + + if ((error = devconfig_get_type(comp, &ctype)) != 0) { + volume_set_error( + gettext("error getting requested component type."), + voltype); + + continue; + } + + if ((error = devconfig_get_name(comp, &cname)) != 0) { + volume_set_error( + gettext("error getting requested component name.")); + + continue; + } + + if (cname == NULL || cname[0] == '\0') { + volume_set_error( + gettext("%s requested component has no name."), + voltype); + + error = -1; + continue; + } + + if (ctype == TYPE_SLICE) { + + boolean_t in_set = B_FALSE; + boolean_t is_avail = B_FALSE; + boolean_t is_rsvd = B_FALSE; + dm_descriptor_t disk = (dm_descriptor_t)0; + + /* is the slice known and explicitly available? */ + if ((error = slice_is_available(cname, req, + &is_avail)) != 0) { + + if (error == ENODEV) { + volume_set_error( + gettext("%s requested component does not " + "exist: \"%s\"."), + voltype, cname); + error = -1; + } + continue; + } + + if (is_avail != B_TRUE) { + volume_set_error( + gettext("%s requested component is " + "unavailable: \"%s\"."), + voltype, cname); + + error = -1; + continue; + } + + /* get slice and its disk */ + ((error = slice_get_by_name(cname, &slice)) != 0) || + (error = slice_get_disk(slice, &disk)) || + (error = is_reserved_slice(slice, &is_rsvd)) || + (error = is_disk_in_diskset(disk, dsname, &in_set)); + if (error != 0) { + continue; + } + + /* is disk in the set? */ + if (in_set != B_TRUE) { + volume_set_error( + gettext("%s specifies a component not in " + "disk set \"%s\": \"%s\"."), + voltype, dsname, cname); + + error = -1; + continue; + } + + /* was slice specified in some other request? */ + if (is_rsvd == B_TRUE) { + /* include aliases in the error */ + char *aliases = + get_device_aliases_string((dm_descriptor_t)slice); + + if (aliases[0] != NULL) { + volume_set_error( + gettext("%s specifies a previously used " + "component: \"%s\" " + "(aliases: %s).\n"), + voltype, cname, aliases); + } else { + volume_set_error( + gettext("%s specifies a previously used " + "component: \"%s\"\n"), + voltype, cname); + } + + error = -1; + continue; + } + + /* component is ok, reserve it */ + error = add_reserved_slice(slice); + + /* + * the reserved slice component still needs to be + * checked against slices in use by SVM, but that + * information isn't available yet: the usable + * slice derivation happens after validation. + * + * validate_reserved_slices() can be used to check + * them once the usable slices are determined. + */ + + } else { + volume_set_error( + gettext("%s requested component has illegal type."), + voltype); + + error = -1; + continue; + } + } + + if (error != 0) { + return (error); + } + + ncomp = dlist_length(list); + if ((ncomp > 0) && (type == TYPE_CONCAT || type == TYPE_STRIPE)) { + /* explicit size requested for the stripe/concat? */ + uint64_t size = 0; + if ((error = devconfig_get_size(req, &size)) != 0) { + if (error == ERR_ATTR_UNSET) { + error = 0; + } + } else { + /* size and components both specified */ + char *sizestr = NULL; + + (void) bytes_to_sizestr( + size, &sizestr, universal_units, B_FALSE); + + volume_set_error( + gettext("%s specifies both an explicit size (%s) " + "and components."), + voltype, sizestr); + + free(sizestr); + error = -1; + } + } + + if (error != 0) { + return (error); + } + + if ((ncomp > 0) && (type == TYPE_STRIPE)) { + /* does # of components agree with min & max comps? */ + uint16_t min = 0; + uint16_t max = 0; + if ((error = devconfig_get_stripe_mincomp(req, &min)) != 0) { + if (error == ERR_ATTR_UNSET) { + /* min comp not requested */ + error = 0; + } else { + /* error getting requested mincomp */ + return (error); + } + + } else if (ncomp < min) { + /* specified comps < requested mincomp */ + volume_set_error( + gettext("%s specifies fewer components (%d) than the " + "minimum number requested (%d).\n"), + voltype, ncomp, min); + + error = -1; + return (error); + } + + if ((error = devconfig_get_stripe_maxcomp(req, &max)) != 0) { + if (error == ERR_ATTR_UNSET) { + /* max comp not requested */ + error = 0; + } else { + /* error getting request maxcomp */ + return (error); + } + } else if (ncomp > max) { + /* specified comps > requested maxcomp */ + volume_set_error( + gettext("%s specifies more components (%d) than the " + "maximum number requested (%d).\n"), + voltype, ncomp, max); + error = -1; + return (error); + } + } + + return (error); +} + +/* + * Generate a list of known aliases for the input descriptor. + * + * The returned string buffer is in the form: "alias1", "alias2"... + */ +static char * +get_device_aliases_string( + dm_descriptor_t desc) +{ + static char buf[BUFSIZ]; + dlist_t *aliases = NULL; + dlist_t *iter = NULL; + + buf[0] = '\0'; + (void) get_aliases(desc, &aliases); + for (iter = aliases; iter != NULL; iter = iter->next) { + if (*buf == '\0') { + (void) snprintf(buf, BUFSIZ-1, "\"%s\"", (char *)iter->obj); + } else { + char tmp[BUFSIZ]; + (void) strcpy(buf, tmp); + (void) snprintf(buf, BUFSIZ-1, "%s, \"%s\"", + tmp, (char *)iter->obj); + } + } + dlist_free_items(aliases, free); + + return (buf); +} diff --git a/usr/src/cmd/lvm/metassist/layout/layout_validate.h b/usr/src/cmd/lvm/metassist/layout/layout_validate.h new file mode 100644 index 0000000000..f4af5e7839 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/layout/layout_validate.h @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LAYOUT_VALIDATE_H +#define _LAYOUT_VALIDATE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "volume_devconfig.h" + +extern int validate_request(devconfig_t *req); + +extern int validate_request_sizes(devconfig_t *req); + +extern int validate_request_avail_unavail(devconfig_t *req); + +extern int validate_reserved_slices(); + +extern int release_validation_caches(); + +extern int validate_basic_svm_config(); + +#ifdef __cplusplus +} +#endif + +#endif /* _LAYOUT_VALIDATE_H */ diff --git a/usr/src/cmd/lvm/metassist/scripts/Makefile b/usr/src/cmd/lvm/metassist/scripts/Makefile new file mode 100644 index 0000000000..92eff55f20 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/scripts/Makefile @@ -0,0 +1,40 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +SHFILES = errifoutput + +CLEANFILES += $(SHFILES) + +all: $(SHFILES) + +include $(METASSIST_TOPLEVEL)/Makefile.targ diff --git a/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh b/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh new file mode 100644 index 0000000000..cab32d4ab3 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Runs the command passed as arguments, echoes the output to stderr. +# +# Exits with 0 (success) if the command exits with 0 and has no +# output. +# +# Exits with 1 (failure) if the command exits with 0 and has output. +# +# Exits with the exit code of the command if it exits with a non-zero +# exit code. + +output=`"$@" 2>&1` +result=$? + +if [ -n "$output" ] +then + echo "$output" >&2 + test $result = 0 && result=1 +fi + +exit $result diff --git a/usr/src/cmd/lvm/metassist/sysfiles/Makefile b/usr/src/cmd/lvm/metassist/sysfiles/Makefile new file mode 100644 index 0000000000..13a60dcf9c --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/Makefile @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +# Files to be copied to /etc/defaults +DEFAULTFILES = \ + metassist.xml + +# Files to be copied to /usr/share/lib/xml/dtd +DTDFILES = \ + volume-config.dtd \ + volume-defaults.dtd \ + volume-request.dtd + +# Files to be copied to /usr/share/lib/xml/style +STYLEFILES = \ + volume-command.xsl + +include $(METASSIST_TOPLEVEL)/Makefile.targ diff --git a/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml b/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml new file mode 100644 index 0000000000..3fae198174 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + * + * ident "%Z%%M% %I% %E% SMI" + * + * Specifies the default settings for volume requests used by + * metassist(1M). + * + * See volume-request(4) for a detailed description of the syntax. + --> +<!DOCTYPE volume-defaults SYSTEM "/usr/share/lib/xml/dtd/volume-defaults.dtd"> + +<volume-defaults> +</volume-defaults> diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl b/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl new file mode 100644 index 0000000000..1d669ab420 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl @@ -0,0 +1,706 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 + * + * ident "%Z%%M% %I% %E% SMI" + * + * Used by metassist(1M) to create a Bourne shell script from an XML + * file conforming to the volume-config DTD. + * + * See volume-config(4) for a detailed description of the volume- + * config syntax. + --> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="text"/> + + <!-- Set a default lang --> + <xsl:param name="lang">en</xsl:param> + + <!-- The file containing localized <message> elements --> + <!-- Currently set to local doc until an i18n scheme is established --> + <xsl:variable name="msgfile" select="document('')"/> + <xsl:variable name="langprefix" select="substring-before($lang,'-')"/> + + <!-- Root template --> + <xsl:template match="/"> + + <!-- Use Bourne shell --> + <xsl:text>#!/bin/sh + +# +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Environment</xsl:with-param> + </xsl:call-template> + + <xsl:text> +# + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Amend PATH</xsl:with-param> + </xsl:call-template> + + <xsl:text> +PATH="/usr/sbin:/usr/bin:$PATH" +export PATH + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Disk set name</xsl:with-param> + </xsl:call-template> + + <!-- Set disk set --> + <xsl:text>
</xsl:text> + <xsl:text>diskset='</xsl:text> + <xsl:value-of select="//diskset/@name" /> + + <!-- 
 is a newline entity --> + <xsl:text>'
</xsl:text> + + <xsl:text> +# +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Functions</xsl:with-param> + </xsl:call-template> + + <xsl:text> +# + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Echo (verbose) and exec given command, exit on error</xsl:with-param> + </xsl:call-template> + + <xsl:text> +execho () { + test -n "$verbose" && echo "$@" + "$@" || exit +} + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Get full /dev/rdsk path of given slice</xsl:with-param> + </xsl:call-template> + + <xsl:text> +fullpath () { + case "$1" in + /dev/dsk/*|/dev/did/dsk/*) echo "$1" | sed 's/dsk/rdsk/' ;; + /*) echo "$1" ;; + *) echo /dev/rdsk/"$1" ;; + esac +} + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Run fmthard, ignore partboot error, error if output</xsl:with-param> + </xsl:call-template> + + <xsl:text> +fmthard_special () { + ignore='Error writing partboot' + out=`fmthard "$@" 2>&1` + result=$? + echo "$out" | + case "$out" in + *"$ignore"*) grep -v "$ignore"; return 0 ;; + '') return "$result" ;; + *) cat; return 1 ;; + esac >&2 +} + +# +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Main</xsl:with-param> + </xsl:call-template> + + <xsl:text> +# + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Verify root</xsl:with-param> + </xsl:call-template> + + <xsl:text> +if [ "`id | sed 's/^[^(]*(\([^)]*\).*/\1/'`" != root ] +then + echo "</xsl:text> + + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">This script must be run as root.</xsl:with-param> + </xsl:call-template> + + <xsl:text>" >&2 + exit 1; +fi + +# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Check for verbose option</xsl:with-param> + </xsl:call-template> + + <xsl:text> +case "$1" in + -v) verbose=1 ;; + *) verbose= ;; +esac + </xsl:text> + + <!-- Create disk set --> + <xsl:apply-templates select="//diskset" mode="create"/> + + <!-- Format unused slices --> + <xsl:apply-templates select="//slice[@sizeinblocks = 0]" mode="create"/> + + <!-- Add disks to set --> + <xsl:apply-templates select="//disk" mode="add"/> + + <!-- Format used slices --> + <xsl:apply-templates select="//slice[@sizeinblocks != 0]" mode="create"/> + + <!-- Create HSPs --> + <xsl:apply-templates select="//hsp" mode="create"/> + + <!-- Create stripes --> + <xsl:apply-templates select="//stripe" mode="create"/> + + <!-- Create concats --> + <xsl:apply-templates select="//concat" mode="create"/> + + <!-- Create mirrors --> + <xsl:apply-templates select="//mirror" mode="create"/> + + </xsl:template> + + <!-- "Create disk set" template --> + <xsl:template match="diskset" mode="create"> + <xsl:text>
# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Does the disk set exist?</xsl:with-param> + </xsl:call-template> + + <xsl:text> +if metaset -s "$diskset" >/dev/null 2>&1 +then + # </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Take control of disk set</xsl:with-param> + </xsl:call-template> + + <xsl:text> + execho metaset -s "$diskset" -t +else + # </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Create the disk set</xsl:with-param> + </xsl:call-template> + + <xsl:text> + autotakeargs= + /usr/sbin/clinfo || autotakeargs='-A enable' + execho metaset -s "$diskset" $autotakeargs -a -h `uname -n | cut -f1 -d.` +fi + </xsl:text> + </xsl:template> + + <!-- "Add disk" template --> + <xsl:template match="disk" mode="add"> + <!-- Add comment --> + <xsl:if test="position() = 1"> + <xsl:text>
# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Add disks to set</xsl:with-param> + </xsl:call-template> + <xsl:text>
</xsl:text> + </xsl:if> + + <!-- Output command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho metaset -s "$diskset" -a {1}</xsl:with-param> + <xsl:with-param name="1" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + </xsl:template> + + <!-- "Create slice" template --> + <xsl:template match="slice" mode="create"> + + <!-- Add comment --> + <xsl:if test="position() = 1"> + <xsl:text>
# </xsl:text> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Format slices</xsl:with-param> + </xsl:call-template> + <xsl:text>
</xsl:text> + </xsl:if> + + <!-- Does this slice have a start sector and size? --> + <xsl:if test="(@startsector and @sizeinblocks) or (@sizeinblocks = 0)"> + + <!-- Output command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho fmthard_special -d {1}:{5}:0:{2}:{3} `fullpath {4}`</xsl:with-param> + <xsl:with-param name="1"> + <xsl:call-template name="getslice"> + <xsl:with-param name="device" select="@name" /> + </xsl:call-template> + </xsl:with-param> + <xsl:with-param name="2"> + <xsl:choose> + <!-- When zeroing out a slice, use 0 for start sector --> + <xsl:when test="@sizeinblocks = 0">0</xsl:when> + <!-- Otherwise, use the start sector supplied --> + <xsl:otherwise> + <xsl:value-of select="@startsector" /> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + <xsl:with-param name="3" select="@sizeinblocks" /> + <xsl:with-param name="4" select="@name" /> + <xsl:with-param name="5"> + <xsl:choose> + <!-- When zeroing out a slice, use 0 (V_UNASSIGNED) slice tag --> + <xsl:when test="@sizeinblocks = 0">0</xsl:when> + <!-- Otherwise, use 4 (V_USR) --> + <xsl:otherwise>4</xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + + <xsl:text>
</xsl:text> + </xsl:if> + </xsl:template> + + <!-- Template for a "create volume" comment --> + <xsl:template name="createdevcomment"> + <!-- Indent parameter --> + <xsl:param name = "indent" /> + + <xsl:text>
</xsl:text> + <xsl:value-of select="$indent" /> + <xsl:text># </xsl:text> + + <!-- Add comment --> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Create {1} {2}</xsl:with-param> + <xsl:with-param name="1" select="name()"/> + <xsl:with-param name="2" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + </xsl:template> + + <!-- "Create hsp" template --> + <xsl:template match="hsp" mode="create"> + + <!-- Does this HSP contain slice elements? --> + <xsl:if test="slice"> + + <xsl:text>
# </xsl:text> + + <!-- Add comment --> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Does {1} exist?</xsl:with-param> + <xsl:with-param name="1" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + + <!-- Output command to test for existence of HSP --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">metahs -s "$diskset" -i {1} >/dev/null 2>&1 || {</xsl:with-param> + <xsl:with-param name="1" select="@name"/> + </xsl:call-template> + + <!-- Add comment --> + <xsl:call-template name="createdevcomment"> + <xsl:with-param name = "indent" xml:space="preserve"> </xsl:with-param> + </xsl:call-template> + + <!-- Output command to create HSP --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string"> execho metainit -s "$diskset" {1}</xsl:with-param> + <xsl:with-param name="1" select="@name"/> + </xsl:call-template> + + <xsl:text>
}

# </xsl:text> + + <!-- Add comment --> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Add slices to {1}</xsl:with-param> + <xsl:with-param name="1" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + + <xsl:for-each select="slice"> + + <!-- Output command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho metahs -s "$diskset" -a {1} {2}</xsl:with-param> + <xsl:with-param name="1" select="../@name"/> + <xsl:with-param name="2" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + + </xsl:for-each> + </xsl:if> + + </xsl:template> + + <!-- "Create stripe/concat" template --> + <xsl:template match="stripe|concat" mode="create"> + + <!-- Does this stripe/concat contain slice elements? --> + <xsl:if test="slice"> + + <!-- Add comment --> + <xsl:call-template name="createdevcomment"/> + + <!-- Output command --> + <xsl:text>execho metainit -s "$diskset" </xsl:text> + <xsl:value-of select="@name" /> + + <xsl:choose> + <!-- Stripe-specific parameters --> + <xsl:when test="name() = 'stripe'"> + <xsl:text> 1 </xsl:text> + <xsl:value-of select="count(slice)" /> + + <xsl:for-each select="slice"> + <xsl:text> </xsl:text> + <xsl:value-of select="@name" /> + </xsl:for-each> + + <!-- Does this stripe contain an interlace attribute? --> + <xsl:if test="@interlace"> + + <!-- Write interlace with unit string --> + <xsl:variable name="interlace" + select="substring-before(@interlace, 'KB')"/> + <xsl:choose> + <xsl:when test="$interlace != ''"> + <xsl:value-of select="concat(' -i ', $interlace, 'k')" /> + </xsl:when> + <xsl:otherwise> + <xsl:variable name="interlace" + select="substring-before(@interlace, 'MB')"/> + <xsl:choose> + <xsl:when test="$interlace != ''"> + <xsl:value-of select="concat(' -i ', $interlace, 'm')" /> + </xsl:when> + <xsl:otherwise> + <xsl:variable name="interlace" + select="substring-before(@interlace, 'BLOCKS')"/> + <xsl:if test="$interlace != ''"> + <xsl:value-of select="concat(' -i ', $interlace, 'b')" /> + </xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:if> + </xsl:when> + + <!-- Concat-specific parameters --> + <xsl:otherwise> + <xsl:text> </xsl:text> + <xsl:value-of select="count(slice)" /> + + <xsl:for-each select="slice"> + <xsl:text> 1 </xsl:text> + <xsl:value-of select="@name" /> + </xsl:for-each> + </xsl:otherwise> + </xsl:choose> + + <xsl:text>
</xsl:text> + </xsl:if> + + <!-- Does this stripe/concat contain hsp elements? --> + <xsl:if test="hsp"> + + <xsl:text>
# </xsl:text> + + <!-- Add comment --> + <xsl:call-template name="gettext"> + <xsl:with-param name="msgid">Associate {1} {2} with hot spare pool {3}</xsl:with-param> + <xsl:with-param name="1" select="name()"/> + <xsl:with-param name="2" select="@name"/> + <xsl:with-param name="3" select="hsp/@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + + <!-- Output command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho metaparam -s "$diskset" -h {1} {2}</xsl:with-param> + <xsl:with-param name="1" select="hsp/@name"/> + <xsl:with-param name="2" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + </xsl:if> + </xsl:template> + + <!-- "Create mirror" template --> + <xsl:template match="mirror" mode="create"> + <!-- Add comment --> + <xsl:call-template name="createdevcomment"/> + + <!-- Attach submirrors --> + <xsl:for-each select="stripe|concat"> + <xsl:choose > + <xsl:when test="position() = 1"> + + <!-- Output create command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho metainit -s "$diskset" {1} -m {2}</xsl:with-param> + <xsl:with-param name="1" select="../@name"/> + <xsl:with-param name="2" select="@name"/> + </xsl:call-template> + + <!-- Read option --> + <xsl:choose > + <!-- Geometric --> + <xsl:when test="../@read = 'GEOMETRIC'"> + <xsl:text> -g</xsl:text> + </xsl:when> + + <!-- First --> + <xsl:when test="../@read = 'FIRST'"> + <xsl:text> -r</xsl:text> + </xsl:when> + </xsl:choose> + + <!-- Write option - serial --> + <xsl:if test="../@write = 'SERIAL'"> + <xsl:text> -S</xsl:text> + </xsl:if> + + <!-- Pass number --> + <xsl:if test="../@passnum"> + <xsl:text> </xsl:text> + <xsl:value-of select="../@passnum" /> + </xsl:if> + + <xsl:text>
</xsl:text> + </xsl:when> + + <xsl:otherwise> + <!-- Output attach command --> + <xsl:call-template name="parameterize"> + <xsl:with-param name="string">execho metattach -s "$diskset" {1} {2}</xsl:with-param> + <xsl:with-param name="1" select="../@name"/> + <xsl:with-param name="2" select="@name"/> + </xsl:call-template> + + <xsl:text>
</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </xsl:template> + + <!-- Get the slice index from a device string --> + <xsl:template name="getslice"> + <xsl:param name="device" /> + + <xsl:choose> + + <!-- Does $device contain 's'? --> + <xsl:when test="contains($device, 's')"> + + <!-- Recurse with remaining text --> + <xsl:call-template name="getslice"> + <xsl:with-param name="device" select="substring-after($device, 's')" /> + </xsl:call-template> + </xsl:when> + + <!-- No match --> + <xsl:otherwise> + <xsl:value-of select="$device" /> + </xsl:otherwise> + + </xsl:choose> + </xsl:template> + + <!-- Generic (global) search and replace template --> + <xsl:template name="searchreplace"> + <xsl:param name="text" /> + <xsl:param name="search" /> + <xsl:param name="replace" /> + + <xsl:choose> + + <!-- Does $text contain $search? --> + <xsl:when test="contains($text, $search)"> + + <!-- Print text before match --> + <xsl:value-of select="substring-before($text, $search)" /> + + <!-- Print replaced text --> + <xsl:value-of select="$replace" /> + + <!-- Recurse with remaining text --> + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text" select="substring-after($text, $search)" /> + <xsl:with-param name="search" select="$search" /> + <xsl:with-param name="replace" select="$replace" /> + </xsl:call-template> + </xsl:when> + + <!-- No match --> + <xsl:otherwise> + <xsl:value-of select="$text" /> + </xsl:otherwise> + + </xsl:choose> + </xsl:template> + + <!-- + * Given a message ID (msgid), find a localized message string + * stored in $msgfile as a xsl:variable. Return the first + * occurance of: + * + * 1. The message localized for the language code and country + * code, ie. "en-us" + * + * 2. The message localized for a sublanguage + * + * 3. The message localized for the language code only, ie. "en" + * + * 4. The message localized for the language code, with any + * country code, ie. "en-gb" + * + * 5. $msgid + * + * Parameters: + * + * msgid: The message identification key + * + * 1, 2, 3, 4, 5: Parameters to replace "{1}", "{2}", "{3}", + * "{4}", "{5}" respectively, in the retrieved message + --> + <xsl:template name="gettext"> + <xsl:param name="msgid"/> + <xsl:param name="1"/> + <xsl:param name="2"/> + <xsl:param name="3"/> + <xsl:param name="4"/> + <xsl:param name="5"/> + <xsl:variable name="messages" select="$msgfile//message[@msgid=$msgid]"/> + + <xsl:call-template name="parameterize"> + <xsl:with-param name="1" select="$1"/> + <xsl:with-param name="2" select="$2"/> + <xsl:with-param name="3" select="$3"/> + <xsl:with-param name="4" select="$4"/> + <xsl:with-param name="5" select="$5"/> + <xsl:with-param name="string"> + + <xsl:choose> + + <!-- Exact match for $lang --> + <xsl:when test="$messages[@xml:lang=$lang]"> + <xsl:value-of select="$messages[@xml:lang=$lang][1]"/> + </xsl:when> + + <!-- Sublanguage of $lang --> + <xsl:when test="$messages[lang($lang)]"> + <xsl:value-of name="message" select="$messages[lang($lang)][1]"/> + </xsl:when> + + <!-- Exact match for $langprefix --> + <xsl:when test="$messages[@xml:lang=$langprefix]"> + <xsl:value-of select="$messages[@xml:lang=$langprefix][1]"/> + </xsl:when> + + <!-- Sublanguage of $langprefix --> + <xsl:when test="$messages[lang($langprefix)]"> + <xsl:value-of select="$messages[lang($langprefix)][1]"/> + </xsl:when> + + <!-- No match found, return msgid --> + <xsl:otherwise> + <xsl:value-of select="$msgid"/> + </xsl:otherwise> + + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- Parameterize up to 5 parameters --> + <xsl:template name="parameterize"> + <xsl:param name="string"/> + <xsl:param name="1"/> + <xsl:param name="2"/> + <xsl:param name="3"/> + <xsl:param name="4"/> + <xsl:param name="5"/> + + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text"> + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text"> + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text"> + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text"> + <xsl:call-template name="searchreplace"> + <xsl:with-param name="text" select="$string"/> + <xsl:with-param name="search">{1}</xsl:with-param> + <xsl:with-param name="replace" select="$1"/> + </xsl:call-template> + </xsl:with-param> + <xsl:with-param name="search">{2}</xsl:with-param> + <xsl:with-param name="replace" select="$2"/> + </xsl:call-template> + </xsl:with-param> + <xsl:with-param name="search">{3}</xsl:with-param> + <xsl:with-param name="replace" select="$3"/> + </xsl:call-template> + </xsl:with-param> + <xsl:with-param name="search">{4}</xsl:with-param> + <xsl:with-param name="replace" select="$4"/> + </xsl:call-template> + </xsl:with-param> + <xsl:with-param name="search">{5}</xsl:with-param> + <xsl:with-param name="replace" select="$5"/> + </xsl:call-template> + </xsl:template> + + <!-- Localized message strings used throughout --> + <xsl:template name="localization"> + <message xml:lang="de" msgid="Sample message">Beispielanzeige</message> + </xsl:template> + +</xsl:stylesheet> diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd new file mode 100644 index 0000000000..979e6e0e59 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + * + * Describes a storage configuration used by metassist(1M). + * + * See volume-config(4) for a detailed description of the syntax. + --> +<!ELEMENT volume-config (diskset,disk*,slice*,hsp*,(concat|stripe)*,mirror*)> + +<!-- The existing disk set to use --> +<!ELEMENT diskset EMPTY> +<!ATTLIST diskset name CDATA #REQUIRED> + +<!-- + ***************************************************************** + * + * Disk definition + * + ***************************************************************** + --> + +<!-- Describes a disk that should be added to the disk set --> +<!ELEMENT disk EMPTY> + +<!-- The full name of the disk --> +<!ATTLIST disk name CDATA #REQUIRED> + +<!-- + ***************************************************************** + * + * Slice/soft partition definitions + * + ***************************************************************** + --> + +<!-- + * Describes a newly-defined or existing slice. + * + * If existing, only the name is required. + * + * If newly-defined, the start sector and size are also required. + --> +<!ELEMENT slice EMPTY> + +<!-- The full name of the slice --> +<!ATTLIST slice name CDATA #REQUIRED> + +<!-- The starting sector for this slice --> +<!ATTLIST slice startsector CDATA #IMPLIED> + +<!-- + * The size of this slice in blocks or sectors. + --> +<!ATTLIST slice sizeinblocks CDATA #IMPLIED> + + +<!-- + ***************************************************************** + * + * HSP definition + * + ***************************************************************** + --> + +<!-- + * Describes a new or existing HSP + * + * The slices defined within will be added to this HSP. + --> +<!ELEMENT hsp (slice*)> +<!ATTLIST hsp name CDATA #REQUIRED> + + +<!-- + ***************************************************************** + * + * Concat definition + * + ***************************************************************** + --> + +<!-- + * Describes a new or existing concat + * + * If existing, only the name is required. + * + * If new, the underlying slices and soft partitions are also + * required. The HSP definition is optional. + --> +<!ELEMENT concat (slice*,hsp?)> +<!ATTLIST concat name CDATA #REQUIRED> + +<!-- + ***************************************************************** + * + * Stripe definition + * + ***************************************************************** + --> + +<!-- + * Describes a new or existing stripe + * + * If existing, only the name is required. + * + * If new, the underlying slices and soft partitions are also + * required. The interlace and HSP definitions are optional. + --> +<!ELEMENT stripe (slice*,hsp?)> +<!ATTLIST stripe name CDATA #REQUIRED> + +<!-- + * The interlace of this stripe, in the format <value><units>, + * where <units> is "BLOCKS", "KB", or "MB", and <value> is the + * multiplier of the units. + --> +<!ATTLIST stripe interlace CDATA #IMPLIED> + + +<!-- + ***************************************************************** + * + * Mirror definition + * + ***************************************************************** + --> + +<!-- + * Describes a new mirror + * + * The name of the mirror, and the underlying submirrors are + * required. The remaining attributes are optional. + --> +<!ELEMENT mirror ((concat|stripe)+)> +<!ATTLIST mirror name CDATA #REQUIRED> +<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED> +<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED> +<!ATTLIST mirror passnum CDATA #IMPLIED> diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd new file mode 100644 index 0000000000..42e023e116 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + * + * Describes the system- and diskset-wide defaults for new volume + * requests used by metassist(1M). Both global and diskset-specific + * defaults can be specified. + * + * See volume-request(4) for a detailed description of the syntax. + --> +<!ELEMENT volume-defaults ((available|unavailable)*,(hsp|concat|stripe|mirror|volume)*,diskset*)> + +<!-- + * + * Specify global defaults + * + --> + +<!-- Specify device to use instead of defaults --> +<!ELEMENT available EMPTY> +<!ATTLIST available name CDATA #REQUIRED> + +<!-- Specify device not to use --> +<!ELEMENT unavailable EMPTY> +<!ATTLIST unavailable name CDATA #REQUIRED> + +<!ELEMENT hsp EMPTY> +<!ATTLIST hsp name CDATA #IMPLIED> + +<!ELEMENT concat EMPTY> +<!ATTLIST concat usehsp (TRUE|FALSE) #IMPLIED> + +<!ELEMENT stripe EMPTY> +<!ATTLIST stripe interlace CDATA #IMPLIED> +<!ATTLIST stripe maxcomp CDATA #IMPLIED> +<!ATTLIST stripe mincomp CDATA #IMPLIED> +<!ATTLIST stripe usehsp (TRUE|FALSE) #IMPLIED> + +<!ELEMENT mirror EMPTY> +<!ATTLIST mirror nsubmirrors CDATA #IMPLIED> +<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED> +<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED> +<!ATTLIST mirror passnum CDATA #IMPLIED> +<!ATTLIST mirror usehsp (TRUE|FALSE) #IMPLIED> + +<!ELEMENT volume EMPTY> +<!ATTLIST volume redundancy CDATA #IMPLIED> +<!ATTLIST volume datapaths CDATA #IMPLIED> +<!ATTLIST volume faultrecovery (TRUE|FALSE) #IMPLIED> + +<!-- + * + * Specify per-disk set defaults + * + --> + +<!ELEMENT diskset ((available|unavailable)*,hsp?,(concat|stripe|mirror|volume)*)> +<!ATTLIST diskset name CDATA #REQUIRED> diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd new file mode 100644 index 0000000000..f4b3f9e68f --- /dev/null +++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd @@ -0,0 +1,388 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + * + * Describes the request for a new volume configuration used by + * metassist(1M). + * + * See volume-request(4) for a detailed description of the syntax. + --> +<!ELEMENT volume-request (diskset,(available|unavailable)*,hsp?,(concat|stripe|mirror|volume)*)> + +<!-- + ***************************************************************** + * + * Disk set definition + * + ***************************************************************** + --> + +<!-- + * The new or existing disk set to use. If the disk set does not + * exist, it will be created. + --> +<!ELEMENT diskset EMPTY> +<!ATTLIST diskset name CDATA #REQUIRED> + + +<!-- + ***************************************************************** + * + * Available devices definition + * + ***************************************************************** + --> + +<!-- + * Specify a controller, target, disk, or slice that may be used in + * the construction of new SVM configuration. + * + * Disks specified herein should be: + * + * 1. part of the disk set named above + * + * or + * + * 2. unused and not belong to any disk set + * + * If a controller is specified, all slices on all disks on the + * controller which match the above criteria are considered + * available. + * + * If a target is specified, all slices on all disks on the target + * which match the above criteria are considered available. + * + * If no available devices are specified, all disks on the system + * which match the above criteria are considered available. + --> +<!ELEMENT available EMPTY> +<!ATTLIST available name CDATA #REQUIRED> + +<!-- + * Specify a controller, target, disk, or slice to exclude from the + * available pool. + --> +<!ELEMENT unavailable EMPTY> +<!ATTLIST unavailable name CDATA #REQUIRED> + + +<!-- + ***************************************************************** + * + * Hot spare pool definition + * + ***************************************************************** + --> + +<!-- + * Specify a new or existing hot spare pool for submirrors defined + * in this request. If a HSP is required by this request, and no + * HSP is specified here, the first existing HSP in the above disk + * set will be used instead. If no HSPs exist, a new HSP will be + * created. + * + * Optionally specify disks that can be used/excluded if new hot + * spares must be created. + --> +<!ELEMENT hsp ((available|unavailable)*)> + +<!-- + * The name to assign to this hot spare pool. If no name is + * specified, a name will be chosen automatically. No guarantees + * are made about the algorithm used, except that the resulting name + * will be one that is not currently in use. + --> +<!ATTLIST hsp name CDATA #IMPLIED> + +<!-- + ***************************************************************** + * + * Slice definition + * + ***************************************************************** + --> + +<!ELEMENT slice EMPTY> +<!ATTLIST slice name CDATA #REQUIRED> + + +<!-- + ***************************************************************** + * + * Stripe definition + * + ***************************************************************** + --> + +<!-- + * the following line should work, but fails xmllint for some reason + * + * <!ELEMENT stripe (slice*|(available|unavailable)*)> + --> + +<!-- + * Create a new top-level stripe either by specifying the underlying + * components (slices and soft partitions) or by specifying the + * required size and optional available/unavailable resources. + --> +<!ELEMENT stripe ((available|unavailable)*,slice*)> + +<!-- + * The volume name to assign to this stripe. If no name is + * specified, a name will be chosen automatically. No guarantees + * are made about the algorithm used, except that the resulting name + * will be one that is not currently in use. + --> +<!ATTLIST stripe name CDATA #IMPLIED> + +<!-- + * The size of this stripe, in the format <value><units>, where + * <units> is "KB", "MB", "GB", or "TB", and <value> is the + * multiplier of the units. + * + * This attribute is ignored if the underlying components are + * explicitly specified. + --> +<!ATTLIST stripe size CDATA #IMPLIED> + +<!-- + * Specify the minimum and maximum number of components (slices and + * soft partitions) to use when constructing this stripe. + * + * The default value for mincomp is 4. + * + * The default value for maxcomp is 10. + * + * These attributes should not be specified if the underlying + * components are explicitly specified. + --> +<!ATTLIST stripe mincomp CDATA #IMPLIED> +<!ATTLIST stripe maxcomp CDATA #IMPLIED> + +<!-- + * The interlace of this stripe, in the format <value><units>, where + * <units> is "BLOCKS", "KB", or "MB", and <value> is the multiplier + * of the units. + * + * The default value is 64KB. + --> +<!ATTLIST stripe interlace CDATA #IMPLIED> + +<!-- + * Boolean (TRUE or FALSE) indicating whether to use a HSP. + * + * This is only relevant if this stripe is a submirror, i.e. it is + * defined within a <mirror> element. If not, this attribute is + * ignored. + * + * The default value is FALSE. + --> +<!ATTLIST stripe usehsp (TRUE|FALSE) #IMPLIED> + + +<!-- + ***************************************************************** + * + * Concat definition + * + ***************************************************************** + --> + +<!-- + * the following line should work, but fails xmllint for some reason + * + * <!ELEMENT concat (slice+|(available|unavailable)*)> + --> + +<!-- + * Create a new concat either by specifying the underlying slices + * and soft partitions or by specifying the required size and + * optional available/unavailable resources. + --> +<!ELEMENT concat ((available|unavailable)*,slice*)> + +<!-- + * The volume name to assign to this concat. If no name is + * specified, a name will be chosen automatically. No guarantees + * are made about the algorithm used, except that the resulting name + * will be one that is not currently in use. + --> +<!ATTLIST concat name CDATA #IMPLIED> + +<!-- + * The size of this concat, in the format <value><units>, where + * <units> is "KB", "MB", "GB", or "TB", and <value> is the + * multiplier of the units. + * + * This attribute is ignored if the underlying slices/soft + * partitions are explicitly specified. + --> +<!ATTLIST concat size CDATA #IMPLIED> + +<!-- + * Boolean (TRUE or FALSE) indicating whether to use a HSP. + * + * This is only relevant if this concat is a submirror, i.e. it is + * defined within a <mirror> element. If not, this attribute is + * ignored. + * + * The default value is FALSE. + --> +<!ATTLIST concat usehsp (TRUE|FALSE) #IMPLIED> + + +<!-- + ***************************************************************** + * + * Mirror definition + * + ***************************************************************** + --> + +<!-- + * Create a new mirror either by specifying the underlying + * submirrors or by specifying the required size and optional + * available/unavailable resources. + --> +<!ELEMENT mirror ((available|unavailable)*,(stripe|concat)*)> + +<!-- + * The volume name to assign to this mirror. If no name is + * specified, a name will be chosen automatically. No guarantees + * are made about the algorithm used, except that the resulting name + * will be one that is not currently in use. + --> +<!ATTLIST mirror name CDATA #IMPLIED> + +<!-- + * The number of submirrors under this mirror. If this number + * exceeds the number of explicitly specified submirrors, the + * remaining submirrors will be created from the available + * resources. + * + * The default value is 2. + --> +<!ATTLIST mirror nsubmirrors CDATA #IMPLIED> + +<!-- + * The size of this mirror, in the format <value><units>, where + * <units> is "KB", "MB", "GB", or "TB", and <value> is the + * multiplier of the units. + * + * This attribute is required if no submirrors are defined. + --> +<!ATTLIST mirror size CDATA #IMPLIED> + +<!-- + * Specify values for common mirror options. + * + * The default values are described in the metainint(1M) man page. + --> +<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED> +<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED> +<!ATTLIST mirror passnum CDATA #IMPLIED> + +<!-- + * Boolean (TRUE or FALSE) indicating whether to use a HSP for each + * submirror. This can be overridden using the submirror's usehsp + * attribute. + * + * The default value is FALSE. + --> +<!ATTLIST mirror usehsp (TRUE|FALSE) #IMPLIED> + + + +<!-- + ***************************************************************** + * + * QoS volume definition + * + ***************************************************************** + --> + +<!-- + * Create a volume defined by Quality of Service attributes. The + * default is to create a non-redundant volume. In any case, high + * performance is attempted. + * + * Translates to a stripe (non-redundant) or mirror of stripes + * (redundant) for SVM. + --> +<!ELEMENT volume ((available|unavailable)*)> + +<!-- + * The name to assign to this volume. If no name is specified, a + * name will be chosen automatically. No guarantees are made about + * the algorithm used, except that the resulting name will be one + * that is not currently in use. + --> +<!ATTLIST volume name CDATA #IMPLIED> + +<!-- + * The size of this mirror, in the format <value><units>, where + * <units> is "KB", "MB", "GB", or "TB", and <value> is the + * multiplier of the units. + * + * This attribute is required if no submirrors are defined. + --> +<!ATTLIST volume size CDATA #REQUIRED> + +<!-- + * Specify the redundancy level (0-4) of the data. + * + * If redundancy is 0, a stripe will be created. + * + * If redundancy is > 0, a mirror with this number of submirrors + * will be created. In this case, the volume can suffer a disk + * failure on <n-1> copies without data loss. With the use of HSPs + * (see the "faultrecovery" attribute), a volume can suffer a disk + * failure on <n+hsps-1> volumes without data loss, assuming non- + * concurrent failures. + * + * The default value is 0. + --> +<!ATTLIST volume redundancy CDATA #IMPLIED> + +<!-- + * Boolean (TRUE or FALSE) indicating whether to include fault + * recovery. + * + * If this attribute is TRUE, a mirror will be created and its + * submirror(s) will be associated with a HSP. + * + * The default value is FALSE. + --> +<!ATTLIST volume faultrecovery (TRUE|FALSE) #IMPLIED> + +<!-- + * Number of data paths through which to ensure the volume is + * reachable. + * + * The default value is 1. + --> +<!ATTLIST volume datapaths CDATA #IMPLIED> diff --git a/usr/src/cmd/lvm/metassist/xml/Makefile b/usr/src/cmd/lvm/metassist/xml/Makefile new file mode 100644 index 0000000000..5290dfaaed --- /dev/null +++ b/usr/src/cmd/lvm/metassist/xml/Makefile @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +METASSIST_TOPLEVEL = .. + +SRCS = xml_convert.c +OBJS = $(SRCS:%.c=%.o) +HDRS = $(SRCS:%.c=%.h) +MSGFILES = $(SRCS:%.c=%.i) + +include $(METASSIST_TOPLEVEL)/../../Makefile.cmd +include $(METASSIST_TOPLEVEL)/Makefile.env + +INCLUDES += -I /usr/include/libxml2 -I../common +CFLAGS += $(INCLUDES) + +POFILE = xmlp.po + +include $(METASSIST_TOPLEVEL)/Makefile.targ + +# Build .po file from message files +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) diff --git a/usr/src/cmd/lvm/metassist/xml/xml_convert.c b/usr/src/cmd/lvm/metassist/xml/xml_convert.c new file mode 100644 index 0000000000..fb2ef57e60 --- /dev/null +++ b/usr/src/cmd/lvm/metassist/xml/xml_convert.c @@ -0,0 +1,2311 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "xml_convert.h" + +#include <errno.h> +#include <string.h> +#include <libintl.h> +#include <libxslt/xslt.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/transform.h> +#include <libxslt/xsltutils.h> +#include <locale.h> +#include <unistd.h> +#include "volume_error.h" +#include "volume_output.h" +#include "volume_string.h" + +/* + * IDs for localized messages in the generated command script + */ + +#define CMD_MSG_ENVIRONMENT "Environment" +#define CMD_MSG_AMEND_PATH "Amend PATH" +#define CMD_MSG_DISK_SET_NAME "Disk set name" +#define CMD_MSG_FUNCTIONS "Functions" +/* CSTYLED */ +#define CMD_MSG_ECHO_AND_EXEC "Echo (verbose) and exec given command, exit on error" +#define CMD_MSG_GET_FULL_PATH "Get full /dev/rdsk path of given slice" +/* CSTYLED */ +#define CMD_MSG_FMTHARD_SPECIAL "Run fmthard, ignore partboot error, error if output" +#define CMD_MSG_MAIN "Main" +#define CMD_MSG_VERIFY_ROOT "Verify root" +#define CMD_MSG_RUN_AS_ROOT "This script must be run as root." +#define CMD_MSG_CHECK_FOR_VERBOSE "Check for verbose option" +#define CMD_MSG_DOES_DISK_SET_EXIST "Does the disk set exist?" +#define CMD_MSG_TAKE_DISK_SET "Take control of disk set" +#define CMD_MSG_CREATE_THE_DISK_SET "Create the disk set" +#define CMD_MSG_ADD_DISKS_TO_SET "Add disks to set" +#define CMD_MSG_FORMAT_SLICES "Format slices" +#define CMD_MSG_CREATE "Create {1} {2}" +#define CMD_MSG_DOES_EXIST "Does {1} exist?" +#define CMD_MSG_ADD_SLICES_TO "Add slices to {1}" +/* CSTYLED */ +#define CMD_MSG_ASSOCIATE_WITH_HSP "Associate {1} {2} with hot spare pool {3}" + +/* + * ****************************************************************** + * + * Data types + * + * ****************************************************************** + */ + +/* + * Encapsulates the parsing of an XML attribute + */ +typedef struct { + + /* The name of the attribute */ + char *name; + + /* + * A function to validate and set the XML attribute value in + * the given devconfig_t structure. + * + * @param name + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 if the given value was valid and set + * successfully, non-zero otherwise. + */ + int (*validate_set)(devconfig_t *device, char *name, char *value); + + /* + * A function to get the XML attribute value in the given + * devconfig_t structure. + * + * @param name + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 if the given value was retrieved + * successfully, non-zero otherwise. + */ + int (*get_as_string)(devconfig_t *device, char *name, char **value); +} attr_t; + +/* + * Encapsulates the parsing of an XML element + */ +typedef struct { + /* The name of the element */ + char *name; + + /* The type of element to set in the devconfig_t */ + component_type_t type; + + /* + * When converting from XML to a devconfig_t hierarchy, + * indicates whether to create a new devconfig_t structure in + * the hierarchy when this XML element is encountered. + */ + boolean_t is_hierarchical; + + /* + * If is_hierarchical is B_TRUE, whether to use an existing + * devconfig_t structure of this type when this element is + * encountered + */ + boolean_t singleton; + + /* The valid XML attributes for this element */ + attr_t *attributes; +} element_t; + +typedef struct { + char *msgid; + char *message; +} l10nmessage_t; + +/* + * ****************************************************************** + * + * Function prototypes + * + * ****************************************************************** + */ + +static int validate_doc(xmlDocPtr doc, const char *name, const char *systemID); +static int devconfig_to_xml( + xmlNodePtr parent, element_t elements[], devconfig_t *device); +static int xml_to_devconfig( + xmlNodePtr cur, element_t elements[], devconfig_t *device); +static int compare_is_a_diskset(void *obj1, void *obj2); +static xmlNodePtr xml_find_node( + xmlNodePtr node, xmlChar *element, xmlChar *name); +static xmlDocPtr create_localized_message_doc(); +static int create_localized_message_file(char **tmpfile); +static int strtobool(char *str, boolean_t *value); +static int ofprintf_terse(void *unused, char *fmt, ...); +static int ofprintf_verbose(void *unused, char *fmt, ...); + +static int validate_set_size( + devconfig_t *volume, char *attr, char *value); +static int validate_set_size_in_blocks( + devconfig_t *slice, char *attr, char *value); +static int validate_set_diskset_name( + devconfig_t *diskset, char *attr, char *name); +static int validate_add_available_name( + devconfig_t *device, char *attr, char *name); +static int validate_add_unavailable_name( + devconfig_t *device, char *attr, char *name); +static int validate_set_hsp_name( + devconfig_t *hsp, char *attr, char *name); +static int validate_set_disk_name( + devconfig_t *disk, char *attr, char *name); +static int validate_set_slice_name( + devconfig_t *slice, char *attr, char *name); +static int validate_set_slice_start_block( + devconfig_t *slice, char *attr, char *value); +static int validate_set_volume_name( + devconfig_t *volume, char *attr, char *name); +static int validate_set_stripe_interlace( + devconfig_t *stripe, char *attr, char *value); +static int validate_set_stripe_mincomp( + devconfig_t *stripe, char *attr, char *value); +static int validate_set_stripe_maxcomp( + devconfig_t *stripe, char *attr, char *value); +static int validate_set_volume_usehsp( + devconfig_t *volume, char *attr, char *value); +static int validate_set_mirror_nsubmirrors( + devconfig_t *mirror, char *attr, char *value); +static int validate_set_mirror_read( + devconfig_t *mirror, char *attr, char *value); +static int validate_set_mirror_write( + devconfig_t *mirror, char *attr, char *value); +static int validate_set_mirror_passnum( + devconfig_t *mirror, char *attr, char *value); +static int validate_set_volume_redundancy( + devconfig_t *volume, char *attr, char *value); +static int validate_set_volume_datapaths( + devconfig_t *volume, char *attr, char *value); + +static int get_as_string_name( + devconfig_t *device, char *attr, char **value); +static int get_as_string_mirror_passnum( + devconfig_t *mirror, char *attr, char **value); +static int get_as_string_mirror_read( + devconfig_t *mirror, char *attr, char **value); +static int get_as_string_mirror_write( + devconfig_t *mirror, char *attr, char **value); +static int get_as_string_size_in_blocks( + devconfig_t *device, char *attr, char **value); +static int get_as_string_slice_start_block( + devconfig_t *slice, char *attr, char **value); +static int get_as_string_stripe_interlace( + devconfig_t *stripe, char *attr, char **value); + +/* + * ****************************************************************** + * + * Data + * + * ****************************************************************** + */ + +/* Valid units for the size attribute */ +units_t size_units[] = { + {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, + {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, + {UNIT_GIGABYTES, BYTES_PER_GIGABYTE}, + {UNIT_TERABYTES, BYTES_PER_TERABYTE}, + {NULL, 0} +}; + +/* Valid units for the interlace attribute */ +units_t interlace_units[] = { + {UNIT_BLOCKS, BYTES_PER_BLOCK}, + {UNIT_KILOBYTES, BYTES_PER_KILOBYTE}, + {UNIT_MEGABYTES, BYTES_PER_MEGABYTE}, + {NULL, 0} +}; + +/* <diskset> attributes */ +static attr_t diskset_attrs[] = { + { ATTR_NAME, validate_set_diskset_name, get_as_string_name }, + { NULL, NULL, NULL } +}; + +/* <available> attributes */ +static attr_t available_attrs[] = { + { ATTR_NAME, validate_add_available_name, NULL }, + { NULL, NULL, NULL } +}; + +/* <unavailable> attributes */ +static attr_t unavailable_attrs[] = { + { ATTR_NAME, validate_add_unavailable_name, NULL }, + { NULL, NULL, NULL } +}; + +/* <hsp> attributes */ +static attr_t hsp_attrs[] = { + { ATTR_NAME, validate_set_hsp_name, get_as_string_name }, + { NULL, NULL, NULL } +}; + +/* <disk> attributes */ +static attr_t disk_attrs[] = { + { ATTR_NAME, validate_set_disk_name, get_as_string_name }, + { NULL, NULL, NULL } +}; + +/* <slice> attributes */ +static attr_t slice_attrs[] = { + { ATTR_NAME, validate_set_slice_name, get_as_string_name }, + { ATTR_SIZEINBLOCKS, validate_set_size_in_blocks, + get_as_string_size_in_blocks }, + { ATTR_SLICE_STARTSECTOR, validate_set_slice_start_block, + get_as_string_slice_start_block }, + { NULL, NULL, NULL } +}; + +/* <stripe> attributes */ +static attr_t stripe_attrs[] = { + { ATTR_NAME, validate_set_volume_name, get_as_string_name }, + { ATTR_SIZEINBYTES, validate_set_size, NULL }, + { ATTR_STRIPE_MINCOMP, validate_set_stripe_mincomp, NULL }, + { ATTR_STRIPE_MAXCOMP, validate_set_stripe_maxcomp, NULL }, + { ATTR_STRIPE_INTERLACE, validate_set_stripe_interlace, + get_as_string_stripe_interlace }, + { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, + { NULL, NULL, NULL } +}; + +/* <concat> attributes */ +static attr_t concat_attrs[] = { + { ATTR_NAME, validate_set_volume_name, get_as_string_name }, + { ATTR_SIZEINBYTES, validate_set_size, NULL }, + { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, + { NULL, NULL, NULL } +}; + +/* <mirror> attributes */ +static attr_t mirror_attrs[] = { + { ATTR_NAME, validate_set_volume_name, get_as_string_name }, + { ATTR_MIRROR_NSUBMIRRORS, validate_set_mirror_nsubmirrors, NULL }, + { ATTR_SIZEINBYTES, validate_set_size, NULL }, + { ATTR_MIRROR_READ, validate_set_mirror_read, + get_as_string_mirror_read }, + { ATTR_MIRROR_WRITE, validate_set_mirror_write, + get_as_string_mirror_write }, + { ATTR_MIRROR_PASSNUM, validate_set_mirror_passnum, + get_as_string_mirror_passnum }, + { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL }, + { NULL, NULL, NULL } +}; + +/* <volume> attributes */ +static attr_t volume_attrs[] = { + { ATTR_NAME, validate_set_volume_name, get_as_string_name }, + { ATTR_SIZEINBYTES, validate_set_size, NULL }, + { ATTR_VOLUME_REDUNDANCY, validate_set_volume_redundancy, NULL }, + { ATTR_VOLUME_FAULTRECOVERY, validate_set_volume_usehsp, NULL }, + { ATTR_VOLUME_DATAPATHS, validate_set_volume_datapaths, NULL }, + { NULL, NULL, NULL } +}; + +/* volume-request elements */ +static element_t request_elements[] = { + { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, + { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, available_attrs }, + { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, + unavailable_attrs }, + { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, + { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, + { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, + { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, + { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, + { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_FALSE, volume_attrs }, + { NULL, NULL, B_FALSE, B_FALSE, NULL } +}; + +/* volume-defaults elements */ +static element_t default_elements[] = { + { ELEMENT_DISKSET, TYPE_DISKSET, B_TRUE, B_FALSE, diskset_attrs }, + { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, available_attrs }, + { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, + unavailable_attrs }, + { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_TRUE, hsp_attrs }, + { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_TRUE, slice_attrs }, + { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_TRUE, stripe_attrs }, + { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_TRUE, concat_attrs }, + { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_TRUE, mirror_attrs }, + { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_TRUE, volume_attrs }, + { NULL, NULL, B_FALSE, B_FALSE, NULL } +}; + +/* volume-config elements */ +static element_t config_elements[] = { + { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs }, + { ELEMENT_DISK, TYPE_DRIVE, B_TRUE, B_FALSE, disk_attrs }, + { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs }, + { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs }, + { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs }, + { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs }, + { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs }, + { NULL, NULL, B_FALSE, B_FALSE, NULL } +}; + +/* + * ****************************************************************** + * + * External functions + * + * ****************************************************************** + */ + +/* + * Initialize the XML parser, setting defaults across all XML + * routines. + */ +void +init_xml() +{ + /* COMPAT: Do not generate nodes for formatting spaces */ + LIBXML_TEST_VERSION + xmlKeepBlanksDefault(0); + + /* Turn on line numbers for debugging */ + xmlLineNumbersDefault(1); + + /* Substitute entities as files are parsed */ + xmlSubstituteEntitiesDefault(1); + + /* Don't load external entity subsets */ + xmlLoadExtDtdDefaultValue = 0; + + /* Don't validate against DTD by default */ + xmlDoValidityCheckingDefaultValue = 0; + + /* Set up output handlers for XML parsing */ + xmlDefaultSAXHandler.warning = (warningSAXFunc)ofprintf_verbose; + xmlDefaultSAXHandler.error = (errorSAXFunc)ofprintf_terse; + xmlDefaultSAXHandler.fatalError = (fatalErrorSAXFunc)ofprintf_terse; +} + +/* + * Clean up any remaining structures before exiting. + */ +void +cleanup_xml() +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} + +/* + * Converts a volume-request XML document into a request_t. + * + * @param doc + * an existing volume-request XML document + * + * @param request + * RETURN: a new request_t which must be freed via + * free_request + * + * @return 0 on success, non-zero otherwise. + */ +int +xml_to_request( + xmlDocPtr doc, + request_t **request) +{ + int error = 0; + + *request = NULL; + + /* Validate doc against known DTD */ + if ((error = validate_doc( + doc, ELEMENT_VOLUMEREQUEST, VOLUME_REQUEST_DTD_LOC)) == 0) { + + /* Create a request */ + if ((error = new_request(request)) == 0) { + + /* Convert the XML doc into a request_t */ + error = xml_to_devconfig(xmlDocGetRootElement(doc), + request_elements, request_get_diskset_req(*request)); + } + } + + return (error); +} + +/* + * Converts a volume-defaults XML document into a defaults_t. + * + * @param doc + * an existing volume-defaults XML document + * + * @param defaults + * RETURN: a new defaults_t which must be freed via + * free_defaults + * + * @return 0 on success, non-zero otherwise. + */ +int +xml_to_defaults( + xmlDocPtr doc, + defaults_t **defaults) +{ + int error = 0; + + *defaults = NULL; + + /* Validate doc against known DTD */ + if ((error = validate_doc(doc, ELEMENT_VOLUMEDEFAULTS, + VOLUME_DEFAULTS_DTD_LOC)) == 0) { + + /* Create request defaults */ + if ((error = new_defaults(defaults)) == 0) { + + devconfig_t *global; + + /* Get defaults for all disk sets */ + if ((error = defaults_get_diskset_by_name( + *defaults, NULL, &global)) == 0) { + + /* Populate the global devconfig_t from the XML doc */ + if ((error = xml_to_devconfig(xmlDocGetRootElement(doc), + default_elements, global)) == 0) { + + /* Get the components of the global devconfig_t */ + dlist_t *list = devconfig_get_components(global); + + /* + * Move all named disk set settings out from + * under global settings + */ + /* CONSTANTCONDITION */ + while (1) { + dlist_t *removed = NULL; + devconfig_t *component; + + /* Remove named disk set from under global */ + list = dlist_remove_equivalent_item( + list, NULL, compare_is_a_diskset, &removed); + + if (removed == NULL) { + /* No named disk set found */ + break; + } + + component = removed->obj; + + /* Append named disk set to disk set list */ + defaults_set_disksets(*defaults, + dlist_append(dlist_new_item(component), + defaults_get_disksets(*defaults), AT_TAIL)); + } + } + } + } + } + + return (error); +} + +/* + * Converts a volume-config XML document into a devconfig_t. + * + * @param doc + * an existing volume-config XML document + * + * @param config + * RETURN: a new devconfig_t which must be freed via + * free_devconfig + * + * @return 0 on success, non-zero otherwise. + */ +int +xml_to_config( + xmlDocPtr doc, + devconfig_t **config) +{ + int error = 0; + + *config = NULL; + + /* Validate doc against known DTD */ + if ((error = validate_doc( + doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC)) == 0) { + + /* Create a devconfig_t */ + if ((error = new_devconfig(config, TYPE_DISKSET)) == 0) { + + /* Populate the devconfig_t from the XML doc */ + error = xml_to_devconfig( + xmlDocGetRootElement(doc), config_elements, *config); + } + } + + return (error); +} + +/* + * Converts a devconfig_t into a volume-config XML document. + * + * @param config + * an existing devconfig_t representing a volume + * configuration. + * + * @param doc + * RETURN: a new volume-config XML document which must be + * freed via xmlFreeDoc + * + * @return 0 on success, non-zero otherwise. + */ +int +config_to_xml( + devconfig_t *config, + xmlDocPtr *doc) +{ + xmlNodePtr root; + int error = 0; + + /* Create the XML document */ + *doc = xmlNewDoc((xmlChar *)"1.0"); + + /* Create the root node */ + root = xmlNewDocNode( + *doc, NULL, (xmlChar *)ELEMENT_VOLUMECONFIG, NULL); + xmlAddChild((xmlNodePtr)*doc, (xmlNodePtr)root); + + /* Create sub-nodes from the config devconfig_t */ + if ((error = devconfig_to_xml(root, config_elements, config)) == 0) { + + /* Add DTD node and validate */ + error = validate_doc( + *doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC); + } + + if (error) { + xmlFreeDoc(*doc); + } + + return (error); +} + +/* + * Converts a volume-config XML document into a Bourne shell script. + * + * @param doc + * an existing volume-config XML document + * + * @param commands + * RETURN: a new char* which must be freed + * + * @return 0 on success, non-zero otherwise. + */ +int +xml_to_commands( + xmlDocPtr doc, + char **commands) +{ + char *tmpfile = NULL; + int error = 0; + xsltStylesheetPtr style = NULL; + + /* Read in XSL stylesheet as a normal XML document */ + xmlDocPtr xsl_doc = xmlSAXParseFile((xmlSAXHandlerPtr) + &xmlDefaultSAXHandler, VOLUME_COMMAND_XSL_LOC, 0); + + if (xsl_doc != NULL && xsl_doc->xmlChildrenNode != NULL) { + + /* + * Find the "msgfile" variable node. This is where + * we'll set the location of the file we'll create + * containing the localized messages. + */ + xmlNodePtr msgfile_node = xml_find_node( + xmlDocGetRootElement(xsl_doc), (xmlChar *)ELEMENT_VARIABLE, + (xmlChar *)NAME_L10N_MESSAGE_FILE); + + /* + * Find the "lang" node. This is where we'll set the + * current locale. + */ + xmlNodePtr lang_node = xml_find_node(xmlDocGetRootElement(xsl_doc), + (xmlChar *)ELEMENT_PARAM, (xmlChar *)NAME_LANG); + + /* + * Ignore if the nodes are not found -- the script + * will default to the C locale. + */ + if (msgfile_node != NULL && lang_node != NULL) { + /* Get/set current locale in the "lang" node */ + char *locale = setlocale(LC_MESSAGES, NULL); + xmlNodeSetContent(lang_node, (xmlChar *)locale); + + /* Write localized messages to a temporary file */ + if ((error = create_localized_message_file(&tmpfile)) == 0) { + + char *newsel; + + /* Clear current value of select attribute, if any */ + xmlChar *cursel = xmlGetProp( + msgfile_node, (xmlChar *)ATTR_SELECT); + if (cursel != NULL) { + xmlFree(cursel); + } + + /* + * The select attribute calls the XSLT function + * document() to load an external XML file + */ + newsel = stralloccat(3, "document('", tmpfile, "')"); + + if (newsel == NULL) { + volume_set_error(gettext("out of memory")); + error = -1; + } else { + + /* Set the new value of the select attribute */ + xmlSetProp(msgfile_node, + (xmlChar *)ATTR_SELECT, (xmlChar *)newsel); + + free(newsel); + } + } + } + + if (error == 0) { + style = xsltParseStylesheetDoc(xsl_doc); + } + } + + if (style == NULL) { + volume_set_error( + gettext("could not load stylesheet from %s"), + VOLUME_COMMAND_XSL_LOC); + error = -1; + } else { + + xmlDocPtr result = xsltApplyStylesheet(style, doc, NULL); + + if (result == NULL) { + volume_set_error( + gettext("could not apply stylesheet to volume-config")); + error = -1; + } else { + int length; + + if (xsltSaveResultToString((xmlChar **)commands, + &length, result, style) == -1) { + error = ENOMEM; + } + } + + xsltFreeStylesheet(style); + } + + if (tmpfile != NULL) { + /* Ignore failure */ + unlink(tmpfile); + + free(tmpfile); + } + + return (error); +} + +/* + * ****************************************************************** + * + * Static functions + * + * ****************************************************************** + */ + +/* + * Sets the external DTD node in the given XML document and then + * validates it. + * + * @param doc + * an existing XML document + * + * @param name + * the expected root element name of the XML document + * + * @param systemID + * the location of the DTD + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_doc( + xmlDocPtr doc, + const char *name, + const char *systemID) +{ + xmlValidCtxt context; + xmlDtdPtr dtd; + + if (doc == NULL) { + volume_set_error(gettext("NULL %s document"), name); + return (-1); + } + + /* + * Assume that we can't trust any DTD but our own. + */ + + /* Was a DTD (external or internal) included in the document? */ + if ((dtd = xmlGetIntSubset(doc)) != NULL) { + /* Remove the DTD node */ + oprintf(OUTPUT_DEBUG, gettext("Removing DTD from %s\n"), name); + xmlUnlinkNode((xmlNodePtr)dtd); + xmlFreeDtd(dtd); + } + + /* Create the (external) DTD node */ + oprintf(OUTPUT_DEBUG, + gettext("Creating new external DTD for %s\n"), name); + dtd = xmlCreateIntSubset( + doc, (xmlChar *)name, NULL, (xmlChar *)systemID); + if (dtd == NULL) { + volume_set_error( + gettext("could not create DTD node from %s"), systemID); + return (-1); + } + + /* Validate against DTD */ + oprintf(OUTPUT_DEBUG, gettext("Validating %s against DTD\n"), name); + context.userData = NULL; + context.error = (xmlValidityErrorFunc)ofprintf_terse; + context.warning = (xmlValidityWarningFunc)ofprintf_terse; + if (!xmlValidateDocument(&context, doc)) { + volume_set_error(gettext("invalid %s"), name); + return (-1); + } + + return (0); +} + +/* + * Converts a devconfig_t into an XML node subject to the rules in + * the given element_t array. + * + * @param parent + * the XML node to which to add new XML nodes resulting + * from conversion of the given devconfig_t + * + * @param elements + * the element_ts that describe the structure of the XML + * document and govern the conversion of the given + * devconfig_t + * + * @param device + * the devconfig_t to convert + * + * @return 0 on success, non-zero otherwise. + */ +static int +devconfig_to_xml( + xmlNodePtr parent, + element_t elements[], + devconfig_t *device) +{ + int i; + int error = 0; + xmlNodePtr node = NULL; + + /* Get device type */ + component_type_t type; + if ((error = devconfig_get_type(device, &type)) != 0) { + return (error); + } + + /* Search for this element definition */ + for (i = 0; elements[i].name != NULL; i++) { + element_t *element = &(elements[i]); + + if (element->type == type) { + int j; + char **array; + dlist_t *components; + + oprintf(OUTPUT_DEBUG, gettext("Element: %s\n"), + devconfig_type_to_str(type)); + + /* Create the XML node */ + node = xmlNewChild( + parent, NULL, (xmlChar *)element->name, NULL); + + /* For each attribute defined for this element... */ + for (j = 0; element->attributes[j].name != NULL; j++) { + attr_t *attribute = &(element->attributes[j]); + char *value; + + /* Is there a valid accessor for this attribute? */ + if (attribute->get_as_string != NULL) { + + /* Get the attribute value from the device */ + switch (error = attribute->get_as_string( + device, attribute->name, &value)) { + + /* Attribute is set in this device */ + case 0: + oprintf(OUTPUT_DEBUG, " %s: %s\n", + attribute->name, value); + + /* Set the value in the XML node */ + xmlSetProp(node, (uchar_t *)attribute->name, + (uchar_t *)value); + free(value); + + /* FALLTHROUGH */ + + /* Attribute is not set in this device */ + case ERR_ATTR_UNSET: + + error = 0; + break; + + /* Error */ + default: + return (error); + } + } + } + + /* Is this node hierarchical? */ + if (element->is_hierarchical == B_FALSE) { + node = parent; + } + + /* Create <available> nodes */ + array = devconfig_get_available(device); + if (array != NULL) { + for (j = 0; array[j] != NULL; j++) { + xmlNodePtr child = xmlNewChild( + node, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL); + xmlSetProp(child, + (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); + } + } + + /* Create <unavailable> nodes */ + array = devconfig_get_unavailable(device); + if (array != NULL) { + for (j = 0; array[j] != NULL; j++) { + xmlNodePtr child = xmlNewChild( + node, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL); + xmlSetProp(child, + (xmlChar *)ATTR_NAME, (xmlChar *)array[j]); + } + } + + /* + * Recursively convert subcomponents of this device to + * XML, taking care to encode them in the order + * specified in the element_t list (which should + * mirror what's expected by the DTD). + */ + + /* For each element type... */ + for (j = 0; elements[j].name != NULL; j++) { + + /* For each component of this device... */ + for (components = devconfig_get_components(device); + components != NULL && error == 0; + components = components->next) { + + devconfig_t *component = (devconfig_t *)components->obj; + component_type_t t; + + /* Are the types the same? */ + if ((error = devconfig_get_type(component, &t)) != 0) { + return (error); + } else { + if (elements[j].type == t) { + /* Encode child */ + error = devconfig_to_xml( + node, elements, component); + } + } + } + } + + /* Element found */ + break; + } + } + + /* Was this device successfully converted? */ + if (node == NULL) { + volume_set_error( + gettext("can't convert device of type \"%s\" to XML element"), + devconfig_type_to_str(type)); + error = -1; + } + + return (error); +} + +/* + * Converts an XML node into a devconfig_t subject to the rules in + * the given element_t array. + * + * @param cure + * the existing XML node to convert + * + * @param elements + * the element_ts that describe the structure of the XML + * document and govern the conversion of the given XML + * node + * + * @param device + * the devconfig_t node to which to add new devconfig_ts + * resulting from conversion of the given XML node + * + * @return 0 on success, non-zero otherwise. + */ +static int +xml_to_devconfig( + xmlNodePtr cur, + element_t elements[], + devconfig_t *device) +{ + int error = 0; + + /* For each child node... */ + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + int i; + boolean_t parsed_elem = B_FALSE; + + /* Search for this element definition */ + for (i = 0; elements[i].name != NULL; i++) { + element_t *element = &(elements[i]); + + if (xmlStrcmp(cur->name, (xmlChar *)element->name) == 0) { + int j; + devconfig_t *component = NULL; + + /* Flag that this element has been parsed */ + parsed_elem = B_TRUE; + + oprintf(OUTPUT_DEBUG, gettext("line %d: Element <%s>\n"), + XML_GET_LINE(cur), cur->name); + + /* Should a new device be created for this element? */ + if (element->is_hierarchical == B_TRUE) { + + /* Should we use an existing device of this type? */ + if (element->singleton) { + devconfig_get_component( + device, element->type, &component, B_FALSE); + } + + if (component == NULL) { + oprintf(OUTPUT_DEBUG, + gettext("Creating new device\n")); + + /* Create device of this type */ + if ((error = new_devconfig( + &component, element->type)) != 0) { + return (error); + } + + /* Add component to the toplevel device */ + devconfig_set_components( + device, dlist_append(dlist_new_item(component), + devconfig_get_components(device), AT_TAIL)); + } + } else { + component = device; + } + + /* For each attribute defined for this element... */ + for (j = 0; element->attributes[j].name != NULL; j++) { + attr_t *attribute = &(element->attributes[j]); + + /* Get the value of this attribute */ + char *value = (char *) + xmlGetProp(cur, (xmlChar *)attribute->name); + + /* Was this attribute specified? */ + if (value != NULL) { + oprintf(OUTPUT_DEBUG, + gettext("line %d:\tAttribute %s=%s\n"), + XML_GET_LINE(cur), attribute->name, value); + + /* Set this value in the device */ + if ((error = attribute->validate_set( + component, attribute->name, value)) != 0) { + return (error); + } + } + } + + /* Get recursive sub-elements */ + if ((error = xml_to_devconfig( + cur, elements, component)) != 0) { + return (error); + } + + /* Element found */ + break; + } + } + + + /* Make sure all non-text/comment elements were parsed */ + if (parsed_elem == B_FALSE && + xmlStrcmp(cur->name, (xmlChar *)ELEMENT_TEXT) != 0 && + xmlStrcmp(cur->name, (xmlChar *)ELEMENT_COMMENT) != 0) { + + oprintf(OUTPUT_DEBUG, gettext("Element <%s> NOT PARSED!!!\n"), + cur->name); + } + } + + return (0); +} + +/* + * Returns 0 if obj2 (devconfig_t *) is a disk set, 1 otherwise. + */ +static int +compare_is_a_diskset( + void *obj1, + void *obj2) +{ + return (devconfig_isA( + (devconfig_t *)obj2, TYPE_DISKSET) == B_TRUE ? 0 : 1); +} + +/* + * Recursively searches the given xmlNodePtr for an element of the + * specified type and name. + * + * @param node + * the root node to search + * + * @param element + * the name of the element type + * + * @param name + * the value of the name attribute + * + * @return a valid xmlNodePtr if an element of the specified + * type and name was found, NULL otherwise. + */ +static xmlNodePtr +xml_find_node( + xmlNodePtr node, + xmlChar *element, + xmlChar *name) +{ + xmlNodePtr child; + + /* Is the element the right type? */ + if (xmlStrcmp(element, node->name) == 0 && + + /* Does this element's name attribute match? */ + xmlStrcmp(name, xmlGetProp(node, (xmlChar *)ATTR_NAME)) == 0) { + + return (node); + } + + /* Check child nodes */ + for (child = node->xmlChildrenNode; child != NULL; + child = child->next) { + xmlNodePtr found = xml_find_node(child, element, name); + + if (found != NULL) { + return (found); + } + } + + return (NULL); +} + +/* + * Creates an XML document containing all of the localized message + * strings for the generated command script. + * + * @return a xmlDocPtr which must be freed via xmlFreeDoc + */ +static xmlDocPtr +create_localized_message_doc() +{ + int i; + char *locale; + xmlDocPtr doc; + xmlNodePtr root; + l10nmessage_t _cmd_messages[21]; + + /* Create the XML document */ + doc = xmlNewDoc((xmlChar *)"1.0"); + + /* Create the root node */ + root = xmlNewDocNode( + doc, NULL, (xmlChar *)ELEMENT_L10N, NULL); + xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)root); + + _cmd_messages[0].msgid = CMD_MSG_ENVIRONMENT; + _cmd_messages[0].message = gettext(CMD_MSG_ENVIRONMENT); + _cmd_messages[1].msgid = CMD_MSG_AMEND_PATH; + _cmd_messages[1].message = gettext(CMD_MSG_AMEND_PATH); + _cmd_messages[2].msgid = CMD_MSG_DISK_SET_NAME; + _cmd_messages[2].message = gettext(CMD_MSG_DISK_SET_NAME); + _cmd_messages[3].msgid = CMD_MSG_FUNCTIONS; + _cmd_messages[3].message = gettext(CMD_MSG_FUNCTIONS); + _cmd_messages[4].msgid = CMD_MSG_ECHO_AND_EXEC; + _cmd_messages[4].message = gettext(CMD_MSG_ECHO_AND_EXEC); + _cmd_messages[5].msgid = CMD_MSG_FMTHARD_SPECIAL; + _cmd_messages[5].message = gettext(CMD_MSG_FMTHARD_SPECIAL); + _cmd_messages[6].msgid = CMD_MSG_GET_FULL_PATH; + _cmd_messages[6].message = gettext(CMD_MSG_GET_FULL_PATH); + _cmd_messages[7].msgid = CMD_MSG_MAIN; + _cmd_messages[7].message = gettext(CMD_MSG_MAIN); + _cmd_messages[8].msgid = CMD_MSG_VERIFY_ROOT; + _cmd_messages[8].message = gettext(CMD_MSG_VERIFY_ROOT); + _cmd_messages[9].msgid = CMD_MSG_RUN_AS_ROOT; + _cmd_messages[9].message = gettext(CMD_MSG_RUN_AS_ROOT); + _cmd_messages[10].msgid = CMD_MSG_CHECK_FOR_VERBOSE; + _cmd_messages[10].message = gettext(CMD_MSG_CHECK_FOR_VERBOSE); + _cmd_messages[11].msgid = (CMD_MSG_DOES_DISK_SET_EXIST); + _cmd_messages[11].message = gettext(CMD_MSG_DOES_DISK_SET_EXIST); + _cmd_messages[12].msgid = (CMD_MSG_TAKE_DISK_SET); + _cmd_messages[12].message = gettext(CMD_MSG_TAKE_DISK_SET); + _cmd_messages[13].msgid = (CMD_MSG_CREATE_THE_DISK_SET); + _cmd_messages[13].message = gettext(CMD_MSG_CREATE_THE_DISK_SET); + _cmd_messages[14].msgid = (CMD_MSG_ADD_DISKS_TO_SET); + _cmd_messages[14].message = gettext(CMD_MSG_ADD_DISKS_TO_SET); + _cmd_messages[15].msgid = (CMD_MSG_FORMAT_SLICES); + _cmd_messages[15].message = gettext(CMD_MSG_FORMAT_SLICES); + _cmd_messages[16].msgid = (CMD_MSG_CREATE); + _cmd_messages[16].message = gettext(CMD_MSG_CREATE); + _cmd_messages[17].msgid = (CMD_MSG_DOES_EXIST); + _cmd_messages[17].message = gettext(CMD_MSG_DOES_EXIST); + _cmd_messages[18].msgid = (CMD_MSG_ADD_SLICES_TO); + _cmd_messages[18].message = gettext(CMD_MSG_ADD_SLICES_TO); + _cmd_messages[19].msgid = (CMD_MSG_ASSOCIATE_WITH_HSP); + _cmd_messages[19].message = gettext(CMD_MSG_ASSOCIATE_WITH_HSP); + _cmd_messages[20].msgid = NULL; + + /* Get/set current locale in the "lang" node */ + locale = setlocale(LC_MESSAGES, NULL); + + /* Add localized <message> elements to stylesheet */ + for (i = 0; _cmd_messages[i].msgid != NULL; i++) { + xmlNsPtr ns = xmlNewNs(NULL, NULL, NULL); + + xmlNodePtr node = xmlNewTextChild( + root, ns, (xmlChar *)ELEMENT_MESSAGE, + (xmlChar *)_cmd_messages[i].message); + /* Lang attribute */ + xmlSetProp(node, + (xmlChar *)ATTR_LANG, (xmlChar *)locale); + + /* Message ID attribute */ + xmlSetProp(node, (xmlChar *)ATTR_MESSAGEID, + (xmlChar *)_cmd_messages[i].msgid); + } + + if (get_max_verbosity() >= OUTPUT_DEBUG) { + xmlChar *text; + /* Get the text dump */ + xmlDocDumpFormatMemory(doc, &text, NULL, 1); + oprintf(OUTPUT_DEBUG, + gettext("Generated message file:\n%s"), text); + xmlFree(text); + } + + return (doc); +} + +/* + * Creates a temporary XML file containing all of the localized + * message strings for the generated command script. + * + * @param tmpfile + * RETURN: the name of the temporary XML file + * + * @return 0 on success, non-zero otherwise. + */ +static int +create_localized_message_file( + char **tmpfile) +{ + int error = 0; + + /* + * Create temporary file name -- "XXXXXX" is replaced with + * unique char sequence by mkstemp() + */ + *tmpfile = stralloccat(3, "/tmp/", ELEMENT_L10N, "XXXXXX"); + + if (*tmpfile == NULL) { + volume_set_error(gettext("out of memory")); + error = -1; + } else { + int fildes; + FILE *msgfile = NULL; + + /* Open temp file */ + if ((fildes = mkstemp(*tmpfile)) != -1) { + msgfile = fdopen(fildes, "w"); + } + + if (msgfile == NULL) { + volume_set_error(gettext( + "could not open file for writing: %s"), *tmpfile); + error = -1; + } else { + + xmlChar *text; + xmlDocPtr message_doc = create_localized_message_doc(); + xmlDocDumpFormatMemory(message_doc, &text, NULL, 1); + + if (fprintf(msgfile, "%s", text) < 0) { + volume_set_error(gettext( + "could not create localized message file: %s"), + *tmpfile); + error = -1; + } + + xmlFree(text); + xmlFreeDoc(message_doc); + } + + fclose(msgfile); + } + + return (error); +} + +/* + * Converts the given string into a boolean. The string must be + * either VALID_ATTR_TRUE or VALID_ATTR_FALSE. + * + * @param str + * the string to convert + * + * @param bool + * the addr of the boolean_t + * + * @return 0 if the given string could be converted to a boolean + * non-zero otherwise. + */ +static int +strtobool( + char *str, + boolean_t *value) +{ + int error = 0; + + if (strcmp(str, VALID_ATTR_TRUE) == 0) { + *value = B_TRUE; + } else + + if (strcmp(str, VALID_ATTR_FALSE) == 0) { + *value = B_FALSE; + } else + + error = -1; + + return (error); +} + +/* + * Wrapper for oprintf with a OUTPUT_TERSE level of verbosity. + * Provides an fprintf-like syntax to enable use as substitute output + * handler for man of the XML commands. + * + * @param unused + * unused, in favor of the FILE* passed to + * set_max_verbosity(). + * + * @param fmt + * a printf-style format string + * + * @return the number of characters output + */ +static int +ofprintf_terse( + void *unused, + char *fmt, + ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = oprintf_va(OUTPUT_TERSE, fmt, ap); + va_end(ap); + + return (ret); +} + +/* + * Wrapper for oprintf with a OUTPUT_VERBOSE level of verbosity. + * Provides an fprintf-like syntax to enable use as substitute output + * handler for man of the XML commands. + * + * @param unused + * unused, in favor of the FILE* passed to + * set_max_verbosity(). + * + * @param fmt + * a printf-style format string + * + * @return the number of characters output + */ +static int +ofprintf_verbose( + void *unused, + char *fmt, + ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = oprintf_va(OUTPUT_VERBOSE, fmt, ap); + va_end(ap); + + return (ret); +} + +/* + * ****************************************************************** + * + * XML attribute validators/mutators + * + * These functions convert the given XML attribute string to the + * appropriate data type, and then pass it on to the appropriate + * devconfig_t mutator. A non-zero status is returned if the given + * string could not be converted or was invalid. + * + * ****************************************************************** + */ + +/* + * Validate and set the size attribute in the given volume + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the size + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_size( + devconfig_t *volume, + char *attr, + char *value) +{ + int error; + uint64_t size = 0; + + /* Convert size string to bytes */ + if ((error = sizestr_to_bytes(value, &size, size_units)) != 0) { + return (error); + } + + /* Set size in volume */ + return (devconfig_set_size(volume, size)); +} + +/* + * Validate and set the size_in_blocks attribute in the given slice + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the size_in_blocks + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_size_in_blocks( + devconfig_t *slice, + char *attr, + char *value) +{ + long long size; + + /* Convert string to long long */ + if (sscanf(value, "%lld", &size) != 1) { + volume_set_error(gettext("%s: invalid size in blocks"), value); + return (-1); + } + + /* Set the number of submirrors in the slice */ + return (devconfig_set_size_in_blocks(slice, (uint64_t)size)); +} + +/* + * Validate and set the name attribute in the given diskset + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the name + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_diskset_name( + devconfig_t *diskset, + char *attr, + char *name) +{ + return (devconfig_set_diskset_name(diskset, name)); +} + +/* + * Validate and add the given name to the list of available devices in + * the given volume devconfig_t. + * + * @param device + * the devconfig_t whose available device list to modify + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_add_available_name( + devconfig_t *device, + char *attr, + char *name) +{ + char **available; + + /* Get available devices for this device */ + available = devconfig_get_available(device); + + /* Try to add name to array via realloc */ + if ((available = append_to_string_array(available, name)) == NULL) { + return (ENOMEM); + } + + /* Set available devices in the device */ + devconfig_set_available(device, available); + + return (0); +} + +/* + * Validate and add the given name to the list of unavailable devices + * in the given volume devconfig_t. + * + * @param device + * the devconfig_t whose unavailable device list to modify + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_add_unavailable_name( + devconfig_t *device, + char *attr, + char *name) +{ + char **unavailable; + + /* Get unavailable devices for this device */ + unavailable = devconfig_get_unavailable(device); + + /* Try to add name to array via realloc */ + if ((unavailable = append_to_string_array(unavailable, name)) == NULL) { + return (ENOMEM); + } + + /* Set unavailable devices in the device */ + devconfig_set_unavailable(device, unavailable); + + return (0); +} + +/* + * Validate and set the name attribute in the given hsp devconfig_t. + * + * @param volume + * the devconfig_t in which to set the name + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_hsp_name( + devconfig_t *hsp, + char *attr, + char *name) +{ + return (devconfig_set_hsp_name(hsp, name)); +} + +/* + * Validate and set the name attribute in the given disk devconfig_t. + * + * @param volume + * the devconfig_t in which to set the name + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_disk_name( + devconfig_t *disk, + char *attr, + char *name) +{ + return (devconfig_set_name(disk, name)); +} + +/* + * Validate and set the name attribute in the given slice devconfig_t. + * + * @param volume + * the devconfig_t in which to set the name + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_slice_name( + devconfig_t *slice, + char *attr, + char *name) +{ + return (devconfig_set_name(slice, name)); +} + +/* + * Validate and set the start_block attribute in the given slice + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the start_block + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_slice_start_block( + devconfig_t *slice, + char *attr, + char *value) +{ + long long startsector; + + /* Convert string to long long */ + if (sscanf(value, "%lld", &startsector) != 1) { + volume_set_error(gettext("%s: invalid start sector"), value); + return (-1); + } + + /* Set the number of submirrors in the slice */ + return (devconfig_set_slice_start_block(slice, (uint64_t)startsector)); +} + +/* + * Validate and set the name attribute in the given volume + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the name + * + * @param attr + * the name of the XML attribute + * + * @param name + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_volume_name( + devconfig_t *volume, + char *attr, + char *name) +{ + return (devconfig_set_volume_name(volume, name)); +} + +/* + * Validate and set the interlace attribute in the given stripe + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the interlace + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_stripe_interlace( + devconfig_t *stripe, + char *attr, + char *value) +{ + int error; + uint64_t interlace = 0; + + /* Convert interlace string to bytes */ + if ((error = sizestr_to_bytes( + value, &interlace, interlace_units)) != 0) { + return (error); + } + + /* Set interlace in stripe */ + return (devconfig_set_stripe_interlace(stripe, interlace)); +} + +/* + * Validate and set the mincomp attribute in the given stripe + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the mincomp + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_stripe_mincomp( + devconfig_t *stripe, + char *attr, + char *value) +{ + uint16_t mincomp; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &mincomp) != 0) { + volume_set_error( + gettext("invalid minimum stripe components (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_stripe_mincomp(stripe, mincomp)); +} + +/* + * Validate and set the maxcomp attribute in the given stripe + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the maxcomp + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_stripe_maxcomp( + devconfig_t *stripe, + char *attr, + char *value) +{ + uint16_t maxcomp; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &maxcomp) != 0) { + volume_set_error( + gettext("invalid maximum stripe components (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_stripe_maxcomp(stripe, maxcomp)); +} + +/* + * Validate and set the usehsp attribute in the given volume + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the usehsp + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_volume_usehsp( + devconfig_t *volume, + char *attr, + char *value) +{ + boolean_t usehsp; + + /* Get boolean value */ + if (strtobool(value, &usehsp) != 0) { + volume_set_error( + gettext("%s: invalid boolean value for \"%s\" attribute"), + value, attr); + return (-1); + } + + /* Set in volume */ + return (devconfig_set_volume_usehsp(volume, usehsp)); +} + +/* + * Validate and set the nsubmirrors attribute in the given mirror + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the nsubmirrors + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_mirror_nsubmirrors( + devconfig_t *mirror, + char *attr, + char *value) +{ + uint16_t nsubmirrors; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &nsubmirrors) != 0) { + volume_set_error( + gettext("invalid number of submirrors (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_mirror_nsubs(mirror, nsubmirrors)); +} + +/* + * Validate and set the read attribute in the given mirror + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the read + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_mirror_read( + devconfig_t *mirror, + char *attr, + char *value) +{ + mirror_read_strategy_t strategy; + + if (strcmp(value, VALID_MIRROR_READ_ROUNDROBIN) == 0) { + strategy = MIRROR_READ_ROUNDROBIN; + } else + + if (strcmp(value, VALID_MIRROR_READ_GEOMETRIC) == 0) { + strategy = MIRROR_READ_GEOMETRIC; + } else + + if (strcmp(value, VALID_MIRROR_READ_FIRST) == 0) { + strategy = MIRROR_READ_FIRST; + } else + + { + volume_set_error(gettext("%s: invalid mirror read value"), value); + return (-1); + } + + return (devconfig_set_mirror_read(mirror, strategy)); +} + +/* + * Validate and set the write attribute in the given mirror + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the write + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_mirror_write( + devconfig_t *mirror, + char *attr, + char *value) +{ + mirror_write_strategy_t strategy; + + if (strcmp(value, VALID_MIRROR_WRITE_PARALLEL) == 0) { + strategy = MIRROR_WRITE_PARALLEL; + } else + + if (strcmp(value, VALID_MIRROR_WRITE_SERIAL) == 0) { + strategy = MIRROR_WRITE_SERIAL; + } else + + { + volume_set_error(gettext("%s: invalid mirror write value"), value); + return (-1); + } + + return (devconfig_set_mirror_write(mirror, strategy)); +} + +/* + * Validate and set the passnum attribute in the given mirror + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the passnum + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_mirror_passnum( + devconfig_t *mirror, + char *attr, + char *value) +{ + uint16_t passnum; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &passnum) != 0) { + volume_set_error( + gettext("invalid mirror pass number (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_mirror_pass(mirror, passnum)); +} + +/* + * Validate and set the redundancy attribute in the given volume + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the redundancy + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_volume_redundancy( + devconfig_t *volume, + char *attr, + char *value) +{ + uint16_t redundancy; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &redundancy) != 0) { + volume_set_error( + gettext("invalid redundancy level (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_volume_redundancy_level(volume, redundancy)); +} + +/* + * Validate and set the datapaths attribute in the given volume + * devconfig_t. + * + * @param volume + * the devconfig_t in which to set the datapaths + * + * @param attr + * the name of the XML attribute + * + * @param value + * the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +validate_set_volume_datapaths( + devconfig_t *volume, + char *attr, + char *value) +{ + uint16_t redundancy; + + /* Convert string to a uint16_t */ + if (str_to_uint16(value, &redundancy) != 0) { + volume_set_error( + gettext("invalid number of data paths (%s): %s"), + attr, value); + return (-1); + } + + /* Set in stripe */ + return (devconfig_set_volume_npaths(volume, redundancy)); +} + +/* + * ****************************************************************** + * + * XML attribute accessors/converters + * + * These functions get a value from the appropriate devconfig_t + * accessor, and then convert it to a string. + * + * ****************************************************************** + */ + +/* + * Get, as a string, the value of the name attribute of the given + * devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the name + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_name( + devconfig_t *device, + char *attr, + char **value) +{ + int error; + char *name; + + /* Get name */ + if ((error = devconfig_get_name(device, &name)) == 0) { + if ((*value = strdup(name)) == NULL) { + error = ENOMEM; + } + } + + return (error); +} + +/* + * Get, as a string, the value of the passnum attribute of the given + * mirror devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the passnum + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_mirror_passnum( + devconfig_t *mirror, + char *attr, + char **value) +{ + int error; + uint16_t passnum; + + /* Get mirror pass number */ + if ((error = devconfig_get_mirror_pass(mirror, &passnum)) == 0) { + error = ll_to_str(passnum, value); + } + + return (error); +} + +/* + * Get, as a string, the value of the read attribute of the given + * mirror devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the read + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_mirror_read( + devconfig_t *mirror, + char *attr, + char **value) +{ + int error; + mirror_read_strategy_t read; + + /* Get mirror read strategy */ + if ((error = devconfig_get_mirror_read(mirror, &read)) == 0) { + if ((*value = strdup( + devconfig_read_strategy_to_str(read))) == NULL) { + error = ENOMEM; + } + } + + return (error); +} + +/* + * Get, as a string, the value of the write attribute of the given + * mirror devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the write + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_mirror_write( + devconfig_t *mirror, + char *attr, + char **value) +{ + int error; + mirror_write_strategy_t write; + + /* Get mirror write strategy */ + if ((error = devconfig_get_mirror_write(mirror, &write)) == 0) { + if ((*value = strdup( + devconfig_write_strategy_to_str(write))) == NULL) { + error = ENOMEM; + } + } + + return (error); +} + +/* + * Get, as a string, the value of the in_blocks attribute of the given + * device devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the in_blocks + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_size_in_blocks( + devconfig_t *device, + char *attr, + char **value) +{ + int error; + uint64_t size; + + /* Get size in blocks */ + if ((error = devconfig_get_size_in_blocks(device, &size)) == 0) { + error = ll_to_str(size, value); + } + + return (error); +} + +/* + * Get, as a string, the value of the start_block attribute of the + * given slice devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the start_block + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_slice_start_block( + devconfig_t *slice, + char *attr, + char **value) +{ + int error; + uint64_t start; + + /* Get slice start block */ + if ((error = devconfig_get_slice_start_block(slice, &start)) == 0) { + error = ll_to_str(start, value); + } + + return (error); +} + +/* + * Get, as a string, the value of the interlace attribute of the given + * stripe devconfig_t. This data must be freed. + * + * @param device + * the devconfig_t from which to retrieve the interlace + * + * @param attr + * the name of the XML attribute + * + * @param value + * RETURN: the value of the XML attribute + * + * @return 0 on success, non-zero otherwise. + */ +static int +get_as_string_stripe_interlace( + devconfig_t *stripe, + char *attr, + char **value) +{ + int error; + uint64_t interlace; + + /* Get interlace */ + if ((error = devconfig_get_stripe_interlace( + stripe, &interlace)) == 0) { + error = bytes_to_sizestr(interlace, value, interlace_units, B_TRUE); + } + + return (error); +} diff --git a/usr/src/cmd/lvm/metassist/xml/xml_convert.h b/usr/src/cmd/lvm/metassist/xml/xml_convert.h new file mode 100644 index 0000000000..a6017111bc --- /dev/null +++ b/usr/src/cmd/lvm/metassist/xml/xml_convert.h @@ -0,0 +1,157 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _XML_CONVERT_H +#define _XML_CONVERT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libxml/parser.h> +#include "volume_request.h" +#include "volume_defaults.h" + +/* The location of the volume-request.dtd */ +#define VOLUME_REQUEST_DTD_LOC "/usr/share/lib/xml/dtd/volume-request.dtd" + +/* The location of the volume-request-defaults.dtd */ +#define VOLUME_DEFAULTS_DTD_LOC "/usr/share/lib/xml/dtd/volume-defaults.dtd" + +/* The location of the volume-config.dtd */ +#define VOLUME_CONFIG_DTD_LOC "/usr/share/lib/xml/dtd/volume-config.dtd" + +/* Location of the volume-command.xsl file */ +#define VOLUME_COMMAND_XSL_LOC "/usr/share/lib/xml/style/volume-command.xsl" + +/* + * Valid values for attributes + */ +#define VALID_ATTR_TRUE "TRUE" +#define VALID_ATTR_FALSE "FALSE" +#define VALID_MIRROR_READ_GEOMETRIC "GEOMETRIC" +#define VALID_MIRROR_READ_FIRST "FIRST" +#define VALID_MIRROR_READ_ROUNDROBIN "ROUNDROBIN" +#define VALID_MIRROR_WRITE_SERIAL "SERIAL" +#define VALID_MIRROR_WRITE_PARALLEL "PARALLEL" + +/* + * Standard units + */ +#define UNIT_BLOCKS "BLOCKS" +#define UNIT_KILOBYTES "KB" +#define UNIT_MEGABYTES "MB" +#define UNIT_GIGABYTES "GB" +#define UNIT_TERABYTES "TB" + +/* + * Initialize the XML parser, setting defaults across all XML + * routines. + */ +extern void init_xml(); + +/* + * Clean up any remaining structures before exiting. + */ +extern void cleanup_xml(); + +/* + * Converts a volume-request XML document into a request_t. + * + * @param doc + * an existing volume-request XML document + * + * @param request + * RETURN: a new request_t which must be freed via + * free_request + * + * @return 0 on success, non-zero otherwise. + */ +extern int xml_to_request(xmlDocPtr doc, request_t **request); + +/* + * Converts a volume-defaults XML document into a defaults_t. + * + * @param doc + * an existing volume-defaults XML document + * + * @param defaults + * RETURN: a new defaults_t which must be freed via + * free_defaults + * + * @return 0 on success, non-zero otherwise. + */ +extern int xml_to_defaults(xmlDocPtr doc, defaults_t **defaults); + +/* + * Converts a volume-config XML document into a devconfig_t. + * + * @param doc + * an existing volume-config XML document + * + * @param config + * RETURN: a new devconfig_t which must be freed via + * free_devconfig + * + * @return 0 on success, non-zero otherwise. + */ +extern int xml_to_config(xmlDocPtr doc, devconfig_t **config); + +/* + * Converts a devconfig_t into a volume-config XML document. + * + * @param config + * an existing devconfig_t representing a volume + * configuration. + * + * @param doc + * RETURN: a new volume-config XML document which must be + * freed via xmlFreeDoc + * + * @return 0 on success, non-zero otherwise. + */ +extern int config_to_xml(devconfig_t *config, xmlDocPtr *doc); + +/* + * Converts a volume-config XML document into a Bourne shell script. + * + * @param doc + * an existing volume-config XML document + * + * @param commands + * RETURN: a new char* which must be freed + * + * @return 0 on success, non-zero otherwise. + */ +extern int xml_to_commands(xmlDocPtr doc, char **commands); + +#ifdef __cplusplus +} +#endif + +#endif /* _XML_CONVERT_H */ |
