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