/* * 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 #include #include #include "volume_error.h" #include "volume_defaults.h" #include "volume_dlist.h" #include "volume_output.h" #include "volume_request.h" #include "layout_device_cache.h" #include "layout_discovery.h" #include "layout_dlist_util.h" #include "layout_request.h" #include "layout_slice.h" #include "layout_validate.h" #define _LAYOUT_REQUEST_C static char *_request_diskset = NULL; static devconfig_t *_toplevel_request = NULL; static defaults_t *_defaults = NULL; /* * This file contains code which handles various aspects of the * request and defaults devconfig_t structs passed to the layout * module. * * Functions are provided which determine what devices are available * for use by the various volume layout mechanisms. These are based * on the user specified available/unavailable devices included in * a request or in the defaults associated with the destination diskset. */ /* * A struct to hold device "specifications" extracted from a user * specified device name. This struct is used to compare the user's * available and unavailable device specifications against physical * devices attached to the system. * * The spec struct holds one of two different specifications: if the * user supplied device name is parsable as a CTD name, it is parsed * into the component ids. Otherwise, it is stored as is. * * The CTD name space implies a device hierarchy and metassist * supports an implied wildcarding scheme for the CTD name space. * A CTD specification from the user is of the form cX, cXdX, * cXdXsX, cXtX, cXtXdX, or cXtXdXsX, so it may or may nor * correspond to an individual physical device depending on * the context. * * For example, "c1" can mean the controller/HBA with the * name "c1" or it can mean all devices attached to the * controller named "c1". * * The ctd specs make matching physical devices against a * user specification easier since the matching is based on * the numeric values extracted from the cXtXdXsX string * and not on the strings themselves. The strings are * troublesome because of situations like "c1" being * compared to "c11t1d0s0" and getting false matches. * * The ID_UNSPECIFIED value is used to flag components * that were not in the CTD name: * * "c3" -> { ctrl=3, target=ID_UNSPECIFIED, * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } * * "c3t2" -> { ctrl=3, target=2, * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } */ #define ID_UNSPECIFIED -1 typedef struct { int ctrl; int target; int lun; int slice; boolean_t is_ide; } ctd_spec_t; typedef enum { SPEC_TYPE_CTD = 0, SPEC_TYPE_RAW, SPEC_TYPE_OTHER } spec_type_t; typedef struct { spec_type_t type; union { ctd_spec_t *ctd; char *raw; } data; } device_spec_t; static int get_spec_for_name( char *name, device_spec_t **id); static int create_device_spec( char *name, device_spec_t **spec); static int create_device_ctd_spec( char *name, device_spec_t **spec); static int create_device_raw_spec( char *name, device_spec_t **spec); static void destroy_device_spec( device_spec_t *spec); static boolean_t ctd_spec_includes_device( device_spec_t *spec, device_spec_t *device); static boolean_t raw_spec_includes_device( device_spec_t *spec, device_spec_t *device); /* * get_spec_for_name builds up a cached mapping of device * names to the corresponding device_spec_t structs. * * This saves repeatedly converting the device names, which * could get expensive since devices are checked against the * user specified available/unavailable devices a lot. * * The cache is implemented as a list of these structs: */ typedef struct { char *name; device_spec_t *device_spec; } spec_cache_t; static dlist_t *_spec_cache = NULL; static int destroy_spec_cache(); static int compare_name_to_spec_cache_name( void *name, void *list_item); /* * The user specified available/unavailable devices are * accessed frequently during layout. To make this more * efficient, the char *arrays of available/unavailable * specifications for a request or defaults devconfig_t * object are converted to device_spec_ts the first time * they're accessed and then cached using this struct: */ typedef struct { devconfig_t *request; /* * avail_specs_list is a list of device spec_t * corresponding to available devices specified * in the request object */ dlist_t *avail_specs_list; /* * unavail_specs_list is a list of device spec_t * corresponding to unavailable devices specified * in the request object */ dlist_t *unavail_specs_list; } request_spec_list_t; dlist_t *_request_spec_list_cache = NULL; static int destroy_request_spec_list_cache(); static void destroy_request_spec_list_entry(void *obj); static int compare_request_to_request_spec_list_request( void *object, void *list_item); static int convert_usernames_to_specs( char **specs, dlist_t **list); /* other private functions */ static int is_device_avail( dm_descriptor_t desc, devconfig_t *request, boolean_t *avail); static int is_named_device_avail( devconfig_t *request, char *device_name, boolean_t check_aliases, boolean_t *avail); static int avail_list_includes_device_name( dlist_t *list, char *device_name, boolean_t check_aliases, boolean_t *includes); static int unavail_list_includes_device_name( dlist_t *list, char *device_name, boolean_t check_aliases, boolean_t *includes); static int spec_includes_device_name( device_spec_t *spec, char *device_name, boolean_t check_aliases, boolean_t *includes); static boolean_t spec_includes_device( device_spec_t *spec, device_spec_t *device); static int disk_get_avail_space( devconfig_t *request, dm_descriptor_t disk, uint64_t *avail); static int compare_hba_n_avail_disks( void *obj1, void *obj2); /* * FUNCTION: release_request_caches() * * RETURNS: 0 * * PURPOSE: cleanup the module private caches. */ int release_request_caches() { (void) destroy_request_spec_list_cache(); (void) destroy_spec_cache(); return (0); } /* * FUNCTION: int set_request_diskset(char *) * * INPUT: char * - pointer to the diskset name * OUTPUT: 0 - success * !0 - validation failure * RETURNS: * * PURPOSE: set the module global diskset name. */ int set_request_diskset( char *dsname) { _request_diskset = dsname; if (dsname == NULL || dsname[0] == '\0') { volume_set_error( gettext("No disk set specified in request\n")); return (-1); } return (0); } /* * FUNCTION: char *get_request_diskset() * * INPUT: none - * OUTPUT: none - * RETURNS: char * - pointer to the currently set diskset name * * PURPOSE: get the global name of the current diskset. */ char * get_request_diskset() { return (_request_diskset); } /* * FUNCTION: void unset_request_diskset() * * PURPOSE: unset the module global diskset name. */ void unset_request_diskset( char *dsname) { _request_diskset = NULL; } /* * FUNCTION: int set_toplevel_request(devconfig_t *) * * INPUT: devconfig_t * - pointer to the diskset request * OUTPUT: 0 - success * !0 - validation failure * RETURNS: * * PURPOSE: set the module global toplevel request struct. * this will be set within the only public entry * point to the module -- get_layout() * * SIDEEFFECT: The devconfig_t's list of available and unavailable * devices will be validated. */ int set_toplevel_request( devconfig_t *req) { _toplevel_request = req; return (validate_request_avail_unavail(req)); } /* * FUNCTION: void unset_toplevel_request() * * PURPOSE: unset the layout module global toplevel request struct. * */ void unset_toplevel_request() { _toplevel_request = NULL; } /* * FUNCTION: int set_defaults(devconfig_t *) * * INPUT: devconfig_t * - pointer to the global defaults devconfig_t * OUTPUT: 0 - success * !0 - validation failure * RETURNS: * * PURPOSE: set the module global defaults struct. * this will be set within the only public entry * point to the module -- get_layout() * * SIDEEFFECT: The devconfig_t's list of available and unavailable * devices will be validated. */ int set_request_defaults( defaults_t *defaults) { int error = 0; devconfig_t *diskset = NULL; _defaults = defaults; if ((error = defaults_get_diskset_by_name( _defaults, get_request_diskset(), &diskset)) == 0) { error = validate_request_avail_unavail(diskset); } else if (error == ENOENT) { /* no defaults to verify */ error = 0; } return (error); } /* * FUNCTION: void unset_request_defaults() * * PURPOSE: unset the layout module global defaults struct. * */ void unset_request_defaults() { _defaults = NULL; } /* * FUNCTION: get_stripe_min_comp(devconfig_t *req, uint16_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint64_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the minimum of components * for striped volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_stripe_min_comp( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_stripe_mincomp( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_stripe_max_comp(devconfig_t *req, uint16_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint64_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the maximum number of components * for striped volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_stripe_max_comp( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_stripe_maxcomp( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_stripe_interlace(devconfig_t *req, uint64_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint64_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the interlace value for striped * volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. * * If no value is explictly specified, ERR_ATTR_UNSET is * returned. */ int get_stripe_interlace( devconfig_t *req, uint64_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_stripe_interlace(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } error = 0; } if (*val == 0) { if ((error = defaults_get_stripe_interlace( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_mirror_read_strategy(devconfig_t *req, * mirror_read_strategy_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a mirror_read_strategy_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the write strategy mirrored volumes * should have for volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. * * If no value is explictly specified, ERR_ATTR_UNSET is * returned. */ int get_mirror_read_strategy( devconfig_t *req, mirror_read_strategy_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_mirror_read(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_mirror_read( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_mirror_write_strategy(devconfig_t *req, * mirror_write_strategy_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a mirror_write_strategy_t to hold result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the write strategy mirrored volumes * should have for volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. * * If no value is explictly specified, ERR_ATTR_UNSET is * returned. */ int get_mirror_write_strategy( devconfig_t *req, mirror_write_strategy_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_mirror_write(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_mirror_write( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_mirror_pass(devconfig_t *req, uint16_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint16_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the resync pass mirrored volumes * should have for volumes satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. * * If no value is explictly specified, ERR_ATTR_UNSET is * returned. */ int get_mirror_pass( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_mirror_pass(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_mirror_pass( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_mirror_nsubs(devconfig_t *req, uint16_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint16_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines how many submirrors mirrored * volumes should have for volumes satisfying the input * request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_mirror_nsubs( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_mirror_nsubs( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_volume_faultrecov(devconfig_t *req, boolean_t *val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a boolean_t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines whether data redundant volumes * should also have fault recovery (e.g., HSPs) for volumes * satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_volume_faultrecov( devconfig_t *req, boolean_t *val) { int error = 0; *val = B_FALSE; if ((error = devconfig_get_volume_usehsp(req, val)) != 0) { if (error == ERR_ATTR_UNSET) { component_type_t type = TYPE_UNKNOWN; (void) devconfig_get_type(req, &type); switch (type) { case TYPE_MIRROR: error = defaults_get_mirror_usehsp( _defaults, get_request_diskset(), val); break; case TYPE_STRIPE: error = defaults_get_stripe_usehsp( _defaults, get_request_diskset(), val); break; case TYPE_CONCAT: error = defaults_get_concat_usehsp( _defaults, get_request_diskset(), val); break; case TYPE_VOLUME: error = defaults_get_volume_usehsp( _defaults, get_request_diskset(), val); break; } } } return (error); } /* * FUNCTION: get_volume_redundancy_level(devconfig_t *req, uint16_t val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint16-t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the appropriate level of data * redundancy a volume should have for volumes satisfying * the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_volume_redundancy_level( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_volume_redundancy_level( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_volume_npaths(devconfig_t *req, uint16_t val) * INPUT: req - a devconfig_t pointer to the current request * val - pointer to a uint16-t to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the appropriate level of datapath * redundancy a slice component should have for volumes * satisfying the input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_volume_npaths( devconfig_t *req, uint16_t *val) { int error = 0; *val = 0; if ((error = devconfig_get_volume_npaths(req, val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } if (*val == 0) { if ((error = defaults_get_volume_npaths( _defaults, get_request_diskset(), val)) != 0) { if (error != ERR_ATTR_UNSET) { return (error); } } } return (error); } /* * FUNCTION: get_default_hsp_name(devconfig_t *req, char **hspname) * INPUT: req - a devconfig_t pointer to the current request * hspname - pointer to a char * to hold the result, if any * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which determines the default HSP name for the * input request. * * The value to use is taken from the input request, the * toplevel diskset request, the diskset defaults or the * global defaults. */ int get_default_hsp_name( devconfig_t *req, char **name) { int error = 0; *name = NULL; if ((error = defaults_get_hsp_name(_defaults, get_request_diskset(), name)) != 0) { if (error != ENOENT) { return (error); } error = 0; } return (error); } /* * FUNCTION: slice_is_available(char *sname, devconfig_t *request, * boolean_t bool) * INPUT: sname - a slice name * request - pointer to a devconfig_t struct representing * the current layout request being processed * bool - pointer to a boolean to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: Validation helper which determines if the named slice can * be used as a volume component when satisfying the input * request. * * Check if the slice appears in the known slice list, * then check the request's available and unavailable * device specifications. */ int slice_is_available( char *sname, devconfig_t *request, boolean_t *bool) { dm_descriptor_t slice = (dm_descriptor_t)0; int error = 0; *bool = B_FALSE; if ((error = slice_get_by_name(sname, &slice)) != 0) { return (error); } if (slice == (dm_descriptor_t)0) { /* no slice found */ return (ENODEV); } if (error == 0) { error = is_named_device_avail(request, sname, B_TRUE, bool); } return (error); } /* * FUNCTION: get_disks_for_target(char *name, dlist_t **disks) * * INPUT: name - a char* device CTD name * * OUTPUT: disks - disks matching the input target name * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: Validation helper function which finds all disks "on" the * input target. * * The input name is assumed to be a target name, cXtX, and * the list of known disks is searched to find any disk that * looks to be "on" that target. * * "On" is determined by comparing a disk's name and * aliases to the target to see if they match. */ int get_disks_for_target( char *name, dlist_t **disks) { int error = 0; device_spec_t *targetid = NULL; error = get_spec_for_name(name, &targetid); if (error == 0) { dlist_t *known_disks = NULL; dlist_t *iter = NULL; get_known_disks(&known_disks); for (iter = known_disks; (iter != NULL) && (error == 0); iter = iter->next) { dm_descriptor_t disk = (uintptr_t)iter->obj; device_spec_t *diskid = NULL; char *diskname = NULL; dlist_t *diskaliases = NULL; dlist_t *item; ((error = get_display_name(disk, &diskname)) != 0) || (error = get_aliases(disk, &diskaliases)) || (error = get_spec_for_name(diskname, &diskid)); if (error == 0) { if (spec_includes_device(targetid, diskid) == B_TRUE) { /* add disk */ if ((item = dlist_new_item((void *)(uintptr_t)disk)) == NULL) { error = ENOMEM; } else { *disks = dlist_append(item, *disks, AT_HEAD); } } else { /* check disk's aliases */ dlist_t *iter2; for (iter2 = diskaliases; (iter2 != NULL) && (error == 0); iter2 = iter2->next) { char *aliasname = NULL; device_spec_t *aliasid = NULL; error = get_display_name(disk, &aliasname); error = get_spec_for_name(aliasname, &aliasid); if (spec_includes_device( targetid, aliasid) == B_TRUE) { /* alias matched, add disk */ item = dlist_new_item((void *)(uintptr_t)disk); if (item == NULL) { error = ENOMEM; } else { *disks = dlist_append(item, *disks, AT_HEAD); } } } } } } } return (error); } /* * FUNCTION: select_hbas_with_n_disks(devconfig_t *request, * dlist_t *hbas, int mindisks, dlist_t **selhbas, * dlist_t **seldisks) * * INPUT: request - pointer to a devconfig_t struct representing * the current layout request being processed * hbas - pointer to a list of HBAs * mindisks - minimum number of disks required on the HBAs * * OUTPUT: selhbas - pointer to a list containing the HBAs with at * least mindisks available disks. * seldisks - pointer to a list containing the available disks * for the HBAs in selhbas * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which counts the number of available disks associated * with each of the input HBAs and adds those that have at * least mindisks to the output list. * * Only available disks that have available space are counted. * * Disks connected thru multiple HBAs are only counted for * the first HBA they're accessed through. * * The list of HBAs returned will be in descending order, * i.e., HBAs with more disks come before those with fewer. * * The returned lists of HBAs and disks must be passed to * dlist_free_items() to recover the space allocated to hold * each list item. * * for (each HBA) { * * select HBA * get available disks on HBA * * for (each disk) { * if (disk is not in selected disk list) * add it to the list * else * count it as a distinct disk on this HBA * } * * if (this HBA has >= mindisks distinct disks) * add this HBA to the list of returned HBAs * * } */ int select_hbas_with_n_disks( devconfig_t *request, dlist_t *hbas, int mindisks, dlist_t **selhbas, dlist_t **seldisks) { dlist_t *iter = NULL; int error = 0; *selhbas = NULL; *seldisks = NULL; /* for each input HBA */ for (iter = hbas; (error == 0) && (iter != NULL); iter = iter->next) { dm_descriptor_t hba = (uintptr_t)iter->obj; dlist_t *iter2 = NULL; dlist_t *disks = NULL; uint64_t space = 0; uint16_t ndistinct = 0; error = hba_get_avail_disks_and_space(request, hba, &disks, &space); /* for each of this HBA's disks */ for (iter2 = disks; (iter2 != NULL) && (error == 0); iter2 = iter2->next) { dm_descriptor_t disk = (uintptr_t)iter2->obj; /* unique disk? has it been seen thru some other HBA? */ if (dlist_contains(*seldisks, (void *)(uintptr_t)disk, compare_descriptor_names) != B_TRUE) { /* distinct, add to list of all_distinct */ dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); if (item == NULL) { error = ENOMEM; } else { *seldisks = dlist_append(item, *seldisks, AT_HEAD); /* increment this HBA's distinct disk count */ ++ndistinct; } } } if (ndistinct >= mindisks) { /* this HBA has minimum # of disks, add to output list */ dlist_t *item = dlist_new_item((void *)(uintptr_t)hba); if (item == NULL) { error = ENOMEM; } else { *selhbas = dlist_insert_ordered( item, *selhbas, DESCENDING, compare_hba_n_avail_disks); /* save # of disks for ordering the list */ hba_set_n_avail_disks(hba, ndistinct); } } dlist_free_items(disks, NULL); } if (error != 0) { oprintf(OUTPUT_TERSE, gettext("failed selecting HBAs with n disks: %d\n"), error); dlist_free_items(*selhbas, NULL); *selhbas = NULL; dlist_free_items(*seldisks, NULL); *seldisks = NULL; } return (error); } /* * FUNCTION: hba_get_avail_disks_and_space(devconfig_t *request, * dm_descriptor_t hba, dlist_t **disks, uint64_t *space) * * INPUT: request - pointer to a devconfig_t struct representing * the current layout request being processed * hba - dm_descriptor_t handle for an HBA * * OUTPUT: disks - pointer to a list to hold the computed available * disks * avail - pointer to a uint64_t to hold the aggregate * available space on the available disks * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which examines the disks associated with the * input HBA and assembles a list of those that are available. * * Available is defined as being in the usable list, having * unused space and not specifically excluded by the request's * list of unavailable devices. * * The returned list must be passed to dlist_free_items() * to recover the memory allocated to hold each list item. */ int hba_get_avail_disks_and_space( devconfig_t *request, dm_descriptor_t hba, dlist_t **disks, uint64_t *space) { dlist_t *usable_disks = NULL; dlist_t *iter = NULL; int error = 0; *disks = NULL; /* for each usable disk */ error = get_usable_disks(&usable_disks); for (iter = usable_disks; (error == 0) && (iter != NULL); iter = iter->next) { dm_descriptor_t disk = (uintptr_t)iter->obj; boolean_t avail = B_FALSE; dlist_t *hbas = NULL; /* is disk attached to HBA in question? */ error = disk_get_hbas(disk, &hbas); if (error != 0) { continue; } if (dlist_contains(hbas, (void *)(uintptr_t)hba, compare_descriptor_names) == B_TRUE) { /* is disk available? */ error = is_device_avail(disk, request, &avail); if ((error == 0) && (avail == B_TRUE)) { uint64_t disk_space = 0; /* does disk have available space? */ error = disk_get_avail_space(request, disk, &disk_space); if ((error == 0) && (disk_space > 0)) { dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); if (item == NULL) { error = ENOMEM; } else { *disks = dlist_append(item, *disks, AT_HEAD); } *space += disk_space; } } } dlist_free_items(hbas, NULL); } if (error != 0) { dlist_free_items(*disks, NULL); *disks = NULL; } return (error); } /* * FUNCTION: disk_get_avail_space(devconfig_t *request, * dlist_t *disks, uint64_t space) * * INPUT: request - pointer to a devconfig_t struct representing * the current layout request being processed * disks - pointer to a list of disks * space - pointer to a uint64_t to hold the computed available * space * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which iterates the input list of disks and determines * the aggregate amount of available space they represent. * * Only disk slices that are in the usable slice list and not * specifically excluded by the request's list of unavailable * devices will contribute to the aggregate space computation. */ static int disk_get_avail_space( devconfig_t *request, dm_descriptor_t disk, uint64_t *space) { dlist_t *usable_slices = NULL; dlist_t *iter = NULL; int error = 0; *space = 0; /* for each usable slice */ error = get_usable_slices(&usable_slices); for (iter = usable_slices; (error == 0) && (iter != NULL); iter = iter->next) { dm_descriptor_t slice = (uintptr_t)iter->obj; dm_descriptor_t slice_disk; boolean_t avail = B_FALSE; boolean_t reserved = B_FALSE; boolean_t used = B_FALSE; /* is slice on disk in question? */ if (((error = slice_get_disk(slice, &slice_disk)) != 0) || (compare_descriptor_names((void *)(uintptr_t)slice_disk, (void *)(uintptr_t)disk) != 0)) { continue; } /* is slice reserved by an explicit layout request? */ if (((error = is_reserved_slice(slice, &reserved)) != 0) || (reserved == B_TRUE)) { continue; } /* is slice used by a pending layout request? */ if (((error = is_used_slice(slice, &used)) != 0) || (used == B_TRUE)) { continue; } /* is slice available? */ if (((error = is_device_avail(slice, request, &avail)) == 0) && (avail == B_TRUE)) { /* does slice have usable space? */ uint64_t size = 0; if ((error = slice_get_size(slice, &size)) == 0) { *space += size; } } } return (error); } /* * FUNCTION: disks_get_avail_slices(devconfig_t *request, * dlist_t *disks, dlist_t **slices) * * INPUT: request - pointer to a devconfig_t struct representing * the current layout request being processed * disks - pointer to a list of disks * slices - pointer to an output list of disks * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: helper which iterates the input list of disks and builds a * new list which contains disks that are determined to be * available for satisfying the input request. * * A disk must contain at least one slice in the available * slice list as well as have available space in order * to be available. */ int disks_get_avail_slices( devconfig_t *request, dlist_t *disks, dlist_t **slices) { dlist_t *usable_slices = NULL; dlist_t *iter = NULL; int error = 0; *slices = NULL; /* for each usable slice */ error = get_usable_slices(&usable_slices); for (iter = usable_slices; (error == 0) && (iter != NULL); iter = iter->next) { dm_descriptor_t slice = (uintptr_t)iter->obj; dm_descriptor_t disk = (dm_descriptor_t)0; boolean_t avail = B_FALSE; boolean_t reserved = B_FALSE; boolean_t used = B_FALSE; /* is slice on a disk in the input list? */ if (((error = slice_get_disk(slice, &disk)) != 0) || (dlist_contains(disks, (void *)(uintptr_t)disk, compare_descriptor_names) != B_TRUE)) { continue; } /* is slice reserved by an explicit layout request? */ if (((error = is_reserved_slice(slice, &reserved)) != 0) || (reserved == B_TRUE)) { continue; } /* is slice used by a pending layout request? */ if (((error = is_used_slice(slice, &used)) != 0) || (used == B_TRUE)) { continue; } /* is slice available? */ if (((error = is_device_avail(slice, request, &avail)) == 0) && (avail == B_TRUE)) { /* does slice have available space? */ uint64_t size = 0; error = slice_get_size(slice, &size); if ((error == 0) && (size > 0)) { dlist_t *item = dlist_new_item((void *)(uintptr_t)slice); if (item == NULL) { error = ENOMEM; } else { *slices = dlist_append(item, *slices, AT_TAIL); } } } } if (error != 0) { dlist_free_items(*slices, NULL); *slices = NULL; } return (error); } /* * FUNCTION: get_hbas_and_disks_used_by_volumes(dlist_t *volumes, * dlist_t **hbas, dlist_t **disks) * * INPUT: volumes - pointer to a list of devconfig_t volumes * * OUTPUT: hbas - a list of HBAs utilized by the input volumes * disks - a list of disks utilized by the input volumes * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: An aggregate list of HBAs and disks used by the input volumes * is built up by iterating the list of volumes and calling * get_hbas_disks_used_by_volume() to determine the HBAs and disk * used by each volume. * * The returned lists of HBAs and disks may contain duplicates. */ int get_hbas_and_disks_used_by_volumes( dlist_t *volumes, dlist_t **hbas, dlist_t **disks) { dlist_t *iter = NULL; int error = 0; for (iter = volumes; (iter != NULL) && (error == 0); iter = iter->next) { error = get_hbas_and_disks_used_by_volume( (devconfig_t *)iter->obj, hbas, disks); } return (error); } /* * FUNCTION: get_hbas_and_disks_used_by_volume(devconfig_t *volume, * dlist_t **hbas, dlist_t **disks) * * INPUT: volume - pointer to a devconfig_t volume * * OUTPUT: hbas - a list of HBAs updated to include those utilized * by the input volume * disks - a list of disks updated to inlclude those utilized * by the input volume * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: The volume's components are iterated and the disks and HBAs * for each component are determined and appended to the input * lists of HBAs and disks. * * The returned lists of HBAs and disks may contain duplicates. */ int get_hbas_and_disks_used_by_volume( devconfig_t *volume, dlist_t **hbas, dlist_t **disks) { dlist_t *iter = NULL; int error = 0; for (iter = devconfig_get_components(volume); (iter != NULL) && (error == 0); iter = iter->next) { devconfig_t *dev = (devconfig_t *)iter->obj; if (devconfig_isA(dev, TYPE_SLICE)) { dm_descriptor_t disk = NULL; char *name = NULL; /* get disk for component slice */ ((error = devconfig_get_name(dev, &name)) != 0) || (error = get_disk_for_named_slice(name, &disk)); if (error == 0) { dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); if (item == NULL) { error = ENOMEM; } else { *disks = dlist_append(item, *disks, AT_HEAD); } } /* get HBAs for disk */ if (error == 0) { dlist_t *disk_hbas = NULL; if ((error = disk_get_hbas(disk, &disk_hbas)) == 0) { /* the hba list may contain dups, but that's ok */ *hbas = dlist_append(disk_hbas, *hbas, AT_HEAD); } } } else if (devconfig_isA(dev, TYPE_MIRROR)) { /* collect info for submirrors */ dlist_t *iter1; for (iter1 = devconfig_get_components(dev); (iter1 != NULL) && (error == 0); iter1 = iter1->next) { error = get_hbas_and_disks_used_by_volume( (devconfig_t *)iter1->obj, hbas, disks); } } } return (error); } /* * FUNCTION: compare_hba_n_avail_disks(void *obj1, void *obj2) * * INPUT: obj1 - opaque pointer * obj2 - opaque pointer * * RETURNS: int - <0 - if obj1 has fewer available disks than obj2 * 0 - if obj1 has the same # of available disks as obj2 * >0 - if obj1 has more available disks than obj2 * * PURPOSE: dlist_t helper which compares the number of available disks * for two HBAs represented as dm_descriptor_t handles. * * Both input objects are assumed to be dm_descriptor_t handles. * * The number of available disks associated with the HBAs was * computed and saved in select_hbas_with_n_disks(), this * function just checks the saved values. */ static int compare_hba_n_avail_disks( void *obj1, void *obj2) { uint16_t n1 = 0; uint16_t n2 = 0; assert(obj1 != NULL); assert(obj2 != NULL); (void) hba_get_n_avail_disks((uintptr_t)obj1, &n1); (void) hba_get_n_avail_disks((uintptr_t)obj2, &n2); return ((int)n1 - n2); } /* * FUNCTION: is_device_avail(dm_descriptor_t desc, * devconfig_t *request, boolean_t *avail) * * INPUT: desc - a dm_descriptor_t device handle * request - pointer to a devconfig_t struct representing * the current layout request being processed * avail - pointer to a boolean to hold the result * * RETURNS: int - 0 - on success * !0 - otherwise * * PURPOSE: Internal helper which determines if the input device can * be used as a volume component when satisfying the input * request. * * The device is assumed to be a known valid device. * * The function checks if the device passes the request's * available and unavailable device specifications. * * The input device name may be either a DID name or a CTD * name. All name comparisons are done using the CTD name. */ static int is_device_avail( dm_descriptor_t desc, devconfig_t *request, boolean_t *avail) { char *name = NULL; int error = 0; *avail = B_FALSE; if ((error = get_display_name(desc, &name)) == 0) { error = is_named_device_avail(request, name, B_TRUE, avail); } return (error); } /* * FUNCTION: compare_request_to_request_spec_list_request( * void *request, void *list_item) * * INPUT: request - opaque pointer to a devconfig_t * list_item - opaque pointer to a request_spec_list_t * * RETURNS: int - 0 - if request is the same as list_item->request * !0 - otherwise * * PURPOSE: dlist_t helper which compares the input request pointer * to the list_item's request pointer for equality. * * This function is the lookup mechanism for the lists of * cached device_spec_ts representing available/unavailable * devices for a given defaults_t request/defaults struct. * * The defaults_t struct pointer is the lookup key. */ static int compare_request_to_request_spec_list_request( void *request, void *list_item) { request_spec_list_t *entry = (request_spec_list_t *)list_item; assert(request != NULL); assert(entry != NULL); /* compare two devconfig_t pointers, if identical, return 0 */ return ((devconfig_t *)request != entry->request); } /* * FUNCTION: compare_device_spec_specificity(void *spec1, void *spec2) * * INPUT: spec1 - opaque pointer to a device_spec_t * spec2 - opaque pointer to a device_spec_t * * RETURNS: int - <0 - if spec1 is less specific than spec2 * 0 - if spec1 is as specific than spec2 * >0 - if spec1 is more specific than spec2 * * PURPOSE: dlist_t helper which compares the level of specificity * in the two input device_spec_t structs. The one * which specifies more "components" of a cXtXdXsX device * name is considered more specific. */ static int compare_device_spec_specificity( void *spec1, void *spec2) { if (spec1 == NULL || spec2 == NULL) { return (-1); } if ((((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED) && (((device_spec_t *)spec2)->data.ctd->slice == ID_UNSPECIFIED)) { /* spec1 has slice, spec2 does not, spec1 more specific */ return (1); } if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->slice == ID_UNSPECIFIED)) { /* spec2 has slice, spec1 does not, spec2 more specific */ return (-1); } if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED)) { /* both spec1 and spec2 have slice */ return (0); } if ((((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED) && (((device_spec_t *)spec2)->data.ctd->lun == ID_UNSPECIFIED)) { /* spec1 has lun, spec2 does not, spec1 more specific */ return (1); } if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->lun == ID_UNSPECIFIED)) { /* spec2 has lun, spec1 does not, spec2 more specific */ return (-1); } if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED)) { /* both spec1 and spec2 have lun */ return (0); } if ((((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED) && (((device_spec_t *)spec2)->data.ctd->target == ID_UNSPECIFIED)) { /* spec1 has target, spec2 does not, spec1 more specific */ return (1); } if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->target == ID_UNSPECIFIED)) { /* spec2 has target, spec1 does not, spec2 more specific */ return (-1); } if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && (((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED)) { /* both spec1 and spec2 have target */ return (0); } /* both specify just ctrl */ return (0); } /* * FUNCTION: find_request_spec_list_entry(devconfig_t *request) * * INPUT: request - pointer to a devconfig_t struct * * RETURNS: request_spec_list_entry - pointer to a * request_spec_list_entry struct * * PURPOSE: Lookup function which encapsulates the details of locating * the device_spec_list_t cache entry for the input request. */ static request_spec_list_t * find_request_spec_list_entry( devconfig_t *request) { dlist_t *list_item = NULL; request_spec_list_t *entry = NULL; list_item = dlist_find( _request_spec_list_cache, (void *)request, compare_request_to_request_spec_list_request); if (list_item != NULL) { entry = (request_spec_list_t *)list_item->obj; } return (entry); } /* * FUNCTION: add_request_spec_list_entry(devconfig_t *request, * char **avail_device_specs, char **unavail_device_specs, * request_spec_list_entry_t **entry) * * INPUT: entry - pointer to the request_spec_list_entry struct to be * added to the cache. * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which encapsulates the details of adding a * device_spec_list_t cache entry. */ static int add_request_spec_list_entry( request_spec_list_t *entry) { dlist_t *list_item = dlist_new_item((void *)entry); if (list_item == NULL) { return (ENOMEM); } _request_spec_list_cache = dlist_append(list_item, _request_spec_list_cache, AT_HEAD); return (0); } /* * FUNCTION: make_request_spec_list_entry(devconfig_t *request, * char **avail_device_specs, char **unavail_device_specs, * request_spec_list_entry_t **entry) * * INPUT: request - pointer to a devconfig_t struct * avail_device_specs - char * array of user specified available * devices associated with the input request * unavail_device_specs - char * array of user specified * unavailable devices associated with the input * request * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which encapsulates the details of generating a new * the device_spec_list_t cache entry for the input request * and its lists of avail/unavail devices. * * Converts the input arrays of (un)available device names into * equivalent lists of device_spec_t structs. * * Creates a new cache entry, populates it and adds it to the * cache. */ static int make_request_spec_list_entry( devconfig_t *request, char **avail_device_specs, char **unavail_device_specs, request_spec_list_t **entry) { int error = 0; dlist_t *list = NULL; *entry = calloc(1, sizeof (request_spec_list_t)); if (*entry == NULL) { return (ENOMEM); } (*entry)->request = request; /* * map the avail_device_name array into a list of device_spec_t * and save the list as the entry's available list */ error = convert_usernames_to_specs( avail_device_specs, &list); if (error == 0) { (*entry)->avail_specs_list = list; } /* * map the unavail_device_name array into a list of device_spec_t * and save the list as the entry's unavailable list */ list = NULL; error = convert_usernames_to_specs( unavail_device_specs, &list); if (error == 0) { (*entry)->unavail_specs_list = list; } if (error != 0) { /* delete the partial entry */ destroy_request_spec_list_entry((void *)*entry); *entry = NULL; } return (error); } /* * FUNCTION: convert_usernames_to_specs(char **specs, dlist_t **list) * * INPUT: specs - char * array of device CTD names * * OUTPUT: list - pointer to a list of device_spec_t corresponding * to each name in the input array * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which converts the input CTD device names to the * equivalent device_spec_t structs. * * Iterates the input array and converts each CTD name to a * device_spec_t using get_spec_for_name(). */ static int convert_usernames_to_specs( char **specs, dlist_t **list) { int i = 0; int error = 0; /* * For each spec in the array, get the corresponding * device_spec_t and add it to the list. * * Any spec in the array that looks to be a DID name * is first converted to its equivalent CTD name. */ for (i = 0; (specs != NULL) && (specs[i] != NULL) && (error == 0); i++) { device_spec_t *spec = NULL; char *userspec = specs[i]; error = get_spec_for_name(userspec, &spec); if ((error == 0) && (spec != NULL)) { dlist_t *list_item = dlist_new_item((void *)spec); if (spec == NULL) { error = ENOMEM; } else { *list = dlist_insert_ordered (list_item, *list, DESCENDING, compare_device_spec_specificity); } } } if (error != 0) { /* the device_spec_t in the list items are maintained */ /* in a cache elsewhere, so don't free them here. */ dlist_free_items(*list, NULL); *list = NULL; } return (error); } /* * FUNCTION: destroy_request_spec_list_entry(void *entry) * * INPUT: entry - opaque pointer to a request_spec_list_t * * RETURNS: nothing * * PURPOSE: Function which reclaims memory allocated to a * request_spec_list_t. * * Frees memory allocated to the avail_spec_list and * unavail_spec_list. Entries in the list are not freed, * since they are owned by the device_spec cache. */ static void destroy_request_spec_list_entry( void *obj) { request_spec_list_t *entry = (request_spec_list_t *)obj; if (entry != NULL) { /* items in the list are in the spec_cache and will */ /* be cleaned up when it is destroyed. */ dlist_free_items(entry->avail_specs_list, NULL); dlist_free_items(entry->unavail_specs_list, NULL); free(entry); } } /* * FUNCTION: destroy_request_spec_list_cache() * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which destroys all entries in the request_spec_list * cache. */ static int destroy_request_spec_list_cache() { dlist_free_items(_request_spec_list_cache, destroy_request_spec_list_entry); _request_spec_list_cache = NULL; return (0); } /* * FUNCTION: get_request_avail_spec_list(devconfig_t *request, * dlist_t **list) * * INPUT: request - a pointer to a devconfig_t * * OUTPUT: list - pointer to a list of device_spec_t corresponding * to the devices specified as available by the * input request. * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which locates or builds the list of device_spec_t * for the available devices specified in the input request. * * Looks up the input request in the request_spec_list cache. * If there is currently no entry in the cache for the request, * an entry is built and added. * * The entry's list of available device_spec_t is returned. */ static int get_request_avail_spec_list( devconfig_t *request, dlist_t **list) { request_spec_list_t *entry = NULL; int error = 0; if ((entry = find_request_spec_list_entry(request)) == NULL) { /* create cache entry for this request */ error = make_request_spec_list_entry( request, devconfig_get_available(request), devconfig_get_unavailable(request), &entry); if ((error == 0) && (entry != NULL)) { if ((error = add_request_spec_list_entry(entry)) != 0) { destroy_request_spec_list_entry(entry); entry = NULL; } } } if ((error == 0) && (entry != NULL)) { *list = entry->avail_specs_list; } return (error); } /* * FUNCTION: get_request_unavail_spec_list(devconfig_t *request, * dlist_t **list) * * INPUT: request - a pointer to a devconfig_t * * OUTPUT: list - pointer to a list of device_spec_t corresponding * to the devices specified as unavailable by the * input request. * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which locates or builds the list of device_spec_t * for the unavailable devices specified in the input request. * * Looks up the input request in the request_spec_list cache. * If there is currently no entry in the cache for the request, * an entry is built and added. * * The entry's list of unavailable device_spec_t is returned. */ static int get_request_unavail_spec_list( devconfig_t *request, dlist_t **list) { request_spec_list_t *entry = NULL; int error = 0; if ((entry = find_request_spec_list_entry(request)) == NULL) { /* create new entry for this request */ error = make_request_spec_list_entry( request, devconfig_get_available(request), devconfig_get_unavailable(request), &entry); if ((error == 0) && (entry != NULL)) { if ((error = add_request_spec_list_entry(entry)) != 0) { destroy_request_spec_list_entry(entry); entry = NULL; } } } if ((error == 0) && (entry != NULL)) { *list = entry->unavail_specs_list; } return (error); } /* * FUNCTION: get_default_avail_spec_list(defaults_t *defaults, * char *dsname, dlist_t **list) * * INPUT: defaults - a pointer to a defaults_t struct * dsname - the name of the diskset whose defaults should be used * * OUTPUT: list - pointer to a list of device_spec_t corresponding * to the devices specified as available by the * defaults for the named diskset, or the global * defaults for all disksets. * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which locates or builds the list of device_spec_t * for the available devices for the named diskset. * * Locates the defaults for the named diskset, if there are none, * locates the global defaults for all disksets. * * The defaults devconfig_t struct is then used to look up the * the corresponding entry in the request_spec_list cache. * * If there is currently no entry in the cache for the defaults, * an entry is built and added. * * The entry's list of available device_spec_t is returned. */ static int get_default_avail_spec_list( defaults_t *alldefaults, char *dsname, dlist_t **list) { request_spec_list_t *entry = NULL; devconfig_t *defaults = NULL; int error = 0; /* Get diskset defaults, or global if none for diskset */ error = defaults_get_diskset_by_name( alldefaults, dsname, &defaults); if (error != 0) { if (error == ENOENT) { /* to get global defaults, pass a NULL diskset name */ error = defaults_get_diskset_by_name( alldefaults, NULL, &defaults); } if (error != 0) { if (error != ENOENT) { oprintf(OUTPUT_DEBUG, gettext("get defaults for %s returned %d\n"), dsname, error); } else { error = 0; } } } if ((entry = find_request_spec_list_entry(defaults)) == NULL) { /* create new entry for these defaults */ error = make_request_spec_list_entry( defaults, devconfig_get_available(defaults), devconfig_get_unavailable(defaults), &entry); if ((error == 0) && (entry != NULL)) { if ((error = add_request_spec_list_entry(entry)) != 0) { destroy_request_spec_list_entry(entry); entry = NULL; } } } if ((error == 0) && (entry != NULL)) { *list = entry->avail_specs_list; } return (error); } /* * FUNCTION: get_default_unavail_spec_list(defaults_t *defaults, * char *dsname, dlist_t **list) * * INPUT: defaults - a pointer to a defaults_t struct * dsname - the name of the diskset whose defaults should be used * * OUTPUT: list - pointer to a list of device_spec_t corresponding * to the devices specified as unavailable by the * defaults for the named diskset, or the global * defaults for all disksets. * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which locates or builds the list of device_spec_t * for the unavailable devices for the named diskset. * * Locates the defaults for the named diskset, if there are none, * locates the global defaults for all disksets. * * The defaults devconfig_t struct is then used to look up the * the corresponding entry in the request_spec_list cache. * * If there is currently no entry in the cache for the defaults, * an entry is built and added. * * The entry's list of unavailable device_spec_t is returned. */ static int get_default_unavail_spec_list( defaults_t *alldefaults, char *dsname, dlist_t **list) { request_spec_list_t *entry = NULL; devconfig_t *defaults = NULL; int error = 0; /* Get diskset defaults, or global if none for diskset */ error = defaults_get_diskset_by_name( alldefaults, dsname, &defaults); if (error != 0) { if (error == ENOENT) { /* to get global defaults, pass a NULL diskset name */ error = defaults_get_diskset_by_name( alldefaults, NULL, &defaults); } if (error != 0) { if (error != ENOENT) { oprintf(OUTPUT_DEBUG, gettext("get defaults for %s returned %d\n"), dsname, error); } else { error = 0; } } } if ((entry = find_request_spec_list_entry(defaults)) == NULL) { /* create new entry for these defaults */ error = make_request_spec_list_entry( defaults, devconfig_get_available(defaults), devconfig_get_unavailable(defaults), &entry); if ((error == 0) && (entry != NULL)) { if ((error = add_request_spec_list_entry(entry)) != 0) { destroy_request_spec_list_entry(entry); entry = NULL; } } } if ((error == 0) && (entry != NULL)) { *list = entry->unavail_specs_list; } return (error); } /* * FUNCTION: is_named_device_avail(devconfig_t *request, char *device_name, * boolean_t check_aliases, boolean_t *avail) * * INPUT: request - the current request devconfig_t * device_name - char * device name * check_aliases - boolean_t which indicates whether the device's * aliases should be considered by the availability checks. * * OUTPUT: avail - a boolean_t * to hold the result * * RETURNS: int - !0 on error * * avail is set to B_TRUE if the named device is available for * the input request, B_FALSE otherwise. * * PURPOSE: Determine if the named device can be used to satisfy the * input request. * * There are several levels at which device availabiity or * unavailability may be specifed: * * 1. the volume subrequest, * 2. the toplevel (diskset) request, * 3. the diskset-specific defaults * 4. the global defaults * * If the diskset-specific defaults exist, only they are checked. * * The precedence ordering that is enforced: * * 1. if request has an avail list, the name must be in it * and not in the request's unavail list. * 2. if request has an unavail list, the name must not be in it. * 3. if toplevel request has an avail list, the name must be * in it and not in the toplevel request's unavailable * list. * 4. if toplevel request has an unavail list, the name must * not be in it. * 5. if defaults have an avail list, the name must be in it * and not in the defaults unavailable list. * 6. if defaults have an unavail list, the name must not be * in it. */ static int is_named_device_avail( devconfig_t *request, char *device_name, boolean_t check_aliases, boolean_t *avail) { typedef enum check_types { DEVICE_REQUEST = 0, DISKSET_REQUEST, DEFAULTS, N_CHECKS } check_type_t; check_type_t check_type; typedef enum list_types { AVAIL = 0, UNAVAIL, N_LISTS } list_type_t; dlist_t *lists[N_CHECKS][N_LISTS]; boolean_t includes; int error = 0; memset(lists, 0, (N_CHECKS * N_LISTS) * sizeof (dlist_t *)); if (request != NULL) { /* get avail/unavail specs for request */ ((error = get_request_avail_spec_list( request, &lists[DEVICE_REQUEST][AVAIL])) != 0) || (error = get_request_unavail_spec_list( request, &lists[DEVICE_REQUEST][UNAVAIL])); } if ((error == 0) && (_toplevel_request != NULL)) { /* diskset request */ ((error = get_request_avail_spec_list( _toplevel_request, &lists[DISKSET_REQUEST][AVAIL])) != 0) || (error = get_request_unavail_spec_list( _toplevel_request, &lists[DISKSET_REQUEST][UNAVAIL])); } if ((error == 0) && (_defaults != NULL)) { /* and diskset/global defaults */ ((error = get_default_avail_spec_list(_defaults, get_request_diskset(), &lists[DEFAULTS][AVAIL])) != 0) || (error = get_default_unavail_spec_list(_defaults, get_request_diskset(), &lists[DEFAULTS][UNAVAIL])); } if (error != 0) { return (error); } *avail = B_TRUE; for (check_type = DEVICE_REQUEST; (check_type < N_CHECKS) && (error == 0); check_type++) { if (lists[check_type][AVAIL] != NULL) { /* does avail spec list include named device? */ if ((error = avail_list_includes_device_name( lists[check_type][AVAIL], device_name, check_aliases, &includes)) == 0) { if (includes != B_TRUE) { *avail = B_FALSE; } if ((includes == B_TRUE) && (lists[check_type][UNAVAIL] != NULL)) { /* device is available, is it in the unavail list? */ if ((error = unavail_list_includes_device_name( lists[check_type][UNAVAIL], device_name, check_aliases, &includes)) == 0) { if (includes == B_TRUE) { *avail = B_FALSE; } } } } /* lists at this level checked, skip remainder */ break; } else if (lists[check_type][UNAVAIL] != NULL) { /* does unavail spec list include named device? */ if ((error = unavail_list_includes_device_name( lists[check_type][UNAVAIL], device_name, check_aliases, &includes)) == 0) { if (includes == B_TRUE) { *avail = B_FALSE; } } /* list at this level checked, skip remainder */ break; } } return (error); } /* * FUNCTION: avail_list_includes_device_name(dlist_t *list, * char *device_name, boolean_t check_aliases, * boolean_t *includes) * * INPUT: list - a dlist_t list of available device_spec_t * device_name - a char * device CTD name * check_aliases - boolean_t which indicates if the device's * aliases should be considered in the availability * checking. * * OUTPUT: includes - B_TRUE - if named device is "included" by any * specification in the input list * B_FALSE - otherwise * * RETURNS: int - 0 on success * - !0 otherwise * * PURPOSE: Helper used by is_named_device_avail that determines * if the input list of device specifications "includes" * a specific device. * * Iterates the elements of the input array and searches * for a match using spec_includes_device_name(). */ static int avail_list_includes_device_name( dlist_t *list, char *device_name, boolean_t check_aliases, boolean_t *includes) { dlist_t *iter = NULL; int error = 0; *includes = B_FALSE; for (iter = list; (*includes == B_FALSE) && (iter != NULL) && (error == 0); iter = iter->next) { device_spec_t *spec = (device_spec_t *)iter->obj; error = spec_includes_device_name(spec, device_name, check_aliases, includes); } return (0); } /* * FUNCTION: unavail_list_includes_device_name(dlist_t *list, * char *device_name, boolean_t check_aliases, * boolean_t *includes) * * INPUT: list - a dlist_t list of unavailable device_spec_t * device_name - a char * device CTD name * check_aliases - boolean_t which indicates if the device's * aliases should be considered in the availability * checking. * * OUTPUT: includes - B_TRUE - if named device is "included" by any * specification in the input list * B_FALSE - otherwise * * RETURNS: int - 0 on success * - !0 otherwise * * PURPOSE: Helper used by is_named_device_avail that determines * if the input list of device specifications "includes" * a specific device. * * Iterates the elements of the input array and searches * for a match using spec_includes_device_name_or_alias(). */ static int unavail_list_includes_device_name( dlist_t *list, char *device_name, boolean_t check_aliases, boolean_t *includes) { dlist_t *iter = NULL; int error = 0; device_spec_t *unavail_spec; boolean_t check_for_alternate_hba = B_FALSE; *includes = B_FALSE; /* * the specs in the list are in descending order of specificity. * so a more exact spec will rule the device out before a less * exact spec. * * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the * input device name is "c3t0d0s0", it will match "c3t0d0" * before "c3". * * This is important for the multi-path alias checking below. * If the input device name is ruled out by a non-controller * specification, it is really unavailable. */ for (iter = list; (*includes == B_FALSE) && (iter != NULL); iter = iter->next) { unavail_spec = (device_spec_t *)iter->obj; error = spec_includes_device_name( unavail_spec, device_name, check_aliases, includes); } if ((error == 0) && (*includes == B_TRUE)) { /* matched an unavailable spec, was it a controller/HBA? */ oprintf(OUTPUT_DEBUG, "device \"%s\" is unavailable, " "it matched \"c(%d)t(%d)d(%d)s(%d)\"\n", device_name, unavail_spec->data.ctd->ctrl, unavail_spec->data.ctd->target, unavail_spec->data.ctd->lun, unavail_spec->data.ctd->slice); if ((unavail_spec->data.ctd->ctrl != ID_UNSPECIFIED) && (unavail_spec->data.ctd->target == ID_UNSPECIFIED) && (unavail_spec->data.ctd->lun == ID_UNSPECIFIED) && (unavail_spec->data.ctd->slice == ID_UNSPECIFIED)) { /* * Need to see if the named device is a disk or slice, * and if so check to see if the it is multipathed * and possibly accessible thru another controller/HBA. */ check_for_alternate_hba = B_TRUE; } } if ((error == 0) && (check_for_alternate_hba == B_TRUE)) { dm_descriptor_t slice = (dm_descriptor_t)0; dm_descriptor_t disk = (dm_descriptor_t)0; ((error = slice_get_by_name(device_name, &slice)) != 0) || (error = disk_get_by_name(device_name, &disk)); if (error != 0) { return (error); } /* if it is a slice, get its disk */ if ((error == 0) && (slice != (dm_descriptor_t)0)) { error = slice_get_disk(slice, &disk); } if ((error == 0) && (disk != (dm_descriptor_t)0)) { /* see if all the disk's HBAs are unavailable */ dlist_t *hbas = NULL; dlist_t *iter = NULL; error = disk_get_hbas(disk, &hbas); if (hbas != NULL) { oprintf(OUTPUT_DEBUG, gettext(" checking alternate paths for %s\n"), device_name); } else { oprintf(OUTPUT_DEBUG, gettext(" no alternate paths for %s\n"), device_name); } /* for each of the disk's HBAs */ for (iter = hbas; (iter != NULL) && (*includes == B_TRUE) && (error == 0); iter = iter->next) { dm_descriptor_t hba = (uintptr_t)iter->obj; device_spec_t *hbaspec; char *hbaname = NULL; dlist_t *iter2 = NULL; *includes = B_FALSE; ((error = get_display_name(hba, &hbaname)) != 0) || (error = get_spec_for_name(hbaname, &hbaspec)); /* is HBA unavailable? */ for (iter2 = list; (iter2 != NULL) && (error == 0) && (*includes == B_FALSE); iter2 = iter2->next) { device_spec_t *spec = (device_spec_t *)iter2->obj; *includes = spec_includes_device(spec, hbaspec); } } dlist_free_items(hbas, NULL); /* if *includes==B_TRUE here, all HBAs are unavailable */ } } return (error); } /* * FUNCTION: spec_includes_device_name(device_spec_t *spec, * char *device_name, boolean_t check_aliases, * boolean_t *includes) * * INPUT: spec - a device_spec_t CTD specification. * device_name - a char * device CTD name * check_aliases - boolean_t which indicates if the device's * aliases should be considered in the checking. * * OUTPUT: includes - B_TRUE - if device is "included" by the input * specification * B_FALSE - otherwise * * RETURNS: int - 0 on success * - !0 otherwise * * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that * determines if the input device specification "includes" * the named device. * * If check_aliases is true and the named device is a slice or * a disk drive, its multi-pathed aliases are also checked * against the spec. */ static int spec_includes_device_name( device_spec_t *spec, char *device_name, boolean_t check_aliases, boolean_t *includes) { device_spec_t *device_spec; int error = 0; error = get_spec_for_name(device_name, &device_spec); if (error == 0) { *includes = spec_includes_device(spec, device_spec); if ((*includes == B_FALSE) && (check_aliases == B_TRUE)) { /* spec doesn't include name, check aliases */ dm_descriptor_t device = (dm_descriptor_t)0; dlist_t *aliases = NULL; /* only slices and disks have aliases */ error = slice_get_by_name(device_name, &device); if (device != (dm_descriptor_t)0) { error = get_aliases(device, &aliases); } else if (error == 0) { error = disk_get_by_name(device_name, &device); if (device != (dm_descriptor_t)0) { error = get_aliases(device, &aliases); } } if ((error == 0) && (aliases != NULL)) { dlist_t *iter; for (iter = aliases; (iter != NULL) && (*includes == B_FALSE) && (error == 0); iter = iter->next) { char *alias = (char *)iter->obj; device_spec_t *alias_spec; error = get_spec_for_name(alias, &alias_spec); if (error == 0) { /* does spec include alias? */ *includes = spec_includes_device(spec, alias_spec); } } } dlist_free_items(aliases, free); } } return (error); } /* * FUNCTION: destroy_device_spec(device_spec_t *spec) * * INPUT: spec - pointer to a device_spec_t * * RETURNS: nothing * * PURPOSE: Function which reclaims memory allocated to a device_spec_t. * * Frees memory allocated to hold the specific data in the spec. */ static void destroy_device_spec( device_spec_t *spec) { if (spec != NULL) { if (spec->type == SPEC_TYPE_CTD) { free(spec->data.ctd); } else if (spec->type == SPEC_TYPE_RAW) { free(spec->data.raw); } free(spec); } } /* * FUNCTION: create_device_spec(char *name, device_spec_t **spec); * * INPUT: name - pointer to a char* device name * * OUTPUT: spec - pointer to a device_spec_t to hold the result * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: Function which creates a device_spec_t for the input * device name. * */ static int create_device_spec( char *name, device_spec_t **spec) { int error = 0; /* allocate the device spec and try various parsing schemes */ *spec = (device_spec_t *)calloc(1, sizeof (device_spec_t)); if (*spec == NULL) { error = ENOMEM; } else { if (((error = create_device_ctd_spec(name, spec)) != 0) && (error != ENOMEM)) { /* CTD failed, try other parsing schemes */ error = create_device_raw_spec(name, spec); } } return (error); } /* * FUNCTION: create_device_ctd_spec(char *name, device_spec_t **spec); * * INPUT: name - pointer to a char* device name * * OUTPUT: spec - pointer to a device_spec_t updated with the parsed * CTD spec, if successful * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: Function which atttempts to parse the input device name into * cXtXdXsX component ids. The ids are the integer values of each * specified segment of the input name. * * If the name doesn't contain a segment, the id is set to * ID_UNSPECIFIED. * * The input name must be well-formed. * * These are the acceptable forms: * * cXtXdXsX * cXtXdX * cXtX * cXdXsX * cXdX * cX */ static int create_device_ctd_spec( char *name, device_spec_t **spec) { uint_t ctrl; uint_t target; uint_t lun; uint_t slice; uint_t nscan; uint_t nchars; char *device_str; char *target_str; char *ctd_str; char *t_ptr; char *d_ptr; char *s_ptr; boolean_t is_ide = B_FALSE; boolean_t got_slice = B_FALSE; boolean_t got_lun = B_FALSE; boolean_t got_target = B_FALSE; boolean_t got_ctrl = B_FALSE; int error = 0; ctd_str = strdup(name); if (ctd_str == NULL) { return (ENOMEM); } /* trim any leading path (/dev/dsk/cXtXdXsX) */ if ((device_str = strrchr(ctd_str, '/')) != NULL) { ++device_str; } else { device_str = ctd_str; } /* find each segment start position */ t_ptr = strrchr(device_str, 't'); d_ptr = strrchr(device_str, 'd'); s_ptr = strrchr(device_str, 's'); /* * scan ids from each existing segment working backwards * so as to leave the device_str in the correct state * for the next expected segment */ if (s_ptr != NULL) { /* found 's', try to get slice */ nchars = strlen(s_ptr); if ((sscanf(s_ptr, "s%u%n", &slice, &nscan) != 1) || (nscan != nchars)) { error = -1; oprintf(OUTPUT_DEBUG, gettext("no slice component in device " "name \"%s\".\n"), name); } else { got_slice = B_TRUE; *s_ptr = '\0'; } } if ((error == 0) && (d_ptr != NULL)) { /* found 'd', try to get disk/lun */ nchars = strlen(d_ptr); if ((sscanf(d_ptr, "d%u%n", &lun, &nscan) != 1) || (nscan != nchars)) { error = -1; oprintf(OUTPUT_DEBUG, gettext("no disk/lun component " "in device name \"%s\".\n"), name); } else { got_lun = B_TRUE; *d_ptr = '\0'; } } if ((error == 0) && (t_ptr != NULL)) { /* found 't', try to get target, it may be a hex WWN id */ /* skip leading 't' and add two for the 'OX' */ nchars = strlen(t_ptr + 1) + 2; if ((target_str = (char *)malloc(nchars+1)) == NULL) { error = ENOMEM; } else { strcpy(target_str, "0X"); strcpy(target_str+2, t_ptr + 1); target_str[nchars] = '\0'; if ((sscanf(target_str, "%x%n", &target, &nscan) != 1) || (nscan != nchars)) { error = -1; oprintf(OUTPUT_DEBUG, gettext("no target/WWN component " "in device name \"%s\".\n"), name); } else { got_target = B_TRUE; *t_ptr = '\0'; } free(target_str); } } else { is_ide = B_TRUE; } if ((error == 0) && (device_str != NULL)) { /* get controller/hba/channel */ nchars = strlen(device_str); if ((sscanf(device_str, "c%u%n", &ctrl, &nscan) != 1) || (nscan != nchars)) { error = -1; oprintf(OUTPUT_DEBUG, gettext("no channel/HBA component " "in device name \"%s\".\n"), name); } else { got_ctrl = B_TRUE; } } free(ctd_str); if (error == 0) { /* allocate the ctd_spec_t struct and store the ids */ (*spec)->type = SPEC_TYPE_CTD; (*spec)->data.ctd = (ctd_spec_t *)calloc(1, sizeof (ctd_spec_t)); if ((*spec)->data.ctd == NULL) { error = ENOMEM; } (*spec)->data.ctd->slice = ID_UNSPECIFIED; (*spec)->data.ctd->lun = ID_UNSPECIFIED; (*spec)->data.ctd->target = ID_UNSPECIFIED; (*spec)->data.ctd->ctrl = ID_UNSPECIFIED; if (got_slice == B_TRUE) { (*spec)->data.ctd->slice = slice; } if (got_lun == B_TRUE) { (*spec)->data.ctd->lun = lun; } if (got_target == B_TRUE) { (*spec)->data.ctd->target = target; } if (got_ctrl == B_TRUE) { (*spec)->data.ctd->ctrl = ctrl; } (*spec)->data.ctd->is_ide = is_ide; } return (error); } /* * FUNCTION: create_device_raw_spec(char *name, device_spec_t **spec); * * INPUT: name - pointer to a char* device name * * OUTPUT: spec - pointer to a device_spec_t updated with the raw spec * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: Function which creates a "raw" spec for the input name. * * This is a last resort if all other spec parsing schemes failed, * the "raw" spec is just the input device name. */ static int create_device_raw_spec( char *name, device_spec_t **spec) { int error = 0; char *ctd_str = strdup(name); if (ctd_str == NULL) { return (ENOMEM); } (*spec)->type = SPEC_TYPE_RAW; (*spec)->data.raw = ctd_str; oprintf(OUTPUT_DEBUG, gettext("made raw device spec for \"%s\"\n"), ctd_str); return (error); } /* * FUNCTION: get_spec_for_name(char *name, device_spec_t **id); * * INPUT: name - pointer to a char* device name * * OUTPUT: id - pointer to a device_spec_t to hold the result * * RETURNS: int - 0 on success * !0 otherwise * * PURPOSE: Function which finds the device_spec_t that already * exists for the input name or creates it. * * The returned struct should not be freed, it is maintained * in a cache that will be purged when the layout process * is complete. */ int get_spec_for_name( char *name, device_spec_t **id) { dlist_t *item; int error = 0; item = dlist_find(_spec_cache, (void *)name, compare_name_to_spec_cache_name); if (item == NULL) { if ((error = create_device_spec(name, id)) == 0) { spec_cache_t *entry = (spec_cache_t *) calloc(1, sizeof (spec_cache_t)); if (entry == NULL) { destroy_device_spec(*id); error = ENOMEM; } else { char *dup = strdup(name); if (dup == NULL) { free(entry); destroy_device_spec(*id); *id = NULL; error = ENOMEM; } else { entry->name = dup; entry->device_spec = *id; } if (error == 0) { dlist_t *item = dlist_new_item((void *)entry); if (item == NULL) { free(entry); destroy_device_spec(*id); *id = NULL; error = ENOMEM; } else { _spec_cache = dlist_append(item, _spec_cache, AT_HEAD); } } } } } else { *id = ((spec_cache_t *)item->obj)->device_spec; } return (error); } /* * FUNCTION: spec_includes_device(device_spec_t *spec, * device_spec_t *device) * * INPUT: spec - pointer to a device_spec struct * device - pointer to a device_spec struct * * RETURNS: boolean_t - B_TRUE if the device is included in the spec * B_FALSE otherwise * * PURPOSE: Function which determines if the input device matches the * input spec. * * If both specs are of the same type, the appropriate * comparison function is called. * * If the two specs are of different types, no comparison * is done and B_FALSE is returned. */ boolean_t spec_includes_device( device_spec_t *spec, device_spec_t *device) { if ((spec->type == SPEC_TYPE_CTD) && (device->type == SPEC_TYPE_CTD)) { return (ctd_spec_includes_device(spec, device)); } else if ((spec->type == SPEC_TYPE_RAW) && (device->type == SPEC_TYPE_RAW)) { return (raw_spec_includes_device(spec, device)); } return (B_FALSE); } /* * FUNCTION: ctd_spec_includes_device(device_spec_t *spec, * device_spec_t *device) * * INPUT: spec - pointer to a device_spec struct * device - pointer to a device_spec struct * * RETURNS: boolean_t - B_TRUE if the device is included in the spec * B_FALSE otherwise * * PURPOSE: Function which determines if the input CTD device spec * matches the input CTD spec. * * The device_spec_t structs contain component "ids" for * both the specification and the device. * * The device must match each of the ids in the spec that * are specified. * * spec devices matched * -------------------------------------------------------- * cX cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX * cXtX cXtX, cXtXdX, cXtXdXsX * cXtXdX cXtXdX, cXtXdXsX * cXtXdXsX cXtXdXsX * cXdX cXdX, cXdXsX * cXdXsX cXdXsX */ static boolean_t ctd_spec_includes_device( device_spec_t *spec, device_spec_t *device) { boolean_t match = B_FALSE; if (spec->data.ctd->is_ide) { /* valid IDE names are cX, cXdX, cXdXsX, no target */ if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && (spec->data.ctd->lun != ID_UNSPECIFIED) && (spec->data.ctd->slice != ID_UNSPECIFIED)) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && (spec->data.ctd->lun == device->data.ctd->lun) && (spec->data.ctd->slice == device->data.ctd->slice); } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && (spec->data.ctd->lun != ID_UNSPECIFIED)) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && (spec->data.ctd->lun == device->data.ctd->lun); } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); } } else { /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */ if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && (spec->data.ctd->target != ID_UNSPECIFIED) && (spec->data.ctd->lun != ID_UNSPECIFIED) && (spec->data.ctd->slice != ID_UNSPECIFIED)) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && (spec->data.ctd->target == device->data.ctd->target) && (spec->data.ctd->lun == device->data.ctd->lun) && (spec->data.ctd->slice == device->data.ctd->slice); } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && (spec->data.ctd->target != ID_UNSPECIFIED) && (spec->data.ctd->lun != ID_UNSPECIFIED)) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && (spec->data.ctd->target == device->data.ctd->target) && (spec->data.ctd->lun == device->data.ctd->lun); } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && (spec->data.ctd->target != ID_UNSPECIFIED)) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && (spec->data.ctd->target == device->data.ctd->target); } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); } } oprintf(OUTPUT_DEBUG, gettext("spec: c(%d) t(%d) d(%d) s(%d) " "%s: c(%d) t(%d) d(%d) s(%d)\n"), spec->data.ctd->ctrl, spec->data.ctd->target, spec->data.ctd->lun, spec->data.ctd->slice, (match ? gettext("includes") : gettext("does not include")), device->data.ctd->ctrl, device->data.ctd->target, device->data.ctd->lun, device->data.ctd->slice); return (match); } /* * FUNCTION: raw_spec_includes_device(device_spec_t *spec, * device_spec_t *device) * * INPUT: spec - pointer to a device_spec struct * device - pointer to a device_spec struct * * RETURNS: boolean_t - B_TRUE if the device is included in the spec * B_FALSE otherwise * * PURPOSE: Function which determines if the input raw device spec * matches the input spec. * * The device_spec_t raw elements are checked. * * If the spec's raw device name is exactly contained at the * beginning of the device spec's raw name, then the function * evaluates to true. */ static boolean_t raw_spec_includes_device( device_spec_t *spec, device_spec_t *device) { return (strncasecmp(spec->data.raw, device->data.raw, strlen(spec->data.raw)) == 0); } /* * FUNCTION: compare_name_to_spec_cache_name(void *name, void *list_item) * * INPUT: name - opaque pointer to a char * device name * list_item - opaque pointer to a spec_cache_t entry * * RETURNS: int - 0 - if request is the same as list_item->request * !0 - otherwise * * PURPOSE: dlist_t helper which compares the input device name * to the list_item's device name for equality. * * This function is the lookup mechanism for the device_spec * associated with the name. */ static int compare_name_to_spec_cache_name( void *name, void *list_item) { spec_cache_t *entry = (spec_cache_t *)list_item; assert(name != NULL); assert(entry != NULL); return (string_case_compare((char *)name, entry->name)); } /* * FUNCTION: destroy_spec_cache_entry(void *entry) * * INPUT: entry - opaque pointer to a spec_cache_t * * RETURNS: nothing * * PURPOSE: Function which reclaims memory allocated to a * spec_cache_t entry. * * Frees memory allocated to hold the CTD name and the * corresponding device_spec_t. */ static void destroy_spec_cache_entry( void *obj) { spec_cache_t *entry = (spec_cache_t *)obj; if (entry != NULL) { free(entry->name); destroy_device_spec(entry->device_spec); free(entry); } } /* * FUNCTION: destroy_spec_cache() * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Function which destroys all entries in the device_spec * cache. */ static int destroy_spec_cache() { dlist_free_items(_spec_cache, destroy_spec_cache_entry); _spec_cache = NULL; return (0); } /* * FUNCTION: get_device_access_name(devconfig_t *request, * dm_descriptor_t desc, char **name) * * INPUT: request - a devconfig_t request * desc - a dm_descriptor_t device handle * * OUTPUT: name - a char * pointer to hold the preferred name * * RETURNS: int - 0 - if request is the same as list_item->request * !0 - otherwise * * PURPOSE: Utility function to determine which of the possible device * names should be used to access a known available device. * * Devices handled are slices and disks. * * If the input device is a multipathed disk or slice, it * can have several possible names. Determine which of the * names should be used based on the input request's available * or unavailable device specifications. * */ int get_device_access_name( devconfig_t *request, dm_descriptor_t desc, char **name) { int error = 0; boolean_t avail = B_FALSE; dlist_t *aliases = NULL; assert(desc != (dm_descriptor_t)0); *name = NULL; if ((error = get_display_name(desc, name)) != 0) { return (error); } if (is_did_name(*name) == B_TRUE) { oprintf(OUTPUT_DEBUG, gettext("device DID name %s is preferred\n"), *name); return (0); } error = is_named_device_avail(request, *name, B_FALSE, &avail); if (error != 0) { return (error); } if (avail == B_TRUE) { oprintf(OUTPUT_DEBUG, gettext("device name %s is accessible\n"), *name); return (0); } /* search aliases for an 'available' name, prefer DID names */ if ((error = get_aliases(desc, &aliases)) == 0) { dlist_t *iter = aliases; char *availname = NULL; char *didname = NULL; for (; (iter != NULL) && (error == 0); iter = iter->next) { char *alias = (char *)iter->obj; error = is_named_device_avail(request, alias, B_FALSE, &avail); if ((error == 0) && (avail == B_TRUE)) { oprintf(OUTPUT_DEBUG, gettext("device alias %s is accessible for %s\n"), alias, *name); availname = alias; if (is_did_name(availname) == B_TRUE) { didname = alias; break; } } } if (error == 0) { if (didname != NULL) { *name = didname; } else if (availname != NULL) { *name = availname; } } } return (error); }