1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
1N/A */
1N/A
1N/A#include <sys/param.h>
1N/A#include <sys/systm.h>
1N/A#include <sys/conf.h>
1N/A#include <sys/file.h>
1N/A#include <sys/user.h>
1N/A#include <sys/uio.h>
1N/A#include <sys/t_lock.h>
1N/A#include <sys/kmem.h>
1N/A#include <vm/page.h>
1N/A#include <sys/sysmacros.h>
1N/A#include <sys/types.h>
1N/A#include <sys/mkdev.h>
1N/A#include <sys/stat.h>
1N/A#include <sys/open.h>
1N/A#include <sys/modctl.h>
1N/A#include <sys/ddi.h>
1N/A#include <sys/sunddi.h>
1N/A#include <sys/debug.h>
1N/A
1N/A#include <sys/lvm/md_hotspares.h>
1N/A#include <sys/lvm/md_convert.h>
1N/A
1N/A#include <sys/sysevent/eventdefs.h>
1N/A#include <sys/sysevent/svm.h>
1N/A
1N/Amd_ops_t hotspares_md_ops;
1N/A#ifndef lint
1N/Amd_ops_t *md_interface_ops = &hotspares_md_ops;
1N/A#endif
1N/A
1N/Aextern md_ops_t **md_ops;
1N/Aextern md_ops_t *md_opslist;
1N/Aextern md_set_t md_set[];
1N/A
1N/Aextern kmutex_t md_mx; /* used to md global stuff */
1N/Aextern kcondvar_t md_cv; /* md_status events */
1N/Aextern int md_status;
1N/A
1N/Aextern void md_clear_hot_spare_interface();
1N/A
1N/Astatic void
1N/Aset_hot_spare_state(hot_spare_t *hs, hotspare_states_t newstate)
1N/A{
1N/A hs->hs_state = newstate;
1N/A uniqtime32(&hs->hs_timestamp);
1N/A}
1N/A
1N/Astatic hot_spare_t *
1N/Alookup_hot_spare(set_t setno, mddb_recid_t hs_id, int must_exist)
1N/A{
1N/A hot_spare_t *hs;
1N/A
1N/A for (hs = (hot_spare_t *)md_set[setno].s_hs; hs; hs = hs->hs_next) {
1N/A if (hs->hs_record_id == hs_id)
1N/A return (hs);
1N/A }
1N/A if (must_exist)
1N/A ASSERT(0);
1N/A
1N/A return ((hot_spare_t *)NULL);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Aseths_create_hsp(set_hs_params_t *shs)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A mddb_recid_t recid;
1N/A set_t setno;
1N/A mddb_type_t typ1;
1N/A
1N/A setno = HSP_SET(shs->shs_hot_spare_pool);
1N/A
1N/A /* Scan the hot spare pool list */
1N/A hsp = find_hot_spare_pool(setno, shs->shs_hot_spare_pool);
1N/A if (hsp != (hot_spare_pool_t *)0)
1N/A return (0);
1N/A
1N/A typ1 = (mddb_type_t)md_getshared_key(setno,
1N/A hotspares_md_ops.md_driver.md_drivername);
1N/A
1N/A /* create a hot spare pool record */
1N/A if (shs->shs_options & MD_CRO_64BIT) {
1N/A#if defined(_ILP32)
1N/A return (mdhsperror(&shs->mde, MDE_HSP_UNIT_TOO_LARGE,
1N/A shs->shs_hot_spare_pool));
1N/A#else
1N/A recid = mddb_createrec(sizeof (hot_spare_pool_ond_t), typ1,
1N/A HSP_REC, MD_CRO_64BIT | MD_CRO_HOTSPARE_POOL | MD_CRO_FN,
1N/A setno);
1N/A#endif
1N/A } else {
1N/A recid = mddb_createrec(sizeof (hot_spare_pool_ond_t), typ1,
1N/A HSP_REC, MD_CRO_32BIT | MD_CRO_HOTSPARE_POOL | MD_CRO_FN,
1N/A setno);
1N/A }
1N/A
1N/A if (recid < 0) {
1N/A return (mdhsperror(&shs->mde, MDE_HSP_CREATE_FAILURE,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A /* get the record addr */
1N/A hsp = (hot_spare_pool_t *)mddb_getrecaddr_resize(recid, sizeof (*hsp),
1N/A HSP_ONDSK_STR_OFF);
1N/A
1N/A hsp->hsp_self_id = shs->shs_hot_spare_pool;
1N/A hsp->hsp_record_id = recid;
1N/A hsp->hsp_next = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A hsp->hsp_refcount = 0;
1N/A hsp->hsp_nhotspares = 0;
1N/A hsp->hsp_revision |= MD_FN_META_DEV;
1N/A
1N/A md_set[setno].s_hsp = (void *) hsp;
1N/A
1N/A mddb_commitrec_wrapper(recid);
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_CREATE, SVM_TAG_HSP, setno,
1N/A md_expldev(hsp->hsp_self_id));
1N/A
1N/A rw_enter(&hotspares_md_ops.md_link_rw.lock, RW_WRITER);
1N/A hsp->hsp_link.ln_next = hotspares_md_ops.md_head;
1N/A hsp->hsp_link.ln_setno = setno;
1N/A hsp->hsp_link.ln_id = hsp->hsp_self_id;
1N/A hotspares_md_ops.md_head = &hsp->hsp_link;
1N/A rw_exit(&hotspares_md_ops.md_link_rw.lock);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Aseths_add(set_hs_params_t *shs)
1N/A{
1N/A hot_spare_t *hs;
1N/A hot_spare_pool_t *hsp;
1N/A hot_spare_pool_t *prev_hsp;
1N/A hot_spare_pool_t *new_hsp;
1N/A hot_spare_pool_t *old_hsp;
1N/A md_create_rec_option_t options;
1N/A mddb_recid_t recid;
1N/A mddb_recid_t recids[5];
1N/A size_t new_size;
1N/A int i;
1N/A int delete_hsp = 0;
1N/A int irecid;
1N/A set_t setno;
1N/A mddb_type_t typ1;
1N/A int hsp_created = 0;
1N/A mdkey_t key_old;
1N/A int num_keys_old = 0;
1N/A
1N/A /* Not much to do here in case of a dryrun */
1N/A if (shs->shs_options & HS_OPT_DRYRUN) {
1N/A return (0);
1N/A }
1N/A
1N/A /* create an empty hot spare pool */
1N/A if (shs->shs_options & HS_OPT_POOL) {
1N/A return (seths_create_hsp(shs));
1N/A }
1N/A
1N/A setno = HSP_SET(shs->shs_hot_spare_pool);
1N/A typ1 = (mddb_type_t)md_getshared_key(setno,
1N/A hotspares_md_ops.md_driver.md_drivername);
1N/A
1N/A /* Scan the hot spare list */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A while (hs) {
1N/A if (hs->hs_devnum == shs->shs_component_old) {
1N/A break;
1N/A }
1N/A hs = hs->hs_next;
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A /*
1N/A * Did not find match for device using devnum so use
1N/A * key associated with shs_component_old just
1N/A * in case there is a match but the match's dev is NODEV.
1N/A * If unable to find a unique key for shs_component_old
1N/A * then fail since namespace has multiple entries
1N/A * for this old component and we shouldn't allow
1N/A * an addition of a hotspare in this case.
1N/A */
1N/A if (md_getkeyfromdev(setno, mddb_getsidenum(setno),
1N/A shs->shs_component_old, &key_old, &num_keys_old) != 0) {
1N/A return (mddeverror(&shs->mde, MDE_NAME_SPACE,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /*
1N/A * If more than one key matches given old_dev - fail command
1N/A * since shouldn't add new hotspare if namespace has
1N/A * multiple entries.
1N/A */
1N/A if (num_keys_old > 1) {
1N/A return (mddeverror(&shs->mde, MDE_MULTNM,
1N/A shs->shs_component_old));
1N/A }
1N/A /*
1N/A * If there is no key for this entry then fail since
1N/A * a key for this entry should exist.
1N/A */
1N/A if (num_keys_old == 0) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A /* Scan the hot spare list again */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A while (hs) {
1N/A /*
1N/A * Only need to compare keys when hs_devnum is NODEV.
1N/A */
1N/A if ((hs->hs_devnum == NODEV64) &&
1N/A (hs->hs_key == key_old)) {
1N/A break;
1N/A }
1N/A hs = hs->hs_next;
1N/A }
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A /* create a hot spare record */
1N/A if (shs->shs_size_option & MD_CRO_64BIT) {
1N/A#if defined(_ILP32)
1N/A return (mdhserror(&shs->mde, MDE_HS_UNIT_TOO_LARGE,
1N/A shs->shs_hot_spare_pool, shs->shs_component_old));
1N/A#else
1N/A recid = mddb_createrec(HS_ONDSK_STR_SIZE, typ1, HS_REC,
1N/A MD_CRO_64BIT | MD_CRO_HOTSPARE, setno);
1N/A#endif
1N/A } else {
1N/A recid = mddb_createrec(HS_ONDSK_STR_SIZE, typ1, HS_REC,
1N/A MD_CRO_32BIT | MD_CRO_HOTSPARE, setno);
1N/A }
1N/A
1N/A if (recid < 0) {
1N/A return (mdhserror(&shs->mde, MDE_HS_CREATE_FAILURE,
1N/A shs->shs_hot_spare_pool,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /* get the addr */
1N/A hs = (hot_spare_t *)mddb_getrecaddr_resize(recid, sizeof (*hs),
1N/A 0);
1N/A
1N/A hs->hs_record_id = recid;
1N/A
1N/A hs->hs_devnum = shs->shs_component_old;
1N/A hs->hs_key = shs->shs_key_old;
1N/A hs->hs_start_blk = shs->shs_start_blk;
1N/A hs->hs_has_label = shs->shs_has_label;
1N/A hs->hs_number_blks = shs->shs_number_blks;
1N/A set_hot_spare_state(hs, HSS_AVAILABLE);
1N/A hs->hs_refcount = 0;
1N/A hs->hs_next = (hot_spare_t *)md_set[setno].s_hs;
1N/A md_set[setno].s_hs = (void *) hs;
1N/A }
1N/A
1N/A /* Scan the hot spare pool list */
1N/A hsp = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A prev_hsp = (hot_spare_pool_t *)0;
1N/A while (hsp) {
1N/A if (hsp->hsp_self_id == shs->shs_hot_spare_pool) {
1N/A break;
1N/A }
1N/A prev_hsp = hsp;
1N/A hsp = hsp->hsp_next;
1N/A }
1N/A
1N/A if (hsp == NULL) {
1N/A /* create a hot spare pool record */
1N/A recid = mddb_createrec(sizeof (hot_spare_pool_ond_t),
1N/A typ1, HSP_REC,
1N/A MD_CRO_32BIT | MD_CRO_HOTSPARE_POOL | MD_CRO_FN, setno);
1N/A
1N/A if (recid < 0) {
1N/A return (mdhsperror(&shs->mde, MDE_HSP_CREATE_FAILURE,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A /* get the record addr */
1N/A hsp = (hot_spare_pool_t *)mddb_getrecaddr_resize(recid,
1N/A sizeof (*hsp), HSP_ONDSK_STR_OFF);
1N/A
1N/A hsp->hsp_self_id = shs->shs_hot_spare_pool;
1N/A hsp->hsp_record_id = recid;
1N/A hsp->hsp_next = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A hsp->hsp_refcount = 0;
1N/A hsp->hsp_nhotspares = 0;
1N/A hsp->hsp_revision |= MD_FN_META_DEV;
1N/A
1N/A /* force prev_hsp to NULL, this will cause hsp to be linked */
1N/A prev_hsp = (hot_spare_pool_t *)0;
1N/A
1N/A rw_enter(&hotspares_md_ops.md_link_rw.lock, RW_WRITER);
1N/A hsp->hsp_link.ln_next = hotspares_md_ops.md_head;
1N/A hsp->hsp_link.ln_setno = setno;
1N/A hsp->hsp_link.ln_id = hsp->hsp_self_id;
1N/A hotspares_md_ops.md_head = &hsp->hsp_link;
1N/A rw_exit(&hotspares_md_ops.md_link_rw.lock);
1N/A hsp_created = 1;
1N/A } else {
1N/A
1N/A /*
1N/A * Make sure the hot spare is not already in the pool.
1N/A */
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++)
1N/A if (hsp->hsp_hotspares[i] == hs->hs_record_id) {
1N/A return (mdhserror(&shs->mde, MDE_HS_INUSE,
1N/A shs->shs_hot_spare_pool,
1N/A hs->hs_devnum));
1N/A }
1N/A /*
1N/A * Create a new hot spare pool record
1N/A * This gives us the one extra hs slot,
1N/A * because there is one slot in the
1N/A * hot_spare_pool struct
1N/A */
1N/A new_size = sizeof (hot_spare_pool_ond_t) +
1N/A (sizeof (mddb_recid_t) * hsp->hsp_nhotspares);
1N/A
1N/A /*
1N/A * The Friendly Name status of the new HSP should duplicate
1N/A * the status of the existing one.
1N/A */
1N/A if (hsp->hsp_revision & MD_FN_META_DEV) {
1N/A options =
1N/A MD_CRO_32BIT | MD_CRO_HOTSPARE_POOL | MD_CRO_FN;
1N/A } else {
1N/A options = MD_CRO_32BIT | MD_CRO_HOTSPARE_POOL;
1N/A }
1N/A recid = mddb_createrec(new_size, typ1, HSP_REC, options, setno);
1N/A
1N/A if (recid < 0) {
1N/A return (mdhsperror(&shs->mde, MDE_HSP_CREATE_FAILURE,
1N/A hsp->hsp_self_id));
1N/A }
1N/A new_size = sizeof (hot_spare_pool_t) +
1N/A (sizeof (mddb_recid_t) * hsp->hsp_nhotspares);
1N/A
1N/A /* get the record addr */
1N/A new_hsp = (hot_spare_pool_t *)mddb_getrecaddr_resize(recid,
1N/A new_size, HSP_ONDSK_STR_OFF);
1N/A
1N/A /* copy the old record into the new one */
1N/A bcopy((caddr_t)hsp, (caddr_t)new_hsp,
1N/A (size_t)((sizeof (hot_spare_pool_t) +
1N/A (sizeof (mddb_recid_t) * hsp->hsp_nhotspares)
1N/A - sizeof (mddb_recid_t))));
1N/A new_hsp->hsp_record_id = recid;
1N/A
1N/A md_rem_link(setno, hsp->hsp_self_id,
1N/A &hotspares_md_ops.md_link_rw.lock,
1N/A &hotspares_md_ops.md_head);
1N/A
1N/A rw_enter(&hotspares_md_ops.md_link_rw.lock, RW_WRITER);
1N/A new_hsp->hsp_link.ln_next = hotspares_md_ops.md_head;
1N/A new_hsp->hsp_link.ln_setno = setno;
1N/A new_hsp->hsp_link.ln_id = new_hsp->hsp_self_id;
1N/A hotspares_md_ops.md_head = &new_hsp->hsp_link;
1N/A rw_exit(&hotspares_md_ops.md_link_rw.lock);
1N/A
1N/A /* mark the old hsp to be deleted */
1N/A delete_hsp = 1;
1N/A old_hsp = hsp;
1N/A hsp = new_hsp;
1N/A }
1N/A
1N/A if (shs->shs_size_option & MD_CRO_64BIT) {
1N/A hs->hs_revision |= MD_64BIT_META_DEV;
1N/A } else {
1N/A hs->hs_revision &= ~MD_64BIT_META_DEV;
1N/A }
1N/A
1N/A /* lock the db records */
1N/A recids[0] = hs->hs_record_id;
1N/A recids[1] = hsp->hsp_record_id;
1N/A irecid = 2;
1N/A if (delete_hsp)
1N/A recids[irecid++] = old_hsp->hsp_record_id;
1N/A recids[irecid] = 0;
1N/A
1N/A /* increment the reference count */
1N/A hs->hs_refcount++;
1N/A
1N/A /* add the hs at the end of the hot spare pool */
1N/A hsp->hsp_hotspares[hsp->hsp_nhotspares] = hs->hs_record_id;
1N/A hsp->hsp_nhotspares++;
1N/A
1N/A /*
1N/A * NOTE: We do not commit the previous hot spare pool record.
1N/A * There is no need, the link gets rebuilt at boot time.
1N/A */
1N/A if (prev_hsp)
1N/A prev_hsp->hsp_next = hsp;
1N/A else
1N/A md_set[setno].s_hsp = (void *) hsp;
1N/A
1N/A if (delete_hsp)
1N/A old_hsp->hsp_self_id = MD_HSP_NONE;
1N/A
1N/A /* commit the db records */
1N/A mddb_commitrecs_wrapper(recids);
1N/A
1N/A if (delete_hsp) {
1N/A /* delete the old hot spare pool record */
1N/A mddb_deleterec_wrapper(old_hsp->hsp_record_id);
1N/A }
1N/A
1N/A if (hsp_created) {
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_CREATE, SVM_TAG_HSP, setno,
1N/A md_expldev(hsp->hsp_self_id));
1N/A }
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_ADD, SVM_TAG_HSP, setno,
1N/A md_expldev(hsp->hsp_self_id));
1N/A
1N/A return (0);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Aseths_delete_hsp(set_hs_params_t *shs)
1N/A{
1N/A
1N/A hot_spare_pool_t *prev_hsp;
1N/A hot_spare_pool_t *hsp;
1N/A set_t setno;
1N/A hsp_t hspid;
1N/A
1N/A setno = HSP_SET(shs->shs_hot_spare_pool);
1N/A
1N/A /* Scan the hot spare pool list */
1N/A prev_hsp = (hot_spare_pool_t *)0;
1N/A hsp = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A while (hsp) {
1N/A if (hsp->hsp_self_id == shs->shs_hot_spare_pool) {
1N/A break;
1N/A }
1N/A prev_hsp = hsp;
1N/A hsp = hsp->hsp_next;
1N/A }
1N/A
1N/A if (hsp == NULL) {
1N/A return (mdhsperror(&shs->mde, MDE_INVAL_HSP,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A if (hsp->hsp_nhotspares != 0) {
1N/A return (mdhsperror(&shs->mde, MDE_HSP_BUSY,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A if (hsp->hsp_refcount != 0) {
1N/A return (mdhsperror(&shs->mde, MDE_HSP_REF,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A /* In case of a dryrun, we're done here */
1N/A if (shs->shs_options & HS_OPT_DRYRUN) {
1N/A return (0);
1N/A }
1N/A /*
1N/A * NOTE: We do not commit the previous hot spare pool record.
1N/A * There is no need, the link gets rebuilt at boot time.
1N/A */
1N/A if (prev_hsp)
1N/A prev_hsp->hsp_next = hsp->hsp_next;
1N/A else
1N/A md_set[setno].s_hsp = (void *) hsp->hsp_next;
1N/A
1N/A hspid = hsp->hsp_self_id;
1N/A
1N/A md_rem_link(setno, hsp->hsp_self_id,
1N/A &hotspares_md_ops.md_link_rw.lock,
1N/A &hotspares_md_ops.md_head);
1N/A
1N/A mddb_deleterec_wrapper(hsp->hsp_record_id);
1N/A
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_DELETE, SVM_TAG_HSP, setno,
1N/A md_expldev(hspid));
1N/A return (0);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Aseths_delete(set_hs_params_t *shs)
1N/A{
1N/A hot_spare_t *hs;
1N/A hot_spare_t *prev_hs;
1N/A hot_spare_pool_t *hsp;
1N/A mddb_recid_t recids[4];
1N/A int i;
1N/A set_t setno;
1N/A sv_dev_t sv;
1N/A int delete_hs = 0;
1N/A mdkey_t key_old;
1N/A int num_keys_old = 0;
1N/A
1N/A /* delete the hot spare pool */
1N/A if (shs->shs_options & HS_OPT_POOL) {
1N/A return (seths_delete_hsp(shs));
1N/A }
1N/A
1N/A setno = HSP_SET(shs->shs_hot_spare_pool);
1N/A
1N/A /* Scan the hot spare list */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A prev_hs = (hot_spare_t *)0;
1N/A while (hs) {
1N/A if (hs->hs_devnum == shs->shs_component_old) {
1N/A break;
1N/A }
1N/A prev_hs = hs;
1N/A hs = hs->hs_next;
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A /*
1N/A * Unable to find device using devnum so use
1N/A * key associated with shs_component_old instead.
1N/A * If unable to find a unique key for shs_component_old
1N/A * then fail since namespace has multiple entries
1N/A * for this old component and we're unable to determine
1N/A * which key is the valid match for shs_component_old.
1N/A *
1N/A * Only need to compare keys when hs_devnum is NODEV.
1N/A */
1N/A if (md_getkeyfromdev(setno, mddb_getsidenum(setno),
1N/A shs->shs_component_old, &key_old, &num_keys_old) != 0) {
1N/A return (mddeverror(&shs->mde, MDE_NAME_SPACE,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /*
1N/A * If more than one key matches given old_dev - fail command
1N/A * since shouldn't add new hotspare if namespace has
1N/A * multiple entries.
1N/A */
1N/A if (num_keys_old > 1) {
1N/A return (mddeverror(&shs->mde, MDE_MULTNM,
1N/A shs->shs_component_old));
1N/A }
1N/A /*
1N/A * If there is no key for this entry then fail since
1N/A * a key for this entry should exist.
1N/A */
1N/A if (num_keys_old == 0) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A /* Scan the hot spare list again */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A prev_hs = (hot_spare_t *)0;
1N/A while (hs) {
1N/A /*
1N/A * Only need to compare keys when hs_devnum is NODEV.
1N/A */
1N/A if ((hs->hs_devnum == NODEV64) &&
1N/A (hs->hs_key == key_old)) {
1N/A break;
1N/A }
1N/A prev_hs = hs;
1N/A hs = hs->hs_next;
1N/A }
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /* Scan the hot spare pool list */
1N/A hsp = find_hot_spare_pool(setno, shs->shs_hot_spare_pool);
1N/A if (hsp == (hot_spare_pool_t *)0) {
1N/A return (mdhsperror(&shs->mde, MDE_INVAL_HSP,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A /* check for force flag and state of hot spare */
1N/A if (((shs->shs_options & HS_OPT_FORCE) == 0) &&
1N/A (hs->hs_state == HSS_RESERVED)) {
1N/A return (mdhserror(&shs->mde, MDE_HS_RESVD,
1N/A shs->shs_hot_spare_pool, shs->shs_component_old));
1N/A }
1N/A
1N/A if (hsp->hsp_refcount && (hs->hs_state == HSS_RESERVED)) {
1N/A return (mdhserror(&shs->mde, MDE_HS_RESVD,
1N/A shs->shs_hot_spare_pool, shs->shs_component_old));
1N/A }
1N/A
1N/A /*
1N/A * Make sure the device is in the pool.
1N/A */
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A if (hsp->hsp_hotspares[i] == hs->hs_record_id) {
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (i >= hsp->hsp_nhotspares) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A hs->hs_devnum));
1N/A }
1N/A
1N/A /* In case of a dryrun, we're done here */
1N/A if (shs->shs_options & HS_OPT_DRYRUN) {
1N/A return (0);
1N/A }
1N/A
1N/A /* lock the db records */
1N/A recids[0] = hs->hs_record_id;
1N/A recids[1] = hsp->hsp_record_id;
1N/A recids[2] = 0;
1N/A
1N/A sv.setno = setno;
1N/A sv.key = hs->hs_key;
1N/A
1N/A hs->hs_refcount--;
1N/A if (hs->hs_refcount == 0) {
1N/A /*
1N/A * NOTE: We do not commit the previous hot spare record.
1N/A * There is no need, the link we get rebuilt at boot time.
1N/A */
1N/A if (prev_hs) {
1N/A prev_hs->hs_next = hs->hs_next;
1N/A } else
1N/A md_set[setno].s_hs = (void *) hs->hs_next;
1N/A
1N/A /* mark the hot spare to be deleted */
1N/A delete_hs = 1;
1N/A recids[0] = hsp->hsp_record_id;
1N/A recids[1] = 0;
1N/A }
1N/A
1N/A /* find the location of the hs in the hsp */
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A if (hsp->hsp_hotspares[i] == hs->hs_record_id)
1N/A break;
1N/A }
1N/A
1N/A /* remove the hs from the hsp */
1N/A for (i++; i < hsp->hsp_nhotspares; i++)
1N/A hsp->hsp_hotspares[i - 1] = hsp->hsp_hotspares[i];
1N/A
1N/A hsp->hsp_nhotspares--;
1N/A
1N/A /* commit the db records */
1N/A mddb_commitrecs_wrapper(recids);
1N/A
1N/A if (delete_hs)
1N/A mddb_deleterec_wrapper(hs->hs_record_id);
1N/A
1N/A md_rem_names(&sv, 1);
1N/A
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_REMOVE, SVM_TAG_HSP, setno,
1N/A md_expldev(hsp->hsp_self_id));
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Aseths_replace(set_hs_params_t *shs)
1N/A{
1N/A hot_spare_t *hs;
1N/A hot_spare_t *prev_hs;
1N/A hot_spare_t *new_hs;
1N/A hot_spare_pool_t *hsp;
1N/A int new_found = 0;
1N/A mddb_recid_t recid;
1N/A mddb_recid_t recids[5];
1N/A int i;
1N/A sv_dev_t sv;
1N/A int delete_hs = 0;
1N/A set_t setno;
1N/A mddb_type_t typ1;
1N/A mdkey_t key_old;
1N/A int num_keys_old = 0;
1N/A
1N/A setno = HSP_SET(shs->shs_hot_spare_pool);
1N/A typ1 = (mddb_type_t)md_getshared_key(setno,
1N/A hotspares_md_ops.md_driver.md_drivername);
1N/A
1N/A /* Scan the hot spare list */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A prev_hs = (hot_spare_t *)0;
1N/A while (hs) {
1N/A if (hs->hs_devnum == shs->shs_component_old) {
1N/A break;
1N/A }
1N/A prev_hs = hs;
1N/A hs = hs->hs_next;
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A /*
1N/A * Unable to find device using devnum so use
1N/A * key associated with shs_component_old instead.
1N/A * If unable to find a unique key for shs_component_old
1N/A * then fail since namespace has multiple entries
1N/A * for this old component and we're unable to determine
1N/A * which key is the valid match for shs_component_old.
1N/A *
1N/A * Only need to compare keys when hs_devnum is NODEV.
1N/A */
1N/A if (md_getkeyfromdev(setno, mddb_getsidenum(setno),
1N/A shs->shs_component_old, &key_old, &num_keys_old) != 0) {
1N/A return (mddeverror(&shs->mde, MDE_NAME_SPACE,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /*
1N/A * If more than one key matches given old_dev - fail command
1N/A * since unable to determine which key is correct.
1N/A */
1N/A if (num_keys_old > 1) {
1N/A return (mddeverror(&shs->mde, MDE_MULTNM,
1N/A shs->shs_component_old));
1N/A }
1N/A /*
1N/A * If there is no key for this entry then fail since
1N/A * a key for this entry should exist.
1N/A */
1N/A if (num_keys_old == 0) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A /* Scan the hot spare list again */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A prev_hs = (hot_spare_t *)0;
1N/A while (hs) {
1N/A /*
1N/A * Only need to compare keys when hs_devnum is NODEV.
1N/A */
1N/A if ((hs->hs_devnum == NODEV64) &&
1N/A (hs->hs_key == key_old)) {
1N/A break;
1N/A }
1N/A prev_hs = hs;
1N/A hs = hs->hs_next;
1N/A }
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /* check the force flag and the state of the hot spare */
1N/A if (((shs->shs_options & HS_OPT_FORCE) == 0) &&
1N/A (hs->hs_state == HSS_RESERVED)) {
1N/A return (mdhserror(&shs->mde, MDE_HS_RESVD,
1N/A shs->shs_hot_spare_pool,
1N/A hs->hs_devnum));
1N/A }
1N/A
1N/A /* Scan the hot spare pool list */
1N/A hsp = find_hot_spare_pool(setno, shs->shs_hot_spare_pool);
1N/A if (hsp == (hot_spare_pool_t *)0) {
1N/A return (mdhsperror(&shs->mde, MDE_INVAL_HSP,
1N/A shs->shs_hot_spare_pool));
1N/A }
1N/A
1N/A /*
1N/A * Make sure the old device is in the pool.
1N/A */
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A if (hsp->hsp_hotspares[i] == hs->hs_record_id) {
1N/A break;
1N/A }
1N/A }
1N/A if (i >= hsp->hsp_nhotspares) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A hs->hs_devnum));
1N/A }
1N/A
1N/A /* Scan the hot spare list for the new hs */
1N/A new_hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A new_found = 0;
1N/A while (new_hs) {
1N/A if (new_hs->hs_devnum == shs->shs_component_new) {
1N/A new_found = 1;
1N/A break;
1N/A }
1N/A new_hs = new_hs->hs_next;
1N/A }
1N/A
1N/A /*
1N/A * Make sure the new device is not already in the pool.
1N/A * We don't have to search the hs in this hsp, if the
1N/A * new hs was just created. Only if the hot spare was found.
1N/A */
1N/A if (new_found) {
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++)
1N/A if (hsp->hsp_hotspares[i] == new_hs->hs_record_id) {
1N/A return (mdhserror(&shs->mde, MDE_HS_INUSE,
1N/A shs->shs_hot_spare_pool,
1N/A new_hs->hs_devnum));
1N/A }
1N/A }
1N/A
1N/A /* In case of a dryrun, we're done here */
1N/A if (shs->shs_options & HS_OPT_DRYRUN) {
1N/A return (0);
1N/A }
1N/A
1N/A /*
1N/A * Create the new hotspare
1N/A */
1N/A if (!new_found) {
1N/A /* create a hot spare record */
1N/A if (shs->shs_size_option & MD_CRO_64BIT) {
1N/A#if defined(_ILP32)
1N/A return (mdhserror(&shs->mde, MDE_HS_UNIT_TOO_LARGE,
1N/A shs->shs_hot_spare_pool, shs->shs_component_new));
1N/A#else
1N/A recid = mddb_createrec(HS_ONDSK_STR_SIZE, typ1, HS_REC,
1N/A MD_CRO_64BIT | MD_CRO_HOTSPARE, setno);
1N/A#endif
1N/A } else {
1N/A recid = mddb_createrec(HS_ONDSK_STR_SIZE, typ1, HS_REC,
1N/A MD_CRO_32BIT | MD_CRO_HOTSPARE, setno);
1N/A }
1N/A
1N/A if (recid < 0) {
1N/A return (mdhserror(&shs->mde, MDE_HS_CREATE_FAILURE,
1N/A shs->shs_hot_spare_pool,
1N/A shs->shs_component_new));
1N/A }
1N/A
1N/A /* get the addr */
1N/A new_hs = (hot_spare_t *)mddb_getrecaddr_resize(recid,
1N/A sizeof (*new_hs), 0);
1N/A
1N/A new_hs->hs_record_id = recid;
1N/A new_hs->hs_devnum = shs->shs_component_new;
1N/A new_hs->hs_key = shs->shs_key_new;
1N/A new_hs->hs_start_blk = shs->shs_start_blk;
1N/A new_hs->hs_has_label = shs->shs_has_label;
1N/A new_hs->hs_number_blks = shs->shs_number_blks;
1N/A set_hot_spare_state(new_hs, HSS_AVAILABLE);
1N/A new_hs->hs_refcount = 0;
1N/A new_hs->hs_isopen = 1;
1N/A }
1N/A
1N/A /* lock the db records */
1N/A recids[0] = hs->hs_record_id;
1N/A recids[1] = new_hs->hs_record_id;
1N/A recids[2] = hsp->hsp_record_id;
1N/A recids[3] = 0;
1N/A
1N/A sv.setno = setno;
1N/A sv.key = hs->hs_key;
1N/A
1N/A hs->hs_refcount--;
1N/A if (hs->hs_refcount == 0) {
1N/A /*
1N/A * NOTE: We do not commit the previous hot spare record.
1N/A * There is no need, the link we get rebuilt at boot time.
1N/A */
1N/A if (prev_hs) {
1N/A prev_hs->hs_next = hs->hs_next;
1N/A } else
1N/A md_set[setno].s_hs = (void *) hs->hs_next;
1N/A
1N/A /* mark hs to be deleted in the correct order */
1N/A delete_hs = 1;
1N/A
1N/A recids[0] = new_hs->hs_record_id;
1N/A recids[1] = hsp->hsp_record_id;
1N/A recids[2] = 0;
1N/A }
1N/A
1N/A /* link into the hs list */
1N/A new_hs->hs_refcount++;
1N/A if (!new_found) {
1N/A /* do this AFTER the old dev is possibly removed */
1N/A new_hs->hs_next = (hot_spare_t *)md_set[setno].s_hs;
1N/A md_set[setno].s_hs = (void *) new_hs;
1N/A }
1N/A
1N/A /* find the location of the old hs in the hsp */
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A if (hsp->hsp_hotspares[i] == hs->hs_record_id) {
1N/A hsp->hsp_hotspares[i] = new_hs->hs_record_id;
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (shs->shs_size_option & MD_CRO_64BIT) {
1N/A new_hs->hs_revision |= MD_64BIT_META_DEV;
1N/A } else {
1N/A new_hs->hs_revision &= ~MD_64BIT_META_DEV;
1N/A }
1N/A
1N/A /* commit the db records */
1N/A mddb_commitrecs_wrapper(recids);
1N/A
1N/A if (delete_hs)
1N/A mddb_deleterec_wrapper(hs->hs_record_id);
1N/A
1N/A md_rem_names(&sv, 1);
1N/A
1N/A SE_NOTIFY(EC_SVM_CONFIG, ESC_SVM_REPLACE, SVM_TAG_HSP, setno,
1N/A md_expldev(hsp->hsp_self_id));
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Aseths_enable(set_hs_params_t *shs)
1N/A{
1N/A hot_spare_t *hs;
1N/A mddb_recid_t recids[2];
1N/A set_t setno = shs->md_driver.md_setno;
1N/A mdkey_t key_old;
1N/A int num_keys_old = 0;
1N/A
1N/A
1N/A /*
1N/A * Find device by using key associated with shs_component_old.
1N/A * If unable to find a unique key for shs_component_old
1N/A * then fail since namespace has multiple entries
1N/A * for this old component and we're unable to determine
1N/A * which key is the valid match for shs_component_old.
1N/A * This failure keeps a hotspare from being enabled on a slice
1N/A * that may already be in use by another metadevice.
1N/A */
1N/A if (md_getkeyfromdev(setno, mddb_getsidenum(setno),
1N/A shs->shs_component_old, &key_old, &num_keys_old) != 0) {
1N/A return (mddeverror(&shs->mde, MDE_NAME_SPACE,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /*
1N/A * If more than one key matches given old_dev - fail command
1N/A * since unable to determine which key is correct.
1N/A */
1N/A if (num_keys_old > 1) {
1N/A return (mddeverror(&shs->mde, MDE_MULTNM,
1N/A shs->shs_component_old));
1N/A }
1N/A /*
1N/A * If there is no key for this entry then fail since
1N/A * a key for this entry should exist.
1N/A */
1N/A if (num_keys_old == 0) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /* Scan the hot spare list for the hs */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A while (hs) {
1N/A /*
1N/A * Since component may or may not be currently in the system,
1N/A * use the keys to find a match (not the devt).
1N/A */
1N/A if (hs->hs_key == key_old) {
1N/A break;
1N/A }
1N/A hs = hs->hs_next;
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A return (mddeverror(&shs->mde, MDE_INVAL_HS,
1N/A shs->shs_component_old));
1N/A }
1N/A
1N/A /* make sure it's broken */
1N/A if (hs->hs_state != HSS_BROKEN) {
1N/A return (mddeverror(&shs->mde, MDE_FIX_INVAL_HS_STATE,
1N/A hs->hs_devnum));
1N/A }
1N/A
1N/A /* In case of a dryrun, we're done here */
1N/A if (shs->shs_options & HS_OPT_DRYRUN) {
1N/A return (0);
1N/A }
1N/A
1N/A /* fix it */
1N/A set_hot_spare_state(hs, HSS_AVAILABLE);
1N/A hs->hs_start_blk = shs->shs_start_blk;
1N/A hs->hs_has_label = shs->shs_has_label;
1N/A hs->hs_number_blks = shs->shs_number_blks;
1N/A
1N/A /* commit the db records */
1N/A recids[0] = hs->hs_record_id;
1N/A recids[1] = 0;
1N/A mddb_commitrecs_wrapper(recids);
1N/A SE_NOTIFY(EC_SVM_STATE, ESC_SVM_ENABLE, SVM_TAG_HS, setno,
1N/A shs->shs_component_old);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Aget_hs(
1N/A get_hs_params_t *ghs
1N/A)
1N/A{
1N/A hot_spare_t *hs;
1N/A set_t setno = ghs->md_driver.md_setno;
1N/A
1N/A mdclrerror(&ghs->mde);
1N/A
1N/A /* Scan the hot spare list for the hs */
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A while (hs) {
1N/A if (hs->hs_key == ghs->ghs_key) {
1N/A break;
1N/A }
1N/A hs = hs->hs_next;
1N/A }
1N/A
1N/A if (hs == NULL) {
1N/A return (mddeverror(&ghs->mde, MDE_INVAL_HS,
1N/A ghs->ghs_devnum));
1N/A }
1N/A
1N/A ghs->ghs_start_blk = hs->hs_start_blk;
1N/A ghs->ghs_number_blks = hs->hs_number_blks;
1N/A ghs->ghs_state = hs->hs_state;
1N/A ghs->ghs_timestamp = hs->hs_timestamp;
1N/A ghs->ghs_revision = hs->hs_revision;
1N/A return (0);
1N/A}
1N/A
1N/Astatic void
1N/Abuild_key_list(set_t setno, hot_spare_pool_t *hsp, mdkey_t *list)
1N/A{
1N/A int i;
1N/A
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A hot_spare_t *hs;
1N/A hs = lookup_hot_spare(setno, hsp->hsp_hotspares[i], 1);
1N/A list[i] = hs->hs_key;
1N/A }
1N/A}
1N/A
1N/Astatic int
1N/Aget_hsp(
1N/A void *d,
1N/A int mode
1N/A)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A get_hsp_t *ghsp;
1N/A size_t size;
1N/A set_t setno;
1N/A int err = 0;
1N/A md_i_get_t *migp = (md_i_get_t *)d;
1N/A
1N/A
1N/A setno = migp->md_driver.md_setno;
1N/A
1N/A mdclrerror(&migp->mde);
1N/A
1N/A /* Scan the hot spare pool list */
1N/A hsp = find_hot_spare_pool(setno, migp->id);
1N/A if (hsp == NULL) {
1N/A return (mdhsperror(&migp->mde, MDE_INVAL_HSP,
1N/A migp->id));
1N/A }
1N/A
1N/A size = (sizeof (ghsp->ghsp_hs_keys[0]) * (hsp->hsp_nhotspares - 1)) +
1N/A sizeof (get_hsp_t);
1N/A
1N/A if (migp->size == 0) {
1N/A migp->size = (int)size;
1N/A return (0);
1N/A }
1N/A
1N/A if (migp->size < size)
1N/A return (EFAULT);
1N/A
1N/A ghsp = kmem_alloc(size, KM_SLEEP);
1N/A
1N/A ghsp->ghsp_id = hsp->hsp_self_id;
1N/A ghsp->ghsp_refcount = hsp->hsp_refcount;
1N/A ghsp->ghsp_nhotspares = hsp->hsp_nhotspares;
1N/A build_key_list(setno, hsp, ghsp->ghsp_hs_keys);
1N/A if (ddi_copyout(ghsp, (caddr_t)(uintptr_t)migp->mdp, size, mode))
1N/A err = EFAULT;
1N/A kmem_free(ghsp, size);
1N/A return (err);
1N/A}
1N/A
1N/Astatic int
1N/Aset_hs(
1N/A set_hs_params_t *shs
1N/A)
1N/A{
1N/A mdclrerror(&shs->mde);
1N/A
1N/A if (md_get_setstatus(shs->md_driver.md_setno) & MD_SET_STALE)
1N/A return (mdmddberror(&shs->mde, MDE_DB_STALE, NODEV32,
1N/A shs->md_driver.md_setno));
1N/A
1N/A switch (shs->shs_cmd) {
1N/A case ADD_HOT_SPARE:
1N/A return (seths_add(shs));
1N/A case DELETE_HOT_SPARE:
1N/A return (seths_delete(shs));
1N/A case REPLACE_HOT_SPARE:
1N/A return (seths_replace(shs));
1N/A case FIX_HOT_SPARE:
1N/A return (seths_enable(shs));
1N/A default:
1N/A return (mderror(&shs->mde, MDE_INVAL_HSOP));
1N/A }
1N/A}
1N/A
1N/Astatic void
1N/Ahotspares_poke_hotspares(void)
1N/A{
1N/A intptr_t (*poke_hs)();
1N/A int i;
1N/A
1N/A for (i = 0; i < MD_NOPS; i++) {
1N/A /* handle change */
1N/A poke_hs = md_get_named_service(NODEV64, i, "poke hotspares", 0);
1N/A if (poke_hs)
1N/A (void) (*poke_hs)();
1N/A }
1N/A}
1N/A
1N/A
1N/A/*ARGSUSED4*/
1N/Astatic int
1N/Ahotspares_ioctl(
1N/A dev_t dev,
1N/A int cmd,
1N/A void *data,
1N/A int mode,
1N/A IOLOCK *lockp
1N/A)
1N/A{
1N/A size_t sz = 0;
1N/A void *d = NULL;
1N/A int err = 0;
1N/A
1N/A /* single thread */
1N/A if (getminor(dev) != MD_ADM_MINOR)
1N/A return (ENOTTY);
1N/A
1N/A /* We can only handle 32-bit clients for internal commands */
1N/A if ((mode & DATAMODEL_MASK) != DATAMODEL_ILP32) {
1N/A return (EINVAL);
1N/A }
1N/A
1N/A mutex_enter(&md_mx);
1N/A while (md_status & MD_GBL_HS_LOCK)
1N/A cv_wait(&md_cv, &md_mx);
1N/A md_status |= MD_GBL_HS_LOCK;
1N/A mutex_exit(&md_mx);
1N/A
1N/A /* dispatch ioctl */
1N/A switch (cmd) {
1N/A
1N/A case MD_IOCSET_HS: /* setup hot spares and pools */
1N/A {
1N/A if (! (mode & FWRITE)) {
1N/A err = EACCES;
1N/A break;
1N/A }
1N/A
1N/A sz = sizeof (set_hs_params_t);
1N/A d = kmem_alloc(sz, KM_SLEEP);
1N/A
1N/A if (ddi_copyin(data, d, sz, mode)) {
1N/A err = EFAULT;
1N/A break;
1N/A }
1N/A
1N/A err = set_hs(d);
1N/A break;
1N/A }
1N/A
1N/A case MD_IOCGET_HS: /* get hot spare info */
1N/A {
1N/A if (! (mode & FREAD)) {
1N/A err = EACCES;
1N/A break;
1N/A }
1N/A
1N/A sz = sizeof (get_hs_params_t);
1N/A d = kmem_alloc(sz, KM_SLEEP);
1N/A
1N/A if (ddi_copyin(data, d, sz, mode)) {
1N/A err = EFAULT;
1N/A break;
1N/A }
1N/A
1N/A err = get_hs(d);
1N/A break;
1N/A }
1N/A
1N/A case MD_IOCGET: /* get hot spare pool info */
1N/A {
1N/A if (! (mode & FREAD)) {
1N/A err = EACCES;
1N/A break;
1N/A }
1N/A
1N/A sz = sizeof (md_i_get_t);
1N/A d = kmem_alloc(sz, KM_SLEEP);
1N/A
1N/A if (ddi_copyin(data, d, sz, mode)) {
1N/A err = EFAULT;
1N/A break;
1N/A }
1N/A
1N/A err = get_hsp(d, mode);
1N/A break;
1N/A }
1N/A
1N/A default:
1N/A err = ENOTTY;
1N/A }
1N/A
1N/A /*
1N/A * copyout and free any args
1N/A */
1N/A if (sz != 0) {
1N/A if (err == 0) {
1N/A if (ddi_copyout(d, data, sz, mode) != 0) {
1N/A err = EFAULT;
1N/A }
1N/A }
1N/A kmem_free(d, sz);
1N/A }
1N/A
1N/A /* un single thread */
1N/A mutex_enter(&md_mx);
1N/A md_status &= ~MD_GBL_HS_LOCK;
1N/A cv_broadcast(&md_cv);
1N/A mutex_exit(&md_mx);
1N/A
1N/A /* handle change */
1N/A hotspares_poke_hotspares();
1N/A
1N/A /* return success */
1N/A return (err);
1N/A}
1N/A
1N/A
1N/Astatic void
1N/Aload_hotspare(set_t setno, mddb_recid_t recid)
1N/A{
1N/A hot_spare_t *hs;
1N/A mddb_de_ic_t *dep;
1N/A mddb_rb32_t *rbp;
1N/A size_t newreqsize;
1N/A hot_spare_t *b_hs;
1N/A hot_spare32_od_t *s_hs;
1N/A
1N/A mddb_setrecprivate(recid, MD_PRV_GOTIT);
1N/A
1N/A dep = mddb_getrecdep(recid);
1N/A dep->de_flags = MDDB_F_HOTSPARE;
1N/A rbp = dep->de_rb;
1N/A switch (rbp->rb_revision) {
1N/A case MDDB_REV_RB:
1N/A case MDDB_REV_RBFN:
1N/A /*
1N/A * Needs to convert to internal 64 bit
1N/A */
1N/A s_hs = (hot_spare32_od_t *)mddb_getrecaddr(recid);
1N/A newreqsize = sizeof (hot_spare_t);
1N/A b_hs = (hot_spare_t *)kmem_zalloc(newreqsize, KM_SLEEP);
1N/A hs_convert((caddr_t)s_hs, (caddr_t)b_hs, SMALL_2_BIG);
1N/A kmem_free(s_hs, dep->de_reqsize);
1N/A dep->de_rb_userdata = b_hs;
1N/A dep->de_reqsize = newreqsize;
1N/A hs = b_hs;
1N/A break;
1N/A case MDDB_REV_RB64:
1N/A case MDDB_REV_RB64FN:
1N/A hs = (hot_spare_t *)mddb_getrecaddr_resize
1N/A (recid, sizeof (*hs), 0);
1N/A break;
1N/A }
1N/A MDDB_NOTE_FN(rbp->rb_revision, hs->hs_revision);
1N/A
1N/A#if defined(_ILP32)
1N/A if (hs->hs_revision & MD_64BIT_META_DEV) {
1N/A char devname[MD_MAX_CTDLEN];
1N/A
1N/A set_hot_spare_state(hs, HSS_BROKEN);
1N/A (void) md_devname(setno, hs->hs_devnum, devname,
1N/A sizeof (devname));
1N/A cmn_err(CE_NOTE, "%s is unavailable because 64 bit hotspares "
1N/A "are not accessible on a 32 bit kernel\n", devname);
1N/A }
1N/A#endif
1N/A
1N/A ASSERT(hs != NULL);
1N/A
1N/A if (hs->hs_refcount == 0) {
1N/A mddb_setrecprivate(recid, MD_PRV_PENDDEL);
1N/A return;
1N/A }
1N/A
1N/A hs->hs_next = (hot_spare_t *)md_set[setno].s_hs;
1N/A md_set[setno].s_hs = (void *)hs;
1N/A
1N/A hs->hs_isopen = 0;
1N/A
1N/A hs->hs_devnum = md_getdevnum(setno, mddb_getsidenum(setno),
1N/A hs->hs_key, MD_NOTRUST_DEVT);
1N/A}
1N/A
1N/A
1N/Astatic void
1N/Aload_hotsparepool(set_t setno, mddb_recid_t recid)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A hot_spare_pool_ond_t *hsp_ond;
1N/A size_t hsp_icsize;
1N/A
1N/A mddb_setrecprivate(recid, MD_PRV_GOTIT);
1N/A
1N/A hsp_ond = (hot_spare_pool_ond_t *)mddb_getrecaddr(recid);
1N/A ASSERT(hsp_ond != NULL);
1N/A
1N/A if (hsp_ond->hsp_self_id == MD_HSP_NONE) {
1N/A mddb_setrecprivate(recid, MD_PRV_PENDDEL);
1N/A return;
1N/A }
1N/A
1N/A hsp_icsize = HSP_ONDSK_STR_OFF + mddb_getrecsize(recid);
1N/A
1N/A hsp = (hot_spare_pool_t *)mddb_getrecaddr_resize(recid, hsp_icsize,
1N/A HSP_ONDSK_STR_OFF);
1N/A hsp->hsp_next = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A md_set[setno].s_hsp = (void *) hsp;
1N/A
1N/A rw_enter(&hotspares_md_ops.md_link_rw.lock, RW_WRITER);
1N/A hsp->hsp_link.ln_next = hotspares_md_ops.md_head;
1N/A hsp->hsp_link.ln_setno = setno;
1N/A hsp->hsp_link.ln_id = hsp->hsp_self_id;
1N/A hotspares_md_ops.md_head = &hsp->hsp_link;
1N/A rw_exit(&hotspares_md_ops.md_link_rw.lock);
1N/A}
1N/A
1N/Astatic int
1N/Ahotspares_snarf(md_snarfcmd_t cmd, set_t setno)
1N/A{
1N/A mddb_recid_t recid;
1N/A int gotsomething;
1N/A mddb_type_t typ1;
1N/A
1N/A if (cmd == MD_SNARF_CLEANUP)
1N/A return (0);
1N/A
1N/A gotsomething = 0;
1N/A
1N/A typ1 = (mddb_type_t)md_getshared_key(setno,
1N/A hotspares_md_ops.md_driver.md_drivername);
1N/A recid = mddb_makerecid(setno, 0);
1N/A while ((recid = mddb_getnextrec(recid, typ1, 0)) > 0) {
1N/A if (mddb_getrecprivate(recid) & MD_PRV_GOTIT)
1N/A continue;
1N/A
1N/A switch (mddb_getrectype2(recid)) {
1N/A case HSP_REC:
1N/A load_hotsparepool(setno, recid);
1N/A gotsomething = 1;
1N/A break;
1N/A case HS_REC:
1N/A load_hotspare(setno, recid);
1N/A gotsomething = 1;
1N/A break;
1N/A default:
1N/A ASSERT(0);
1N/A }
1N/A }
1N/A
1N/A if (gotsomething)
1N/A return (gotsomething);
1N/A
1N/A recid = mddb_makerecid(setno, 0);
1N/A while ((recid = mddb_getnextrec(recid, typ1, 0)) > 0)
1N/A if (!(mddb_getrecprivate(recid) & MD_PRV_GOTIT))
1N/A mddb_setrecprivate(recid, MD_PRV_PENDDEL);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Ahotspares_halt(md_haltcmd_t cmd, set_t setno)
1N/A{
1N/A hot_spare_t *hs, **p_hs;
1N/A hot_spare_pool_t *hsp, **p_hsp;
1N/A
1N/A if (cmd == MD_HALT_CLOSE)
1N/A return (0);
1N/A
1N/A if (cmd == MD_HALT_OPEN)
1N/A return (0);
1N/A
1N/A if (cmd == MD_HALT_CHECK)
1N/A return (0);
1N/A
1N/A if (cmd == MD_HALT_UNLOAD)
1N/A return (0);
1N/A
1N/A if (cmd != MD_HALT_DOIT)
1N/A return (1);
1N/A /*
1N/A * Find all the hotspares for set "setno"
1N/A * and remove them from the hot_spare_list.
1N/A */
1N/A p_hs = (hot_spare_t **)&md_set[setno].s_hs;
1N/A hs = (hot_spare_t *)md_set[setno].s_hs;
1N/A for (; hs != NULL; hs = *p_hs)
1N/A *p_hs = hs->hs_next;
1N/A
1N/A /*
1N/A * Find all the hotspare pools for set "setno"
1N/A * and remove them from the hot_spare_pools list.
1N/A * Also remove from the get_next list.
1N/A */
1N/A p_hsp = (hot_spare_pool_t **)&md_set[setno].s_hsp;
1N/A hsp = (hot_spare_pool_t *)md_set[setno].s_hsp;
1N/A for (; hsp != NULL; hsp = *p_hsp) {
1N/A md_rem_link(setno, hsp->hsp_self_id,
1N/A &hotspares_md_ops.md_link_rw.lock,
1N/A &hotspares_md_ops.md_head);
1N/A *p_hsp = hsp->hsp_next;
1N/A }
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic hot_spare_t *
1N/Ausable_hs(
1N/A set_t setno,
1N/A mddb_recid_t hs_id,
1N/A diskaddr_t nblks,
1N/A int labeled,
1N/A diskaddr_t *start)
1N/A{
1N/A hot_spare_t *hs;
1N/A
1N/A hs = lookup_hot_spare(setno, hs_id, 1);
1N/A
1N/A if (hs->hs_state != HSS_AVAILABLE)
1N/A return ((hot_spare_t *)0);
1N/A
1N/A if (labeled && hs->hs_has_label && (hs->hs_number_blks >= nblks)) {
1N/A *start = 0;
1N/A return (hs);
1N/A } else if ((hs->hs_number_blks - hs->hs_start_blk) >= nblks) {
1N/A *start = hs->hs_start_blk;
1N/A return (hs);
1N/A }
1N/A return ((hot_spare_t *)0);
1N/A}
1N/A
1N/Astatic int
1N/Areserve_a_hs(
1N/A set_t setno,
1N/A mddb_recid_t id,
1N/A uint64_t size,
1N/A int labeled,
1N/A mddb_recid_t *hs_id,
1N/A mdkey_t *key,
1N/A md_dev64_t *dev,
1N/A diskaddr_t *sblock)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A hot_spare_t *hs;
1N/A int i;
1N/A
1N/A *hs_id = 0;
1N/A
1N/A hsp = find_hot_spare_pool(setno, id);
1N/A if (hsp == NULL)
1N/A return (-1);
1N/A
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A hs = usable_hs(setno, hsp->hsp_hotspares[i],
1N/A size, labeled, sblock);
1N/A if (hs == NULL)
1N/A continue;
1N/A
1N/A set_hot_spare_state(hs, HSS_RESERVED);
1N/A *hs_id = hs->hs_record_id;
1N/A *key = hs->hs_key;
1N/A *dev = hs->hs_devnum;
1N/A /* NOTE: Mirror code commits the hs record */
1N/A return (0);
1N/A }
1N/A
1N/A return (-1);
1N/A}
1N/A
1N/A
1N/A/* ARGSUSED3 */
1N/Astatic int
1N/Areturn_a_hs(
1N/A set_t setno,
1N/A mddb_recid_t id,
1N/A mddb_recid_t *hs_id,
1N/A mdkey_t key,
1N/A diskaddr_t sblock,
1N/A uint64_t size,
1N/A hotspare_states_t new_state)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A hot_spare_t *hs;
1N/A int i;
1N/A
1N/A /*
1N/A * NOTE: sblock/size are not currently being used.
1N/A * That is because we always allocate the whole hs.
1N/A * Later if we choose to allocate only what is needed
1N/A * then the sblock/size can be used to determine
1N/A * which part is being unreseved.
1N/A */
1N/A *hs_id = 0;
1N/A
1N/A hsp = find_hot_spare_pool(setno, id);
1N/A if (hsp == NULL)
1N/A return (-1);
1N/A
1N/A for (i = 0; i < hsp->hsp_nhotspares; i++) {
1N/A hs = lookup_hot_spare(setno, hsp->hsp_hotspares[i], 1);
1N/A if (hs->hs_key != key)
1N/A continue;
1N/A
1N/A set_hot_spare_state(hs, new_state);
1N/A *hs_id = hs->hs_record_id;
1N/A if (new_state == HSS_BROKEN) {
1N/A SE_NOTIFY(EC_SVM_STATE, ESC_SVM_ERRED, SVM_TAG_HS,
1N/A setno, hs->hs_devnum);
1N/A }
1N/A if (new_state == HSS_AVAILABLE) {
1N/A SE_NOTIFY(EC_SVM_STATE, ESC_SVM_HS_FREED, SVM_TAG_HS,
1N/A setno, hs->hs_devnum);
1N/A }
1N/A
1N/A /* NOTE: Mirror/Raid code commits the hs record */
1N/A return (0);
1N/A }
1N/A
1N/A return (-1);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Amodify_hsp_ref(set_t setno, mddb_recid_t id, int incref, mddb_recid_t *hsp_id)
1N/A{
1N/A hot_spare_pool_t *hsp;
1N/A
1N/A *hsp_id = 0;
1N/A
1N/A if (id < 0)
1N/A return (0);
1N/A
1N/A hsp = find_hot_spare_pool(setno, id);
1N/A if (hsp == NULL)
1N/A return (-1);
1N/A
1N/A if (incref)
1N/A hsp->hsp_refcount++;
1N/A else
1N/A hsp->hsp_refcount--;
1N/A
1N/A *hsp_id = hsp->hsp_record_id;
1N/A
1N/A /* NOTE: Stripe code commits the hsp record */
1N/A return (0);
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Amkdev_for_a_hs(mddb_recid_t hs_id, md_dev64_t *dev)
1N/A{
1N/A hot_spare_t *hs;
1N/A
1N/A hs = lookup_hot_spare(mddb_getsetnum(hs_id), hs_id, 0);
1N/A if (hs == NULL)
1N/A return (0);
1N/A
1N/A *dev = hs->hs_devnum;
1N/A return (0);
1N/A}
1N/A
1N/Astatic intptr_t
1N/Ahotspares_interface(
1N/A hs_cmds_t cmd,
1N/A mddb_recid_t id,
1N/A uint64_t size,
1N/A int bool,
1N/A mddb_recid_t *hs_id,
1N/A mdkey_t *key,
1N/A md_dev64_t *dev,
1N/A diskaddr_t *sblock)
1N/A{
1N/A set_t setno;
1N/A int err = -1;
1N/A
1N/A mutex_enter(&md_mx);
1N/A while (md_status & MD_GBL_HS_LOCK)
1N/A cv_wait(&md_cv, &md_mx);
1N/A
1N/A /* If md_halt has been run do not continue */
1N/A if (md_status & (MD_GBL_HALTED | MD_GBL_DAEMONS_DIE)) {
1N/A mutex_exit(&md_mx);
1N/A return (ENXIO);
1N/A }
1N/A
1N/A md_status |= MD_GBL_HS_LOCK;
1N/A mutex_exit(&md_mx);
1N/A
1N/A setno = mddb_getsetnum(id);
1N/A
1N/A switch (cmd) {
1N/A case HS_GET:
1N/A err = reserve_a_hs(setno, id, size, bool, hs_id,
1N/A key, dev, sblock);
1N/A break;
1N/A case HS_FREE:
1N/A err = return_a_hs(setno, id, hs_id, *key, 0, 0, HSS_AVAILABLE);
1N/A hotspares_poke_hotspares();
1N/A break;
1N/A case HS_BAD:
1N/A err = return_a_hs(setno, id, hs_id, *key, 0, 0, HSS_BROKEN);
1N/A break;
1N/A case HSP_INCREF:
1N/A err = modify_hsp_ref(setno, id, 1, hs_id);
1N/A break;
1N/A case HSP_DECREF:
1N/A err = modify_hsp_ref(setno, id, 0, hs_id);
1N/A break;
1N/A case HS_MKDEV:
1N/A err = mkdev_for_a_hs(*hs_id, dev);
1N/A break;
1N/A }
1N/A
1N/A mutex_enter(&md_mx);
1N/A md_status &= ~MD_GBL_HS_LOCK;
1N/A cv_broadcast(&md_cv);
1N/A mutex_exit(&md_mx);
1N/A
1N/A return (err);
1N/A}
1N/A
1N/Astatic void
1N/Aimp_hotsparepool(
1N/A set_t setno,
1N/A mddb_recid_t recid
1N/A)
1N/A{
1N/A hot_spare_pool_ond_t *hsp_ond;
1N/A mddb_recid_t *hsp_recid, *hs_recid;
1N/A int i;
1N/A uint_t *hsp_selfid;
1N/A
1N/A mddb_setrecprivate(recid, MD_PRV_GOTIT);
1N/A
1N/A hsp_ond = (hot_spare_pool_ond_t *)mddb_getrecaddr(recid);
1N/A hsp_recid = &(hsp_ond->hsp_record_id);
1N/A hsp_selfid = &(hsp_ond->hsp_self_id);
1N/A /*
1N/A * Fixup the pool and hotspares
1N/A */
1N/A *hsp_recid = MAKERECID(setno, DBID(*hsp_recid));
1N/A *hsp_selfid = MAKERECID(setno, DBID(*hsp_selfid));
1N/A
1N/A for (i = 0; i < hsp_ond->hsp_nhotspares; i++) {
1N/A hs_recid = &(hsp_ond->hsp_hotspares[i]);
1N/A *hs_recid = MAKERECID(setno, DBID(*hs_recid));
1N/A }
1N/A}
1N/A
1N/Astatic void
1N/Aimp_hotspare(
1N/A set_t setno,
1N/A mddb_recid_t recid
1N/A)
1N/A{
1N/A mddb_de_ic_t *dep;
1N/A mddb_rb32_t *rbp;
1N/A hot_spare_t *hs64;
1N/A hot_spare32_od_t *hs32;
1N/A mddb_recid_t *hs_recid;
1N/A
1N/A mddb_setrecprivate(recid, MD_PRV_GOTIT);
1N/A
1N/A dep = mddb_getrecdep(recid);
1N/A rbp = dep->de_rb;
1N/A switch (rbp->rb_revision) {
1N/A case MDDB_REV_RB:
1N/A case MDDB_REV_RBFN:
1N/A /*
1N/A * 32 bit hotspare
1N/A */
1N/A hs32 = (hot_spare32_od_t *)mddb_getrecaddr(recid);
1N/A hs_recid = &(hs32->hs_record_id);
1N/A break;
1N/A case MDDB_REV_RB64:
1N/A case MDDB_REV_RB64FN:
1N/A hs64 = (hot_spare_t *)mddb_getrecaddr(recid);
1N/A hs_recid = &(hs64->hs_record_id);
1N/A break;
1N/A }
1N/A
1N/A /*
1N/A * Fixup the setno
1N/A */
1N/A *hs_recid = MAKERECID(setno, DBID(*hs_recid));
1N/A}
1N/A
1N/Astatic int
1N/Ahotspares_imp_set(
1N/A set_t setno
1N/A)
1N/A{
1N/A mddb_recid_t recid;
1N/A int gotsomething;
1N/A mddb_type_t typ1;
1N/A
1N/A
1N/A gotsomething = 0;
1N/A
1N/A typ1 = (mddb_type_t)md_getshared_key(setno,
1N/A hotspares_md_ops.md_driver.md_drivername);
1N/A recid = mddb_makerecid(setno, 0);
1N/A while ((recid = mddb_getnextrec(recid, typ1, 0)) > 0) {
1N/A if (mddb_getrecprivate(recid) & MD_PRV_GOTIT)
1N/A continue;
1N/A
1N/A switch (mddb_getrectype2(recid)) {
1N/A case HSP_REC:
1N/A imp_hotsparepool(setno, recid);
1N/A gotsomething = 1;
1N/A break;
1N/A case HS_REC:
1N/A imp_hotspare(setno, recid);
1N/A gotsomething = 1;
1N/A break;
1N/A default:
1N/A ASSERT(0);
1N/A }
1N/A }
1N/A
1N/A return (gotsomething);
1N/A}
1N/A
1N/Astatic md_named_services_t hotspares_named_services[] = {
1N/A {hotspares_interface, "hot spare interface"},
1N/A {NULL, 0}
1N/A};
1N/A
1N/Amd_ops_t hotspares_md_ops = {
1N/A NULL, /* open */
1N/A NULL, /* close */
1N/A NULL, /* strategy */
1N/A NULL, /* print */
1N/A NULL, /* dump */
1N/A NULL, /* read */
1N/A NULL, /* write */
1N/A hotspares_ioctl, /* hotspares_ioctl, */
1N/A hotspares_snarf, /* hotspares_snarf */
1N/A hotspares_halt, /* halt */
1N/A NULL, /* aread */
1N/A NULL, /* awrite */
1N/A hotspares_imp_set, /* import set */
1N/A hotspares_named_services /* named_services */
1N/A};
1N/A
1N/Astatic void
1N/Afini_uninit()
1N/A{
1N/A /* prevent access to services that may have been imported */
1N/A md_clear_hot_spare_interface();
1N/A}
1N/A
1N/A/* define the module linkage */
1N/AMD_PLUGIN_MISC_MODULE("hot spares module", md_noop, fini_uninit())
1N/A