/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Just in case we're not in a build environment, make sure that
* TEXT_DOMAIN gets set to something.
*/
#if !defined(TEXT_DOMAIN)
#endif
/*
* hotspares utilities
*/
#include <meta.h>
/*
* FUNCTION: meta_get_hsp_names()
* INPUT: sp - the set name to get hotspares from
* options - options from the command line
* OUTPUT: hspnlpp - list of all hotspare names
* ep - return error pointer
* RETURNS: int - -1 if error, 0 success
* PURPOSE: returns a list of all hotspares in the metadb
* for all devices in the specified set
*/
/*ARGSUSED*/
int
int options,
)
{
int i;
/* we must have a set */
/* get number of devices */
} else {
return (-1);
}
}
/* malloc minor number buffer to be filled by ioctl */
return (ENOMEM);
}
return (-1);
}
/* get name */
== NULL)
goto out;
/* append to list */
/* next device */
m_ptr++;
}
}
out:
return (-1);
}
/*
* get information of a specific hotspare pool from driver
*/
static get_hsp_t *
)
{
/* should have a set */
/* get size of unit structure */
return (NULL);
}
/* get actual unit structure */
return (NULL);
}
}
/*
* free hotspare pool unit
*/
void
)
{
}
}
/*
* get hotspare pool unit (common)
*/
md_hsp_t *
int fast,
)
{
/* must have set */
/* short circuit */
/* get unit */
return (NULL);
/* allocate hsp */
/* allocate hotspares */
/* if empty hotspare pool, we are done */
/* get name, refcount */
/* get hotspares */
/* get hotspare name */
goto out;
/* get hotspare state */
goto out;
}
}
/* cleanup, return success */
return (hspp);
/* cleanup, return error */
out:
return (NULL);
}
/*
* get hotspare pool unit
*/
md_hsp_t *
)
{
}
/*
* check hotspare pool for dev
*/
static int
)
{
uint_t i;
/* should be in the same set */
/* get unit */
return (-1);
/* look in hotspares */
/* check overlap */
if (metaismeta(hsnp))
continue;
return (-1);
}
/* return success */
return (0);
}
/*
* check to see if we're in a hotspare pool
*/
int
)
{
mdhspnamelist_t *p;
int rval = 0;
/* should have a set */
/* for each hotspare pool */
return (-1);
/* check hotspare pool */
rval = -1;
break;
}
}
/* cleanup, return success */
return (rval);
}
/*
* check hotspare
*/
int
)
{
/* make sure we have a disk */
return (-1);
/* check to ensure that it is not already in use */
return (-1);
}
/* make sure it is in the set */
return (-1);
/* make sure its not in a metadevice */
return (-1);
/* return success */
return (0);
}
/*
* print hsp
*/
static int
char *fname,
)
{
/* print name */
goto out;
/* print hotspares */
/* print hotspare */
/*
* then just print out the cxtxdxsx or the dx, metainit
* will assume the default, otherwise we need the full
* pathname to make sure this works as we intend.
*/
/* not standard path, print full pathname */
goto out;
} else {
/* standard path, just print ctd or d value */
goto out;
}
}
/* terminate last line */
goto out;
/* success */
rval = 0;
/* cleanup, return error */
out:
if (rval != 0)
return (rval);
}
/*
* hotspare state name
*/
char *
)
{
/* grab time */
switch (state) {
case HSS_AVAILABLE:
case HSS_RESERVED:
case HSS_BROKEN:
case HSS_UNUSED:
default:
}
}
/*
* report hsp
*/
static int
mdnamelist_t **nlpp,
char *fname,
md_error_t *ep,
)
{
int large_hs_dev_cnt = 0;
int fn_hs_dev_cnt = 0;
if (options & PRINT_LARGEDEVICES) {
large_hs_dev_cnt += 1;
!= 0)
goto out;
}
}
if (large_hs_dev_cnt == 0) {
rval = 0;
goto out;
}
}
rval = 0;
goto out;
}
fn_hs_dev_cnt += 1;
!= 0)
goto out;
}
}
/* print header */
goto out;
}
/*
* This allows the length
* of the ctd to vary from small to large without
* looking horrible.
*/
/*
* if the length is to short to print out all of the header
* force the matter
*/
len += 2;
if (options & PRINT_LARGEDEVICES) {
"%s: 1 hot spare (1 big device)\n\t%-*.*s "
"%-12.12s%-8.6s\t\t%s\n",
goto out;
}
} else {
"%s: 1 hot spare\n\t%-*.*s %-12.12s%-8.6s\t\t%s\n",
goto out;
}
}
} else {
/*
* This allows the length
* of the ctd to vary from small to large without
* looking horrible.
*/
len = 0;
}
len += 2;
if (options & PRINT_LARGEDEVICES) {
"%s: %u hot spares (%d big device(s))\n\t%-*.*s "
"%-12.12s%-8.6s\t\t%s\n",
goto out;
}
} else {
"%-12.12s%-8.6s\t\t%s\n",
goto out;
}
}
}
/* print hotspares */
char *hs_state;
char *timep;
/* populate the key in the name_p structure */
return (-1);
}
if (options & PRINT_LARGEDEVICES) {
continue;
}
/* determine if devid does NOT exist */
if (options & PRINT_DEVID) {
else {
}
}
/* print hotspare */
/*
* This allows the length
* of the ctd to vary from small to large without
* looking horrible.
*/
if (! (options & PRINT_TIMES)) {
" %-*s %-12s %lld blocks\t%s\n",
goto out;
}
} else {
" %-*s\t %-11s %8lld blocks%s\t%s\n",
goto out;
}
}
}
/* add extra line */
goto out;
/* success */
rval = 0;
/* cleanup, return error */
out:
if (rval != 0)
return (rval);
}
/*
*/
int
mdnamelist_t **nlpp,
char *fname,
)
{
/* should have same set */
/* print all hsps */
mdhspnamelist_t *p;
int cnt;
int rval = 0;
return (-1);
else if (cnt == 0)
return (0);
/* recurse */
rval = -1;
}
/* cleanup, return success */
return (rval);
}
/* get unit structure */
return (-1);
/* print appropriate detail */
if (options & PRINT_SHORT)
else
}
/*
* check for valid hotspare pool
*/
int
)
{
return (-1);
return (0);
}
/*
* invalidate hotspare pool info
*/
void
)
{
/* free it up */
return;
/* clear cache */
}
/*
* FUNCTION: del_hsp_name_mn_sides()
* INPUT: sp - set name
* curside - side of this node
* key - key of records to delete
* OUTPUT: ep - error information
* RETURNS: none.
* PURPOSE: There are name records for each side in a set. This
* function deletes the records associated with the specified
* key for all sides except curside. This function is used
* when the set is a multinode set.
*/
static void
)
{
continue;
if (error_seen == FALSE) {
error_seen = TRUE;
}
}
}
}
/*
* FUNCTION: del_hsp_name_trad_sides()
* INPUT: sp - set name
* curside - side of this node
* key - key of records to delete
* OUTPUT: ep - error information
* RETURNS: none.
* PURPOSE: There are name records for each side in a set. This
* function deletes the records associated with the specified
* key for all sides except curside. This function is used
* when the set is a traditional set.
*/
static void
)
{
int i;
for (i = 0; i < MD_MAXSIDES; i++) {
if (i == curside)
continue;
if (error_seen == FALSE) {
error_seen = TRUE;
}
}
}
}
}
/*
* FUNCTION: del_hsp_keys()
* INPUT: sp - set name
* hspid - ID of records to delete
* OUTPUT: ep - error information
* RETURNS: 0 - success
* -1 - error
* PURPOSE: Remove the NM records associated with hspid from all sides
* of the set. Missing records are not considered to be an
* error. The key associated with the current side is removed
* last.
*
* This function is very similar to del_key_name(), except it
* does not require any device look up. This is because the
* hot spare pool is not a device.
*/
static int
{
/*
* If there is no key, this means that the hot spare was created
* before the introduction of friendly names. Thus, the is no NM
* record and nothing for us to do in this function.
*/
return (0);
/* Find our current side */
mdclrerror(ep);
return (-1);
/*
* If not the local set, we need to process the non-local sides
* first.
*/
if (!metaislocalset(sp)) {
return (-1);
if (MD_MNSET_DESC(sd)) {
/* Multinode set. Sides are in a linked list. */
&first_error);
} else {
/* Sides are in an array. */
&first_error);
}
}
/* Now delete the name for the current side. */
if (! mdisok(&first_error))
}
/*
* FUNCTION: add_hsp_name_mn_sides()
* INPUT: sp - set name
* curside - side number for this node
* key - key to use for the name record
* hsp_name - name of the hot spare
* OUTPUT: ep - error information
* RETURNS: 0 indicates success, and -1 indicates failure.
* PURPOSE: Once the name record has been added for the current side,
* this function adds the record to the remaining sides. This
* function is to be used when the set is a multinode set.
* The side designated by curside will be ignored when adding
* records.
*/
static int
char *hsp_name,
)
{
continue;
return (-1);
}
}
return (0);
}
/*
* FUNCTION: add_hsp_name_trad_sides()
* INPUT: sp - set name
* curside - side number for this node
* key - key to use for the name record
* hsp_name - name of the hot spare
* OUTPUT: ep - error information
* RETURNS: 0 indicates success, and -1 indicates failure.
* PURPOSE: Once the name record has been added for the current side,
* this function adds the record to the remaining sides. This
* function is to be used when the set is a traditional set.
* The side designated by curside will be ignored when adding
* records.
*/
static int
char *hsp_name,
)
{
int i;
for (i = 0; i < MD_MAXSIDES; i++) {
if (i == curside)
continue;
return (-1);
}
}
}
return (0);
}
/*
* FUNCTION: add_hsp_name()
* INPUT: sp - Name of the set containing the hsp
* hsp_name - Hot spare pool name to be added
* OUTPUT: ep - Error information
* RETURNS: If successful the key of the newly added record is
* returned. MD_KEYBAD is returned to indicate a failure.
* PURPOSE: This function creates a new NM record containing the name
* of the hotspare pool. A record containing the name is
* added to each active side, but the record is added first to
* the current side. This function is modeled on
* add_key_name() in meta_namespace. The difference is that
* there is no device associated with a hot spare pool
*/
static hsp_t
char *hsp_name,
)
{
return (MD_KEYBAD);
}
return (MD_KEYBAD);
}
mdclrerror(ep);
return (MD_HSPID_WILD);
/* First add the record for the side of the current node. */
if (key == -1) {
goto cleanup;
}
/* Make sure that we can use the key */
if (!HSP_KEY_OK(key)) {
hsp_name);
goto cleanup;
}
/*
* Now that we have a key, we will use it to add a record to the
* rest of the sides in the set. For multinode sets, the sides are
* in a linked list that is anchored on the set descriptor. For
* traditional sets the side information is in an array in the set
* descriptor.
*/
if (!metaislocalset(sp)) {
goto cleanup;
}
if (MD_MNSET_DESC(sd)) {
/* Multinode set. Sides are in linked list. */
goto cleanup;
}
} else {
/* Traditional set. Sides are in an array. */
goto cleanup;
}
}
}
/* Get rid records that we added. */
return (MD_HSPID_WILD);
}
/*
*/
int
)
{
mdnamelist_t *p;
/* should have a set */
/* clear cache */
/* setup hotspare pool info */
/* Get key for hot spare pool name record. */
if (options & MDCMD_DOIT) {
/* First see if the name record already exists. */
mdclrerror(ep);
return (-1);
/*
* If the error is ENOENT, then we will create a
* hot spare pool name records. For other types of
* errors, however, we'll bail out.
*/
return (-1);
mdclrerror(ep);
/* make sure that the name isn't already in use */
if ((shs.shs_hot_spare_pool =
return (-1);
}
}
}
/* add empty hotspare pool */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
if (options & MDCMD_DOIT) {
(void) del_hsp_keys(sp,
&ignore_error);
}
}
goto success;
}
/* add hotspares */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
/* should be in same set */
/* check it out */
return (-1);
return (-1);
else if (size == 0)
return (-1);
return (-1);
/* In dryrun mode (DOIT not set) we must not alter the mddb */
if (options & MDCMD_DOIT) {
/* store name in namespace */
return (-1);
}
if ((options & MDCMD_DOIT) &&
}
}
}
/* print success message */
if (options & MDCMD_PRINT) {
"%s: Hotspare pool is setup\n"),
"%s: Hotspare is added\n"),
} else {
"%s: Hotspares are added\n"),
}
}
/* return success */
return (0);
}
/*
* FUNCTION: meta_hsp_delete()
* INPUT: sp - Name of the set containing the hsp
* hspnp - Hot spare pool name information
* options - Options from command line
* OUTPUT: ep - Error information
* RETURNS: 0 on success and -1 on failure.
* PURPOSE: Common code to delete an empty hot spare pool.
*/
static int
)
{
/* setup hotspare pool info */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
/* Remove hsp record. */
/* Get rid of hsp NM records */
if ((options & MDCMD_DOIT) &&
return (-1);
}
return (0);
}
/*
* delete hotspares from pool
*/
int
)
{
mdnamelist_t *p;
/* should have a set */
/* clear cache */
/* setup hotspare pool info */
/* delete empty hotspare pool */
return (-1);
goto success;
}
/* delete hotspares */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
/* should be in same set */
/* delete hotspare */
}
/* print success message */
if (options & MDCMD_PRINT) {
"%s: Hotspare pool is cleared\n"),
"%s: Hotspare is deleted\n"),
} else {
"%s: Hotspares are deleted\n"),
}
}
/* return success */
return (0);
}
/*
* replace hotspare in pool
*/
int
)
{
int rebind;
int ret;
/* should be in same set */
/* save new binding incase this is a rebind where oldnp==newnp */
/* invalidate, then get the hotspare (fill in oldnp from metadb) */
return (-1);
/* the old device binding is now established */
/*
* check for the case where oldnp and newnp indicate the same
* device, but the dev_t of the device has changed between old
* and new. This is called a rebind. On entry the dev_t
* represents the new device binding determined from the
* filesystem (meta_getdev). After calling meta_get_hsp
* oldnp (and maybe newnp if this is a rebind) is updated based
* to the old binding from the metadb (done by metakeyname).
*/
rebind = 1;
} else {
rebind = 0;
}
if (rebind) {
}
/*
* Save a copy of the devid associated with the new disk, the reason
* is that the meta_check_hotspare() call could cause the devid to
* be changed to that of the devid that is currently stored in the
* replica namespace for the disk in question. This devid could be
* stale if we are replacing the disk. The function that overwrites
* the devid is dr2drivedesc().
*/
/* if it's a multi-node diskset clear new_devidp */
if (!metaislocalset(sp)) {
return (-1);
}
if (MD_MNSET_DESC(sd)) {
new_devidp = NULL;
}
}
/* check it out */
return (-1);
}
mdclrerror(ep);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/*
* Copy back the saved devid.
*/
if (new_devidp != NULL) {
new_devidp = NULL;
}
/* In dryrun mode (DOIT not set) we must not alter the mddb */
if (options & MDCMD_DOIT) {
/* store name in namespace */
return (-1);
}
/*
* We are 'rebind'ing a disk that is in a diskset so as well
* as updating the diskset's namespace the local set needs
* to be updated because it also contains a reference to the
* disk in question.
*/
ep);
if (ret != METADEVADM_SUCCESS) {
/*
* In dryrun mode (DOIT not set) we must not alter
* the mddb
*/
if (options & MDCMD_DOIT) {
mdclrerror(&xep);
return (-1);
}
}
}
/* replace hotspare */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
if (options & MDCMD_DOIT) {
}
}
/* clear cache */
/* let em know */
if (options & MDCMD_PRINT) {
"%s: Hotspare %s is replaced with %s\n"),
}
/* return success */
return (0);
}
/*
* enable hotspares
*/
int
)
{
/* should have a set */
/* setup device info */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
/* get the list of hotspare names */
goto out;
/* enable hotspares for each components */
int rebind = 0;
/* get the file_system dev binding */
return (-1);
/*
* search for the component in each hotspare pool
* and replace it (instead of enable) if the binding
* has changed.
*/
/*
* in_hsp will call meta_get_hsp which will fill
* in hspnp with metadb version of component
*/
/*
* check for the case where the dev_t has
* changed between the filesystem and the
* metadb. This is called a rebind, and
* is handled by meta_hs_replace.
*/
/*
* establish file system binding
*/
rebind++;
if (rval != 0)
goto out;
}
}
}
if (rebind)
continue;
/* enable the component in all hotspares that use it */
goto out;
goto out;
goto out;
goto out;
goto out;
}
/* enable hotspare */
goto out;
}
/*
* Are we dealing with a non-local set? If so need to update
* the local namespace so that the disk record has the correct
* devid.
*/
if (!metaislocalset(sp)) {
if (rval != METADEVADM_SUCCESS) {
/*
* Failed to update the local set. Nothing to
* do here apart from report the error. The
* namespace is most likely broken and some
* form of remedial recovery is going to
* be required.
*/
mdclrerror(ep);
}
}
/* clear cache */
/* let em know */
if (options & MDCMD_PRINT) {
"hotspare %s is enabled\n"),
}
}
/* clear whole cache */
}
/* return success */
rval = 0;
out:
if (hspnlp)
return (rval);
}
/*
* check for dups in the hsp itself
*/
static int
)
{
uint_t h;
for (h = 0; (h < hsi); ++h) {
return (-1);
}
return (0);
}
/*
* check hsp
*/
/*ARGSUSED2*/
int
)
{
/* check hotspares */
/* check hotspare */
return (-1);
return (-1);
} else if (size == 0) {
}
/* check this hsp too */
return (-1);
}
/* return success */
return (0);
}
/*
* create hsp
*/
int
)
{
/* validate hsp */
return (-1);
/* if we're not doing anything, return success */
if (! (options & MDCMD_DOIT))
return (0);
/* create hsp */
}
options |= MDCMD_INIT;
/* cleanup, return success */
return (rval);
}
/*
* initialize hsp
* NOTE: this functions is metainit(1m)'s command line parser!
*/
int
mdsetname_t **spp,
int argc,
char *argv[],
)
{
/* get hsp name */
if (argc < 1)
goto syntax;
goto out;
if (!(options & MDCMD_NOLOCK)) {
/* grab set lock */
goto out;
goto out;
}
/* see if it exists already */
}
goto out;
goto out;
} else {
mdclrerror(ep);
}
/* parse general options */
optind = 0;
opterr = 0;
goto options;
/* allocate hsp */
if (argc > 0) {
}
/* setup pool */
/* parse hotspares */
++hsi) {
/* parse hotspare name */
goto out;
}
/* we should be at the end */
if (argc != 0)
goto syntax;
/* create hotspare pool */
goto out;
rval = 0; /* success */
goto out;
/* syntax error */
goto out;
/* options error */
goto out;
/* cleanup, return error */
out:
return (rval);
}
/*
* reset hotspare pool
*/
int
)
{
uint_t i;
/* should have the same set */
/* reset all hotspares */
mdhspnamelist_t *p;
/* for each hotspare pool */
rval = 0;
return (-1);
/* reset hotspare pool */
/*
* If this is a multi-node set, we send a series
* of individual metaclear commands.
*/
rval = -1;
break;
}
} else {
ep) != 0) {
rval = -1;
break;
}
}
}
/* cleanup, return success */
return (rval);
}
/* get unit structure */
return (-1);
/* make sure nobody owns us */
}
/* clear hotspare pool members */
/* clear cache */
/* clear hotspare */
/* If DOIT is not set, it's a dryrun */
if ((options & MDCMD_DOIT) == 0) {
}
goto out;
}
}
/* clear hotspare pool */
goto out;
rval = 0; /* success */
/* let em know */
if (options & MDCMD_PRINT) {
"%s: Hotspare pool is cleared\n"),
}
/* clear subdevices (nothing to do) */
/* cleanup, return success */
out:
return (rval);
}