layout_slice.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/param.h>
#include <meta.h>
#include "volume_string.h"
#include "volume_devconfig.h"
#include "volume_error.h"
#include "volume_dlist.h"
#include "volume_output.h"
#include "layout_device_cache.h"
#include "layout_device_util.h"
#include "layout_discovery.h"
#include "layout_dlist_util.h"
#include "layout_messages.h"
#include "layout_request.h"
#include "layout_slice.h"
#define _LAYOUT_SLICE_C
static int pick_from_best_hba_and_disk(
dlist_t *list,
dlist_t *used,
dm_descriptor_t *chosen);
static int slice_has_same_disk_geom(
dm_descriptor_t slice,
dlist_t *used,
boolean_t *bool);
static int slice_on_unique_disk(
dm_descriptor_t slice,
dlist_t *used,
dlist_t *othervols,
boolean_t *bool);
static int slice_on_unique_hba(
dm_descriptor_t slice,
dlist_t *used,
dlist_t *othervols,
boolean_t *bool);
static int slice_on_similar_bus(
dm_descriptor_t slice,
dlist_t *used,
boolean_t *bool);
static int slice_has_n_paths(
dm_descriptor_t slice,
uint16_t npaths,
boolean_t *bool);
static int compare_modslice_names(
void *obj1,
void *obj2);
static int compare_string_to_modslice_name(
void *str,
void *modslice);
static int create_new_slice(
dm_descriptor_t oslice,
uint64_t nbytes,
boolean_t add_extra_cyl,
devconfig_t **nslice);
static int create_modified_slice(
dm_descriptor_t oslice,
char *oname,
uint32_t oindex,
uint64_t ostart,
uint64_t osize,
uint64_t bps,
char *nname,
uint32_t nindex,
uint64_t nsize,
devconfig_t **nslice);
/*
* list to track resized slices
*/
static dlist_t *_modified_slices = NULL;
/*
* struct to track used slices and their disks...
*/
typedef struct {
char *slicename;
dm_descriptor_t disk;
} usedslice_t;
/*
* list to of usedslice_t to track slices that have been
* used for any reason.
*/
static dlist_t *_used_slices = NULL;
static int add_used_slice_list_entry(char *slicename, dm_descriptor_t disk);
static int compare_usedslice_name_to_string(void *obj1, void *obj2);
static void free_used_slice(void *obj);
/*
* list of slices reserved to be used for explicit
* volume requests
*/
static dlist_t *_rsvd_slices = NULL;
/*
* list of slices needing to be removed (zeroed out) prior to
* applying any metassist modifications to the system.
*/
static dlist_t *_rmvd_slices = NULL;
/*
* FUNCTION: choose_slice(
* uint64_t nbytes,
* uint16_t npaths,
* dlist_t *slices,
* dlist_t *used,
* dlist_t *used_hbas,
* dlist_t *used_disks,
* boolean_t unused_disk,
* boolean_t nbytes_is_min,
* boolean_t add_extra_cyl,
* devconfig_t **chosen)
*
* INPUT: nbytes - required size
* npaths - minimum required data paths
* *slices - slices from which to choose
* *used - slices used by the volume under construction
* *used_hbas - hbas used by other volumes relevant to
* the volume under construction
* *used_disks - disks used by other volumes relevant to
* the volume under construction
* unused_disk - if true, the chosen slice must be from an
* unused disk
* nbytes_is_min - if true, the chosen slice may be larger than
* nbytes.
* add_extra_cyl - passed to create_new_slice, see comment there.
* **chosen - pointer to hold the chosen slice
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Choosen a slice from the list of those available.
*
* Of those available, choose in order of preference:
*
* - one on a unique HBA and disk that is of the exact size
* - one on a unique HBA and disk that is of sufficient size
* - one on unique HBA that is of the exact size
* - one on unique HBA that is of sufficient size
* - one on unique disk that is of the exact size
* - one on unique disk that is of sufficient size
* - one on any HBA that is of exact size
* - one on any HBA that is of sufficient size
* - one on a unique HBA that is the largest size
* - one on a unique disk that is the largest size
* - one on any HBA that is the largest size
*
* The function scans the available slices and builds lists of
* those meeting the criteria above. After the scan is complete,
* the lists are examined in order, the first non-empty list is
* chosen. If there are several possibilities in the chosen list,
* see if it is possible select the slice from the least used HBA
* and/or disk.
*
* If nbytes_is_min is true, the returned slice will be
* at least nbytes in capacity.
*
* If unused_disk is true, the returned slice will be from
* a disk with no other known uses.
*/
int
choose_slice(
uint64_t nbytes,
uint16_t npaths,
dlist_t *slices,
dlist_t *used,
dlist_t *used_hbas,
dlist_t *used_disks,
boolean_t unused_disk,
boolean_t nbytes_is_min,
boolean_t add_extra_cyl,
devconfig_t **chosen)
{
dlist_t *iter = NULL;
dm_descriptor_t slice = NULL;
boolean_t resize = B_FALSE;
boolean_t verbose = (get_max_verbosity() == OUTPUT_VERBOSE);
int error = 0;
/*
* indexes into the list array:
* i -> unique controller 0 = yes, 1 = no
* j -> same bus type 0 = yes, 1 = no
* k -> unique disk 0 = yes, 1 = no
* l -> same disk geom 0 = yes, 1 = no
* m -> size 0 == exact, 1 = larger, 2 = any
*/
int i, j, k, l, m;
dlist_t *list[2][2][2][2][3];
/* output string arrays for each array dimension and index */
char *uniqhba[2];
char *samebus[2];
char *uniqdisk[2];
char *samegeom[2];
char *sizes[3];
/* other output strings */
char *look_msg = NULL;
char *npaths_msg = NULL;
char *samegeom_msg = NULL;
char *samebus_msg = NULL;
char *uniqhba_msg = NULL;
char *uniqdisk_msg = NULL;
char *exact_msg = NULL;
char *larger_msg = NULL;
char *smaller_msg = NULL;
char *insuff_paths = NULL;
char *too_small = NULL;
char *useddisk_msg = NULL;
if (verbose == B_TRUE) {
/* only initialize the output strings if needed */
/* BEGIN CSTYLED */
look_msg = gettext(
"\tlooking at slice: %s (%s)\n");
npaths_msg = gettext(
"\t has the requested number of data paths (%d)\n");
samegeom_msg = gettext(
"\t has the same disk geometry relative to used slices\n");
samebus_msg = gettext(
"\t on a similar I/O bus/HBA relative to used slices\n");
uniqhba_msg = gettext(
"\t on a unique HBA relative to used slices\n");
uniqdisk_msg = gettext(
"\t on a unique disk relative to used slices\n");
exact_msg = gettext(
"\t the exact size necessary\n");
larger_msg = gettext(
"\t larger than necessary\n");
smaller_msg = gettext(
"\t smaller than necessary\n");
insuff_paths = gettext(
"\t rejected: not enough paths (%d requested)\n");
too_small = gettext(
"\t rejected: too small\n");
useddisk_msg = gettext(
"\t rejected: on a disk with other volume component(s)\n");
uniqhba[0] = gettext("unique HBA");
uniqhba[1] = gettext("non unique HBA");
samebus[0] = gettext("same bus type");
samebus[1] = gettext("different bus type");
uniqdisk[0] = gettext("unique disk");
uniqdisk[1] = gettext("non unique disk");
samegeom[0] = gettext("same geometry");
samegeom[1] = gettext("different geometry");
sizes[0] = gettext("an exact size slice");
sizes[1] = gettext("a larger slice");
sizes[2] = gettext("a smaller slice");
/* END CSTYLED */
}
/* init list array pointers */
(void) memset(list, 0, 2*2*2*2*3 * sizeof (dlist_t *));
for (iter = slices;
(iter != NULL) && (error == 0); iter = iter->next) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
uint64_t snbytes = 0;
boolean_t uniqdisk = B_FALSE;
boolean_t uniqhba = B_FALSE;
boolean_t samegeom = B_FALSE;
boolean_t samebus = B_FALSE;
boolean_t paths = B_FALSE;
dlist_t *item = NULL;
((error = slice_get_size(slice, &snbytes)) != 0) ||
(error = slice_has_n_paths(slice, npaths, &paths)) ||
(error = slice_on_unique_hba(slice, used, used_hbas, &uniqhba)) ||
(error = slice_on_unique_disk(slice, used, used_disks,
&uniqdisk)) ||
(error = slice_on_similar_bus(slice, used, &samebus)) ||
(error = slice_has_same_disk_geom(slice, used, &samegeom));
if (error != 0) {
continue;
}
if (verbose == B_TRUE) {
char *sname = NULL;
char *sizestr = NULL;
(void) get_display_name(slice, &sname);
if (bytes_to_sizestr(snbytes, &sizestr,
universal_units, B_FALSE) == 0) {
oprintf(OUTPUT_VERBOSE, look_msg, sname, sizestr);
free(sizestr);
}
}
if (npaths > 1) {
if (paths && verbose) {
/* specifically asked for more paths, ... */
oprintf(OUTPUT_VERBOSE, npaths_msg);
}
} else if (npaths == 1) {
/* every disk has at least 1 path */
paths = B_TRUE;
}
if (verbose == B_TRUE) {
if (uniqhba) {
oprintf(OUTPUT_VERBOSE, uniqhba_msg);
}
if (uniqdisk) {
oprintf(OUTPUT_VERBOSE, uniqdisk_msg);
}
if (used != NULL) {
if (samebus) {
oprintf(OUTPUT_VERBOSE, samebus_msg);
}
if (samegeom) {
oprintf(OUTPUT_VERBOSE, samegeom_msg);
}
}
if (snbytes > nbytes) {
oprintf(OUTPUT_VERBOSE, larger_msg);
} else if (snbytes == nbytes) {
oprintf(OUTPUT_VERBOSE, exact_msg);
} else {
oprintf(OUTPUT_VERBOSE, smaller_msg);
}
}
/* filter slices not meeting minimum criteria */
if (nbytes_is_min && (snbytes < nbytes)) {
/* not large enough */
if (verbose == B_TRUE) {
oprintf(OUTPUT_VERBOSE, too_small);
}
continue;
}
if (paths == B_FALSE) {
/* not connected thru enough paths */
if (verbose == B_TRUE) {
oprintf(OUTPUT_VERBOSE, insuff_paths, npaths);
}
continue;
}
if (uniqdisk != B_TRUE && unused_disk == TRUE) {
/* not on a unique disk */
if (verbose == B_TRUE) {
oprintf(OUTPUT_VERBOSE, useddisk_msg);
}
continue;
}
/* map slice properties into array indices */
i = (uniqhba ? 0 : 1);
j = (samebus ? 0 : 1);
k = (uniqdisk ? 0 : 1);
l = (samegeom ? 0 : 1);
m = (snbytes == nbytes ? 0 : (snbytes > nbytes ? 1 : 2));
/*
* insert slice into the list array using derived indices.
* NB: lists of slices larger than necessary are kept in
* ascending order (results in best fit, not worst fit)
*/
if ((item = dlist_new_item((void*)slice)) == NULL) {
error = ENOMEM;
} else {
list[i][j][k][l][m] =
dlist_insert_ordered(
item,
list[i][j][k][l][m],
(m == 1 ? ASCENDING : DESCENDING),
compare_slice_sizes);
}
}
/*
* Select a slice from one of the lists.
*
* The list with the combination of lowest indices
* is the most preferred list... in rough order:
*
* one on a unique HBA and disk that is of the exact size
* one on a unique HBA and disk that is of sufficient size (resize)
* one on unique HBA that is of the exact size
* one on unique HBA that is of sufficient size (resize)
* one on unique disk that is of the exact size
* one on unique disk that is of sufficient size (resize)
* one on any HBA that is of exact size
* one on any HBA that is of sufficient size (resize)
* one on a unique HBA that is the largest size
* one on a unique disk that is the largest size
* one on any HBA that is the largest size
*/
slice = NULL;
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
for (k = 0; k < 2; k++) {
for (l = 0; l < 2; l++) {
for (m = 0; m < 3; m++) {
if (list[i][j][k][l][m] != NULL) {
/* pick least used slice from this list */
error = pick_from_best_hba_and_disk(
list[i][j][k][l][m],
used, &slice);
resize = (m == 1);
/* terminate all loops */
goto stop;
}
}
}
}
}
}
stop:
/*
* Slice chosen, is a resize necessary?
*/
if ((error == 0) && (slice != NULL)) {
if (error == 0) {
if (verbose == B_TRUE) {
uint64_t snbytes = 0;
char *sname = NULL;
char *sizestr = NULL;
(void) get_display_name(slice, &sname);
(void) slice_get_size(slice, &snbytes);
if (bytes_to_sizestr(snbytes, &sizestr,
universal_units, B_FALSE) == 0) {
oprintf(OUTPUT_VERBOSE,
gettext(" selected %s (%s)\n"
" it is %s on a\n"
" %s (%s) and a\n"
" %s (%s)\n"),
sname, sizestr,
sizes[m],
uniqhba[i], samebus[j],
uniqdisk[k], samegeom[l]);
free(sizestr);
}
}
if (resize) {
if (verbose == B_TRUE) {
oprintf(OUTPUT_VERBOSE,
gettext(" it has excess space, "
"resizing...\n"));
}
error = create_new_slice(slice, nbytes, add_extra_cyl,
chosen);
if ((error == 0) && (*chosen != NULL) && verbose) {
oprintf(OUTPUT_VERBOSE,
gettext(" exactly resized\n"));
}
}
if (error == 0) {
/* either no resize was necessary or the resize failed */
if (*chosen == NULL) {
/*
* use the original slice as it is.
* Make a devconfig_t for it.
*/
error = create_devconfig_for_slice(slice, chosen);
}
}
}
} else if (slice == NULL) {
oprintf(OUTPUT_DEBUG,
gettext(" no possible slice\n"));
}
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
for (k = 0; k < 2; k++) {
for (l = 0; l < 2; l++) {
for (m = 0; m < 3; m++) {
if (list[i][j][k][l][m] != NULL) {
dlist_free_items(list[i][j][k][l][m], NULL);
}
}
}
}
}
}
return (error);
}
/*
* FUNCTION: create_devconfig_for_slice(dm_descriptor_t slice,
* devconfig_t **nslice)
*
* INPUT: slice - dm_descriptor_t handle to an existing slice
* nslice - devconfig_t pointer to hold the new slice
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Creates a devconfig_t struct representation of the input
* slice dm_descriptor.
*/
int
create_devconfig_for_slice(
dm_descriptor_t slice,
devconfig_t **nslice)
{
uint64_t nbytes = 0;
uint64_t nblks = 0;
uint64_t stblk = 0;
uint32_t index = 0;
char *name = NULL;
int error = 0;
((error = get_display_name(slice, &name)) != 0) ||
(error = slice_get_size(slice, &nbytes)) ||
(error = slice_get_size_in_blocks(slice, &nblks)) ||
(error = slice_get_start_block(slice, &stblk)) ||
(error = slice_get_index(slice, &index));
if (error != 0) {
return (error);
}
((error = new_devconfig(nslice, TYPE_SLICE)) != 0) ||
(error = devconfig_set_name(*nslice, name)) ||
(error = devconfig_set_slice_index(*nslice, index)) ||
(error = devconfig_set_slice_start_block(*nslice, stblk)) ||
(error = devconfig_set_size_in_blocks(*nslice, nblks)) ||
(error = devconfig_set_size(*nslice, nbytes));
if (error != 0) {
free_devconfig(*nslice);
}
return (error);
}
/*
* FUNCTION: make_slicename_for_disk_and_index(dm_descriptor_t disk,
* uint32_t index, char **slicename)
*
* INPUT: disk - a dm_descriptor_t disk handle
* index - a slice index
*
* OUTPUT slicename - a char * pointer to hold the resulting slicename
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Utility function to manufacture a new slice name given the
* "parent" disk and an available slice index.
*
* The caller should free the returned name when done with it.
*/
static int
make_slicename_for_disk_and_index(
dm_descriptor_t disk,
uint16_t index,
char **slicename)
{
char *dname;
int error = 0;
if ((error = get_display_name(disk, &dname)) == 0) {
error = make_slicename_for_diskname_and_index(dname,
index, slicename);
}
return (error);
}
/*
* FUNCTION: make_slicename_for_diskname_and_index(char *diskname,
* uint32_t index, char **slicename)
*
* INPUT: diskname - a char * disk name
* index - a slice index
*
* OUTPUT slicename - a char * pointer to hold the resulting slicename
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Utility function to manufacture a new slice name given the
* name of a disk and an available slice index.
*
* The caller should free the returned name when done with it.
*/
int
make_slicename_for_diskname_and_index(
char *diskname,
uint16_t index,
char **slicename)
{
int error = 0;
char buf[MAXNAMELEN+1];
(void) snprintf(buf, sizeof (buf), "%ss%u", diskname, index);
if ((*slicename = strdup(buf)) == NULL) {
*slicename = NULL;
error = ENOMEM;
}
return (error);
}
/*
* FUNCTION: create_new_slice(dm_descriptor_t oslice, uint64_t nbytes,
* boolean_t add_extra_cyl, devconfig_t **nslice)
*
* INPUT: oslice - dm_descriptor_t handle to an existing slice
* nbytes - desired minimum size of the new slice
* add_extra_cyl - boolean indicating whether the resized slice
* needs to be oversized by 1 cylinder to account for
* interlace rounding done for stripe components.
* nslice - devconfig_t pointer to hold the new slice
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Creates a new slice object using space from the input slice.
*
* If there is an open slice slot in the disk VTOC, it will be
* reserved for the new slice. Space for the new slice will be
* taken from the original slice.
*
* If there is no open slice slot, the original slice will be
* returned as the usable new slice.
*
* The new slice will be of at least 'nbytes' bytes and possibly
* larger due to sector and cylinder boundary alignment.
*
* For EFI labeled disks, nbytes is rounded up to the next block
* boundary.
*
* For VTOC labeled disks, nbytes is rounded up to the next
* cylinder boundary.
*
* Additionally, if add_extra_cyl is true, the new slice will be
* made 1 cylinder larger than necessary. This accounts for the
* interlace rounding done within libmeta when computing the
* usable size of stripe components on disks with VTOC labels.
* Rounding the size up to the next cylinder boundary is not
* sufficient because libmeta will round this size down to an
* integral multiple of the stripe interlace and then round that
* result down to a cylinder boundary. This makes the usable
* size of the slice one cylinder smaller and possibly less than
* nbytes. Adding an extra cylinder ensures the usable size is
* greater than nbytes despite the rounding.
*
* If the resize is successful a pointer to the devconfig_t
* representing the new slice will be returned in "newslice".
*
* If the resize cannot be done, the newslice pointer will
* be NULL.
*/
static int
create_new_slice(
dm_descriptor_t oslice,
uint64_t nbytes,
boolean_t add_extra_cyl,
devconfig_t **nslice)
{
dm_descriptor_t odisk = NULL;
boolean_t efi = B_FALSE;
char *oname = NULL;
uint64_t osize = 0; /* orig size (bytes) */
uint64_t ostart = 0; /* orig start (byte) */
uint64_t ostblk = 0; /* orig start (blk) */
uint64_t nsize = 0; /* new size (bytes) */
uint64_t bytes_per_sect = 0;
uint32_t oindex = 0;
uint32_t nindex = oindex;
int error = 0;
*nslice = NULL;
((error = slice_get_disk(oslice, &odisk)) != 0) ||
(error = slice_get_index(oslice, &oindex));
if (error != 0) {
return (error);
}
/* find an unused slice number, default to oindex */
nindex = oindex;
if ((error = disk_get_available_slice_index(odisk, &nindex)) != 0) {
return (error);
}
((error = get_display_name(oslice, &oname)) != 0) ||
(error = slice_get_size(oslice, &osize)) ||
(error = slice_get_start(oslice, &ostart)) ||
(error = slice_get_start_block(oslice, &ostblk)) ||
(error = disk_get_is_efi(odisk, &efi)) ||
(error = disk_get_blocksize(odisk, &bytes_per_sect));
if (error != 0) {
return (error);
}
if (efi) {
/* EFI: round size to an integral number of blocks (sectors) */
nsize = bytes_per_sect *
((nbytes + (bytes_per_sect - 1)) / bytes_per_sect);
oprintf(OUTPUT_DEBUG,
gettext(" "
"rounded up to %10.2f blocks\n"),
(double)(nsize/bytes_per_sect));
} else {
/* VTOC: round size to an integral number of cylinders */
uint64_t nhead = 0;
uint64_t nsect = 0;
uint64_t ncyls = 0;
((error = disk_get_ncylinders(odisk, &ncyls)) != 0) ||
(error = disk_get_nheads(odisk, &nhead)) ||
(error = disk_get_nsectors(odisk, &nsect));
if (error == 0) {
uint64_t bytes_per_cyl = nhead * nsect * bytes_per_sect;
nsize = bytes_per_cyl *
((nbytes + (bytes_per_cyl - 1)) / bytes_per_cyl);
if (add_extra_cyl == TRUE) {
nsize += bytes_per_cyl;
}
oprintf(OUTPUT_DEBUG,
gettext(" "
"rounded VTOC slice to %10.2f cylinders "
"(out of %llu)\n"),
(double)(nsize/bytes_per_cyl), ncyls);
}
}
/* is sufficient space still available? */
if (error == 0) {
if (osize == nsize) {
/* use existing slice as is */
((error = create_devconfig_for_slice(oslice, nslice)) != 0) ||
(error = disk_reserve_index(odisk, (uint16_t)nindex));
} else if (osize > nsize) {
if (nindex == oindex) {
/* no more slices, resize existing slice */
((error = create_devconfig_for_slice(oslice,
nslice)) != 0) ||
(error = devconfig_set_size(*nslice, nsize)) ||
(error = devconfig_set_size_in_blocks(*nslice,
nsize/bytes_per_sect));
(error = disk_reserve_index(odisk, (uint16_t)nindex));
} else {
/* make a new slice */
char *nname = NULL;
((error = make_slicename_for_disk_and_index(odisk,
nindex, &nname)) != 0) ||
(error = create_modified_slice(oslice, oname, oindex,
ostart, osize, bytes_per_sect, nname, nindex, nsize,
nslice)) ||
/* mark the new slice's index as used */
(error = disk_reserve_index(odisk, (uint16_t)nindex));
if ((error != 0) && (*nslice == NULL)) {
free(nname);
}
}
}
}
return (error);
}
/*
* FUNCTION: create_modified_slice(dm_descriptor_t oslice, char *oname,
* uint32_t oindex, uint64_t ostart, uint64_t osize,
* uint64_t bytes_per_sect, uint64_t nsize,
* char *nname, uint32_t nindex, devconfig_t **nslice)
*
* INPUT: oslice - dm_descriptor_t handle for the original slice
* oname - existing source slice name
* oindex - existing source slice VTOC index
* ostart - existing source slice start byte
* osize - existing source slice size in bytes
* bytes_per_sect - bytes per block (sector) for the disk
* nname - new slice name
* nindex - new slice VTOC index
* nsize - new slice size in bytes (cylinder and block aligned)
*
* SIDEEFFECTS: updates the module private list of modified slices
*
* OUTPUT: nslice - pointer to a devconfig_t to hold the new slice
*
* PURPOSE: create a new VTOC slice by taking space from an
* existing slice.
*
* The input size for the new slice is expected to be
* cylinder aligned.
*/
static int
create_modified_slice(
dm_descriptor_t oslice,
char *oname,
uint32_t oindex,
uint64_t ostart,
uint64_t osize,
uint64_t bytes_per_sect,
char *nname,
uint32_t nindex,
uint64_t nsize,
devconfig_t **nslice)
{
int error = 0;
/* compute start sector and size in sectors for the new slice */
/* subtract nsize from original slice to get starting byte */
uint64_t nstart = (ostart + osize) - nsize;
/* convert starting byte to a sector */
uint64_t nstblk = (uint64_t)(nstart / bytes_per_sect);
/* convert nsize to an integral number of blocks (sectors) */
uint64_t nblks = (uint64_t)(nsize / bytes_per_sect);
/* create a modified slice record for the new slice */
error = assemble_modified_slice(oslice, nname, nindex,
nstblk, nblks, nsize, nslice);
if (error != 0) {
free(nname);
return (error);
}
/* update the existing source slice's new size */
osize = osize - nsize;
(void) slice_set_size(oslice, osize);
/* update/create the modified slice record gfor the source slice */
error = assemble_modified_slice((dm_descriptor_t)0,
oname, oindex, (uint64_t)(ostart / bytes_per_sect),
(uint64_t)(osize / bytes_per_sect),
osize, NULL);
return (error);
}
/*
* FUNCTION: assemble_modified_slice(dm_descriptor_t src_slice,
* char *mod_name, uint32_t mod_index,
* uint64_t mod_stblk, uint64_t mod_nblks,
* uint64_t mod_size, devconfig_t **modslice)
*
* INPUT: src_slice - dm_descriptor_t handle of the slice space
* was taken from to create the modified slice
* mod_name - name of the modified slice
* mod_index - name of the modified slice
* mod_stblk - start block of the modified slice
* mod_nblks - size in blocks of the modified slice
* mod_size - size in bytes of the modified slice
*
* OUTPUT: mod_slice - if non-NULL, will be populated with a
* devconfig_t representing the modified slice.
*
* SIDEEFFECTS: adds or updates an entry in the modified slice list
* tracking the slices that have been explicitly modified
* by the layout code.
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Utility function to which updates or creates a devconfig_t
* representing a slice that needs to be modified.
*
* If a modified slice record does not exist for the named
* slice, a new devconfig_t struct is allocated and added
* to the modified slice list.
*
* The existing or created devconfig_t struct is updated with
* the input values.
*
* The information about the slices in the modified slice list
* will eventually be handed to fmthard.
*/
int
assemble_modified_slice(
dm_descriptor_t src_slice,
char *mod_name,
uint32_t mod_index,
uint64_t mod_stblk,
uint64_t mod_nblks,
uint64_t mod_size,
devconfig_t **mod_slice)
{
devconfig_t *slice = NULL;
modslice_t *mstp = NULL;
dlist_t *item = NULL;
int error = 0;
/* see if the slice has been modified before */
if ((item = dlist_find(_modified_slices, mod_name,
compare_string_to_modslice_name)) != NULL) {
/* yes, update the resize count and attributes */
mstp = (modslice_t *)item->obj;
slice = mstp->slice_devcfg;
mstp->times_modified += 1;
mstp->src_slice_desc = src_slice;
((error = devconfig_set_slice_start_block(slice,
mod_stblk)) != 0) ||
(error = devconfig_set_size(slice, mod_size)) ||
(error = devconfig_set_size_in_blocks(slice, mod_nblks));
} else {
/* no, first modification... */
/* create a devconfig_t representing the new slice */
((error = new_devconfig(&slice, TYPE_SLICE)) != 0) ||
(error = devconfig_set_name(slice, mod_name)) ||
(error = devconfig_set_slice_index(slice, mod_index)) ||
(error = devconfig_set_slice_start_block(slice, mod_stblk)) ||
(error = devconfig_set_size_in_blocks(slice, mod_nblks)) ||
(error = devconfig_set_size(slice, mod_size));
if (error == 0) {
/* add to list of modified slices */
if ((mstp = (modslice_t *)
calloc(1, sizeof (modslice_t))) != NULL) {
/* count # of times source slice has been modified */
if (src_slice != (dm_descriptor_t)0) {
mstp->times_modified = 0;
} else {
mstp->times_modified = 1;
}
mstp->src_slice_desc = src_slice;
mstp->slice_devcfg = slice;
if ((item = dlist_new_item(mstp)) != NULL) {
_modified_slices =
dlist_insert_ordered(
item,
_modified_slices,
ASCENDING,
compare_modslice_names);
} else {
error = ENOMEM;
}
} else {
error = ENOMEM;
}
}
if (error != 0) {
free_devconfig(mstp);
free_devconfig(slice);
}
}
if (error == 0) {
oprintf(OUTPUT_DEBUG,
" "
"modified %s (start blk: %9llu, nblks: %9llu)\n",
mod_name, mod_stblk, mod_nblks);
/* return devconfig_t for modified slice */
if (mod_slice != NULL) {
*mod_slice = slice;
mstp->volume_component = B_TRUE;
}
}
return (error);
}
/*
* FUNCTION: dlist_t *get_modified_slices()
*
* RETURNS: pointer to the list of modslice_t structs representing
* modified slices
*
* PURPOSE: public accessor to the list of slices modified while
* processing a request.
*/
dlist_t *
get_modified_slices()
{
return (_modified_slices);
}
/*
* FUNCTION: free_modslice_object(void *obj)
*
* INPUT: obj - opaque pointer
*
* PURPOSE: Frees memory associated with a modslice_t struct.
*/
static void
free_modslice_object(
void *obj)
{
assert(obj != (modslice_t *)NULL);
if (((modslice_t *)obj)->slice_devcfg != NULL) {
if (((modslice_t *)obj)->volume_component != B_TRUE) {
free_devconfig(((modslice_t *)obj)->slice_devcfg);
}
}
free(obj);
}
/*
* FUNCTION: void release_modified_slices()
*
* INPUT: none -
* OUTPUT: none -
*
* PURPOSE: cleanup the module global list of slices modified
* while processing a request.
*/
int
release_modified_slices()
{
dlist_free_items(_modified_slices, free_modslice_object);
_modified_slices = NULL;
return (0);
}
/*
* FUNCTION: destroy_new_slice(devconfig_t *dev)
*
* INPUT: dev - a devconfig_t pointer to a slice object
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Undoes slice creation done by create_new_slice():
*
* release index
* remove from used_slices
* remove from modified_slices
* return space to source slice
* free memory
*/
int
destroy_new_slice(
devconfig_t *dev)
{
dm_descriptor_t disk = NULL;
uint64_t size = 0;
uint16_t index = 0;
modslice_t *modified = NULL;
dlist_t *item = NULL;
char *name = NULL;
int error = 0;
((error = devconfig_get_name(dev, &name)) != 0) ||
(error = devconfig_get_slice_index(dev, &index)) ||
(error = devconfig_get_size(dev, &size)) ||
(error = get_disk_for_named_slice(name, &disk)) ||
(error = disk_release_index(disk, index)) ||
(error = remove_used_slice_by_name(name));
if (error != 0) {
return (error);
}
/* remove from the modified_slices list */
_modified_slices =
dlist_remove_equivalent_item(
_modified_slices, name,
compare_string_to_modslice_name, &item);
if (item != NULL) {
modified = (modslice_t *)item->obj;
free((void*) item);
}
/* space from an existing slice? if so reclaim it. */
if (modified != NULL) {
dm_descriptor_t src = modified->src_slice_desc;
char *srcname = NULL;
dlist_t *srcitem = NULL;
if (src != (dm_descriptor_t)0) {
if ((error = get_display_name(src, &srcname)) == 0) {
srcitem =
dlist_find(
_modified_slices,
srcname,
compare_string_to_modslice_name);
}
}
if ((error == 0) && (srcitem != NULL)) {
modslice_t *source = (modslice_t *)srcitem->obj;
devconfig_t *srcdevcfg = NULL;
uint64_t srcsize = NULL;
uint64_t srcsizeblks = NULL;
uint64_t inblks = NULL;
srcdevcfg = source->slice_devcfg;
source->times_modified -= 1;
((error = devconfig_get_size(srcdevcfg, &srcsize)) != 0) ||
(error = devconfig_set_size(srcdevcfg, srcsize + size)) ||
(error = slice_set_size(src, srcsize + size)) ||
(error = slice_get_size_in_blocks(src, &srcsizeblks)) ||
(error = devconfig_get_size_in_blocks(srcdevcfg, &inblks));
(error = devconfig_set_size_in_blocks(srcdevcfg, srcsizeblks));
if (error == 0) {
/* was only modification undone? */
if (source->times_modified == 0) {
_modified_slices =
dlist_remove_equivalent_item(
_modified_slices, srcname,
compare_string_to_modslice_name,
&srcitem);
free_modslice_object((modslice_t *)srcitem->obj);
free((void *)srcitem);
}
}
}
free_modslice_object(modified);
}
return (error);
}
/*
* FUNCTION: pick_from_best_hba_and_disk(dlist_t *slices,
* dlist_t *used, dm_descriptor_t *chosen)
*
* INPUT: slices - a dlist_t poitner to a list of slices
* used - a dlist_t pointer to a list of used slices
* chosen - a dm_descriptor_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Examines the input list of slices and chooses the one
* that is on the least used HBA and disk.
*
* HBA and disk usage is determined by examining the input
* list of used slices and counting the number of slices
* each HBA and disk contributes.
*
* The HBA which contributes the fewest is selected, and
* then the disk on that HBA which contributes the fewest
* is selected.
*
* The largest slice from that disk is then returned.
*/
static int
pick_from_best_hba_and_disk(
dlist_t *slices,
dlist_t *used,
dm_descriptor_t *chosen)
{
dlist_t *iter = NULL;
dlist_t *iter1 = NULL;
dlist_t *iter2 = NULL;
dlist_t *item = NULL;
dlist_t *used_slice_hbas = NULL;
int maxuses = 128;
int maxslices = VTOC_SIZE; /* meta.h */
int i = 0;
int error = 0;
/*
* allocate an array to hold lists of slices grouped by
* HBA contribution... the list indexed by N is the list
* of slices that are on HBAs contributing N slices
*/
dlist_t **prefhbas = (dlist_t **)calloc(maxuses, sizeof (dlist_t *));
/*
* allocate an array to hold lists of slices grouped by
* disk contribution... the list indexed by N is the list
* of slices that are on disks contributing N slices
*/
dlist_t **prefdisks = (dlist_t **)calloc(maxslices, sizeof (dlist_t *));
*chosen = (dm_descriptor_t)0;
if (prefhbas == NULL || prefdisks == NULL) {
free(prefhbas);
free(prefdisks);
return (ENOMEM);
}
/*
* precompute the used slices' lists of HBAS: iterate the list
* of used slices and determine the HBA(s) each is connected thru.
* construct a list of lists containing the HBAs.
*/
for (iter = used;
(iter != NULL) && (error == 0);
iter = iter->next) {
devconfig_t *uslice = (devconfig_t *)iter->obj;
dm_descriptor_t udisk = NULL;
char *uname = NULL;
dlist_t *uhbas = NULL;
/* need to use disk to get to HBAs because */
/* the slice doesn't exist yet */
((error = devconfig_get_name(uslice, &uname)) != 0) ||
(error = get_disk_for_named_slice(uname, &udisk)) ||
(error = disk_get_hbas(udisk, &uhbas));
if (error == 0) {
if ((item = dlist_new_item((void *)uhbas)) == NULL) {
error = ENOMEM;
} else {
used_slice_hbas = dlist_append(
item, used_slice_hbas, AT_HEAD);
}
}
}
/*
* iterate the list of chosen slices and for each,
* determine how many other slices from its HBA(s)
* are already being used...
*
* iter steps thru the list of slices
* iter1 steps thru each of the slice's HBAs
* iter2 steps thru the precomputed list of used slice's HBAs
* dlist_contains then searches each used slice's HBAs
* to see if it contains iter1's HBA
*
* If it does, increment the count for that HBA.
*/
for (iter = slices;
(iter != NULL) && (error == 0);
iter = iter->next) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
dlist_t *hbas = NULL;
int n = 0; /* # slices each HBA contributes */
if ((error = slice_get_hbas(slice, &hbas)) != 0) {
continue;
}
for (iter1 = hbas; iter1 != NULL; iter1 = iter1->next) {
for (iter2 = used_slice_hbas; iter2 != NULL;
iter2 = iter2->next) {
dlist_t *uhbas = (dlist_t *)iter2->obj;
if (dlist_contains(uhbas, iter1->obj,
compare_descriptor_names) == B_TRUE) {
n++;
}
}
}
dlist_free_items(hbas, NULL);
/* group slices from HBAs contributing more than maxuses */
if (n >= maxuses) {
n = maxuses - 1;
}
/* add slice to list in descending size order */
if ((item = dlist_new_item((void*)slice)) == NULL) {
error = ENOMEM;
} else {
prefhbas[n] =
dlist_insert_ordered(
item,
prefhbas[n],
DESCENDING,
compare_slice_sizes);
}
}
/* free list of lists of used slices HBAs */
for (iter = used_slice_hbas; iter != NULL; iter = iter->next) {
dlist_free_items((dlist_t *)iter->obj, NULL);
}
dlist_free_items(used_slice_hbas, NULL);
/*
* Select the list of slices that are on the HBA(s) contributing
* the fewest slices... iterate these slices and for each, detemmine
* how many other slices from its disk are already being used...
*/
for (i = 0; (i < maxuses) && (error == 0); i++) {
for (iter = (dlist_t *)prefhbas[i];
(iter != NULL) && (error == 0);
iter = iter->next) {
dm_descriptor_t slice = (uintptr_t)iter->obj;
dm_descriptor_t disk;
int n = 0;
(void) slice_get_disk(slice, &disk);
/*
* count how many slices this slice's disk is contributing
* by comparing it to the list of used slices
*/
for (iter1 = _used_slices; iter1 != NULL; iter1 = iter1->next) {
usedslice_t *used = (usedslice_t *)iter1->obj;
if (compare_descriptors(
(void *)disk, (void *)used->disk) == 0) {
n++;
}
}
/* add slice to list in descending size order */
if ((item = dlist_new_item((void *)slice)) == NULL) {
error = ENOMEM;
} else {
prefdisks[n] =
dlist_insert_ordered(
item,
prefdisks[n],
DESCENDING,
compare_slice_sizes);
}
}
}
if (error == 0) {
/* select largest slice from least used disk */
for (i = 0; (i < maxslices) && (*chosen == NULL); i++) {
if (prefdisks[i] != NULL) {
*chosen = (uintptr_t)prefdisks[i]->obj;
}
}
}
for (i = 0; i < maxuses; i++) {
dlist_free_items(prefhbas[i], NULL);
}
for (i = 0; i < maxslices; i++) {
dlist_free_items(prefdisks[i], NULL);
}
free((void*)prefhbas);
free((void*)prefdisks);
return (error);
}
/*
* FUNCTION: slice_on_unique_hba(dm_descriptor_t slice,
* dlist_t *used, dlist_t *used_hbas,
* boolean_t *unique)
*
* INPUT: slice - a dm_descriptor_t handle for the slice of interest
* used - a dlist_t pointer to a list of used slices
* used_hbas - a dlist_t pointer to a list of used_hbas
* unique - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determines if the input slice is connected thru the same HBA
* as a slice in the used list.
*
* Also checks to see if the input slice is connected thru any
* HBA in the used_hbas list.
*
* If the slice is found to be on a unique HBA, bool is set
* to B_TRUE, B_FALSE otherwise.
*/
static int
slice_on_unique_hba(
dm_descriptor_t slice,
dlist_t *used,
dlist_t *used_hbas,
boolean_t *unique)
{
dlist_t *iter = NULL;
dlist_t *iter1 = NULL;
dlist_t *hbas = NULL;
int error = 0;
*unique = B_TRUE;
if ((error = slice_get_hbas(slice, &hbas)) != 0) {
return (error);
}
/*
* check to see if any of slice's HBAs is the same
* as the HBA for any of the used
*/
for (iter = used;
(iter != NULL) && (*unique == B_TRUE) && (error == 0);
iter = iter->next) {
devconfig_t *dev = (devconfig_t *)iter->obj;
if (devconfig_isA(dev, TYPE_SLICE)) {
dm_descriptor_t odisk = NULL;
char *oname = NULL;
dlist_t *ohbas = NULL;
/* get HBAs for other slice using its disk */
/* because the slice doesn't exist yet. */
((error = devconfig_get_name(dev, &oname)) != 0) ||
(error = get_disk_for_named_slice(oname, &odisk)) ||
(error = disk_get_hbas(odisk, &ohbas));
/* any HBA overlap? */
for (iter1 = hbas;
(iter1 != NULL) && (*unique == B_TRUE) && (error == 0);
iter1 = iter1->next) {
if (dlist_contains(ohbas, iter1->obj,
compare_descriptor_names) == B_TRUE) {
*unique = B_FALSE;
}
}
dlist_free_items(ohbas, NULL);
}
}
/*
* check to see if any of slice's HBAs is the contained
* in the list of used hbas
*/
for (iter = hbas;
(iter != NULL) && (*unique == B_TRUE) && (error == 0);
iter = iter->next) {
if (dlist_contains(used_hbas,
iter->obj, compare_descriptor_names) == B_TRUE) {
*unique = B_FALSE;
}
}
dlist_free_items(hbas, NULL);
return (error);
}
/*
* FUNCTION: slice_on_unique_disk(dm_descriptor_t slice,
* dlist_t *used, dlist_t *used_disks,
* boolean_t *unique)
*
* INPUT: slice - a dm_descriptor_t handle for the slice of interest
* used - a dlist_t pointer to a list of used slices
* othervols - a dlist_t pointer to a list of other volumes
* bool - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determines if the input slice is on a drive that is not
* part of any volume in the othervols list, or on the same
* drive as any slice in the used list.
*
* If the slice is found to be on a unique disk, bool is set
* to B_TRUE, B_FALSE otherwise.
*/
static int
slice_on_unique_disk(
dm_descriptor_t slice,
dlist_t *used,
dlist_t *used_disks,
boolean_t *unique)
{
dm_descriptor_t disk = NULL;
dlist_t *iter = NULL;
int error = 0;
*unique = B_TRUE;
if ((error = slice_get_disk(slice, &disk)) != 0) {
return (error);
}
/*
* check to see if this disk is the same as the
* disk for any of the used
*/
for (iter = used;
(iter != NULL) && (*unique == B_TRUE) && (error == 0);
iter = iter->next) {
devconfig_t *dev = (devconfig_t *)iter->obj;
if (devconfig_isA(dev, TYPE_SLICE)) {
/* get disk for otherslice */
dm_descriptor_t odisk = NULL;
char *oname = NULL;
((error = devconfig_get_name(dev, &oname)) != 0) ||
(error = get_disk_for_named_slice(oname, &odisk));
if ((error == 0) &&
(compare_descriptor_names(
(void*)disk, (void*)odisk) == 0)) {
/* origslice is on same disk, stop */
*unique = B_FALSE;
}
}
}
/* check disk against the used disks */
if ((error == 0) && (*unique == B_TRUE) &&
dlist_contains(used_disks, (void *)disk,
compare_descriptor_names) == B_TRUE) {
*unique = B_FALSE;
}
return (error);
}
/*
* FUNCTION: slice_has_same_disk_geom(dm_descriptor_t slice,
* dlist_t *used, boolean_t *has_same_geom)
*
* INPUT: slice - a dm_descriptor_t handle for the slice of interest
* used - a dlist_t pointer to a list of used slices
* bool - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determines if the input slice is on a drive with similar
* hardware geometry as the slices in the used list.
*
* If the slice is found to be on a disk with similar geometry,
* bool is set to B_TRUE, B_FALSE otherwise.
*
* The comparison is based on the available disk geometry
* information which may not be relevant or accurate for
* EFI labeled disks, so the disk drive type needs to be
* checked as well.
*/
static int
slice_has_same_disk_geom(
dm_descriptor_t slice,
dlist_t *used,
boolean_t *has_same_geom)
{
dm_descriptor_t disk = NULL;
boolean_t efi = B_FALSE;
uint64_t bsize = 0;
uint64_t ncyls = 0;
uint64_t nsects = 0;
uint64_t nheads = 0;
dlist_t *iter = NULL;
int error = 0;
*has_same_geom = B_TRUE;
((error = slice_get_disk(slice, &disk)) != 0) ||
(error = disk_get_is_efi(disk, &efi)) ||
(error = disk_get_blocksize(disk, &bsize));
if ((error == 0) && (efi == B_FALSE)) {
((error = disk_get_ncylinders(disk, &ncyls)) != 0) ||
(error = disk_get_nheads(disk, &nheads)) ||
(error = disk_get_nsectors(disk, &nsects));
}
if (error != 0) {
return (error);
}
/*
* check to see if slice's disk has the same geometry
* as the disks for the slices in the used list
*/
for (iter = used;
(iter != NULL) && (*has_same_geom == B_TRUE) && (error = 0);
iter = iter->next) {
devconfig_t *dev = (devconfig_t *)iter->obj;
if (devconfig_isA(dev, TYPE_SLICE)) {
/* get disk info for otherslice */
dm_descriptor_t odisk = NULL;
char *oname = NULL;
boolean_t oefi = B_FALSE;
uint64_t obsize = 0;
uint64_t oncyls = 0;
uint64_t onsects = 0;
uint64_t onheads = 0;
((error = devconfig_get_name(dev, &oname)) != 0) ||
(error = get_disk_for_named_slice(oname, &odisk)) ||
(error = disk_get_is_efi(odisk, &oefi)) ||
(error = disk_get_blocksize(odisk, &obsize));
if ((error == 0) && (oefi == B_FALSE)) {
((error = disk_get_ncylinders(odisk, &oncyls)) != 0) ||
(error = disk_get_nheads(odisk, &onheads)) ||
(error = disk_get_nsectors(odisk, &onsects));
}
if (error == 0) {
if ((bsize != obsize) || (ncyls != oncyls) ||
(nsects != onsects) || (nheads != onheads)) {
/* this disk has a different geometry */
*has_same_geom = B_FALSE;
}
}
}
}
return (error);
}
/*
* FUNCTION: slice_on_similar_bus(dm_descriptor_t slice,
* dlist_t *used, boolean_t *on_smlr_bus)
*
* INPUT: slice - a dm_descriptor_t handle for the slice of interest
* used - a dlist_t pointer to a list of used slices
* bool - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determines if the input slice is connected thru a bus with
* characteristics similar to the slices in the used list.
*
* If the slice is found to be on a similar bus, bool is set
* to B_TRUE, B_FALSE otherwise.
*
* The comparison is actually between any of the HBA/controllers
* thru which the slices are connected to the system.
* If any are of similar type (e.g., fibre, SCSI) and
* protocol (SCSI-2, -3, fast/wide), then the slices are
* considered to be on similar busses.
*/
static int
slice_on_similar_bus(
dm_descriptor_t slice,
dlist_t *used,
boolean_t *on_smlr_bus)
{
dlist_t *iter = NULL;
dlist_t *iter1 = NULL;
dlist_t *hbas = NULL;
int error = 0;
/* if there are no used slices, then the bus is similar */
*on_smlr_bus = B_TRUE;
if (dlist_length(used) == 0) {
return (0);
}
(error = slice_get_hbas(slice, &hbas));
if (error != 0) {
return (error);
}
/* if there are used slices, then make sure the bus is similar */
*on_smlr_bus = B_FALSE;
for (iter = hbas;
(iter != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0);
iter = iter->next) {
dm_descriptor_t hba = (uintptr_t)iter->obj;
char *type = NULL;
boolean_t fast80 = B_FALSE;
boolean_t fast40 = B_FALSE;
boolean_t fast20 = B_FALSE;
boolean_t wide = B_FALSE;
((error = hba_get_type(hba, &type)) != 0) ||
(error = hba_is_fast_80(hba, &fast80)) ||
(error = hba_is_fast_40(hba, &fast40)) ||
(error = hba_is_fast_20(hba, &fast20)) ||
(error = hba_supports_wide(hba, &wide));
if (error != 0) {
continue;
}
/* check against the HBAs for the used slices */
for (iter1 = used;
(iter1 != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0);
iter1 = iter1->next) {
devconfig_t *used = (devconfig_t *)iter1->obj;
/* get HBAs for otherslice */
dm_descriptor_t udisk = NULL;
char *uname = NULL;
dlist_t *uhbas = NULL;
dlist_t *iter2 = NULL;
((error = devconfig_get_name(used, &uname)) != 0) ||
(error = get_disk_for_named_slice(uname, &udisk)) ||
(error = disk_get_hbas(udisk, &uhbas));
for (iter2 = uhbas;
(iter2 != NULL) && (*on_smlr_bus == B_FALSE) &&
(error == 0);
iter2 = iter2 ->next) {
dm_descriptor_t uhba = (uintptr_t)iter2->obj;
char *utype = NULL;
boolean_t ufast80 = B_FALSE;
boolean_t ufast40 = B_FALSE;
boolean_t ufast20 = B_FALSE;
boolean_t uwide = B_FALSE;
((error = hba_get_type(uhba, &utype)) != 0) ||
(error = hba_is_fast_80(uhba, &ufast80)) ||
(error = hba_is_fast_40(uhba, &ufast40)) ||
(error = hba_is_fast_20(uhba, &ufast20)) ||
(error = hba_supports_wide(uhba, &uwide));
if (error == 0) {
/* check sync speed ? */
if ((fast80 == ufast80) && (fast40 == ufast40) &&
(fast20 == ufast20) && (wide == uwide) &&
(type == utype)) {
*on_smlr_bus = B_TRUE;
}
}
}
dlist_free_items(uhbas, NULL);
}
}
dlist_free_items(hbas, NULL);
return (error);
}
/*
* FUNCTION: slice_has_n_paths(dm_descriptor_t slice,
* uint16_t npaths, boolean_t *has_n_paths)
* INPUT: slice - a dm_descriptor_t handle for the slice of interest
* npaths - the number of paths desired
* has_n_paths - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determines if the input slice is connected via npaths.
* has_n_paths is set to B_TRUE if so, B_FALSE otherwise.
*
* In order for a disk to have multiple paths, MPXIO must
* be enabled and these conditions should hold:
*
* Slice will have one drive object.
* Drive will have one HBA (scsi_vhci)
* Drive will have one alias.
* Drive will have possibly > 1 paths.
*
* Getting the HBAs and aliases for the disk is relatively
* expensive, so they aren't checked. The actual number of
* paths is only checked if MPXIO is known to be enabled on
* the system and the input npaths is > 1.
*/
static int
slice_has_n_paths(
dm_descriptor_t slice,
uint16_t npaths,
boolean_t *has_n_paths)
{
int error = 0;
*has_n_paths = B_FALSE;
if ((npaths > 1) && (is_mpxio_enabled() == B_TRUE)) {
dm_descriptor_t disk = NULL;
dlist_t *paths = NULL;
((error = slice_get_disk(slice, &disk)) != 0) ||
(error = disk_get_paths(disk, &paths));
if ((error == 0) && (dlist_length(paths) == npaths)) {
*has_n_paths = B_TRUE;
}
dlist_free_items(paths, NULL);
}
return (error);
}
/*
* FUNCTION: compare_string_to_modslice_name(void *str, void *modslice)
*
* INPUT: str - opaque char * pointer
* modslice - opaque modslice_t pointer
*
* RETURNS: int - <0 - if str < modslice->slice_devcfg.name
* 0 - if str == modslice->slice_devcfg.name
* >0 - if str > modslice->slice_devcfg.name
*
* PURPOSE: dlist_t helper which compares the input string to
* the name of a slice represented as modslice_t struct.
*
* Comparison is done via string_case_compare.
*/
static int
compare_string_to_modslice_name(
void *str,
void *modslice)
{
char *name = NULL;
assert(str != NULL);
assert(modslice != NULL);
(void) devconfig_get_name(
((modslice_t *)modslice)->slice_devcfg, &name);
return (string_case_compare((char *)str, name));
}
/*
* FUNCTION: compare_modslice_names(void *obj1, void *obj2)
*
* INPUT: obj1 - opaque pointer
* obj2 - opaque pointer
*
* RETURNS: int - <0 - if obj1 name < obj2 name
* 0 - if obj1 name == obj2 name
* >0 - if obj1 name > obj2 name
*
* PURPOSE: dlist_t helper which compares the names of two slices
* represented as modslice_t structs.
*
* Comparison is done by string_case_compare
*/
static int
compare_modslice_names(
void *obj1,
void *obj2)
{
char *name1 = NULL;
char *name2 = NULL;
assert(obj1 != NULL);
assert(obj2 != NULL);
(void) devconfig_get_name(
((modslice_t *)obj1)->slice_devcfg, &name1);
(void) devconfig_get_name(
((modslice_t *)obj2)->slice_devcfg, &name2);
return (string_case_compare(name1, name2));
}
/*
* FUNCTION: release_used_slices()
*
* PURPOSE: Helper which cleans up the module private list of used
* slices.
*/
void
release_used_slices()
{
dlist_free_items(_used_slices, free_used_slice);
_used_slices = NULL;
}
static void
free_used_slice(
void *obj)
{
if (obj != NULL) {
usedslice_t *used = (usedslice_t *)obj;
free(used->slicename);
free(used);
}
}
/*
* FUNCTION: is_used_slice(dm_descriptor_t slice, boolean_t *is_used)
*
* INPUT: slice - a dm_descriptor_t slice handle
*
* OUTPUT: is_reserved - pointer to a boolean_t to hold the
* return result.
*
* PURPOSE: Helper which checks to see if the input slice
* is in the used_slice list.
*
* Check the input name against any used slice name or alias.
* is_used is set to B_TRUE if the input slice is already used,
* B_FALSE otherwise.
*/
int
is_used_slice(
dm_descriptor_t slice,
boolean_t *is_used)
{
char *name;
int error = 0;
if ((error = get_display_name(slice, &name)) == 0) {
*is_used = dlist_contains(_used_slices, (void *)name,
compare_usedslice_name_to_string);
}
return (error);
}
/*
* FUNCTIONS: add_used_slice(dm_descriptor_t slice)
* add_used_slice_by_name(char *slicename)
* add_used_slice_list_entry(char *slice)
* remove_used_slice_by_name(char *slicename)
*
* INPUT: diskset - a char * diskset name.
* slice - a dm_descriptor_t slice handle
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Access or maintain the list of used slices.
*/
int
add_used_slice(
dm_descriptor_t slice)
{
dm_descriptor_t disk;
char *name;
int error = 0;
assert(slice != (dm_descriptor_t)0);
((error = get_display_name(slice, &name)) != 0) ||
(error = slice_get_disk(slice, &disk)) ||
(error = add_used_slice_list_entry(name, disk));
return (error);
}
int
add_used_slice_by_name(
char *slicename)
{
dm_descriptor_t disk = (dm_descriptor_t)0;
int error = 0;
assert(slicename != NULL);
/* find disk for slice */
error = get_disk_for_named_slice(slicename, &disk);
if (error == 0) {
error = add_used_slice_list_entry(slicename, disk);
}
return (error);
}
static int
add_used_slice_list_entry(
char *slicename,
dm_descriptor_t disk)
{
usedslice_t *used = NULL;
int error = 0;
assert(slicename != NULL);
assert(disk != (dm_descriptor_t)0);
used = (usedslice_t *)calloc(1, sizeof (usedslice_t));
if (used == NULL) {
error = ENOMEM;
} else {
used->disk = disk;
if ((used->slicename = strdup(slicename)) == NULL) {
free(used);
error = ENOMEM;
} else {
dlist_t *item = dlist_new_item((void *) used);
if (item == NULL) {
free(used->slicename);
free(used);
error = ENOMEM;
} else {
_used_slices =
dlist_append(item, _used_slices, AT_HEAD);
}
}
}
return (error);
}
int
remove_used_slice_by_name(
char *slice)
{
dlist_t *removed = NULL;
_used_slices =
dlist_remove_equivalent_item(_used_slices, (void *)slice,
compare_usedslice_name_to_string, &removed);
if (removed != NULL) {
free_used_slice(removed->obj);
removed->obj = NULL;
free(removed);
}
return (0);
}
/*
* FUNCTION: compare_usedslice_name_to_string(void *obj1, void *obj2)
* INPUT: obj1 - opaque pointer
* obj2 - opaque pointer
*
* RETURNS: int - <0 - if obj1 name < obj2 name
* 0 - if obj1 name == obj2 name
* >0 - if obj1 name > obj2 name
*
* PURPOSE: dlist_t helper which compares the names of a slice
* represented as modslice_t struct to a string.
*
* obj1 is assumed to be a char *
* obj2 is assumed to be a usedslice_t *
*
* Comparison is done via string_case_compare.
*/
static int
compare_usedslice_name_to_string(
void *obj1,
void *obj2)
{
assert(obj1 != NULL);
assert(obj2 != NULL);
return (string_case_compare((char *)obj1,
((usedslice_t *)obj2)->slicename));
}
/*
* FUNCTION: disk_has_used_slice(dm_descriptor_t disk, boolean_t *hasused)
*
* INPUT: disk - a dm_descriptor_t disk handle.
* inuse - a boolean_t pointer to hold the result
*
* RETURNS: int - 0 on success
* !0 othersize.
*
* PURPOSE: Determines if any of the known used slices is on the
* input disk.
*/
int
disk_has_used_slice(
dm_descriptor_t disk,
boolean_t *hasused)
{
dlist_t *iter;
int error = 0;
*hasused = B_FALSE;
for (iter = _used_slices;
(iter != NULL) && (*hasused == B_FALSE);
iter = iter->next) {
usedslice_t *used = (usedslice_t *)iter->obj;
/* compare used slice's disk to disk */
if (compare_descriptors((void *)disk, (void *)used->disk) == 0) {
*hasused = B_TRUE;
}
}
return (error);
}
/*
* FUNCTION: add_reserved_slice(dm_descriptor_t slice)
*
* INPUT: slice - a dm_descriptor_t slice handle
*
* RETURNS: int - 0 on success
* !0 otherwise.
*
* PURPOSE: Helper which remembers specfically requested slices
* in a private list to ensure that the same slice isn't
* requested more than once.
*
* Does not check to see if the slice already exists
* in the list of reserved slices. Assumes that the
* caller has checked using is_reserved_slice().
*
* The reserved slice list is used by several functions:
*
* 1. layout_validate.validate_slice_components() adds user
* requested slices to the list.
*
* 2. After all potentially usable slices have been scanned,
* layout_validate.validate_reserved_slices() checks the
* slices in the reserved and ensures that each slice is
* actually usable as a volume component.
*
* 3. layout.disk_get_avail_space(), layout.disk_get_avail_slices()
* exclude slices in the reserved list from being considered
* available for general layout use.
*/
int
add_reserved_slice(
dm_descriptor_t slice)
{
dlist_t *item = NULL;
if ((item = dlist_new_item((void *)slice)) == NULL) {
return (ENOMEM);
}
_rsvd_slices = dlist_append(item, _rsvd_slices, AT_HEAD);
return (0);
}
/*
* FUNCTION: is_reserved_slice(dm_descriptor_t slice,
* boolean_t *is_reserved)
*
* INPUT: slice - a dm_descriptor_t slice handle
*
* OUTPUT: is_reserved - pointer to a boolean_t to hold the
* return result.
*
* PURPOSE: Helper which checks to see if the input slice
* was previously reserved.
*
* Check the input name against any reserved slice
* name or alias. is_reserved is set to B_TRUE if the
* input slice is already reserved, B_FALSE otherwise.
*/
int
is_reserved_slice(
dm_descriptor_t slice,
boolean_t *is_reserved)
{
*is_reserved = dlist_contains(
_rsvd_slices, (void *)slice, compare_descriptor_names);
return (0);
}
/*
* FUNCTION: release_reserved_slice()
*
* PURPOSE: Helper which cleans up the module private list of reserved
* slices.
*/
void
release_reserved_slices()
{
dlist_free_items(_rsvd_slices, free);
_rsvd_slices = NULL;
}
/*
* FUNCTION: get_reserved_slices(dlist_t **list)
*
* OUTPUT: list - a dlist_t pointer to hold the returned list of
* reserverd slices.
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Accessor to retrieve the current list of reserved slice
* dm_descriptor_t handles.
*/
int
get_reserved_slices(
dlist_t **list)
{
*list = _rsvd_slices;
return (0);
}
/*
* FUNCTION: add_slice_to_remove(char *name, uint32_t index)
*
* INPUT: name - name of a slice
* index - index for the slice
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Utility function to add the named slice to the list of
* those that need to be "removed" by having their sizes
* set to 0.
*/
int
add_slice_to_remove(
char *name,
uint32_t index)
{
rmvdslice_t *rmvd = NULL;
int error = 0;
assert(name != NULL);
rmvd = (rmvdslice_t *)calloc(1, sizeof (rmvdslice_t));
if (rmvd == NULL) {
error = ENOMEM;
} else {
rmvd->slice_index = index;
if ((rmvd->slice_name = strdup(name)) == NULL) {
free(rmvd);
error = ENOMEM;
} else {
dlist_t *item = dlist_new_item((void *) rmvd);
if (item == NULL) {
free(rmvd->slice_name);
free(rmvd);
error = ENOMEM;
} else {
_rmvd_slices =
dlist_append(item, _rmvd_slices, AT_HEAD);
}
}
}
return (error);
}
/*
* FUNCTION: get_removed_slices()
*
* RETURNS: dlist_t * - pointer to a list of rmvdslice_t structs
*
* PURPOSE: Accessor to retrieve the current list of names of slices
* to be removed.
*/
dlist_t *
get_slices_to_remove(
dlist_t **list)
{
return (_rmvd_slices);
}
static void
free_rmvd_slice(
void *obj)
{
if (obj != NULL) {
rmvdslice_t *rmvd = (rmvdslice_t *)obj;
free(rmvd->slice_name);
free(rmvd);
}
}
/*
* FUNCTION: release_removed_slices()
*
* PURPOSE: Helper which cleans up the module private list of removed
* slices.
*/
void
release_slices_to_remove()
{
dlist_free_items(_rmvd_slices, free_rmvd_slice);
_rmvd_slices = NULL;
}