/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <string.h>
#include <libintl.h>
#include "volume_error.h"
#include "volume_defaults.h"
#include "volume_dlist.h"
#include "volume_output.h"
#include "volume_request.h"
#include "layout.h"
#include "layout_request.h"
#include "layout_concat.h"
#include "layout_discovery.h"
#include "layout_device_cache.h"
#include "layout_device_util.h"
#include "layout_dlist_util.h"
#include "layout_hsp.h"
#include "layout_mirror.h"
#include "layout_slice.h"
#include "layout_stripe.h"
#include "layout_svm_util.h"
#include "layout_validate.h"
#define _LAYOUT_C
/*
* created by the toplevel request
*/
static void unset_hsp_request();
/*
* struct to track which disks have been explicitly modified
* during the layout process...
*
* disk is the dm_descriptor_t of the modified disk
* accessname is the name to access the disk thru
* slices is the list of modified slices on the disk
*/
typedef struct {
char *accessname;
} moddisk_t;
/*
* modified_disks is a list of moddisk_t structs
* tracking disks have been modified during layout.
*/
static int add_modified_disks_to_diskset(
static int release_modified_disks();
static int get_removed_slices_for_disks(
static int get_modified_slices_for_disks(
static int compare_disk_to_moddisk_disk(
void *disk,
void *moddisk);
/*
* FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults)
*
* INPUT: request - a devconfig_t pointer to the toplevel request
* defaults - a results_t pointer to the defaults
*
* RETURNS: int - 0 - on success
* !0 - otherwise
*
* PURPOSE: Public entry point to layout module.
*/
int
{
int error = 0;
/* initialize using the the top-level disk set request... */
return (error);
}
gettext("\nProcessing volume request...\n"));
/* process each volume request, stop on any error */
if (error == 0) {
}
}
if (error == 0) {
/* process HSP request */
if (error == 0) {
}
}
if (error == 0) {
gettext("\nAssembling volume specification...\n"));
/* determine required diskset modifications */
}
if (error == 0) {
gettext("\nVolume request completed successfully.\n"));
}
} else {
gettext("Malformed request, missing top level "
"disk set request."));
}
return (error);
}
/*
* FUNCTION: layout_clean_up()
*
* PURPOSE: function which handles the details of cleaning up cached
* data and any other memory allocated during the layout
* process.
*
* release physical device data structs
* release SVM logical device data structs
* release validation data structs
* release modified device data structs
* release request processing data structs
*
* This function is also exported as part of the public
* interface to the layout module, clients of layout
* are required to call this function if get_layout()
* was called and was not allowed to return. For example,
* if SIGINT was received while a layout request was in
* process.
*/
void
{
(void) release_request_caches();
(void) release_validation_caches();
(void) release_slices_to_remove();
(void) release_modified_slices();
(void) release_modified_disks();
(void) release_reserved_slices();
(void) release_used_slices();
(void) release_usable_devices();
(void) release_svm_names(get_request_diskset());
(void) release_known_devices();
(void) unset_hsp_request(NULL);
(void) unset_request_defaults(NULL);
(void) unset_request_diskset(NULL);
(void) unset_toplevel_request(NULL);
}
/*
* FUNCTION: layout_init(devconfig_t *diskset, defaults_t *defaults)
*
* INPUT: diskset - a devconfig_t pointer to the toplevel request
* defaults - a results_t pointer to the defaults
*
* RETURNS: int - 0 - on success
* !0 - otherwise
*
* PURPOSE: function which handles the details of initializing the layout
* module prior to processing a request.
*
* Determines the requested disk set and validates it.
*
* Scans the physical device configuration.
* Scans the SVM logical device configuration.
*
* Initializes layout private global data structures and does
* semantic validation of the request.
*/
static int
{
int error = 0;
((error = validate_basic_svm_config()) != 0) ||
/* determine & validate requested disk set name */
/* discover known physical and logical devices */
(error = discover_known_devices()) ||
/* validate and remember toplevel request */
/* validate and remember defaults for this request */
if (error != 0) {
return (error);
}
gettext("\nValidating volume request...\n"));
}
if (error == 0) {
}
if (error == 0) {
/* final validation on explicitly requested components */
}
if (error == 0) {
/* final validation on request sizes vs. actual avail space */
}
return (error);
}
/*
* FUNCTION: process_request(devconfig_t *req, dlist_t **results)
*
* INPUT: req - a devconfig_t pointer to the current request
* results - pointer to a list of resulting volumes
*
* RETURNS: int - 0 - on success
* !0 - otherwise
*
* PURPOSE: function which handles the details of an explicit
* volume request.
*
* Determines the requested volume type, whether the
* request contains specific subcomponents and dispatches
* to the appropriate layout function for that type.
*
* Resulting volumes are appended to the results list.
*
* Note that an HSP request is held until all the volumes
* in the request have been successfully composed. This
* ensures that HSP spare sizing can be appropriate to
* those volumes.
*/
static int
{
int ncomps = 0;
int error = 0;
/* HSP processing needs to happen after all other volumes. */
/* set the HSP request aside until all other requests have */
/* been completed successfully */
return (0);
}
if (type == TYPE_STRIPE) {
if (ncomps > 0) {
} else {
}
}
if (type == TYPE_CONCAT) {
if (ncomps > 0) {
} else {
}
}
if (type == TYPE_MIRROR) {
if (ncomps > 0) {
} else {
return (error);
} else {
}
}
}
if (type == TYPE_VOLUME) {
}
return (error);
}
/*
* FUNCTION: process_qos_request(devconfig_t *req, dlist_t **results)
*
* INPUT: req - a devconfig_t pointer to the current request
* results - pointer to a list of resulting volumes
*
* RETURNS: int - 0 - on success
* !0 - otherwise
*
* PURPOSE: function which handles the details of mapping an implicit
* volume request of QoS attributes into a volume type.
*
* Resulting volumes are appended to the results list.
*/
static int
{
int error = 0;
/* get QoS attributes */
if (error == ERR_ATTR_UNSET) {
error = 0;
rlevel = 0;
}
}
if (error == 0) {
if (rlevel == 0) {
} else {
}
}
return (error);
}
/*
* FUNCTION: layout_diskset(request_t *req, dlist_t **results)
*
* INPUT: req - a request_t pointer to the toplevel request
* results - pointer to the list of composed result volumes
*
* RETURNS: int - 0 - on success
* !0 - otherwise
*
* PURPOSE: function which handles the details of completing an layout
* request.
*
* Determines if the disk set specified in the request currently
* exists and sets it up for creation if it doesn't.
*
* Adds new disks required by the result volumes to the disk set.
*
* Attaches the result volumes to the disk set result.
*
* Convert slice and disk names to preferred names.
*
* Attaches the disk set result to the toplevel request.
*/
static int
{
int error = 0;
if (error != 0) {
return (error);
}
/* add resulting volumes */
}
return (error);
}
/*
* FUNCTION: convert_device_names(devconfig_t request, dlist_t *devices)
*
* INPUT: request - a devconfig_t request pointer
* devices - a list of devconfig_t devices
*
* RETURNS: int - 0 - on success
* !0 - on any error
*
* PURPOSE: Utility function to convert any slice or disk drive
* names in a result devconfig_t to the preferred name
* which should be used to access the device.
*
* This convert the temporary names used by layout to
*/
static int
{
int error = 0;
switch (type) {
case TYPE_MIRROR:
case TYPE_STRIPE:
case TYPE_CONCAT:
case TYPE_HSP:
break;
case TYPE_SLICE:
&diskname)) ||
}
break;
}
}
}
return (error);
}
/*
* FUNCTION: add_modified_disk(devconfig_t request, dm_descriptor_t disk);
*
* INPUT: request - a pointr to a devconfig_t request
* disk - dm_descriptor_t handle for a disk that has been modified
*
* SIDEEFFECTS: adds an entry to the _modified_disks list which tracks
* the disks that have been explicitly modified by
* the layout code.
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Adds the input disk to the list of those that have been
* modified.
*
* Disks are modified during layout for two reasons:
*
* 1. any disk that is to be added to the disk set gets
* an explicitly updated label.
*
* 2. once a disk is in the disk set, existing slices
* may be resized or new slices can be added.
*/
int
{
int error = 0;
/* already in list */
return (0);
}
}
} else {
if (error == 0) {
/* add to list of modified disks */
} else {
}
}
}
return (error);
}
/*
* FUNCTION: collect_modified_disks(devconfig_t *request, dlist_t* devs)
*
* INPUT: devs - pointer to a list of composed volumes
* OUTPUT: none -
* SIDEEFFECT: updates the module global list _modified_disks
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper to maintain the list of disks to be added to the
* disk set.
*
* Iterates the input list of devices and determines which
* disks they use. If a disk is not in the _modified_disks
* list, it is added.
*/
static int
{
int error = 0;
switch (type) {
case TYPE_MIRROR:
case TYPE_STRIPE:
case TYPE_CONCAT:
case TYPE_HSP:
break;
case TYPE_SLICE:
break;
}
}
}
return (error);
}
/*
* FUNCTION: add_modified_disks_to_diskset(dlist_t *devices,
* devconfig_t *diskset)
*
* INPUT: devices - pointer to a list of devices
*
* OUTPUT: diskset - pointer to a devconfig_t representing the disk set,
* updated to include modified disks and slices as
* components.
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper to add devconfig_t structs for disks and slices
* to the disk set.
*
* Updates the list of _modified_disks by examining the input
* list of composed devices.
*
* Iterates _modified_disks and creates a devconfig_t component
* for each disk in the list, the list of disks is then attached
* to the input disk set.
*
* Modified slices for disks in the disk set are added as well.
*/
static int
{
int error = 0;
/* add modified disks to disk set's component list */
gettext(" Collecting modified disks...\n"));
/* collect removed slices for modified disks */
/* collect modified slices for modified disks */
for (iter = _modified_disks;
/* New disk, add it to the disk set */
if (error == 0) {
} else {
gettext(" must add %s to disk set \"%s\"\n"),
}
} else {
}
}
/* move moddisk's slice list to disk set comp list */
}
}
if (error == 0) {
} else {
}
return (error);
}
/*
* FUNCTIONS: void release_modified_disks()
*
* INPUT: none -
* OUTPUT: none -
*
* PURPOSE: cleanup the module global list of disks that need
* to be added to the disk set to satisfy the request.
*/
static int
{
}
}
return (0);
}
/*
* FUNCTION: get_removed_slices_for_disks(dlist_t *mod_disks)
*
* INPUT: mod_disks - a list of moddisk_t structs
*
* OUTPUT: mod_disks - the list of moddisk_t structs updated with
* the slices to be removed for each disk
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper to create a list of devconfig_t structs
* for slices on the input disks which need to be
* removed from the system.
*
* Iterates the list of slices to be removed and
* creates a devconfig_t component for each slice
* in the list that is on any of the input modified
* disks.
*
* Slice names are constructed using the modified disk's
* access name to ensure that the correct alias is
* used to get to the slice.
*/
static int
{
int error = 0;
/* collect slices to be removed for the modified disks */
for (iter = get_slices_to_remove();
compare_disk_to_moddisk_disk)) == NULL) {
/* slice on disk that we don't care about */
continue;
}
/* create output slice struct for the removed slice */
/* add to the moddisk's list of slices */
if (error == 0) {
} else {
}
} else {
}
}
return (error);
}
/*
* FUNCTION: get_modified_slices_for_disks(dlist_t *mod_disks)
*
* INPUT: mod_disks - a list of moddisk_t structs
*
* OUTPUT: mod_disks - the list of moddisk_t structs updated with
* the modified slices for each disk
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper to create a list of devconfig_t structs
* for slices on the input disks which have been
* modified for use by layout.
*
* Iterates the list of modified slices and creates a
* devconfig_t component for each slice in the list
* that is on any of the input modified disks.
*
* Slice names are constructed using the modified disk's
* access name to ensure that the correct alias is
* used to get to the slice.
*/
int
{
int error = 0;
for (iter = get_modified_slices();
/* only add modified slices that were sources */
if ((mods->times_modified == 0) ||
continue;
}
compare_disk_to_moddisk_disk)) == NULL) {
/* slice on disk that we don't care about */
continue;
}
/* create output slice struct for the modified slice */
&stblk)) != 0) ||
/* add to the moddisk's list of slices */
if (error == 0) {
} else {
}
} else {
}
}
return (error);
}
/*
* FUNCTION: compare_disk_to_moddisk_disk(void *disk, void *moddisk)
*
* INPUT: disk - opaque pointer to a dm_descriptor_t
* moddisk - opaque moddisk_t pointer
*
* RETURNS: int - 0 - if disk == moddisk->disk
* !0 - otherwise
*
* PURPOSE: dlist_t helper which compares the input disk dm_descriptor_t
* handle to the disk dm_descriptor_t handle in the input
* moddisk_t struct.
*
* Comparison is done via compare_descriptor_names.
*/
static int
void *disk,
void *moddisk)
{
return (compare_descriptor_names((void *)disk,
}
/*
* FUNCTIONS: void set_hsp_request()
*
* INPUT: none -
* OUTPUT: none -
*
* PURPOSE: set the module global HSP request struct.
*/
static void
{
_hsp_request = req;
}
/*
* FUNCTIONS: void unset_hsp_request()
*
* INPUT: none -
* OUTPUT: none -
*
* PURPOSE: unset the module global HSP request struct.
*/
static void
{
_hsp_request = NULL;
}
/*
* FUNCTION: process_hsp_request(devconfig_t *req, dlist_t **results)
* INPUT: req - pointer to the toplevel disk set devconfig_t request
* results - pointer to a list of composed results
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper which determines HSP processing for the
* composed volumes which need HSP spares.
*/
static int
{
int error = 0;
if (_hsp_request != NULL) {
gettext("\nProcessing HSP...\n"));
}
if (_hsp_devices == NULL) {
/* no devices -> no HSP */
gettext(" No devices require hot spares...\n"));
} else {
results)) != 0) ||
}
return (error);
}
/*
* FUNCTION: add_to_hsp_list(dlist_t* list)
* INPUT: devs - pointer to a list of composed volumes
* OUTPUT: none -
* SIDEEFFECT: updates the module global list _hsp_devices
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper to update the list of devices which need HSP spares.
*
* Iterates the input list of devices and adds them them to the
* module provate list of devices needing spares.
*/
int
{
int error = 0;
break;
}
}
return (error);
}
/*
* FUNCTION: string_case_compare(
* char *str1, char *str2)
*
* INPUT: str1 - char *
* str2 - char *
*
* RETURNS: int - <0 - if str1 < str2
* 0 - if str1 == str2
* >0 - if str1 > str2
*
* PURPOSE: More robust case independent string comparison function.
*
* Assumes str1 and str2 are both char *
*
* Compares the lengths of each and if equivalent compares
* the strings using strcasecmp.
*/
int
char *str1,
char *str2)
{
int result = 0;
}
return (result);
}