/*
* 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 <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*)(uintptr_t)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*)(uintptr_t)disk, (void*)(uintptr_t)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 *)(uintptr_t)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);
}