2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1992, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Just in case we're not in a build environment, make sure that
2N/A * TEXT_DOMAIN gets set to something.
2N/A */
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/A/*
2N/A * hotspares utilities
2N/A */
2N/A
2N/A#include <meta.h>
2N/A#include <sys/lvm/md_hotspares.h>
2N/A#include <sys/lvm/md_convert.h>
2N/A
2N/A/*
2N/A * FUNCTION: meta_get_hsp_names()
2N/A * INPUT: sp - the set name to get hotspares from
2N/A * options - options from the command line
2N/A * OUTPUT: hspnlpp - list of all hotspare names
2N/A * ep - return error pointer
2N/A * RETURNS: int - -1 if error, 0 success
2N/A * PURPOSE: returns a list of all hotspares in the metadb
2N/A * for all devices in the specified set
2N/A */
2N/A/*ARGSUSED*/
2N/Aint
2N/Ameta_get_hsp_names(
2N/A mdsetname_t *sp,
2N/A mdhspnamelist_t **hspnlpp,
2N/A int options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_i_getnum_t gn; /* MD_IOCGET_NUM params */
2N/A minor_t *minors = NULL;
2N/A minor_t *m_ptr;
2N/A int i;
2N/A
2N/A /* we must have a set */
2N/A assert(sp != NULL);
2N/A
2N/A (void) memset(&gn, 0, sizeof (gn));
2N/A MD_SETDRIVERNAME(&gn, MD_HOTSPARES, sp->setno);
2N/A
2N/A /* get number of devices */
2N/A if (metaioctl(MD_IOCGET_NUM, &gn, &gn.mde, NULL) != 0) {
2N/A if (mdiserror(&gn.mde, MDE_UNIT_NOT_FOUND)) {
2N/A mdclrerror(&gn.mde);
2N/A } else {
2N/A (void) mdstealerror(ep, &gn.mde);
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if (gn.size > 0) {
2N/A /* malloc minor number buffer to be filled by ioctl */
2N/A if ((minors = (minor_t *)malloc(
2N/A gn.size * sizeof (minor_t))) == 0) {
2N/A return (ENOMEM);
2N/A }
2N/A gn.minors = (uintptr_t)minors;
2N/A if (metaioctl(MD_IOCGET_NUM, &gn, &gn.mde, NULL) != 0) {
2N/A (void) mdstealerror(ep, &gn.mde);
2N/A free(minors);
2N/A return (-1);
2N/A }
2N/A m_ptr = minors;
2N/A for (i = 0; i < gn.size; i++) {
2N/A mdhspname_t *hspnp;
2N/A
2N/A
2N/A /* get name */
2N/A if ((hspnp = metahsphspname(&sp, *m_ptr, ep))
2N/A == NULL)
2N/A goto out;
2N/A
2N/A /* append to list */
2N/A (void) metahspnamelist_append(hspnlpp, hspnp);
2N/A
2N/A /* next device */
2N/A m_ptr++;
2N/A }
2N/A free(minors);
2N/A }
2N/A return (gn.size);
2N/A
2N/Aout:
2N/A if (minors != NULL)
2N/A free(minors);
2N/A metafreehspnamelist(*hspnlpp);
2N/A *hspnlpp = NULL;
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * get information of a specific hotspare pool from driver
2N/A */
2N/Astatic get_hsp_t *
2N/Aget_hspinfo(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_i_get_t mig;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* get size of unit structure */
2N/A (void) memset(&mig, 0, sizeof (mig));
2N/A MD_SETDRIVERNAME(&mig, MD_HOTSPARES, sp->setno);
2N/A mig.id = hspnp->hsp;
2N/A if (metaioctl(MD_IOCGET, &mig, &mig.mde, hspnp->hspname) != 0) {
2N/A (void) mdstealerror(ep, &mig.mde);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* get actual unit structure */
2N/A assert(mig.size > 0);
2N/A mig.mdp = (uintptr_t)Zalloc(mig.size);
2N/A if (metaioctl(MD_IOCGET, &mig, &mig.mde, hspnp->hspname) != 0) {
2N/A (void) mdstealerror(ep, &mig.mde);
2N/A Free((void *)(uintptr_t)mig.mdp);
2N/A return (NULL);
2N/A }
2N/A return ((get_hsp_t *)(uintptr_t)mig.mdp);
2N/A}
2N/A
2N/A/*
2N/A * free hotspare pool unit
2N/A */
2N/Avoid
2N/Ameta_free_hsp(
2N/A md_hsp_t *hspp
2N/A)
2N/A{
2N/A if (hspp->hotspares.hotspares_val != NULL) {
2N/A assert(hspp->hotspares.hotspares_len > 0);
2N/A Free(hspp->hotspares.hotspares_val);
2N/A }
2N/A Free(hspp);
2N/A}
2N/A
2N/A/*
2N/A * get hotspare pool unit (common)
2N/A */
2N/Amd_hsp_t *
2N/Ameta_get_hsp_common(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A int fast,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A get_hsp_t *ghsp;
2N/A md_hsp_t *hspp;
2N/A uint_t hsi;
2N/A
2N/A /* must have set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* short circuit */
2N/A if (hspnp->unitp != NULL)
2N/A return (hspnp->unitp);
2N/A
2N/A /* get unit */
2N/A if ((ghsp = get_hspinfo(sp, hspnp, ep)) == NULL)
2N/A return (NULL);
2N/A
2N/A /* allocate hsp */
2N/A hspp = Zalloc(sizeof (*hspp));
2N/A
2N/A /* allocate hotspares */
2N/A hspp->hotspares.hotspares_len = ghsp->ghsp_nhotspares;
2N/A
2N/A /* if empty hotspare pool, we are done */
2N/A if (hspp->hotspares.hotspares_len != 0)
2N/A hspp->hotspares.hotspares_val =
2N/A Zalloc(hspp->hotspares.hotspares_len *
2N/A sizeof (*hspp->hotspares.hotspares_val));
2N/A
2N/A /* get name, refcount */
2N/A hspp->hspnamep = hspnp;
2N/A hspp->refcount = ghsp->ghsp_refcount;
2N/A
2N/A /* get hotspares */
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A mdkey_t hs_key = ghsp->ghsp_hs_keys[hsi];
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A get_hs_params_t ghs;
2N/A
2N/A /* get hotspare name */
2N/A hsp->hsnamep = metakeyname(&sp, hs_key, fast, ep);
2N/A if (hsp->hsnamep == NULL)
2N/A goto out;
2N/A
2N/A /* get hotspare state */
2N/A (void) memset(&ghs, 0, sizeof (ghs));
2N/A MD_SETDRIVERNAME(&ghs, MD_HOTSPARES, sp->setno);
2N/A ghs.ghs_key = hs_key;
2N/A if (metaioctl(MD_IOCGET_HS, &ghs, &ghs.mde, NULL) != 0) {
2N/A (void) mdstealerror(ep, &ghs.mde);
2N/A goto out;
2N/A }
2N/A hsp->state = ghs.ghs_state;
2N/A hsp->size = ghs.ghs_number_blks;
2N/A hsp->timestamp = ghs.ghs_timestamp;
2N/A hsp->revision = ghs.ghs_revision;
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A Free(ghsp);
2N/A hspnp->unitp = hspp;
2N/A return (hspp);
2N/A
2N/A /* cleanup, return error */
2N/Aout:
2N/A Free(ghsp);
2N/A meta_free_hsp(hspp);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * get hotspare pool unit
2N/A */
2N/Amd_hsp_t *
2N/Ameta_get_hsp(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A return (meta_get_hsp_common(sp, hspnp, 0, ep));
2N/A}
2N/A
2N/A/*
2N/A * check hotspare pool for dev
2N/A */
2N/Astatic int
2N/Ain_hsp(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdname_t *np,
2N/A diskaddr_t slblk,
2N/A diskaddr_t nblks,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_hsp_t *hspp;
2N/A uint_t i;
2N/A
2N/A /* should be in the same set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* get unit */
2N/A if ((hspp = meta_get_hsp(sp, hspnp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* look in hotspares */
2N/A for (i = 0; (i < hspp->hotspares.hotspares_len); ++i) {
2N/A md_hs_t *hs = &hspp->hotspares.hotspares_val[i];
2N/A mdname_t *hsnp = hs->hsnamep;
2N/A
2N/A /* check overlap */
2N/A if (metaismeta(hsnp))
2N/A continue;
2N/A if (meta_check_overlap(hspnp->hspname, np, slblk, nblks,
2N/A hsnp, 0, -1, ep) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * check to see if we're in a hotspare pool
2N/A */
2N/Aint
2N/Ameta_check_inhsp(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A diskaddr_t slblk,
2N/A diskaddr_t nblks,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdhspnamelist_t *hspnlp = NULL;
2N/A mdhspnamelist_t *p;
2N/A int rval = 0;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A
2N/A /* for each hotspare pool */
2N/A if (meta_get_hsp_names(sp, &hspnlp, 0, ep) < 0)
2N/A return (-1);
2N/A for (p = hspnlp; (p != NULL); p = p->next) {
2N/A mdhspname_t *hspnp = p->hspnamep;
2N/A
2N/A /* check hotspare pool */
2N/A if (in_hsp(sp, hspnp, np, slblk, nblks, ep) != 0) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A metafreehspnamelist(hspnlp);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * check hotspare
2N/A */
2N/Aint
2N/Ameta_check_hotspare(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdchkopts_t options = (MDCHK_ALLOW_HS);
2N/A
2N/A /* make sure we have a disk */
2N/A if (metachkcomp(np, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* check to ensure that it is not already in use */
2N/A if (meta_check_inuse(sp, np, MDCHK_INUSE, ep) != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* make sure it is in the set */
2N/A if (meta_check_inset(sp, np, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* make sure its not in a metadevice */
2N/A if (meta_check_inmeta(sp, np, options, 0, -1, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * print hsp
2N/A */
2N/Astatic int
2N/Ahsp_print(
2N/A md_hsp_t *hspp,
2N/A char *fname,
2N/A FILE *fp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A uint_t hsi;
2N/A int rval = -1;
2N/A
2N/A /* print name */
2N/A if (fprintf(fp, "%s", hspp->hspnamep->hspname) == EOF)
2N/A goto out;
2N/A
2N/A /* print hotspares */
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A
2N/A /* print hotspare */
2N/A /*
2N/A * If the path is our standard /dev/rdsk or /dev/md/rdsk
2N/A * then just print out the cxtxdxsx or the dx, metainit
2N/A * will assume the default, otherwise we need the full
2N/A * pathname to make sure this works as we intend.
2N/A */
2N/A if ((strstr(hsp->hsnamep->rname, "/dev/rdsk") == NULL) &&
2N/A (strstr(hsp->hsnamep->rname, "/dev/md/rdsk") == NULL) &&
2N/A (strstr(hsp->hsnamep->rname, "/dev/td/") == NULL)) {
2N/A /* not standard path, print full pathname */
2N/A if (fprintf(fp, " %s", hsp->hsnamep->rname) == EOF)
2N/A goto out;
2N/A } else {
2N/A /* standard path, just print ctd or d value */
2N/A if (fprintf(fp, " %s", hsp->hsnamep->cname) == EOF)
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* terminate last line */
2N/A if (fprintf(fp, "\n") == EOF)
2N/A goto out;
2N/A
2N/A /* success */
2N/A rval = 0;
2N/A
2N/A /* cleanup, return error */
2N/Aout:
2N/A if (rval != 0)
2N/A (void) mdsyserror(ep, errno, fname);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * hotspare state name
2N/A */
2N/Achar *
2N/Ahs_state_to_name(
2N/A md_hs_t *hsp,
2N/A md_timeval32_t *tvp
2N/A)
2N/A{
2N/A hotspare_states_t state = hsp->state;
2N/A
2N/A /* grab time */
2N/A if (tvp != NULL)
2N/A *tvp = hsp->timestamp;
2N/A
2N/A switch (state) {
2N/A case HSS_AVAILABLE:
2N/A return (dgettext(TEXT_DOMAIN, "Available"));
2N/A case HSS_RESERVED:
2N/A return (dgettext(TEXT_DOMAIN, "In use"));
2N/A case HSS_BROKEN:
2N/A return (dgettext(TEXT_DOMAIN, "Broken"));
2N/A case HSS_UNUSED:
2N/A default:
2N/A return (dgettext(TEXT_DOMAIN, "invalid"));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * report hsp
2N/A */
2N/Astatic int
2N/Ahsp_report(
2N/A md_hsp_t *hspp,
2N/A mdnamelist_t **nlpp,
2N/A char *fname,
2N/A FILE *fp,
2N/A mdprtopts_t options,
2N/A md_error_t *ep,
2N/A mdsetname_t *sp
2N/A)
2N/A{
2N/A uint_t hsi;
2N/A int rval = -1;
2N/A char *devid = "";
2N/A mdname_t *didnp = NULL;
2N/A uint_t len;
2N/A int large_hs_dev_cnt = 0;
2N/A int fn_hs_dev_cnt = 0;
2N/A
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A if (hsp->revision & MD_64BIT_META_DEV) {
2N/A large_hs_dev_cnt += 1;
2N/A if (meta_getdevs(sp, hsp->hsnamep, nlpp, ep)
2N/A != 0)
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A if (large_hs_dev_cnt == 0) {
2N/A rval = 0;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A if (options & PRINT_FN) {
2N/A if (!HSP_ID_IS_FN(hspp->hspnamep->hsp)) {
2N/A rval = 0;
2N/A goto out;
2N/A }
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A fn_hs_dev_cnt += 1;
2N/A if (meta_getdevs(sp, hsp->hsnamep, nlpp, ep)
2N/A != 0)
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* print header */
2N/A if (hspp->hotspares.hotspares_len == 0) {
2N/A if (fprintf(fp, dgettext(TEXT_DOMAIN, "%s: is empty\n"),
2N/A hspp->hspnamep->hspname) == EOF) {
2N/A goto out;
2N/A }
2N/A } else if (hspp->hotspares.hotspares_len == 1) {
2N/A
2N/A /*
2N/A * This allows the length
2N/A * of the ctd to vary from small to large without
2N/A * looking horrible.
2N/A */
2N/A
2N/A len = strlen(hspp->hotspares.hotspares_val[0].hsnamep->cname);
2N/A /*
2N/A * if the length is to short to print out all of the header
2N/A * force the matter
2N/A */
2N/A len = max(len, strlen(dgettext(TEXT_DOMAIN, "Device")));
2N/A len += 2;
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A if (fprintf(fp,
2N/A "%s: 1 hot spare (1 big device)\n\t%-*.*s "
2N/A "%-12.12s%-8.6s\t\t%s\n",
2N/A hspp->hspnamep->hspname, len, len,
2N/A dgettext(TEXT_DOMAIN, "Device"),
2N/A dgettext(TEXT_DOMAIN, "Status"),
2N/A dgettext(TEXT_DOMAIN, "Length"),
2N/A dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
2N/A goto out;
2N/A }
2N/A } else {
2N/A if (fprintf(fp,
2N/A "%s: 1 hot spare\n\t%-*.*s %-12.12s%-8.6s\t\t%s\n",
2N/A hspp->hspnamep->hspname, len, len,
2N/A dgettext(TEXT_DOMAIN, "Device"),
2N/A dgettext(TEXT_DOMAIN, "Status"),
2N/A dgettext(TEXT_DOMAIN, "Length"),
2N/A dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
2N/A goto out;
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * This allows the length
2N/A * of the ctd to vary from small to large without
2N/A * looking horrible.
2N/A */
2N/A len = 0;
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A len = max(len, strlen(hspp->
2N/A hotspares.hotspares_val[hsi].hsnamep->cname));
2N/A }
2N/A len = max(len, strlen(dgettext(TEXT_DOMAIN, "Device")));
2N/A len += 2;
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A if (fprintf(fp,
2N/A "%s: %u hot spares (%d big device(s))\n\t%-*.*s "
2N/A "%-12.12s%-8.6s\t\t%s\n",
2N/A hspp->hspnamep->hspname,
2N/A hspp->hotspares.hotspares_len,
2N/A large_hs_dev_cnt, len, len,
2N/A dgettext(TEXT_DOMAIN, "Device"),
2N/A dgettext(TEXT_DOMAIN, "Status"),
2N/A dgettext(TEXT_DOMAIN, "Length"),
2N/A dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
2N/A goto out;
2N/A }
2N/A } else {
2N/A if (fprintf(fp, "%s: %u hot spares\n\t%-*.*s "
2N/A "%-12.12s%-8.6s\t\t%s\n",
2N/A hspp->hspnamep->hspname,
2N/A hspp->hotspares.hotspares_len, len, len,
2N/A dgettext(TEXT_DOMAIN, "Device"),
2N/A dgettext(TEXT_DOMAIN, "Status"),
2N/A dgettext(TEXT_DOMAIN, "Length"),
2N/A dgettext(TEXT_DOMAIN, "Reloc")) == EOF) {
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* print hotspares */
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A char *cname = hsp->hsnamep->cname;
2N/A char *hs_state;
2N/A md_timeval32_t tv;
2N/A char *timep;
2N/A ddi_devid_t dtp;
2N/A
2N/A /* populate the key in the name_p structure */
2N/A if ((didnp = metadevname(&sp, hsp->hsnamep->dev, ep)) == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A if (options & PRINT_LARGEDEVICES) {
2N/A if ((hsp->revision & MD_64BIT_META_DEV) == 0)
2N/A continue;
2N/A }
2N/A /* determine if devid does NOT exist */
2N/A if (options & PRINT_DEVID) {
2N/A if ((dtp = meta_getdidbykey(sp->setno,
2N/A getmyside(sp, ep), didnp->key, ep)) == NULL)
2N/A devid = dgettext(TEXT_DOMAIN, "No ");
2N/A else {
2N/A devid = dgettext(TEXT_DOMAIN, "Yes");
2N/A free(dtp);
2N/A }
2N/A }
2N/A /* print hotspare */
2N/A hs_state = hs_state_to_name(hsp, &tv);
2N/A /*
2N/A * This allows the length
2N/A * of the ctd to vary from small to large without
2N/A * looking horrible.
2N/A */
2N/A if (! (options & PRINT_TIMES)) {
2N/A if (fprintf(fp,
2N/A " %-*s %-12s %lld blocks\t%s\n",
2N/A len, cname, hs_state,
2N/A hsp->size, devid) == EOF) {
2N/A goto out;
2N/A }
2N/A } else {
2N/A timep = meta_print_time(&tv);
2N/A
2N/A if (fprintf(fp,
2N/A " %-*s\t %-11s %8lld blocks%s\t%s\n",
2N/A len, cname, hs_state,
2N/A hsp->size, devid, timep) == EOF) {
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* add extra line */
2N/A if (fprintf(fp, "\n") == EOF)
2N/A goto out;
2N/A
2N/A /* success */
2N/A rval = 0;
2N/A
2N/A /* cleanup, return error */
2N/Aout:
2N/A if (rval != 0)
2N/A (void) mdsyserror(ep, errno, fname);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * print/report hsp
2N/A */
2N/Aint
2N/Ameta_hsp_print(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdnamelist_t **nlpp,
2N/A char *fname,
2N/A FILE *fp,
2N/A mdprtopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_hsp_t *hspp;
2N/A
2N/A /* should have same set */
2N/A assert(sp != NULL);
2N/A assert(hspnp == NULL || hspnp->hsp == MD_HSP_NONE ||
2N/A sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* print all hsps */
2N/A if (hspnp == NULL) {
2N/A mdhspnamelist_t *hspnlp = NULL;
2N/A mdhspnamelist_t *p;
2N/A int cnt;
2N/A int rval = 0;
2N/A
2N/A if ((cnt = meta_get_hsp_names(sp, &hspnlp, options, ep)) < 0)
2N/A return (-1);
2N/A else if (cnt == 0)
2N/A return (0);
2N/A
2N/A /* recurse */
2N/A for (p = hspnlp; (p != NULL); p = p->next) {
2N/A mdhspname_t *hspnp = p->hspnamep;
2N/A
2N/A if (meta_hsp_print(sp, hspnp, nlpp, fname, fp,
2N/A options, ep) != 0)
2N/A rval = -1;
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A metafreehspnamelist(hspnlp);
2N/A return (rval);
2N/A }
2N/A
2N/A /* get unit structure */
2N/A if ((hspp = meta_get_hsp_common(sp, hspnp,
2N/A ((options & PRINT_FAST) ? 1 : 0), ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* print appropriate detail */
2N/A if (options & PRINT_SHORT)
2N/A return (hsp_print(hspp, fname, fp, ep));
2N/A else
2N/A return (hsp_report(hspp, nlpp, fname, fp, options, ep, sp));
2N/A}
2N/A
2N/A/*
2N/A * check for valid hotspare pool
2N/A */
2N/Aint
2N/Ametachkhsp(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (meta_get_hsp(sp, hspnp, ep) == NULL)
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * invalidate hotspare pool info
2N/A */
2N/Avoid
2N/Ameta_invalidate_hsp(
2N/A mdhspname_t *hspnp
2N/A)
2N/A{
2N/A md_hsp_t *hspp = hspnp->unitp;
2N/A
2N/A /* free it up */
2N/A if (hspp == NULL)
2N/A return;
2N/A meta_free_hsp(hspp);
2N/A
2N/A /* clear cache */
2N/A hspnp->unitp = NULL;
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: del_hsp_name_mn_sides()
2N/A * INPUT: sp - set name
2N/A * curside - side of this node
2N/A * key - key of records to delete
2N/A * OUTPUT: ep - error information
2N/A * RETURNS: none.
2N/A * PURPOSE: There are name records for each side in a set. This
2N/A * function deletes the records associated with the specified
2N/A * key for all sides except curside. This function is used
2N/A * when the set is a multinode set.
2N/A */
2N/Astatic void
2N/Adel_hsp_name_mn_sides(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A side_t curside,
2N/A mdkey_t key,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_error_t first_error = MDNULLERROR;
2N/A int error_seen = FALSE;
2N/A md_mnnode_desc *nd;
2N/A
2N/A for (nd = sd->sd_nodelist; nd; nd = nd->nd_next) {
2N/A if (nd->nd_nodeid == curside)
2N/A continue;
2N/A if (del_name(sp, nd->nd_nodeid, key, &first_error) == -1) {
2N/A if (error_seen == FALSE) {
2N/A error_seen = TRUE;
2N/A (void) mdstealerror(ep, &first_error);
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: del_hsp_name_trad_sides()
2N/A * INPUT: sp - set name
2N/A * curside - side of this node
2N/A * key - key of records to delete
2N/A * OUTPUT: ep - error information
2N/A * RETURNS: none.
2N/A * PURPOSE: There are name records for each side in a set. This
2N/A * function deletes the records associated with the specified
2N/A * key for all sides except curside. This function is used
2N/A * when the set is a traditional set.
2N/A */
2N/Astatic void
2N/Adel_hsp_name_trad_sides(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A side_t curside,
2N/A mdkey_t key,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int error_seen = FALSE;
2N/A md_error_t first_error = MDNULLERROR;
2N/A int i;
2N/A
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A if (i == curside)
2N/A continue;
2N/A if (sd->sd_nodes[i][0] != '\0') {
2N/A if (del_name(sp, i, key, &first_error) == -1) {
2N/A if (error_seen == FALSE) {
2N/A error_seen = TRUE;
2N/A (void) mdstealerror(ep, &first_error);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: del_hsp_keys()
2N/A * INPUT: sp - set name
2N/A * hspid - ID of records to delete
2N/A * OUTPUT: ep - error information
2N/A * RETURNS: 0 - success
2N/A * -1 - error
2N/A * PURPOSE: Remove the NM records associated with hspid from all sides
2N/A * of the set. Missing records are not considered to be an
2N/A * error. The key associated with the current side is removed
2N/A * last.
2N/A *
2N/A * This function is very similar to del_key_name(), except it
2N/A * does not require any device look up. This is because the
2N/A * hot spare pool is not a device.
2N/A */
2N/Astatic int
2N/Adel_hsp_keys(mdsetname_t *sp, hsp_t hspid, md_error_t *ep)
2N/A{
2N/A md_error_t first_error = MDNULLERROR;
2N/A mdkey_t key = HSP_ID_TO_KEY(hspid);
2N/A md_set_desc *sd;
2N/A side_t thisside; /* Side # of this node. */
2N/A
2N/A /*
2N/A * If there is no key, this means that the hot spare was created
2N/A * before the introduction of friendly names. Thus, the is no NM
2N/A * record and nothing for us to do in this function.
2N/A */
2N/A if (key == MD_KEYBAD)
2N/A return (0);
2N/A
2N/A /* Find our current side */
2N/A mdclrerror(ep);
2N/A thisside = getmyside(sp, ep);
2N/A if (! mdisok(ep))
2N/A return (-1);
2N/A
2N/A /*
2N/A * If not the local set, we need to process the non-local sides
2N/A * first.
2N/A */
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* Multinode set. Sides are in a linked list. */
2N/A del_hsp_name_mn_sides(sp, sd, thisside, key,
2N/A &first_error);
2N/A } else {
2N/A /* Sides are in an array. */
2N/A del_hsp_name_trad_sides(sp, sd, thisside, key,
2N/A &first_error);
2N/A }
2N/A }
2N/A
2N/A /* Now delete the name for the current side. */
2N/A (void) del_name(sp, thisside, key, ep);
2N/A if (! mdisok(&first_error))
2N/A (void) mdstealerror(ep, &first_error);
2N/A return (mdisok(ep) ? 0 : -1);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: add_hsp_name_mn_sides()
2N/A * INPUT: sp - set name
2N/A * curside - side number for this node
2N/A * key - key to use for the name record
2N/A * hsp_name - name of the hot spare
2N/A * OUTPUT: ep - error information
2N/A * RETURNS: 0 indicates success, and -1 indicates failure.
2N/A * PURPOSE: Once the name record has been added for the current side,
2N/A * this function adds the record to the remaining sides. This
2N/A * function is to be used when the set is a multinode set.
2N/A * The side designated by curside will be ignored when adding
2N/A * records.
2N/A */
2N/Astatic int
2N/Aadd_hsp_name_mn_sides(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A side_t curside,
2N/A mdkey_t key,
2N/A char *hsp_name,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_mnnode_desc *nd;
2N/A
2N/A for (nd = sd->sd_nodelist; nd; nd = nd->nd_next) {
2N/A if (nd->nd_nodeid == curside)
2N/A continue;
2N/A if (add_name(sp, nd->nd_nodeid, key, MD_HOTSPARES,
2N/A minor(NODEV), hsp_name, NULL, NULL, ep) == -1) {
2N/A return (-1);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: add_hsp_name_trad_sides()
2N/A * INPUT: sp - set name
2N/A * curside - side number for this node
2N/A * key - key to use for the name record
2N/A * hsp_name - name of the hot spare
2N/A * OUTPUT: ep - error information
2N/A * RETURNS: 0 indicates success, and -1 indicates failure.
2N/A * PURPOSE: Once the name record has been added for the current side,
2N/A * this function adds the record to the remaining sides. This
2N/A * function is to be used when the set is a traditional set.
2N/A * The side designated by curside will be ignored when adding
2N/A * records.
2N/A */
2N/Astatic int
2N/Aadd_hsp_name_trad_sides(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A side_t curside,
2N/A mdkey_t key,
2N/A char *hsp_name,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A if (i == curside)
2N/A continue;
2N/A if (sd->sd_nodes[i][0] != '\0') {
2N/A if (add_name(sp, i, key, MD_HOTSPARES, minor(NODEV),
2N/A hsp_name, NULL, NULL, ep) == -1) {
2N/A return (-1);
2N/A }
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: add_hsp_name()
2N/A * INPUT: sp - Name of the set containing the hsp
2N/A * hsp_name - Hot spare pool name to be added
2N/A * OUTPUT: ep - Error information
2N/A * RETURNS: If successful the key of the newly added record is
2N/A * returned. MD_KEYBAD is returned to indicate a failure.
2N/A * PURPOSE: This function creates a new NM record containing the name
2N/A * of the hotspare pool. A record containing the name is
2N/A * added to each active side, but the record is added first to
2N/A * the current side. This function is modeled on
2N/A * add_key_name() in meta_namespace. The difference is that
2N/A * there is no device associated with a hot spare pool
2N/A */
2N/Astatic hsp_t
2N/Aadd_hsp_name(
2N/A mdsetname_t *sp,
2N/A char *hsp_name,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_error_t ignore_error = MDNULLERROR;
2N/A mdkey_t key;
2N/A md_set_desc *sd;
2N/A side_t thisside; /* Side # of this node. */
2N/A
2N/A if (sp == NULL) {
2N/A (void) mderror(ep, MDE_NO_SET, NULL);
2N/A return (MD_KEYBAD);
2N/A }
2N/A if (hsp_name == NULL) {
2N/A (void) mderror(ep, MDE_INVAL_HSOP, NULL);
2N/A return (MD_KEYBAD);
2N/A }
2N/A
2N/A mdclrerror(ep);
2N/A thisside = getmyside(sp, ep);
2N/A if (! mdisok(ep))
2N/A return (MD_HSPID_WILD);
2N/A
2N/A /* First add the record for the side of the current node. */
2N/A key = add_name(sp, thisside, MD_KEYWILD, MD_HOTSPARES, minor(NODEV),
2N/A hsp_name, NULL, NULL, ep);
2N/A if (key == -1) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* Make sure that we can use the key */
2N/A if (!HSP_KEY_OK(key)) {
2N/A (void) mdhsperror(ep, MDE_HSP_CREATE_FAILURE, MD_HSPID_WILD,
2N/A hsp_name);
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Now that we have a key, we will use it to add a record to the
2N/A * rest of the sides in the set. For multinode sets, the sides are
2N/A * in a linked list that is anchored on the set descriptor. For
2N/A * traditional sets the side information is in an array in the set
2N/A * descriptor.
2N/A */
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* Multinode set. Sides are in linked list. */
2N/A if (add_hsp_name_mn_sides(sp, sd, thisside, key,
2N/A hsp_name, ep) == -1) {
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A /* Traditional set. Sides are in an array. */
2N/A if (add_hsp_name_trad_sides(sp, sd, thisside, key,
2N/A hsp_name, ep) == -1) {
2N/A goto cleanup;
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (KEY_TO_HSP_ID(sp->setno, key));
2N/A
2N/Acleanup:
2N/A /* Get rid records that we added. */
2N/A (void) del_hsp_keys(sp, KEY_TO_HSP_ID(sp->setno, key), &ignore_error);
2N/A return (MD_HSPID_WILD);
2N/A}
2N/A
2N/A/*
2N/A * add hotspares and/or hotspare pool
2N/A */
2N/Aint
2N/Ameta_hs_add(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdnamelist_t *hsnlp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_error_t ignore_error = MDNULLERROR;
2N/A mdnamelist_t *p;
2N/A set_hs_params_t shs;
2N/A side_t thisside;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* clear cache */
2N/A meta_invalidate_hsp(hspnp);
2N/A
2N/A /* setup hotspare pool info */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A shs.shs_cmd = ADD_HOT_SPARE;
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A
2N/A /* Get key for hot spare pool name record. */
2N/A if (options & MDCMD_DOIT) {
2N/A /* First see if the name record already exists. */
2N/A mdclrerror(ep);
2N/A thisside = getmyside(sp, ep);
2N/A if (! mdisok(ep))
2N/A return (-1);
2N/A shs.shs_hot_spare_pool = meta_gethspnmentbyname(sp->setno,
2N/A thisside, hspnp->hspname, ep);
2N/A if (! mdisok(ep)) {
2N/A /*
2N/A * If the error is ENOENT, then we will create a
2N/A * hot spare pool name records. For other types of
2N/A * errors, however, we'll bail out.
2N/A */
2N/A if (! mdissyserror(ep, ENOENT))
2N/A return (-1);
2N/A mdclrerror(ep);
2N/A /* make sure that the name isn't already in use */
2N/A if (is_existing_metadevice(sp, hspnp->hspname))
2N/A return (mderror(ep, MDE_NAME_IN_USE,
2N/A hspnp->hspname));
2N/A if ((shs.shs_hot_spare_pool = add_hsp_name(sp,
2N/A hspnp->hspname, ep)) == MD_HSPID_WILD) {
2N/A return (-1);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* add empty hotspare pool */
2N/A if (hsnlp == NULL) {
2N/A shs.shs_options = HS_OPT_POOL;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde,
2N/A hspnp->hspname) != 0) {
2N/A if (options & MDCMD_DOIT) {
2N/A (void) del_hsp_keys(sp, shs.shs_hot_spare_pool,
2N/A &ignore_error);
2N/A }
2N/A return (mdstealerror(ep, &shs.mde));
2N/A }
2N/A goto success;
2N/A }
2N/A
2N/A /* add hotspares */
2N/A shs.shs_options = HS_OPT_NONE;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A for (p = hsnlp; (p != NULL); p = p->next) {
2N/A mdname_t *hsnp = p->namep;
2N/A diskaddr_t size, label, start_blk;
2N/A
2N/A /* should be in same set */
2N/A assert(hspnp->hsp == MD_HSP_NONE ||
2N/A sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* check it out */
2N/A if (meta_check_hotspare(sp, hsnp, ep) != 0)
2N/A return (-1);
2N/A if ((size = metagetsize(hsnp, ep)) == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A else if (size == 0)
2N/A return (mdsyserror(ep, ENOSPC, hsnp->cname));
2N/A if ((label = metagetlabel(hsnp, ep)) == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A if ((start_blk = metagetstart(sp, hsnp, ep))
2N/A == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A
2N/A shs.shs_size_option = meta_check_devicesize(size);
2N/A
2N/A /* In dryrun mode (DOIT not set) we must not alter the mddb */
2N/A if (options & MDCMD_DOIT) {
2N/A /* store name in namespace */
2N/A if (add_key_name(sp, hsnp, NULL, ep) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A /* add hotspare and/or hotspare pool */
2N/A shs.shs_component_old = hsnp->dev;
2N/A shs.shs_start_blk = start_blk;
2N/A shs.shs_has_label = ((label > 0) ? 1 : 0);
2N/A shs.shs_number_blks = size;
2N/A shs.shs_key_old = hsnp->key;
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde, NULL) != 0) {
2N/A if ((options & MDCMD_DOIT) &&
2N/A (shs.shs_options != HS_OPT_POOL)) {
2N/A (void) del_key_name(sp, hsnp, ep);
2N/A }
2N/A return (mdstealerror(ep, &shs.mde));
2N/A }
2N/A }
2N/A
2N/A /* print success message */
2N/Asuccess:
2N/A if (options & MDCMD_PRINT) {
2N/A if ((options & MDCMD_INIT) || (hsnlp == NULL)) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare pool is setup\n"),
2N/A hspnp->hspname);
2N/A } else if (hsnlp->next == NULL) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare is added\n"),
2N/A hspnp->hspname);
2N/A } else {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspares are added\n"),
2N/A hspnp->hspname);
2N/A }
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: meta_hsp_delete()
2N/A * INPUT: sp - Name of the set containing the hsp
2N/A * hspnp - Hot spare pool name information
2N/A * options - Options from command line
2N/A * OUTPUT: ep - Error information
2N/A * RETURNS: 0 on success and -1 on failure.
2N/A * PURPOSE: Common code to delete an empty hot spare pool.
2N/A */
2N/Astatic int
2N/Ameta_hsp_delete(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A set_hs_params_t shs;
2N/A
2N/A /* setup hotspare pool info */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A shs.shs_hot_spare_pool = hspnp->hsp;
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A shs.shs_cmd = DELETE_HOT_SPARE;
2N/A shs.shs_options = HS_OPT_POOL;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A
2N/A /* Remove hsp record. */
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde,
2N/A hspnp->hspname) != 0)
2N/A return (mdstealerror(ep, &shs.mde));
2N/A
2N/A /* Get rid of hsp NM records */
2N/A if ((options & MDCMD_DOIT) &&
2N/A (del_hsp_keys(sp, hspnp->hsp, ep) == -1)) {
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * delete hotspares from pool
2N/A */
2N/Aint
2N/Ameta_hs_delete(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdnamelist_t *hsnlp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdnamelist_t *p;
2N/A set_hs_params_t shs;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* clear cache */
2N/A meta_invalidate_hsp(hspnp);
2N/A
2N/A /* setup hotspare pool info */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A shs.shs_hot_spare_pool = hspnp->hsp;
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A shs.shs_cmd = DELETE_HOT_SPARE;
2N/A
2N/A /* delete empty hotspare pool */
2N/A if (hsnlp == NULL) {
2N/A if (meta_hsp_delete(sp, hspnp, options, ep) != 0)
2N/A return (-1);
2N/A goto success;
2N/A }
2N/A
2N/A /* delete hotspares */
2N/A shs.shs_options = HS_OPT_NONE;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A for (p = hsnlp; (p != NULL); p = p->next) {
2N/A mdname_t *hsnp = p->namep;
2N/A
2N/A /* should be in same set */
2N/A assert(hspnp->hsp == MD_HSP_NONE ||
2N/A sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* delete hotspare */
2N/A shs.shs_component_old = hsnp->dev;
2N/A meta_invalidate_name(hsnp);
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde, hsnp->cname) != 0)
2N/A return (mdstealerror(ep, &shs.mde));
2N/A }
2N/A
2N/A /* print success message */
2N/Asuccess:
2N/A if (options & MDCMD_PRINT) {
2N/A if (hsnlp == NULL) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare pool is cleared\n"),
2N/A hspnp->hspname);
2N/A } else if (hsnlp->next == NULL) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare is deleted\n"),
2N/A hspnp->hspname);
2N/A } else {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspares are deleted\n"),
2N/A hspnp->hspname);
2N/A }
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * replace hotspare in pool
2N/A */
2N/Aint
2N/Ameta_hs_replace(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdname_t *oldnp,
2N/A mdname_t *newnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A set_hs_params_t shs;
2N/A diskaddr_t size, label, start_blk;
2N/A md_dev64_t old_dev, new_dev;
2N/A diskaddr_t new_start_blk, new_end_blk;
2N/A int rebind;
2N/A char *new_devidp = NULL;
2N/A int ret;
2N/A
2N/A /* should be in same set */
2N/A assert(sp != NULL);
2N/A assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* save new binding incase this is a rebind where oldnp==newnp */
2N/A new_dev = newnp->dev;
2N/A new_start_blk = newnp->start_blk;
2N/A new_end_blk = newnp->end_blk;
2N/A
2N/A /* invalidate, then get the hotspare (fill in oldnp from metadb) */
2N/A meta_invalidate_hsp(hspnp);
2N/A if (meta_get_hsp(sp, hspnp, ep) == NULL)
2N/A return (-1);
2N/A
2N/A /* the old device binding is now established */
2N/A if ((old_dev = oldnp->dev) == NODEV64)
2N/A return (mdsyserror(ep, ENODEV, oldnp->cname));
2N/A
2N/A /*
2N/A * check for the case where oldnp and newnp indicate the same
2N/A * device, but the dev_t of the device has changed between old
2N/A * and new. This is called a rebind. On entry the dev_t
2N/A * represents the new device binding determined from the
2N/A * filesystem (meta_getdev). After calling meta_get_hsp
2N/A * oldnp (and maybe newnp if this is a rebind) is updated based
2N/A * to the old binding from the metadb (done by metakeyname).
2N/A */
2N/A if ((strcmp(oldnp->rname, newnp->rname) == 0) &&
2N/A (old_dev != new_dev)) {
2N/A rebind = 1;
2N/A } else {
2N/A rebind = 0;
2N/A }
2N/A if (rebind) {
2N/A newnp->dev = new_dev;
2N/A newnp->start_blk = new_start_blk;
2N/A newnp->end_blk = new_end_blk;
2N/A }
2N/A
2N/A /*
2N/A * Save a copy of the devid associated with the new disk, the reason
2N/A * is that the meta_check_hotspare() call could cause the devid to
2N/A * be changed to that of the devid that is currently stored in the
2N/A * replica namespace for the disk in question. This devid could be
2N/A * stale if we are replacing the disk. The function that overwrites
2N/A * the devid is dr2drivedesc().
2N/A */
2N/A if (newnp->drivenamep->devid != NULL)
2N/A new_devidp = Strdup(newnp->drivenamep->devid);
2N/A
2N/A /* check it out */
2N/A if (meta_check_hotspare(sp, newnp, ep) != 0) {
2N/A if ((! rebind) || (! mdisuseerror(ep, MDE_ALREADY))) {
2N/A Free(new_devidp);
2N/A return (-1);
2N/A }
2N/A mdclrerror(ep);
2N/A }
2N/A if ((size = metagetsize(newnp, ep)) == MD_DISKADDR_ERROR) {
2N/A Free(new_devidp);
2N/A return (-1);
2N/A }
2N/A if ((label = metagetlabel(newnp, ep)) == MD_DISKADDR_ERROR) {
2N/A Free(new_devidp);
2N/A return (-1);
2N/A }
2N/A if ((start_blk = metagetstart(sp, newnp, ep)) == MD_DISKADDR_ERROR) {
2N/A Free(new_devidp);
2N/A return (-1);
2N/A }
2N/A if (start_blk >= size) {
2N/A (void) mdsyserror(ep, ENOSPC, newnp->cname);
2N/A Free(new_devidp);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Copy back the saved devid.
2N/A */
2N/A Free(newnp->drivenamep->devid);
2N/A if (new_devidp != NULL) {
2N/A newnp->drivenamep->devid = new_devidp;
2N/A new_devidp = NULL;
2N/A }
2N/A
2N/A /* In dryrun mode (DOIT not set) we must not alter the mddb */
2N/A if (options & MDCMD_DOIT) {
2N/A /* store name in namespace */
2N/A if (add_key_name(sp, newnp, NULL, ep) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A if (rebind && !metaislocalset(sp)) {
2N/A /*
2N/A * We are 'rebind'ing a disk that is in a diskset so as well
2N/A * as updating the diskset's namespace the local set needs
2N/A * to be updated because it also contains a reference to the
2N/A * disk in question.
2N/A */
2N/A ret = meta_fixdevid(sp, DEV_UPDATE|DEV_LOCAL_SET, newnp->cname,
2N/A ep);
2N/A
2N/A if (ret != METADEVADM_SUCCESS) {
2N/A md_error_t xep = mdnullerror;
2N/A
2N/A /*
2N/A * In dryrun mode (DOIT not set) we must not alter
2N/A * the mddb
2N/A */
2N/A if (options & MDCMD_DOIT) {
2N/A (void) del_key_name(sp, newnp, &xep);
2N/A mdclrerror(&xep);
2N/A return (-1);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* replace hotspare */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A
2N/A shs.shs_size_option = meta_check_devicesize(size);
2N/A
2N/A shs.shs_cmd = REPLACE_HOT_SPARE;
2N/A shs.shs_hot_spare_pool = hspnp->hsp;
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A shs.shs_component_old = old_dev;
2N/A shs.shs_options = HS_OPT_NONE;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A shs.shs_component_new = new_dev;
2N/A shs.shs_start_blk = start_blk;
2N/A shs.shs_has_label = ((label > 0) ? 1 : 0);
2N/A shs.shs_number_blks = size;
2N/A shs.shs_key_new = newnp->key;
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde, NULL) != 0) {
2N/A if (options & MDCMD_DOIT) {
2N/A (void) del_key_name(sp, newnp, ep);
2N/A }
2N/A return (mdstealerror(ep, &shs.mde));
2N/A }
2N/A
2N/A /* clear cache */
2N/A meta_invalidate_name(oldnp);
2N/A meta_invalidate_name(newnp);
2N/A meta_invalidate_hsp(hspnp);
2N/A
2N/A /* let em know */
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare %s is replaced with %s\n"),
2N/A hspnp->hspname, oldnp->cname, newnp->cname);
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * enable hotspares
2N/A */
2N/Aint
2N/Ameta_hs_enable(
2N/A mdsetname_t *sp,
2N/A mdnamelist_t *hsnlp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdhspnamelist_t *hspnlp = NULL;
2N/A mdhspnamelist_t *hspnp;
2N/A set_hs_params_t shs;
2N/A int rval = -1;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A
2N/A /* setup device info */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A shs.shs_cmd = FIX_HOT_SPARE;
2N/A shs.shs_options = HS_OPT_NONE;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A
2N/A /* get the list of hotspare names */
2N/A if (meta_get_hsp_names(sp, &hspnlp, 0, ep) < 0)
2N/A goto out;
2N/A
2N/A /* enable hotspares for each components */
2N/A for (; (hsnlp != NULL); hsnlp = hsnlp->next) {
2N/A mdname_t *hsnp = hsnlp->namep;
2N/A md_dev64_t fs_dev;
2N/A int rebind = 0;
2N/A diskaddr_t size, label, start_blk;
2N/A
2N/A /* get the file_system dev binding */
2N/A if (meta_getdev(sp, hsnp, ep) != 0)
2N/A return (-1);
2N/A fs_dev = hsnp->dev;
2N/A
2N/A /*
2N/A * search for the component in each hotspare pool
2N/A * and replace it (instead of enable) if the binding
2N/A * has changed.
2N/A */
2N/A for (hspnp = hspnlp; (hspnp != NULL); hspnp = hspnp->next) {
2N/A /*
2N/A * in_hsp will call meta_get_hsp which will fill
2N/A * in hspnp with metadb version of component
2N/A */
2N/A meta_invalidate_hsp(hspnp->hspnamep);
2N/A if (in_hsp(sp, hspnp->hspnamep, hsnp, 0, -1, ep) != 0) {
2N/A /*
2N/A * check for the case where the dev_t has
2N/A * changed between the filesystem and the
2N/A * metadb. This is called a rebind, and
2N/A * is handled by meta_hs_replace.
2N/A */
2N/A if (fs_dev != hsnp->dev) {
2N/A /*
2N/A * establish file system binding
2N/A * with invalid start/end
2N/A */
2N/A rebind++;
2N/A hsnp->dev = fs_dev;
2N/A hsnp->start_blk = -1;
2N/A hsnp->end_blk = -1;
2N/A rval = meta_hs_replace(sp,
2N/A hspnp->hspnamep,
2N/A hsnp, hsnp, options, ep);
2N/A if (rval != 0)
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A if (rebind)
2N/A continue;
2N/A
2N/A /* enable the component in all hotspares that use it */
2N/A if (meta_check_hotspare(sp, hsnp, ep) != 0)
2N/A goto out;
2N/A
2N/A if ((size = metagetsize(hsnp, ep)) == MD_DISKADDR_ERROR)
2N/A goto out;
2N/A if ((label = metagetlabel(hsnp, ep)) == MD_DISKADDR_ERROR)
2N/A goto out;
2N/A if ((start_blk = metagetstart(sp, hsnp, ep))
2N/A == MD_DISKADDR_ERROR)
2N/A goto out;
2N/A if (start_blk >= size) {
2N/A (void) mdsyserror(ep, ENOSPC, hsnp->cname);
2N/A goto out;
2N/A }
2N/A
2N/A /* enable hotspare */
2N/A shs.shs_component_old = hsnp->dev;
2N/A shs.shs_component_new = hsnp->dev;
2N/A shs.shs_start_blk = start_blk;
2N/A shs.shs_has_label = ((label > 0) ? 1 : 0);
2N/A shs.shs_number_blks = size;
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde, hsnp->cname) != 0) {
2N/A rval = mdstealerror(ep, &shs.mde);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Are we dealing with a non-local set? If so need to update
2N/A * the local namespace so that the disk record has the correct
2N/A * devid.
2N/A */
2N/A if (!metaislocalset(sp)) {
2N/A rval = meta_fixdevid(sp, DEV_UPDATE|DEV_LOCAL_SET,
2N/A hsnp->cname, ep);
2N/A
2N/A if (rval != METADEVADM_SUCCESS) {
2N/A /*
2N/A * Failed to update the local set. Nothing to
2N/A * do here apart from report the error. The
2N/A * namespace is most likely broken and some
2N/A * form of remedial recovery is going to
2N/A * be required.
2N/A */
2N/A mde_perror(ep, "");
2N/A mdclrerror(ep);
2N/A }
2N/A }
2N/A
2N/A /* clear cache */
2N/A meta_invalidate_name(hsnp);
2N/A
2N/A /* let em know */
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "hotspare %s is enabled\n"),
2N/A hsnp->cname);
2N/A (void) fflush(stdout);
2N/A }
2N/A }
2N/A
2N/A /* clear whole cache */
2N/A for (hspnp = hspnlp; (hspnp != NULL); hspnp = hspnp->next) {
2N/A meta_invalidate_hsp(hspnp->hspnamep);
2N/A }
2N/A
2N/A
2N/A /* return success */
2N/A rval = 0;
2N/A
2N/Aout:
2N/A if (hspnlp)
2N/A metafreehspnamelist(hspnlp);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * check for dups in the hsp itself
2N/A */
2N/Astatic int
2N/Acheck_twice(
2N/A md_hsp_t *hspp,
2N/A uint_t hsi,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdhspname_t *hspnp = hspp->hspnamep;
2N/A mdname_t *thisnp;
2N/A uint_t h;
2N/A
2N/A thisnp = hspp->hotspares.hotspares_val[hsi].hsnamep;
2N/A for (h = 0; (h < hsi); ++h) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[h];
2N/A mdname_t *hsnp = hsp->hsnamep;
2N/A
2N/A if (meta_check_overlap(hspnp->hspname, thisnp, 0, -1,
2N/A hsnp, 0, -1, ep) != 0)
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * check hsp
2N/A */
2N/A/*ARGSUSED2*/
2N/Aint
2N/Ameta_check_hsp(
2N/A mdsetname_t *sp,
2N/A md_hsp_t *hspp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdhspname_t *hspnp = hspp->hspnamep;
2N/A uint_t hsi;
2N/A
2N/A /* check hotspares */
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A mdname_t *hsnp = hsp->hsnamep;
2N/A diskaddr_t size;
2N/A
2N/A /* check hotspare */
2N/A if (meta_check_hotspare(sp, hsnp, ep) != 0)
2N/A return (-1);
2N/A if ((size = metagetsize(hsnp, ep)) == MD_DISKADDR_ERROR) {
2N/A return (-1);
2N/A } else if (size == 0) {
2N/A return (mdsyserror(ep, ENOSPC, hspnp->hspname));
2N/A }
2N/A
2N/A /* check this hsp too */
2N/A if (check_twice(hspp, hsi, ep) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * create hsp
2N/A */
2N/Aint
2N/Ameta_create_hsp(
2N/A mdsetname_t *sp,
2N/A md_hsp_t *hspp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdhspname_t *hspnp = hspp->hspnamep;
2N/A mdnamelist_t *hsnlp = NULL;
2N/A uint_t hsi;
2N/A int rval = -1;
2N/A
2N/A /* validate hsp */
2N/A if (meta_check_hsp(sp, hspp, options, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* if we're not doing anything, return success */
2N/A if (! (options & MDCMD_DOIT))
2N/A return (0);
2N/A
2N/A /* create hsp */
2N/A for (hsi = 0; (hsi < hspp->hotspares.hotspares_len); ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A mdname_t *hsnp = hsp->hsnamep;
2N/A
2N/A (void) metanamelist_append(&hsnlp, hsnp);
2N/A }
2N/A options |= MDCMD_INIT;
2N/A rval = meta_hs_add(sp, hspnp, hsnlp, options, ep);
2N/A
2N/A /* cleanup, return success */
2N/A metafreenamelist(hsnlp);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * initialize hsp
2N/A * NOTE: this functions is metainit(1m)'s command line parser!
2N/A */
2N/Aint
2N/Ameta_init_hsp(
2N/A mdsetname_t **spp,
2N/A int argc,
2N/A char *argv[],
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *uname = argv[0];
2N/A mdhspname_t *hspnp = NULL;
2N/A md_hsp_t *hspp = NULL;
2N/A uint_t hsi;
2N/A int rval = -1;
2N/A
2N/A
2N/A /* get hsp name */
2N/A assert(argc > 0);
2N/A if (argc < 1)
2N/A goto syntax;
2N/A if ((hspnp = metahspname(spp, uname, ep)) == NULL)
2N/A goto out;
2N/A assert(*spp != NULL);
2N/A uname = hspnp->hspname;
2N/A
2N/A if (!(options & MDCMD_NOLOCK)) {
2N/A /* grab set lock */
2N/A if (meta_lock(*spp, TRUE, ep))
2N/A goto out;
2N/A
2N/A if (meta_check_ownership(*spp, ep) != 0)
2N/A goto out;
2N/A }
2N/A
2N/A /* see if it exists already */
2N/A if (is_existing_metadevice(*spp, uname)) {
2N/A mdname_t *np;
2N/A if ((np = metaname(spp, uname, META_DEVICE, ep)) != NULL)
2N/A if ((meta_get_unit(*spp, np, ep)) != NULL)
2N/A return (mderror(ep, MDE_NAME_IN_USE, uname));
2N/A }
2N/A
2N/A if (meta_get_hsp(*spp, hspnp, ep) != NULL) {
2N/A (void) mdhsperror(ep, MDE_HSP_ALREADY_SETUP,
2N/A hspnp->hsp, uname);
2N/A goto out;
2N/A } else if (! mdishsperror(ep, MDE_INVAL_HSP)) {
2N/A goto out;
2N/A } else {
2N/A mdclrerror(ep);
2N/A }
2N/A --argc, ++argv;
2N/A
2N/A /* parse general options */
2N/A optind = 0;
2N/A opterr = 0;
2N/A if (getopt(argc, argv, "") != -1)
2N/A goto options;
2N/A
2N/A /* allocate hsp */
2N/A hspp = Zalloc(sizeof (*hspp));
2N/A hspp->hotspares.hotspares_len = argc;
2N/A if (argc > 0) {
2N/A hspp->hotspares.hotspares_val =
2N/A Zalloc(argc * sizeof (*hspp->hotspares.hotspares_val));
2N/A }
2N/A
2N/A /* setup pool */
2N/A hspp->hspnamep = hspnp;
2N/A
2N/A /* parse hotspares */
2N/A for (hsi = 0; ((argc > 0) && (hsi < hspp->hotspares.hotspares_len));
2N/A ++hsi) {
2N/A md_hs_t *hsp = &hspp->hotspares.hotspares_val[hsi];
2N/A mdname_t *hsnamep;
2N/A
2N/A /* parse hotspare name */
2N/A if ((hsnamep = metaname(spp, argv[0],
2N/A LOGICAL_DEVICE, ep)) == NULL)
2N/A goto out;
2N/A hsp->hsnamep = hsnamep;
2N/A --argc, ++argv;
2N/A }
2N/A
2N/A /* we should be at the end */
2N/A if (argc != 0)
2N/A goto syntax;
2N/A
2N/A /* create hotspare pool */
2N/A if (meta_create_hsp(*spp, hspp, options, ep) != 0)
2N/A goto out;
2N/A rval = 0; /* success */
2N/A goto out;
2N/A
2N/A /* syntax error */
2N/Asyntax:
2N/A rval = meta_cook_syntax(ep, MDE_SYNTAX, uname, argc, argv);
2N/A goto out;
2N/A
2N/A /* options error */
2N/Aoptions:
2N/A rval = meta_cook_syntax(ep, MDE_OPTION, uname, argc, argv);
2N/A goto out;
2N/A
2N/A /* cleanup, return error */
2N/Aout:
2N/A if (hspp != NULL)
2N/A meta_free_hsp(hspp);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * reset hotspare pool
2N/A */
2N/Aint
2N/Ameta_hsp_reset(
2N/A mdsetname_t *sp,
2N/A mdhspname_t *hspnp,
2N/A mdcmdopts_t options,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_hsp_t *hspp;
2N/A set_hs_params_t shs;
2N/A uint_t i;
2N/A int rval = -1;
2N/A
2N/A /* should have the same set */
2N/A assert(sp != NULL);
2N/A assert(hspnp == NULL || hspnp->hsp == MD_HSP_NONE ||
2N/A sp->setno == HSP_SET(hspnp->hsp));
2N/A
2N/A /* reset all hotspares */
2N/A if (hspnp == NULL) {
2N/A mdhspnamelist_t *hspnlp = NULL;
2N/A mdhspnamelist_t *p;
2N/A
2N/A /* for each hotspare pool */
2N/A rval = 0;
2N/A if (meta_get_hsp_names(sp, &hspnlp, 0, ep) < 0)
2N/A return (-1);
2N/A for (p = hspnlp; (p != NULL); p = p->next) {
2N/A /* reset hotspare pool */
2N/A hspnp = p->hspnamep;
2N/A
2N/A /*
2N/A * If this is a multi-node set, we send a series
2N/A * of individual metaclear commands.
2N/A */
2N/A if (meta_is_mn_set(sp, ep)) {
2N/A if (meta_mn_send_metaclear_command(sp,
2N/A hspnp->hspname, options, 0, ep) != 0) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A } else {
2N/A if (meta_hsp_reset(sp, hspnp, options,
2N/A ep) != 0) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A metafreehspnamelist(hspnlp);
2N/A return (rval);
2N/A }
2N/A
2N/A /* get unit structure */
2N/A if ((hspp = meta_get_hsp(sp, hspnp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* make sure nobody owns us */
2N/A if (hspp->refcount > 0) {
2N/A return (mdhsperror(ep, MDE_HSP_IN_USE, hspnp->hsp,
2N/A hspnp->hspname));
2N/A }
2N/A
2N/A /* clear hotspare pool members */
2N/A (void) memset(&shs, 0, sizeof (shs));
2N/A MD_SETDRIVERNAME(&shs, MD_HOTSPARES, sp->setno);
2N/A shs.shs_cmd = DELETE_HOT_SPARE;
2N/A shs.shs_hot_spare_pool = hspnp->hsp;
2N/A for (i = 0; (i < hspp->hotspares.hotspares_len); ++i) {
2N/A md_hs_t *hs = &hspp->hotspares.hotspares_val[i];
2N/A mdname_t *hsnamep = hs->hsnamep;
2N/A
2N/A /* clear cache */
2N/A meta_invalidate_name(hsnamep);
2N/A
2N/A /* clear hotspare */
2N/A shs.shs_component_old = hsnamep->dev;
2N/A shs.shs_options = HS_OPT_FORCE;
2N/A /* If DOIT is not set, it's a dryrun */
2N/A if ((options & MDCMD_DOIT) == 0) {
2N/A shs.shs_options |= HS_OPT_DRYRUN;
2N/A }
2N/A if (metaioctl(MD_IOCSET_HS, &shs, &shs.mde, NULL) != 0) {
2N/A (void) mdstealerror(ep, &shs.mde);
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* clear hotspare pool */
2N/A if (meta_hsp_delete(sp, hspnp, options, ep) != 0)
2N/A goto out;
2N/A rval = 0; /* success */
2N/A
2N/A /* let em know */
2N/A if (options & MDCMD_PRINT) {
2N/A (void) printf(dgettext(TEXT_DOMAIN,
2N/A "%s: Hotspare pool is cleared\n"),
2N/A hspnp->hspname);
2N/A (void) fflush(stdout);
2N/A }
2N/A
2N/A /* clear subdevices (nothing to do) */
2N/A
2N/A /* cleanup, return success */
2N/Aout:
2N/A meta_invalidate_hsp(hspnp);
2N/A return (rval);
2N/A}