/*
* 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
* 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_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
/*
* 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 }
*/
typedef struct {
int ctrl;
int target;
int lun;
int slice;
} ctd_spec_t;
typedef enum {
SPEC_TYPE_CTD = 0,
} spec_type_t;
typedef struct {
union {
char *raw;
} data;
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(
static boolean_t ctd_spec_includes_device(
static boolean_t raw_spec_includes_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;
} spec_cache_t;
static int destroy_spec_cache();
static int compare_name_to_spec_cache_name(
/*
* 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 {
/*
* avail_specs_list is a list of device spec_t
* corresponding to available devices specified
* in the request object
*/
/*
* unavail_specs_list is a list of device spec_t
* corresponding to unavailable devices specified
* in the request object
*/
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,
/* other private functions */
static int is_device_avail(
static int is_named_device_avail(
char *device_name,
static int avail_list_includes_device_name(
char *device_name,
static int unavail_list_includes_device_name(
char *device_name,
static int spec_includes_device_name(
char *device_name,
static boolean_t spec_includes_device(
static int disk_get_avail_space(
static int compare_hba_n_avail_disks(
void *obj1,
void *obj2);
/*
* FUNCTION: release_request_caches()
*
* RETURNS: 0
*
* PURPOSE: cleanup the module private caches.
*/
int
{
(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
char *dsname)
{
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 *
{
return (_request_diskset);
}
/*
* FUNCTION: void unset_request_diskset()
*
* PURPOSE: unset the module global diskset name.
*/
void
char *dsname)
{
}
/*
* 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
{
return (validate_request_avail_unavail(req));
}
/*
* FUNCTION: void unset_toplevel_request()
*
* PURPOSE: unset the layout module global toplevel request struct.
*
*/
void
{
}
/*
* 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
{
int error = 0;
if ((error = defaults_get_diskset_by_name(
/* no defaults to verify */
error = 0;
}
return (error);
}
/*
* FUNCTION: void unset_request_defaults()
*
* PURPOSE: unset the layout module global defaults struct.
*
*/
void
{
}
/*
* 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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_stripe_mincomp(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_stripe_maxcomp(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
error = 0;
}
if (*val == 0) {
if ((error = defaults_get_stripe_interlace(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_mirror_read(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_mirror_write(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_mirror_pass(
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_mirror_nsubs(
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
{
int error = 0;
if (error == ERR_ATTR_UNSET) {
switch (type) {
case TYPE_MIRROR:
break;
case TYPE_STRIPE:
break;
case TYPE_CONCAT:
break;
case TYPE_VOLUME:
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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*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
{
int error = 0;
*val = 0;
if (error != ERR_ATTR_UNSET) {
return (error);
}
}
if (*val == 0) {
if ((error = defaults_get_volume_npaths(
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
char **name)
{
int error = 0;
get_request_diskset(), name)) != 0) {
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
char *sname,
boolean_t *bool)
{
int error = 0;
*bool = B_FALSE;
return (error);
}
if (slice == (dm_descriptor_t)0) {
/* no slice found */
return (ENODEV);
}
if (error == 0) {
}
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
char *name,
{
int error = 0;
if (error == 0) {
for (iter = known_disks;
if (error == 0) {
/* add disk */
NULL) {
} else {
}
} else {
/* check disk's aliases */
for (iter2 = diskaliases;
if (spec_includes_device(
/* alias matched, add disk */
} else {
*disks =
}
}
}
}
}
}
}
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
int mindisks,
{
int error = 0;
/* for each input HBA */
/* for each of this HBA's disks */
/* unique disk? has it been seen thru some other HBA? */
compare_descriptor_names) != B_TRUE) {
/* distinct, add to list of all_distinct */
} else {
*seldisks =
/* increment this HBA's distinct disk count */
++ndistinct;
}
}
}
/* this HBA has minimum # of disks, add to output list */
} else {
*selhbas =
/* save # of disks for ordering the list */
}
}
}
if (error != 0) {
gettext("failed selecting HBAs with n disks: %d\n"),
error);
}
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
{
int error = 0;
/* for each usable disk */
for (iter = usable_disks;
/* is disk attached to HBA in question? */
if (error != 0) {
continue;
}
compare_descriptor_names) == B_TRUE) {
/* is disk available? */
/* does disk have available space? */
if ((error == 0) && (disk_space > 0)) {
} else {
}
*space += disk_space;
}
}
}
}
if (error != 0) {
}
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
{
int error = 0;
*space = 0;
/* for each usable slice */
for (iter = usable_slices;
/* is slice on disk in question? */
continue;
}
/* is slice reserved by an explicit layout request? */
continue;
}
/* is slice used by a pending layout request? */
continue;
}
/* is slice available? */
/* does slice have usable space? */
}
}
}
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
{
int error = 0;
/* for each usable slice */
for (iter = usable_slices;
/* is slice on a disk in the input list? */
compare_descriptor_names) != B_TRUE)) {
continue;
}
/* is slice reserved by an explicit layout request? */
continue;
}
/* is slice used by a pending layout request? */
continue;
}
/* is slice available? */
/* does slice have available space? */
} else {
}
}
}
}
if (error != 0) {
}
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
{
int error = 0;
}
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
{
int error = 0;
/* get disk for component slice */
if (error == 0) {
} else {
}
}
/* get HBAs for disk */
if (error == 0) {
/* the hba list may contain dups, but that's ok */
}
}
/* collect info for submirrors */
}
}
}
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
void *obj1,
void *obj2)
{
}
/*
* 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
{
int error = 0;
}
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
*
* The defaults_t struct pointer is the lookup key.
*/
static int
void *request,
void *list_item)
{
/* compare two devconfig_t pointers, if identical, return 0 */
}
/*
* 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
void *spec1,
void *spec2)
{
return (-1);
}
/* spec1 has slice, spec2 does not, spec1 more specific */
return (1);
}
/* spec2 has slice, spec1 does not, spec2 more specific */
return (-1);
}
/* both spec1 and spec2 have slice */
return (0);
}
/* spec1 has lun, spec2 does not, spec1 more specific */
return (1);
}
/* spec2 has lun, spec1 does not, spec2 more specific */
return (-1);
}
/* both spec1 and spec2 have lun */
return (0);
}
/* spec1 has target, spec2 does not, spec1 more specific */
return (1);
}
/* spec2 has target, spec1 does not, spec2 more specific */
return (-1);
}
/* 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 *
{
(void *)request,
}
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
{
return (ENOMEM);
}
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
*
* 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
char **avail_device_specs,
char **unavail_device_specs,
{
int error = 0;
return (ENOMEM);
}
/*
* map the avail_device_name array into a list of device_spec_t
* and save the list as the entry's available list
*/
if (error == 0) {
}
/*
* map the unavail_device_name array into a list of device_spec_t
* and save the list as the entry's unavailable list
*/
if (error == 0) {
}
if (error != 0) {
/* delete the partial entry */
destroy_request_spec_list_entry((void *)*entry);
}
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
char **specs,
{
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;
i++) {
} else {
}
}
}
if (error != 0) {
/* the device_spec_t in the list items are maintained */
/* in a cache elsewhere, so don't free them here. */
}
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
void *obj)
{
/* items in the list are in the spec_cache and will */
/* be cleaned up when it is destroyed. */
}
}
/*
* 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
{
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
{
int error = 0;
/* create cache entry for this request */
&entry);
}
}
}
}
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
{
int error = 0;
/* create new entry for this request */
&entry);
}
}
}
}
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
char *dsname,
{
int error = 0;
/* Get diskset defaults, or global if none for diskset */
if (error != 0) {
/* to get global defaults, pass a NULL diskset name */
}
if (error != 0) {
gettext("get defaults for %s returned %d\n"),
} else {
error = 0;
}
}
}
/* create new entry for these defaults */
&entry);
}
}
}
}
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
char *dsname,
{
int error = 0;
/* Get diskset defaults, or global if none for diskset */
if (error != 0) {
/* to get global defaults, pass a NULL diskset name */
}
if (error != 0) {
gettext("get defaults for %s returned %d\n"),
} else {
error = 0;
}
}
}
/* create new entry for these defaults */
&entry);
}
}
}
}
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
char *device_name,
{
typedef enum check_types {
DEVICE_REQUEST = 0,
} check_type_t;
typedef enum list_types {
AVAIL = 0,
} list_type_t;
int error = 0;
}
/* diskset request */
}
}
if (error != 0) {
return (error);
}
for (check_type = DEVICE_REQUEST;
check_type++) {
/* does avail spec list include named device? */
if ((error = avail_list_includes_device_name(
&includes)) == 0) {
}
/* device is available, is it in the unavail list? */
check_aliases, &includes)) == 0) {
}
}
}
}
/* lists at this level checked, skip remainder */
break;
/* does unavail spec list include named device? */
check_aliases, &includes)) == 0) {
}
}
/* 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
char *device_name,
{
int error = 0;
}
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
char *device_name,
{
int error = 0;
/*
* 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.
*/
}
/* matched an unavailable spec, was it a controller/HBA? */
"device \"%s\" is unavailable, "
"it matched \"c(%d)t(%d)d(%d)s(%d)\"\n",
/*
* 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.
*/
}
}
if (error != 0) {
return (error);
}
/* if it is a slice, get its disk */
}
/* see if all the disk's HBAs are unavailable */
gettext(" checking alternate paths for %s\n"),
} else {
gettext(" no alternate paths for %s\n"),
}
/* for each of the disk's HBAs */
/* is HBA unavailable? */
}
}
/* 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
char *device_name,
{
int error = 0;
if (error == 0) {
/* spec doesn't include name, check aliases */
/* only slices and disks have aliases */
if (device != (dm_descriptor_t)0) {
} else if (error == 0) {
if (device != (dm_descriptor_t)0) {
}
}
(error == 0);
if (error == 0) {
/* does spec include alias? */
}
}
}
}
}
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
{
}
}
}
/*
* 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
char *name,
{
int error = 0;
/* allocate the device spec and try various parsing schemes */
} else {
/* CTD failed, try other parsing schemes */
}
}
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
char *name,
{
char *device_str;
char *target_str;
char *ctd_str;
char *t_ptr;
char *d_ptr;
char *s_ptr;
int error = 0;
return (ENOMEM);
}
++device_str;
} else {
}
/* find each segment start position */
/*
* scan ids from each existing segment working backwards
* so as to leave the device_str in the correct state
* for the next expected segment
*/
/* found 's', try to get slice */
error = -1;
gettext("no slice component in device "
"name \"%s\".\n"),
name);
} else {
*s_ptr = '\0';
}
}
error = -1;
"in device name \"%s\".\n"),
name);
} else {
*d_ptr = '\0';
}
}
/* found 't', try to get target, it may be a hex WWN id */
/* skip leading 't' and add two for the 'OX' */
} else {
error = -1;
"in device name \"%s\".\n"),
name);
} else {
got_target = B_TRUE;
*t_ptr = '\0';
}
}
} else {
}
/* get controller/hba/channel */
error = -1;
"in device name \"%s\".\n"),
name);
} else {
}
}
if (error == 0) {
/* allocate the ctd_spec_t struct and store the ids */
}
}
}
if (got_target == B_TRUE) {
}
}
}
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
char *name,
{
int error = 0;
return (ENOMEM);
}
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
char *name,
device_spec_t **id)
{
int error = 0;
} else {
} else {
}
if (error == 0) {
} else {
}
}
}
}
} else {
}
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.
*/
{
}
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
{
/* valid IDE names are cX, cXdX, cXdXsX, no target */
}
} else {
/* valid names are cX, cXtX, cXtXdX, cXtXdXsX */
}
}
gettext("spec: c(%d) t(%d) d(%d) s(%d) "
"%s: c(%d) t(%d) d(%d) s(%d)\n"),
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
{
}
/*
* 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
void *name,
void *list_item)
{
}
/*
* 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
void *obj)
{
}
}
/*
* FUNCTION: destroy_spec_cache()
*
* RETURNS: int - 0 on success
* !0 otherwise.
*
* PURPOSE: Function which destroys all entries in the device_spec
* cache.
*/
static int
{
_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
char **name)
{
int error = 0;
return (error);
}
gettext("device DID name %s is preferred\n"),
*name);
return (0);
}
if (error != 0) {
return (error);
}
gettext("device name %s is accessible\n"),
*name);
return (0);
}
/* search aliases for an 'available' name, prefer DID names */
gettext("device alias %s is accessible for %s\n"),
break;
}
}
}
if (error == 0) {
}
}
}
return (error);
}