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/*
2N/A * Copyright (c) 1995, 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 * Metadevice diskset interfaces
2N/A */
2N/A
2N/A#include "meta_set_prv.h"
2N/A#include <meta.h>
2N/A#include <sys/lvm/md_crc.h>
2N/A#include <sys/time.h>
2N/A#include <sdssc.h>
2N/A
2N/Astatic int
2N/Aadd_db_sidenms(
2N/A mdsetname_t *sp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_replicalist_t *rlp = NULL;
2N/A md_replicalist_t *rl;
2N/A int rval = 0;
2N/A
2N/A if (metareplicalist(sp, MD_FULLNAME_ONLY, &rlp, ep) < 0)
2N/A return (-1);
2N/A
2N/A for (rl = rlp; rl != NULL; rl = rl->rl_next) {
2N/A md_replica_t *r = rl->rl_repp;
2N/A
2N/A /*
2N/A * This is not the first replica being added to the
2N/A * diskset so call with ADDSIDENMS_BCAST. If this
2N/A * is a traditional diskset, the bcast flag is ignored
2N/A * since traditional disksets don't use the rpc.mdcommd.
2N/A */
2N/A if (meta_db_addsidenms(sp, r->r_namep, r->r_blkno,
2N/A DB_ADDSIDENMS_BCAST, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A metafreereplicalist(rlp);
2N/A return (rval);
2N/A}
2N/A
2N/Astatic int
2N/Aadd_drvs_to_hosts(
2N/A mdsetname_t *sp,
2N/A int node_c,
2N/A char **node_v,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A md_set_desc *sd;
2N/A md_drive_desc *dd;
2N/A md_timeval32_t now;
2N/A ulong_t genid;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((dd = metaget_drivedesc(sp, MD_FULLNAME_ONLY, ep)) == NULL) {
2N/A if (! mdisok(ep))
2N/A return (-1);
2N/A return (0);
2N/A }
2N/A
2N/A now = sd->sd_ctime;
2N/A genid = sd->sd_genid - 1;
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_adddrvs(node_v[i], sp, dd, now, genid, ep) == -1)
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aadd_md_sidenms(mdsetname_t *sp, side_t sideno, side_t otherside, md_error_t *ep)
2N/A{
2N/A mdnm_params_t nm;
2N/A char *cname, *dname;
2N/A side_t tmp_sideno;
2N/A minor_t mnum;
2N/A int done, i;
2N/A int rval = 0;
2N/A md_set_desc *sd;
2N/A
2N/A (void) memset(&nm, '\0', sizeof (nm));
2N/A nm.key = MD_KEYWILD;
2N/A
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A }
2N/A /* Use rpc.mdcommd to add md side info from all nodes */
2N/A if ((! metaislocalset(sp)) && MD_MNSET_DESC(sd) &&
2N/A (sd->sd_mn_mynode->nd_flags & MD_MN_NODE_OWN)) {
2N/A md_mn_result_t *resultp = NULL;
2N/A md_mn_msg_meta_md_addside_t md_as;
2N/A int send_rval;
2N/A
2N/A md_as.msg_sideno = sideno;
2N/A md_as.msg_otherside = otherside;
2N/A /*
2N/A * If reconfig cycle has been started, this node is stuck in
2N/A * in the return step until this command has completed. If
2N/A * mdcommd is suspended, ask send_message to fail (instead of
2N/A * retrying) so that metaset can finish allowing the
2N/A * reconfig cycle to proceed.
2N/A */
2N/A send_rval = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_META_MD_ADDSIDE,
2N/A MD_MSGF_FAIL_ON_SUSPEND | MD_MSGF_PANIC_WHEN_INCONSISTENT,
2N/A 0, (char *)&md_as, sizeof (md_mn_msg_meta_md_addside_t),
2N/A &resultp, ep);
2N/A if (send_rval != 0) {
2N/A (void) mdstealerror(ep, &(resultp->mmr_ep));
2N/A if (resultp)
2N/A free_result(resultp);
2N/A return (-1);
2N/A }
2N/A if (resultp)
2N/A free_result(resultp);
2N/A return (0);
2N/A } else {
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A char *drvnm = NULL;
2N/A
2N/A nm.mde = mdnullerror;
2N/A nm.setno = sp->setno;
2N/A nm.side = otherside;
2N/A if (metaioctl(MD_IOCNXTKEY_NM, &nm, &nm.mde, NULL) != 0)
2N/A return (mdstealerror(ep, &nm.mde));
2N/A
2N/A if (nm.key == MD_KEYWILD)
2N/A return (0);
2N/A
2N/A /*
2N/A * Okay we have a valid key
2N/A * Let's see if it is hsp or not
2N/A */
2N/A nm.devname = (uintptr_t)meta_getnmentbykey(sp->setno,
2N/A otherside, nm.key, &drvnm, NULL, NULL, ep);
2N/A if (nm.devname == NULL || drvnm == NULL) {
2N/A if (nm.devname)
2N/A Free((void *)(uintptr_t)nm.devname);
2N/A if (drvnm)
2N/A Free((void *)(uintptr_t)drvnm);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * If it is hsp add here
2N/A */
2N/A if (strcmp(drvnm, MD_HOTSPARES) == 0) {
2N/A if (add_name(sp, sideno, nm.key, MD_HOTSPARES,
2N/A minor(NODEV), (char *)(uintptr_t)nm.devname,
2N/A NULL, NULL, ep) == -1) {
2N/A Free((void *)(uintptr_t)nm.devname);
2N/A Free((void *)(uintptr_t)drvnm);
2N/A return (-1);
2N/A } else {
2N/A Free((void *)(uintptr_t)nm.devname);
2N/A Free((void *)(uintptr_t)drvnm);
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A nm.side = sideno;
2N/A if (MD_MNSET_DESC(sd)) {
2N/A tmp_sideno = sideno;
2N/A } else {
2N/A tmp_sideno = sideno - 1;
2N/A }
2N/A
2N/A if ((done = meta_getnextside_devinfo(sp,
2N/A (char *)(uintptr_t)nm.devname, &tmp_sideno,
2N/A &cname, &dname, &mnum, ep)) == -1) {
2N/A Free((void *)(uintptr_t)nm.devname);
2N/A return (-1);
2N/A }
2N/A
2N/A assert(done == 1);
2N/A Free((void *)(uintptr_t)nm.devname);
2N/A Free((void *)(uintptr_t)drvnm);
2N/A
2N/A /*
2N/A * The device reference count can be greater than 1 if
2N/A * more than one softpart is configured on top of the
2N/A * same device. If this is the case then we want to
2N/A * increment the count to sync up with the other sides.
2N/A */
2N/A for (i = 0; i < nm.ref_count; i++) {
2N/A if (add_name(sp, sideno, nm.key, dname, mnum,
2N/A cname, NULL, NULL, ep) == -1)
2N/A rval = -1;
2N/A }
2N/A
2N/A Free(cname);
2N/A Free(dname);
2N/A
2N/A if (rval != 0)
2N/A return (rval);
2N/A }
2N/A }
2N/A
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/Astatic int
2N/Acheck_setdrvs_againstnode(mdsetname_t *sp, char *node, md_error_t *ep)
2N/A{
2N/A mddrivename_t *dp;
2N/A md_drive_desc *dd, *ddp;
2N/A
2N/A if ((dd = metaget_drivedesc(sp, MD_FULLNAME_ONLY, ep)) == NULL)
2N/A if (! mdisok(ep))
2N/A return (-1);
2N/A
2N/A for (ddp = dd; ddp != NULL; ddp = ddp->dd_next) {
2N/A dp = ddp->dd_dnp;
2N/A
2N/A if (checkdrive_onnode(sp, dp, node, ep))
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Acreate_multinode_set_on_hosts(
2N/A mdsetname_t *sp,
2N/A int node_c, /* Number of new nodes */
2N/A char **node_v, /* Nodes which are being added */
2N/A int new_set,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A md_set_desc *sd;
2N/A md_timeval32_t now;
2N/A ulong_t genid;
2N/A int rval = 0;
2N/A md_mnnode_desc *nd, *ndm = NULL;
2N/A md_mnnode_desc *nd_prev, *nd_curr;
2N/A int nodecnt;
2N/A mndiskset_membershiplist_t *nl, *nl2;
2N/A
2N/A if (!new_set) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A now = sd->sd_ctime;
2N/A genid = sd->sd_genid - 1;
2N/A if (sd->sd_drvs)
2N/A genid--;
2N/A } else {
2N/A sd = Zalloc(sizeof (*sd));
2N/A
2N/A if (meta_gettimeofday(&now) == -1) {
2N/A (void) mdsyserror(ep, errno,
2N/A dgettext(TEXT_DOMAIN, "meta_gettimeofday()"));
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /* Put the new entries into the set */
2N/A /*
2N/A * Get membershiplist from API routine. If there's
2N/A * an error, fail to create set and pass back error.
2N/A */
2N/A if (meta_read_nodelist(&nodecnt, &nl, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * meta_set_addhosts has already verified that
2N/A * this node list is in the membership list
2N/A * so set ALIVE flag.
2N/A * Since this is a new set, all hosts being
2N/A * added are new to the set, so also set ADD flag.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A nd = Zalloc(sizeof (*nd));
2N/A (void) strcpy(nd->nd_nodename, node_v[i]);
2N/A nd->nd_ctime = now;
2N/A nd->nd_flags = (MD_MN_NODE_ALIVE |
2N/A MD_MN_NODE_ADD);
2N/A nl2 = nl;
2N/A while (nl2) {
2N/A if (strcmp(nl2->msl_node_name,
2N/A node_v[i]) == 0) {
2N/A nd->nd_nodeid = nl2->msl_node_id;
2N/A (void) strcpy(nd->nd_priv_ic,
2N/A nl2->msl_node_addr);
2N/A break;
2N/A }
2N/A nl2 = nl2->next;
2N/A }
2N/A
2N/A /*
2N/A * Nodelist must be kept in ascending
2N/A * nodeid order.
2N/A */
2N/A if (sd->sd_nodelist == NULL) {
2N/A /* Nothing in list, just add it */
2N/A sd->sd_nodelist = nd;
2N/A } else if (nd->nd_nodeid < sd->sd_nodelist->nd_nodeid) {
2N/A /* Add to head of list */
2N/A nd->nd_next = sd->sd_nodelist;
2N/A sd->sd_nodelist = nd;
2N/A } else {
2N/A nd_curr = sd->sd_nodelist->nd_next;
2N/A nd_prev = sd->sd_nodelist;
2N/A /* Search for place ot add it */
2N/A while (nd_curr) {
2N/A if (nd->nd_nodeid <
2N/A nd_curr->nd_nodeid) {
2N/A /* Add before nd_curr */
2N/A nd->nd_next = nd_curr;
2N/A nd_prev->nd_next = nd;
2N/A break;
2N/A }
2N/A nd_prev = nd_curr;
2N/A nd_curr = nd_curr->nd_next;
2N/A }
2N/A /* Add to end of list */
2N/A if (nd_curr == NULL) {
2N/A nd_prev->nd_next = nd;
2N/A }
2N/A
2N/A }
2N/A /* Set master to be first node added */
2N/A if (ndm == NULL)
2N/A ndm = nd;
2N/A }
2N/A
2N/A meta_free_nodelist(nl);
2N/A /*
2N/A * Creating mnset for first time.
2N/A * Set master to be invalid until first drive is
2N/A * in set.
2N/A */
2N/A (void) strcpy(sd->sd_mn_master_nodenm, "");
2N/A sd->sd_mn_master_nodeid = MD_MN_INVALID_NID;
2N/A sd->sd_mn_masternode = ndm;
2N/A sd->sd_ctime = now;
2N/A genid = sd->sd_genid = 0;
2N/A }
2N/A
2N/A /* Create the set where needed */
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * Create the set on each new node. If the set already
2N/A * exists, then the node list being created on each new node
2N/A * is the current node list from before the new nodes
2N/A * were added. If the set doesn't exist, then the node
2N/A * list being created on each new node is the entire
2N/A * new node list.
2N/A */
2N/A if (clnt_mncreateset(node_v[i], sp, sd->sd_nodelist,
2N/A now, genid, sd->sd_mn_master_nodenm,
2N/A sd->sd_mn_master_nodeid, ep) == -1) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (new_set) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A sd->sd_nodelist = nd->nd_next;
2N/A Free(nd);
2N/A nd = sd->sd_nodelist;
2N/A }
2N/A Free(sd);
2N/A }
2N/A
2N/A if (rval != 0 || new_set)
2N/A return (rval);
2N/A
2N/A /*
2N/A * Add the drive records to the new sets
2N/A * and names for the new sides.
2N/A */
2N/A return (add_drvs_to_hosts(sp, node_c, node_v, ep));
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Acreate_traditional_set_on_hosts(
2N/A mdsetname_t *sp,
2N/A int node_c, /* Number of new nodes */
2N/A char **node_v, /* Nodes which are being added */
2N/A int new_set,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A md_set_desc *sd;
2N/A md_timeval32_t now;
2N/A ulong_t genid;
2N/A int rval = 0;
2N/A
2N/A if (!new_set) {
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A now = sd->sd_ctime;
2N/A
2N/A genid = sd->sd_genid;
2N/A
2N/A if (sd->sd_drvs)
2N/A genid--;
2N/A } else {
2N/A if (node_c > MD_MAXSIDES)
2N/A return (mddserror(ep, MDE_DS_SIDENUMNOTAVAIL,
2N/A sp->setno, NULL, NULL, sp->setname));
2N/A
2N/A sd = Zalloc(sizeof (*sd));
2N/A
2N/A /* Put the new entries into the set */
2N/A for (i = 0; i < node_c; i++) {
2N/A (void) strcpy(sd->sd_nodes[i], node_v[i]);
2N/A }
2N/A
2N/A if (meta_gettimeofday(&now) == -1) {
2N/A (void) mdsyserror(ep, errno, "meta_gettimeofday()");
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A sd->sd_ctime = now;
2N/A genid = sd->sd_genid = 0;
2N/A }
2N/A
2N/A /* Create the set where needed */
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * Create the set on each new host
2N/A */
2N/A if (clnt_createset(node_v[i], sp, sd->sd_nodes, now, genid,
2N/A ep) == -1) {
2N/A rval = -1;
2N/A break;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (new_set)
2N/A Free(sd);
2N/A
2N/A if (rval != 0 || new_set)
2N/A return (rval);
2N/A
2N/A /*
2N/A * Add the drive records to the new sets
2N/A * and names for the new sides.
2N/A */
2N/A return (add_drvs_to_hosts(sp, node_c, node_v, ep));
2N/A}
2N/A
2N/Astatic int
2N/Acreate_set_on_hosts(
2N/A mdsetname_t *sp,
2N/A int multi_node, /* Multi_node diskset or not? */
2N/A int node_c, /* Number of new nodes */
2N/A char **node_v, /* Nodes which are being added */
2N/A int new_set,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (multi_node)
2N/A return (create_multinode_set_on_hosts(sp, node_c, node_v,
2N/A new_set, ep));
2N/A else
2N/A return (create_traditional_set_on_hosts(sp, node_c, node_v,
2N/A new_set, ep));
2N/A}
2N/A
2N/Astatic int
2N/Acreate_set(
2N/A mdsetname_t *sp,
2N/A int multi_node, /* Multi-node diskset or not? */
2N/A int node_c,
2N/A char **node_v,
2N/A int auto_take,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A int rval = 0;
2N/A set_t max_sets;
2N/A set_t setno;
2N/A int bool;
2N/A uint_t sr_flags;
2N/A sigset_t oldsigs;
2N/A md_setkey_t *cl_sk;
2N/A int rb_level = 0;
2N/A md_error_t xep = mdnullerror;
2N/A rval_e sdssc_rval;
2N/A int lock_flag = 0;
2N/A int sig_flag = 0;
2N/A
2N/A if ((max_sets = get_max_sets(ep)) == 0)
2N/A return (-1);
2N/A
2N/A /* We must be a member of the set we are creating */
2N/A if (! strinlst(mynode(), node_c, node_v))
2N/A return (mddserror(ep, MDE_DS_SELFNOTIN,
2N/A sp->setno, mynode(), NULL, sp->setname));
2N/A
2N/A /*
2N/A * If auto_take then we must be the only member of the set
2N/A * that we are creating.
2N/A */
2N/A if (auto_take && node_c > 1)
2N/A return (mddserror(ep, MDE_DS_SINGLEHOST, sp->setno, NULL, NULL,
2N/A sp->setname));
2N/A
2N/A /*
2N/A * If we're part of SC3.0 we'll already have allocated the
2N/A * set number so we can skip the allocation algorithm used.
2N/A * Set number is unique across traditional and MN disksets.
2N/A */
2N/A if ((sdssc_rval = sdssc_get_index(sp->setname, &setno))
2N/A == SDSSC_NOT_BOUND) {
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A int has_set;
2N/A
2N/A /* Skip my node */
2N/A if (strcmp(mynode(), node_v[i]) == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * Make sure this set name is not used on the
2N/A * other hosts
2N/A */
2N/A has_set = nodehasset(sp, node_v[i], NHS_N_EQ, ep);
2N/A if (has_set < 0) {
2N/A if (! mdiserror(ep, MDE_NO_SET)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A
2N/A if (has_set) {
2N/A (void) mddserror(ep, MDE_DS_NODEHASSET,
2N/A sp->setno, node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A for (setno = 1; setno < max_sets; setno++) {
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_setnumbusy(node_v[i], setno,
2N/A &bool, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (bool == TRUE)
2N/A break;
2N/A }
2N/A if (i == node_c)
2N/A break;
2N/A }
2N/A } else if (sdssc_rval != SDSSC_OKAY) {
2N/A (void) mddserror(ep, MDE_DS_SETNUMNOTAVAIL, MD_SET_BAD, NULL,
2N/A NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (setno == max_sets) {
2N/A (void) mddserror(ep, MDE_DS_SETNUMNOTAVAIL, MD_SET_BAD, NULL,
2N/A NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A sp->setno = setno;
2N/A
2N/A /*
2N/A * Lock the set on current set members.
2N/A * Set locking done much earlier for MN diskset than for traditional
2N/A * diskset since lock_set is used to protect against
2N/A * other meta* commands running on the other nodes.
2N/A * Don't issue mdcommd SUSPEND command since there is nothing
2N/A * to suspend since there currently is no set.
2N/A */
2N/A if (multi_node) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A sig_flag = 1;
2N/A
2N/A /* Lock the set on new set members */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_lock_set(node_v[i], sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A lock_flag = 1;
2N/A }
2N/A /* Now have the diskset locked, verify set number is still ok */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_setnumbusy(node_v[i], setno,
2N/A &bool, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A
2N/A if (meta_set_checkname(sp->setname, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_setnameok(node_v[i], sp, &bool, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A if (bool == FALSE) {
2N/A (void) mddserror(ep, MDE_DS_SETNAMEBUSY, sp->setno,
2N/A node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* END CHECK CODE */
2N/A
2N/A /* Lock the set on new set members */
2N/A if (!multi_node) {
2N/A md_rb_sig_handling_on();
2N/A sig_flag = 1;
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_lock_set(node_v[i], sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A lock_flag = 1;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(1, "create_set", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 1; /* level 1 */
2N/A
2N/A RB_TEST(2, "create_set", ep)
2N/A
2N/A if ((rval = create_set_on_hosts(sp, multi_node, node_c, node_v,
2N/A 1, ep)) == -1)
2N/A goto rollback;
2N/A
2N/A RB_TEST(3, "create_set", ep)
2N/A
2N/A if (auto_take)
2N/A sr_flags = MD_SR_OK | MD_SR_AUTO_TAKE;
2N/A else
2N/A sr_flags = MD_SR_OK;
2N/A
2N/A /*
2N/A * Mark the set record MD_SR_OK
2N/A */
2N/A for (i = 0; i < node_c; i++)
2N/A if (clnt_upd_sr_flags(node_v[i], sp, sr_flags, ep))
2N/A goto rollback;
2N/A
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A /*
2N/A * For MN diskset:
2N/A * On each added node, set the node record for that node
2N/A * to OK. Then set all node records for the newly added
2N/A * nodes on all nodes to ok.
2N/A *
2N/A * By setting a node's own node record to ok first, even if
2N/A * the node adding the hosts panics, the rest of the nodes can
2N/A * determine the same node list during the choosing of the master
2N/A * during reconfig. So, only nodes considered for mastership
2N/A * are nodes that have both MD_MN_NODE_OK and MD_SR_OK set
2N/A * on that node's rpc.metad. If all nodes have MD_SR_OK set,
2N/A * but no node has its own MD_MN_NODE_OK set, then the set will
2N/A * be removed during reconfig since a panic occurred during the
2N/A * creation of the initial diskset.
2N/A */
2N/A
2N/A if (multi_node) {
2N/A md_mnnode_desc *nd, *saved_nd_next;
2N/A md_set_desc *sd;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A goto rollback;
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, will pick this up in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set node record for added host to ok on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_OK, NULL, ep)) {
2N/A nd->nd_next = saved_nd_next;
2N/A goto rollback;
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A
2N/A /* Now set all node records on all nodes to be ok */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_OK, NULL, ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(4, "create_set", ep)
2N/A
2N/Aout:
2N/A if ((rval == 0) && multi_node) {
2N/A /*
2N/A * Set successfully created.
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Send reinit command to mdcommd which forces it to get
2N/A * fresh set description. Then send resume.
2N/A * Resume on class 0 will resume all classes.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(node_v[i], COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A }
2N/A }
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_mdcommdctl(node_v[i], COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A }
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A if (lock_flag) {
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A }
2N/A
2N/A if (sig_flag) {
2N/A if (multi_node) {
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A } else {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A }
2N/A
2N/A return (rval);
2N/A
2N/Arollback:
2N/A /* all signals already blocked for MN disket */
2N/A if (!multi_node) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A rval = -1;
2N/A
2N/A /*
2N/A * For MN diskset:
2N/A * On each added node (which is now each node to be deleted),
2N/A * set the node record for that node to DEL. Then set all
2N/A * node records for the newly added (soon to be deleted) nodes
2N/A * on all nodes to ok.
2N/A *
2N/A * By setting a node's own node record to DEL first, even if
2N/A * the node doing the rollback panics, the rest of the nodes can
2N/A * determine the same node list during the choosing of the master
2N/A * during reconfig.
2N/A */
2N/A
2N/A /* level 3 */
2N/A if ((rb_level > 1) && (multi_node)) {
2N/A md_mnnode_desc *nd, *saved_nd_next;
2N/A md_set_desc *sd;
2N/A
2N/A if ((sd = metaget_setdesc(sp, &xep)) == NULL) {
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, will pick this up in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set node record for added host to DEL on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_DEL, NULL, &xep)) {
2N/A nd->nd_next = saved_nd_next;
2N/A mdclrerror(&xep);
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A
2N/A /* Now set all node records on all nodes to be DEL */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_DEL, NULL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /* Mark set record on all hosts to be DELETED */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A }
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_delset(node_v[i], sp, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 0 */
2N/A /* Don't test lock flag since guaranteed to be set if in rollback */
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A if ((sig_flag) && (!multi_node))
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/Astatic int
2N/Adel_db_sidenms(
2N/A mdsetname_t *sp,
2N/A side_t sideno,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_replicalist_t *rlp = NULL;
2N/A md_replicalist_t *rl;
2N/A int rval = 0;
2N/A
2N/A if (metareplicalist(sp, MD_BASICNAME_OK, &rlp, ep) < 0)
2N/A return (-1);
2N/A
2N/A for (rl = rlp; rl != NULL; rl = rl->rl_next) {
2N/A md_replica_t *r = rl->rl_repp;
2N/A
2N/A if (meta_db_delsidenm(sp, sideno, r->r_namep, r->r_blkno, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A metafreereplicalist(rlp);
2N/A return (rval);
2N/A}
2N/A
2N/Astatic int
2N/Adel_drvs_from_hosts(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A md_drive_desc *dd,
2N/A int node_c,
2N/A char **node_v,
2N/A int oha,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A md_mnnode_desc *nd;
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL) {
2N/A return (mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename,
2N/A NULL, sp->setname));
2N/A }
2N/A
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A continue;
2N/A }
2N/A if (clnt_deldrvs(node_v[i], sp, dd, ep)) {
2N/A return (-1);
2N/A }
2N/A } else if (MD_MNSET_DESC(sd) && (oha == FALSE)) {
2N/A /*
2N/A * All nodes should be alive in non-oha mode.
2N/A */
2N/A if (clnt_deldrvs(node_v[i], sp, dd, ep)) {
2N/A return (-1);
2N/A }
2N/A } else {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_deldrvs(node_v[i], sp, dd, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A return (-1);
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Adel_host_noset(
2N/A mdsetname_t *sp,
2N/A char **anode,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int rval = 0;
2N/A md_setkey_t *cl_sk;
2N/A md_drive_desc *dd;
2N/A md_error_t xep = mdnullerror;
2N/A md_set_desc *sd;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* Make sure we own the set */
2N/A if (meta_check_ownership(sp, ep) != 0)
2N/A return (-1);
2N/A
2N/A /* Lock the set on our side */
2N/A if (clnt_lock_set(mynode(), sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (clnt_delhosts(mynode(), sp, 1, anode, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (!MD_MNSET_DESC(sd)) {
2N/A if ((dd = metaget_drivedesc(sp, (MD_BASICNAME_OK | PRINT_FAST),
2N/A ep)) == NULL) {
2N/A if (! mdisok(ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* If we have drives */
2N/A if (dd != NULL) {
2N/A if (clnt_del_drv_sidenms(mynode(), sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A if (clnt_unlock_set(mynode(), cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/Astatic int
2N/Adel_md_sidenms(mdsetname_t *sp, side_t sideno, md_error_t *ep)
2N/A{
2N/A mdnm_params_t nm;
2N/A md_set_desc *sd;
2N/A int i;
2N/A
2N/A if (!metaislocalset(sp)) {
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A }
2N/A /* Use rpc.mdcommd to add md side info from all nodes */
2N/A if ((! metaislocalset(sp)) && MD_MNSET_DESC(sd) &&
2N/A (sd->sd_mn_mynode->nd_flags & MD_MN_NODE_OWN)) {
2N/A md_mn_result_t *resultp = NULL;
2N/A md_mn_msg_meta_md_delside_t md_ds;
2N/A int send_rval;
2N/A
2N/A md_ds.msg_sideno = sideno;
2N/A /*
2N/A * If reconfig cycle has been started, this node is stuck in
2N/A * in the return step until this command has completed. If
2N/A * mdcommd is suspended, ask send_message to fail (instead of
2N/A * retrying) so that metaset can finish allowing the
2N/A * reconfig cycle to proceed.
2N/A */
2N/A send_rval = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_META_MD_DELSIDE,
2N/A MD_MSGF_FAIL_ON_SUSPEND | MD_MSGF_PANIC_WHEN_INCONSISTENT,
2N/A 0, (char *)&md_ds, sizeof (md_mn_msg_meta_md_delside_t),
2N/A &resultp, ep);
2N/A if (send_rval != 0) {
2N/A (void) mdstealerror(ep, &(resultp->mmr_ep));
2N/A if (resultp)
2N/A free_result(resultp);
2N/A return (-1);
2N/A }
2N/A if (resultp)
2N/A free_result(resultp);
2N/A } else {
2N/A (void) memset(&nm, '\0', sizeof (nm));
2N/A nm.key = MD_KEYWILD;
2N/A
2N/A /*CONSTCOND*/
2N/A while (1) {
2N/A nm.mde = mdnullerror;
2N/A nm.setno = sp->setno;
2N/A nm.side = MD_SIDEWILD;
2N/A if (metaioctl(MD_IOCNXTKEY_NM, &nm, &nm.mde, NULL) != 0)
2N/A return (mdstealerror(ep, &nm.mde));
2N/A
2N/A if (nm.key == MD_KEYWILD)
2N/A return (0);
2N/A
2N/A /*
2N/A * The device reference count can be greater than 1 if
2N/A * more than one softpart is configured on top of the
2N/A * same device. If this is the case then we want to
2N/A * decrement the count to zero so the entry can be
2N/A * actually removed.
2N/A */
2N/A for (i = 0; i < nm.ref_count; i++) {
2N/A if (del_name(sp, sideno, nm.key, ep) == -1)
2N/A return (-1);
2N/A }
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Arecreate_set(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd
2N/A)
2N/A{
2N/A int i;
2N/A int has_set;
2N/A md_error_t xep = mdnullerror;
2N/A md_mnnode_desc *nd;
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A has_set = nodehasset(sp, nd->nd_nodename,
2N/A NHS_NST_EQ, &xep);
2N/A
2N/A if (has_set >= 0) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A mdclrerror(&xep);
2N/A
2N/A if (clnt_mncreateset(nd->nd_nodename, sp,
2N/A sd->sd_nodelist,
2N/A sd->sd_ctime, sd->sd_genid,
2N/A sd->sd_mn_master_nodenm,
2N/A sd->sd_mn_master_nodeid, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A has_set = nodehasset(sp, sd->sd_nodes[i],
2N/A NHS_NST_EQ, &xep);
2N/A
2N/A if (has_set >= 0)
2N/A continue;
2N/A
2N/A mdclrerror(&xep);
2N/A
2N/A if (clnt_createset(sd->sd_nodes[i], sp, sd->sd_nodes,
2N/A sd->sd_ctime, sd->sd_genid, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * If a MN diskset, set is already locked on all nodes via clnt_lock_set.
2N/A */
2N/Astatic int
2N/Adel_set_nodrives(
2N/A mdsetname_t *sp,
2N/A int node_c,
2N/A char **node_v,
2N/A int oha,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_set_desc *sd;
2N/A int i;
2N/A sigset_t oldsigs;
2N/A md_setkey_t *cl_sk;
2N/A int rb_level = 0;
2N/A ulong_t max_genid = 0;
2N/A int rval = 0;
2N/A md_error_t xep = mdnullerror;
2N/A md_mnnode_desc *nd;
2N/A int delete_end = 1;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A } else {
2N/A md_rb_sig_handling_on();
2N/A }
2N/A
2N/A /*
2N/A * Lock the set on current set members for traditional disksets.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_lock_set(node_v[i], sp, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A
2N/A RB_TEST(1, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 1; /* level 1 */
2N/A
2N/A RB_TEST(2, "deletehosts", ep)
2N/A
2N/A /*
2N/A * Mark the set record MD_SR_DEL
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A
2N/A RB_TEST(3, "deletehosts", ep)
2N/A
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL) {
2N/A (void) mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename,
2N/A NULL, sp->setname);
2N/A goto rollback;
2N/A }
2N/A
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, ep)) {
2N/A goto rollback;
2N/A }
2N/A } else if (MD_MNSET_DESC(sd) && (oha == FALSE)) {
2N/A /*
2N/A * All nodes should be alive in non-oha mode.
2N/A */
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, ep)) {
2N/A goto rollback;
2N/A }
2N/A } else {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(4, "deletehosts", ep)
2N/A }
2N/A
2N/A RB_TEST(5, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A RB_TEST(6, "deletehosts", ep)
2N/A
2N/A if (sdssc_delete_begin(sp->setname) == SDSSC_ERROR)
2N/A if (metad_isautotakebyname(sp->setname))
2N/A delete_end = 0;
2N/A else
2N/A goto rollback;
2N/A
2N/A /* The set is OK to delete, make it so. */
2N/A for (i = 0; i < node_c; i++) {
2N/A
2N/A RB_TEST(7, "deletehosts", ep)
2N/A
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL) {
2N/A (void) mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename,
2N/A NULL, sp->setname);
2N/A goto rollback;
2N/A }
2N/A
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A } else if (MD_MNSET_DESC(sd) && (oha == FALSE)) {
2N/A /*
2N/A * All nodes should be alive in non-oha mode.
2N/A */
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A } else {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(8, "deletehosts", ep)
2N/A }
2N/A
2N/A RB_TEST(9, "deletehosts", ep)
2N/A
2N/Aout:
2N/A /*
2N/A * Unlock the set on current set members
2N/A * for traditional disksets.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep)) {
2N/A if (oha == TRUE && mdanyrpcerror(&xep)) {
2N/A mdclrerror(&xep);
2N/A continue;
2N/A }
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A }
2N/A
2N/A /*
2N/A * A MN diskset has the clnt_locks held by meta_set_deletehosts so
2N/A * don't flush that data until meta_set_deletehosts has finished
2N/A * with it. meta_set_deletehosts will handle the flush of the
2N/A * setname.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A metaflushsetname(sp);
2N/A }
2N/A
2N/A if (delete_end &&
2N/A sdssc_delete_end(sp->setname, SDSSC_COMMIT) == SDSSC_ERROR)
2N/A rval = -1;
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A } else {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A return (rval);
2N/A
2N/Arollback:
2N/A /* all signals already blocked for MN disket */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A rval = -1;
2N/A
2N/A max_genid = sd->sd_genid;
2N/A
2N/A /* level 2 */
2N/A if (rb_level > 1) {
2N/A recreate_set(sp, sd);
2N/A max_genid++;
2N/A
2N/A if (delete_end)
2N/A (void) sdssc_delete_end(sp->setname, SDSSC_CLEANUP);
2N/A }
2N/A
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A max_genid++;
2N/A resync_genid(sp, sd, max_genid, node_c, node_v);
2N/A }
2N/A
2N/A /* level 0 */
2N/A /*
2N/A * Unlock the set on current set members
2N/A * for traditional disksets.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A cl_set_setkey(NULL);
2N/A }
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A /*
2N/A * A MN diskset has the clnt_locks held by meta_set_deletehosts so
2N/A * don't flush that data until meta_set_deletehosts has finished
2N/A * with it. meta_set_deletehosts will handle the flush of the
2N/A * setname.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A metaflushsetname(sp);
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * On entry:
2N/A * procsigs already called for MN diskset.
2N/A * md_rb_sig_handling already called for traditional diskset.
2N/A */
2N/Astatic int
2N/Adel_set_on_hosts(
2N/A mdsetname_t *sp,
2N/A md_set_desc *sd,
2N/A md_drive_desc *dd,
2N/A int node_c, /* Number of nodes */
2N/A char **node_v, /* Nodes being deleted */
2N/A int oha,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A int j;
2N/A side_t sideno;
2N/A md_replicalist_t *rlp = NULL;
2N/A sigset_t oldsigs;
2N/A md_setkey_t *cl_sk;
2N/A ulong_t max_genid = 0;
2N/A int rb_level = 1; /* This is a special case */
2N/A md_error_t xep = mdnullerror;
2N/A md_mnnode_desc *nd;
2N/A
2N/A RB_PREEMPT;
2N/A
2N/A RB_TEST(7, "deletehosts", ep)
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * May need this to re-add sidenames on roll back.
2N/A */
2N/A if (metareplicalist(sp, (MD_BASICNAME_OK | PRINT_FAST), &rlp,
2N/A ep) < 0)
2N/A goto rollback;
2N/A
2N/A RB_TEST(8, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A RB_TEST(9, "deletehosts", ep)
2N/A
2N/A if (del_drvs_from_hosts(sp, sd, dd, node_c, node_v, oha, ep))
2N/A goto rollback;
2N/A
2N/A RB_TEST(10, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 3; /* level 3 */
2N/A
2N/A RB_TEST(11, "deletehosts", ep)
2N/A
2N/A /*
2N/A * Delete the db replica sides
2N/A * This is done before the next loop, so that
2N/A * the db does not get unloaded before we are finished
2N/A * deleting the sides.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip hosts not being deleted */
2N/A if (! strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (del_db_sidenms(sp, nd->nd_nodeid, ep))
2N/A goto rollback;
2N/A
2N/A RB_TEST(12, "deletehosts", ep)
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (sideno = 0; sideno < MD_MAXSIDES; sideno++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[sideno][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip hosts not being deleted */
2N/A if (! strinlst(sd->sd_nodes[sideno], node_c,
2N/A node_v))
2N/A continue;
2N/A
2N/A if (del_db_sidenms(sp, sideno, ep))
2N/A goto rollback;
2N/A
2N/A RB_TEST(12, "deletehosts", ep)
2N/A }
2N/A }
2N/A
2N/A RB_TEST(13, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 4; /* level 4 */
2N/A
2N/A RB_TEST(14, "deletehosts", ep)
2N/A
2N/A /* Delete the names from the namespace */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip hosts not being deleted */
2N/A if (! strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (del_md_sidenms(sp, nd->nd_nodeid, ep))
2N/A goto rollback;
2N/A
2N/A RB_TEST(15, "deletehosts", ep)
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (sideno = 0; sideno < MD_MAXSIDES; sideno++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[sideno][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip hosts not being deleted */
2N/A if (! strinlst(sd->sd_nodes[sideno], node_c,
2N/A node_v))
2N/A continue;
2N/A
2N/A if (del_md_sidenms(sp, sideno, ep))
2N/A goto rollback;
2N/A
2N/A RB_TEST(15, "deletehosts", ep)
2N/A }
2N/A }
2N/A }
2N/A
2N/A RB_TEST(16, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 5; /* level 6 */
2N/A
2N/A RB_TEST(17, "deletehosts", ep)
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL) {
2N/A (void) mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename,
2N/A NULL, sp->setname);
2N/A goto rollback;
2N/A }
2N/A
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A } else if (MD_MNSET_DESC(sd) && (oha == FALSE)) {
2N/A /*
2N/A * All nodes should be alive in non-oha mode.
2N/A */
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A } else {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_delset(node_v[i], sp, ep) == -1) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(18, "deletehosts", ep)
2N/A }
2N/A
2N/A metafreereplicalist(rlp);
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A } else {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A return (0);
2N/A
2N/Arollback:
2N/A /* all signals already blocked for MN disket */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A max_genid = sd->sd_genid;
2N/A
2N/A /* level 5 */
2N/A if (rb_level > 4) {
2N/A recreate_set(sp, sd);
2N/A max_genid++;
2N/A }
2N/A
2N/A /* level 2 */
2N/A if (rb_level > 1 && dd != NULL) {
2N/A /*
2N/A * See if we have to re-add the drives specified.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A md_set_record *sr;
2N/A
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i])
2N/A == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE))
2N/A continue;
2N/A }
2N/A
2N/A /* Don't care if set record is MN or not */
2N/A if (clnt_getset(node_v[i], sp->setname,
2N/A MD_SET_BAD, &sr, &xep) == -1) {
2N/A mdclrerror(&xep);
2N/A continue;
2N/A }
2N/A
2N/A /* Drive already added, skip to next node */
2N/A if (sr->sr_drivechain != NULL) {
2N/A /*
2N/A * Set record structure was allocated from RPC
2N/A * routine getset so this structure is only of
2N/A * size md_set_record even if the MN flag is
2N/A * set. So, clear the flag so that the free
2N/A * code doesn't attempt to free a structure
2N/A * the size of md_mnset_record.
2N/A */
2N/A sr->sr_flags &= ~MD_SR_MN;
2N/A free_sr(sr);
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_adddrvs(node_v[i], sp, dd,
2N/A sr->sr_ctime, sr->sr_genid, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A
2N/A if (clnt_upd_dr_flags(node_v[i], sp, dd,
2N/A MD_DR_OK, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A
2N/A /*
2N/A * Set record structure was allocated from RPC routine
2N/A * getset so this structure is only of size
2N/A * md_set_record even if the MN flag is set. So,
2N/A * clear the flag so that the free code doesn't
2N/A * attempt to free a structure the size of
2N/A * md_mnset_record.
2N/A */
2N/A sr->sr_flags &= ~MD_SR_MN;
2N/A free_sr(sr);
2N/A }
2N/A max_genid += 3;
2N/A }
2N/A
2N/A /* level 3 */
2N/A if (rb_level > 2 && dd != NULL) {
2N/A md_replicalist_t *rl;
2N/A
2N/A for (rl = rlp; rl != NULL; rl = rl->rl_next) {
2N/A md_replica_t *r = rl->rl_repp;
2N/A
2N/A /*
2N/A * This is not the first replica being added to the
2N/A * diskset so call with ADDSIDENMS_BCAST. If this
2N/A * is a traditional diskset, the bcast flag is ignored
2N/A * since traditional disksets don't use the rpc.mdcommd.
2N/A */
2N/A if (meta_db_addsidenms(sp, r->r_namep, r->r_blkno,
2N/A DB_ADDSIDENMS_BCAST, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 4 */
2N/A if (rb_level > 3 && dd != NULL) {
2N/A int nodeid_addsides = 0;
2N/A /*
2N/A * Add the device names for the new sides into the namespace,
2N/A * on all hosts not being deleted.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Find a node that is not being deleted */
2N/A if (! strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nodeid_addsides = nd->nd_nodeid;
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (j = 0; j < MD_MAXSIDES; j++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[j][0] == '\0')
2N/A continue;
2N/A
2N/A /* Find a node that is not being deleted */
2N/A if (! strinlst(sd->sd_nodes[j], node_c,
2N/A node_v))
2N/A break;
2N/A }
2N/A nodeid_addsides = j;
2N/A }
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being deleted */
2N/A if (!strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* this side was just created, add the names */
2N/A if (add_md_sidenms(sp, nd->nd_nodeid,
2N/A nodeid_addsides, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being deleted */
2N/A if (!strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A /* this side was just created, add the names */
2N/A if (add_md_sidenms(sp, i, nodeid_addsides,
2N/A &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A max_genid++;
2N/A resync_genid(sp, sd, max_genid, node_c, node_v);
2N/A }
2N/A
2N/A /* level 0 */
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE))
2N/A continue;
2N/A /* To balance lock/unlock; can send to dead node */
2N/A if (clnt_unlock_set(nd->nd_nodename, cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A metafreereplicalist(rlp);
2N/A
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/Astatic int
2N/Amake_sideno_sidenm(
2N/A mdsetname_t *sp,
2N/A mddrivename_t *dnp,
2N/A side_t sideno,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdsidenames_t *sn, **sn_next;
2N/A md_set_desc *sd;
2N/A mdname_t *np;
2N/A uint_t rep_slice;
2N/A int err = 0;
2N/A
2N/A assert(dnp->side_names_key != MD_KEYWILD);
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* find the end of the link list */
2N/A for (sn = dnp->side_names; sn->next != NULL; sn = sn->next)
2N/A ;
2N/A sn_next = &sn->next;
2N/A
2N/A if (meta_replicaslice(dnp, &rep_slice, ep) != 0)
2N/A return (-1);
2N/A
2N/A if ((np = metaslicename(dnp, rep_slice, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A sn = Zalloc(sizeof (*sn));
2N/A sn->sideno = sideno;
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /*
2N/A * For MO diskset the sideno is not an index into
2N/A * the array of nodes. Hence getside_devinfo is
2N/A * used instead of meta_getnextside_devinfo.
2N/A */
2N/A if (meta_getside_devinfo(sp, np->bname, sideno, &sn->cname,
2N/A &sn->dname, &sn->mnum, ep) == -1)
2N/A err = -1;
2N/A } else {
2N/A /* decrement sideno, to look like the previous sideno */
2N/A sideno--;
2N/A if (meta_getnextside_devinfo(sp, np->bname, &sideno,
2N/A &sn->cname, &sn->dname, &sn->mnum, ep) == -1)
2N/A err = -1;
2N/A }
2N/A
2N/A if (err) {
2N/A Free(sn);
2N/A return (err);
2N/A }
2N/A assert(sn->sideno == sideno);
2N/A
2N/A /* Add to the end of the linked list */
2N/A *sn_next = sn;
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Avalidate_nodes(
2N/A mdsetname_t *sp,
2N/A int node_c,
2N/A char **node_v,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *hostname;
2N/A int i;
2N/A
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (strlen(node_v[i]) > (size_t)MD_MAX_NODENAME)
2N/A return (mddserror(ep, MDE_DS_NODENAMETOOLONG,
2N/A sp->setno, node_v[i], NULL, sp->setname));
2N/A if (clnt_hostname(node_v[i], &hostname, ep))
2N/A return (-1);
2N/A if (strcmp(node_v[i], hostname) != 0) {
2N/A Free(hostname);
2N/A return (mddserror(ep, MDE_DS_NOTNODENAME, sp->setno,
2N/A node_v[i], NULL, sp->setname));
2N/A }
2N/A Free(hostname);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Exported Entry Points
2N/A */
2N/A
2N/A/*
2N/A * Check the given disk set name for syntactic correctness.
2N/A */
2N/Aint
2N/Ameta_set_checkname(char *setname, md_error_t *ep)
2N/A{
2N/A char *cp;
2N/A
2N/A if (strlen(setname) > (size_t)MD_MAX_SETNAME)
2N/A return (mddserror(ep, MDE_DS_SETNAMETOOLONG,
2N/A MD_SET_BAD, NULL, NULL, setname));
2N/A
2N/A for (cp = setname; *cp; cp++)
2N/A if (!isprint(*cp) || strchr(INVALID_IN_NAMES, *cp) != NULL)
2N/A return (mddserror(ep, MDE_DS_INVALIDSETNAME,
2N/A MD_SET_BAD, NULL, NULL, setname));
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Add host(s) to the multi-node diskset provided in sp.
2N/A * - create set if non-existent.
2N/A */
2N/Astatic int
2N/Ameta_multinode_set_addhosts(
2N/A mdsetname_t *sp,
2N/A int multi_node,
2N/A int node_c,
2N/A char **node_v,
2N/A int auto_take,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_set_desc *sd;
2N/A md_drive_desc *dd, *p;
2N/A int rval = 0;
2N/A int bool;
2N/A int nodeindex;
2N/A int i;
2N/A int has_set;
2N/A sigset_t oldsigs;
2N/A md_setkey_t *cl_sk;
2N/A int rb_level = 0;
2N/A md_error_t xep = mdnullerror;
2N/A md_mnnode_desc *nd, *nd_curr, *nd_prev;
2N/A md_timeval32_t now;
2N/A int nodecnt;
2N/A mndiskset_membershiplist_t *nl, *nl2;
2N/A int suspendall_flag = 0;
2N/A int suspend1_flag = 0;
2N/A int lock_flag = 0;
2N/A int stale_flag = 0;
2N/A md_mnnode_desc *saved_nd_next;
2N/A int remote_sets_created = 0;
2N/A
2N/A /*
2N/A * Check membershiplist first. If there's
2N/A * an error, fail to create set and pass back error.
2N/A */
2N/A if (meta_read_nodelist(&nodecnt, &nl, ep) == -1) {
2N/A return (-1);
2N/A }
2N/A /* Verify that all nodes are in member list */
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * If node in list isn't a member of the membership,
2N/A * just return error.
2N/A */
2N/A if (meta_is_member(node_v[i], NULL, nl) == 0) {
2N/A meta_free_nodelist(nl);
2N/A return (mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, node_v[i], NULL, sp->setname));
2N/A }
2N/A }
2N/A /*
2N/A * Node list is needed later, but there is a lot of error
2N/A * checking and possible failures between here and there, so
2N/A * just re-get the list later if there are no errors.
2N/A */
2N/A meta_free_nodelist(nl);
2N/A nl = NULL;
2N/A
2N/A /*
2N/A * Verify that list of nodes being added contains no
2N/A * duplicates.
2N/A */
2N/A if (nodesuniq(sp, node_c, node_v, ep))
2N/A return (-1);
2N/A
2N/A /*
2N/A * Verify that each node being added thinks that its nodename
2N/A * is the same as the nodename given.
2N/A */
2N/A if (validate_nodes(sp, node_c, node_v, ep))
2N/A return (-1);
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A if (! mdiserror(ep, MDE_NO_SET))
2N/A return (-1);
2N/A mdclrerror(ep);
2N/A return (create_set(sp, multi_node, node_c, node_v, auto_take,
2N/A ep));
2N/A } else {
2N/A /*
2N/A * If this node and another node were both attempting to
2N/A * create the same setname at the same time, and the other
2N/A * node has just created the set on this node then sd would
2N/A * be non-NULL, but sp->setno would be null (setno is filled
2N/A * in by the create_set). If this is true, then fail since
2N/A * the other node has already won this race.
2N/A */
2N/A if (sp->setno == NULL) {
2N/A return (mddserror(ep, MDE_DS_NODEINSET,
2N/A NULL, mynode(), NULL, sp->setname));
2N/A }
2N/A }
2N/A
2N/A /* The auto_take behavior is inconsistent with multiple hosts. */
2N/A if (auto_take || sd->sd_flags & MD_SR_AUTO_TAKE) {
2N/A (void) mddserror(ep, MDE_DS_SINGLEHOST, sp->setno, NULL, NULL,
2N/A sp->setname);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * We already have the set.
2N/A */
2N/A
2N/A /* Make sure we own the set */
2N/A if (meta_check_ownership(sp, ep) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * The drive and node records are stored in the local mddbs of each
2N/A * node in the diskset. Each node's rpc.metad daemon reads in the set,
2N/A * drive and node records from that node's local mddb and caches them
2N/A * internally. Any process needing diskset information contacts its
2N/A * local rpc.metad to get this information. Since each node in the
2N/A * diskset is independently reading the set information from its local
2N/A * mddb, the set, drive and node records in the local mddbs must stay
2N/A * in-sync, so that all nodes have a consistent view of the diskset.
2N/A *
2N/A * For a multinode diskset, explicitly verify that all nodes in the
2N/A * diskset are ALIVE (i.e. are in the API membership list). Otherwise,
2N/A * fail this operation since all nodes must be ALIVE in order to add
2N/A * the new node record to their local mddb. If a panic of this node
2N/A * leaves the local mddbs set, node and drive records out-of-sync, the
2N/A * reconfig cycle will fix the local mddbs and force them back into
2N/A * synchronization.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A return (mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename, NULL,
2N/A sp->setname));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /*
2N/A * Check if node is already in set.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Is node already in set? */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd) {
2N/A return (mddserror(ep, MDE_DS_NODEINSET,
2N/A sp->setno, node_v[i], NULL,
2N/A sp->setname));
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Lock the set on current set members.
2N/A * Set locking done much earlier for MN diskset than for traditional
2N/A * diskset since lock_set and SUSPEND are used to protect against
2N/A * other meta* commands running on the other nodes.
2N/A */
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_lock_set(nd->nd_nodename, sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A lock_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Lock out other meta* commands by suspending
2N/A * class 1 messages across the diskset.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* Send suspend to nodes in nodelist before addhosts call */
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1,
2N/A MD_MSCF_NO_FLAGS, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A suspend1_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /* Lock the set on new set members */
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Already verified to be alive */
2N/A if (clnt_lock_set(node_v[i], sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A lock_flag = 1;
2N/A }
2N/A
2N/A /*
2N/A * Perform the required checks for new hosts
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Make sure this set name is not used on the other hosts */
2N/A has_set = nodehasset(sp, node_v[i], NHS_N_EQ, ep);
2N/A if (has_set < 0) {
2N/A if (! mdiserror(ep, MDE_NO_SET)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A /* Keep on truck'n */
2N/A mdclrerror(ep);
2N/A } else if (has_set) {
2N/A (void) mddserror(ep, MDE_DS_NODEHASSET, sp->setno,
2N/A node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (clnt_setnumbusy(node_v[i], sp->setno, &bool, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (bool == TRUE) {
2N/A (void) mddserror(ep, MDE_DS_SETNUMBUSY, sp->setno,
2N/A node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (clnt_setnameok(node_v[i], sp, &bool, ep) == -1) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (bool == FALSE) {
2N/A (void) mddserror(ep, MDE_DS_SETNAMEBUSY, sp->setno,
2N/A node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (check_setdrvs_againstnode(sp, node_v[i], ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* Get drive descriptors for the set */
2N/A if ((dd = metaget_drivedesc(sp, MD_FULLNAME_ONLY, ep)) == NULL) {
2N/A if (! mdisok(ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* END CHECK CODE */
2N/A
2N/A RB_TEST(1, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 1; /* level 1 */
2N/A
2N/A RB_TEST(2, "addhosts", ep)
2N/A
2N/A /*
2N/A * Create the set where needed
2N/A */
2N/A if (create_set_on_hosts(sp, multi_node, node_c, node_v, 0, ep)) {
2N/A goto rollback;
2N/A }
2N/A
2N/A /*
2N/A * Send suspend to rpc.mdcommd on nodes where a set has been
2N/A * created since rpc.mdcommd must now be running on the remote nodes.
2N/A */
2N/A remote_sets_created = 1;
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * Lock out other meta* commands by suspending
2N/A * class 1 messages across the diskset.
2N/A */
2N/A if (clnt_mdcommdctl(node_v[i],
2N/A COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1,
2N/A MD_MSCF_NO_FLAGS, ep)) {
2N/A rval = -1;
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Merge the new entries into the set with the existing sides.
2N/A * Get membershiplist from API routine. If there's
2N/A * an error, fail to create set and pass back error.
2N/A */
2N/A if (meta_read_nodelist(&nodecnt, &nl, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A if (meta_gettimeofday(&now) == -1) {
2N/A meta_free_nodelist(nl);
2N/A (void) mdsyserror(ep, errno,
2N/A dgettext(TEXT_DOMAIN, "meta_gettimeofday()"));
2N/A goto rollback;
2N/A }
2N/A for (nodeindex = 0; nodeindex < node_c; nodeindex++) {
2N/A nd = Zalloc(sizeof (*nd));
2N/A (void) strcpy(nd->nd_nodename, node_v[nodeindex]);
2N/A nd->nd_ctime = now;
2N/A nl2 = nl;
2N/A while (nl2) {
2N/A if (strcmp(nl2->msl_node_name,
2N/A node_v[nodeindex]) == 0) {
2N/A nd->nd_nodeid = nl2->msl_node_id;
2N/A (void) strcpy(nd->nd_priv_ic,
2N/A nl2->msl_node_addr);
2N/A break;
2N/A }
2N/A nl2 = nl2->next;
2N/A }
2N/A
2N/A /*
2N/A * Nodelist must be kept in ascending nodeid order.
2N/A */
2N/A if (sd->sd_nodelist == NULL) {
2N/A /* Nothing in list, just add it */
2N/A sd->sd_nodelist = nd;
2N/A } else if (nd->nd_nodeid <
2N/A sd->sd_nodelist->nd_nodeid) {
2N/A /* Add to head of list */
2N/A nd->nd_next = sd->sd_nodelist;
2N/A sd->sd_nodelist = nd;
2N/A } else {
2N/A nd_curr = sd->sd_nodelist->nd_next;
2N/A nd_prev = sd->sd_nodelist;
2N/A /* Search for place to add it */
2N/A while (nd_curr) {
2N/A if (nd->nd_nodeid < nd_curr->nd_nodeid) {
2N/A /* Add before nd_curr */
2N/A nd->nd_next = nd_curr;
2N/A nd_prev->nd_next = nd;
2N/A break;
2N/A }
2N/A nd_prev = nd_curr;
2N/A nd_curr = nd_curr->nd_next;
2N/A }
2N/A /* Add to end of list */
2N/A if (nd_curr == NULL) {
2N/A nd_prev->nd_next = nd;
2N/A }
2N/A
2N/A }
2N/A /* Node already verified to be in membership */
2N/A nd->nd_flags |= MD_MN_NODE_ALIVE;
2N/A }
2N/A meta_free_nodelist(nl);
2N/A
2N/A /* If we have drives */
2N/A if (dd != NULL) {
2N/A /*
2N/A * For all the hosts being added, create a sidename structure
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being added */
2N/A if (!strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A for (p = dd; p != NULL; p = p->dd_next) {
2N/A if (make_sideno_sidenm(sp, p->dd_dnp,
2N/A nd->nd_nodeid, ep) != 0)
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A RB_TEST(4, "addhosts", ep)
2N/A
2N/A /*
2N/A * Add the new sidename for each drive to all the hosts
2N/A *
2N/A * If a multi-node diskset, each host only stores
2N/A * the side information for itself. So, only send
2N/A * side information to the new hosts where each host
2N/A * will add the appropriate side information to its
2N/A * local mddb.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being added */
2N/A if (!strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* Add side info to new hosts */
2N/A if (clnt_add_drv_sidenms(nd->nd_nodename,
2N/A mynode(), sp, sd, node_c, node_v, ep))
2N/A goto rollback;
2N/A
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_TEST(5, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 3; /* level 3 */
2N/A
2N/A RB_TEST(6, "addhosts", ep)
2N/A
2N/A /*
2N/A * Add the device names for the new sides into the namespace
2N/A * for all hosts being added. This is adding the side
2N/A * names to the diskset's mddb so add sidenames for all
2N/A * of the new hosts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being added */
2N/A if (!strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* this side was just created, add the names */
2N/A if (add_md_sidenms(sp, nd->nd_nodeid,
2N/A MD_SIDEWILD, ep))
2N/A goto rollback;
2N/A
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_TEST(7, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 4; /* level 4 */
2N/A
2N/A RB_TEST(8, "addhosts", ep)
2N/A
2N/A if (add_db_sidenms(sp, ep))
2N/A goto rollback;
2N/A
2N/A } else {
2N/A RB_PREEMPT;
2N/A rb_level = 4;
2N/A }
2N/A
2N/A RB_TEST(9, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 5; /* level 5 */
2N/A
2N/A RB_TEST(10, "addhosts", ep)
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Start by suspending rpc.mdcommd (which drains it of all
2N/A * messages), then change the nodelist followed by a reinit
2N/A * and resume.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* Send suspend_all to nodes in nodelist (existing + new) */
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_SUSPEND,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS, ep)) {
2N/A rval = -1;
2N/A goto rollback;
2N/A }
2N/A suspendall_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /* Add the node(s) to the each host that is currently in the set */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_addhosts(nd->nd_nodename, sp, node_c, node_v, ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_TEST(11, "addhosts", ep)
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * Mark the drives MD_DR_OK.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_upd_dr_flags(nd->nd_nodename, sp, dd,
2N/A MD_DR_OK, ep) == -1)
2N/A goto rollback;
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(12, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 6; /* level 6 */
2N/A
2N/A RB_TEST(13, "addhosts", ep)
2N/A
2N/A
2N/A /* Add the mediator information to all hosts in the set. */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_updmeds(nd->nd_nodename, sp, &sd->sd_med, ep))
2N/A goto rollback;
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_TEST(14, "addhosts", ep)
2N/A
2N/A /*
2N/A * If a MN diskset and there are drives in the set,
2N/A * set the master on the new nodes and
2N/A * automatically join the new nodes into the set.
2N/A */
2N/A if (dd != NULL) {
2N/A mddb_config_t c;
2N/A /*
2N/A * Is current set STALE?
2N/A */
2N/A (void) memset(&c, 0, sizeof (c));
2N/A c.c_id = 0;
2N/A c.c_setno = sp->setno;
2N/A if (metaioctl(MD_DB_GETDEV, &c, &c.c_mde, NULL) != 0) {
2N/A (void) mdstealerror(ep, &c.c_mde);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A if (c.c_flags & MDDB_C_STALE) {
2N/A stale_flag = MNSET_IS_STALE;
2N/A }
2N/A
2N/A /* Set master on newly added nodes */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_mnsetmaster(node_v[i], sp,
2N/A sd->sd_mn_master_nodenm,
2N/A sd->sd_mn_master_nodeid, ep)) {
2N/A goto rollback;
2N/A }
2N/A }
2N/A /* Join newly added nodes to diskset and set OWN flag */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_joinset(node_v[i], sp, stale_flag, ep))
2N/A goto rollback;
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0) {
2N/A nd->nd_flags |= MD_MN_NODE_OWN;
2N/A /*
2N/A * Also set ADD flag since this flag
2N/A * is already set in rpc.metad - it's
2N/A * just not in the local copy.
2N/A * Could flush local cache and call
2N/A * metaget_setdesc, but this just
2N/A * adds time. Since this node knows
2N/A * the state of the node flags in
2N/A * rpc.metad, just set the ADD
2N/A * flag and save time.
2N/A */
2N/A nd->nd_flags |= MD_MN_NODE_ADD;
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /* Send new node flag list to all Owner nodes */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_OWN)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /*
2N/A * Will effectively set OWN flag in records kept
2N/A * cached in rpc.metad. The ADD flag would have
2N/A * already been set by the call to clnt_addhosts.
2N/A */
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_SET, NULL, ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Mark the set record MD_SR_OK
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_upd_sr_flags(nd->nd_nodename, sp, MD_SR_OK,
2N/A ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /*
2N/A * For MN diskset:
2N/A * On each newly added node, set the node record for that node
2N/A * to OK. Then set all node records for the newly added
2N/A * nodes on all nodes to ok.
2N/A *
2N/A * By setting a node's own node record to ok first, even if
2N/A * the node adding the hosts panics, the rest of the nodes can
2N/A * determine the same node list during the choosing of the master
2N/A * during reconfig. So, only nodes considered for mastership
2N/A * are nodes that have both MD_MN_NODE_OK and MD_SR_OK set
2N/A * on that node's rpc.metad. If all nodes have MD_SR_OK set,
2N/A * but no node has its own MD_MN_NODE_OK set, then the set will
2N/A * be removed during reconfig since a panic occurred during the
2N/A * creation of the initial diskset.
2N/A */
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, will pick this up in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set node record for added host to ok on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_OK, NULL, ep)) {
2N/A nd->nd_next = saved_nd_next;
2N/A goto rollback;
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A
2N/A /* Now set all node records on all nodes to be ok */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_OK, NULL, ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A RB_TEST(15, "addhosts", ep)
2N/Aout:
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Send reinit command to mdcommd which forces it to get
2N/A * fresh set description. Then send resume.
2N/A * Resume on class 0 will resume all classes, so can skip
2N/A * doing an explicit resume of class1 (ignore suspend1_flag).
2N/A */
2N/A if (suspendall_flag) {
2N/A /*
2N/A * Don't know if nodelist contains the nodes being added
2N/A * or not, so do reinit to nodes not being added (by skipping
2N/A * any nodes in the nodelist being added) and then do
2N/A * reinit to nodes being added if remote_sets_created is 1.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A /* Skip nodes being added - handled later */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Send reinit to added nodes that had a set created since
2N/A * rpc.mdcommd is running on the nodes with a set.
2N/A */
2N/A if (remote_sets_created == 1) {
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_mdcommdctl(node_v[i], COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A }
2N/A }
2N/A }
2N/A }
2N/A if ((suspend1_flag) || (suspendall_flag)) {
2N/A /*
2N/A * Unlock diskset by resuming messages across the diskset.
2N/A * Just resume all classes so that resume is the same whether
2N/A * just one class was locked or all classes were locked.
2N/A *
2N/A * Don't know if nodelist contains the nodes being added
2N/A * or not, so do resume_all to nodes not being added (by
2N/A * skipping any nodes in the nodelist being added) and then do
2N/A * resume_all to nodes being added if remote_sets_created is 1.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A /* Skip nodes being added - handled later */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Send resume to added nodes that had a set created since
2N/A * rpc.mdcommd is be running on the nodes with a set.
2N/A */
2N/A if (remote_sets_created == 1) {
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Already verified to be alive */
2N/A if (clnt_mdcommdctl(node_v[i], COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS,
2N/A &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A }
2N/A }
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A /*
2N/A * Start a resync thread on the newly added nodes
2N/A * if set is not stale. Also start a thread to update the
2N/A * abr state of all soft partitions
2N/A */
2N/A if (stale_flag != MNSET_IS_STALE) {
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_mn_mirror_resync_all(node_v[i],
2N/A sp->setno, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to start resync "
2N/A "thread.\n"));
2N/A }
2N/A if (clnt_mn_sp_update_abr(node_v[i],
2N/A sp->setno, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to start sp update "
2N/A "thread.\n"));
2N/A }
2N/A }
2N/A }
2N/A }
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A /*
2N/A * Don't know if nodelist contains the nodes being added
2N/A * or not, so do clnt_unlock_set to nodes not being added (by
2N/A * skipping any nodes in the nodelist being added) and then do
2N/A * clnt_unlock_set to nodes being added.
2N/A */
2N/A if (lock_flag) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A /* Skip hosts we get in the next loop */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_unlock_set(nd->nd_nodename, cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A for (i = 0; i < node_c; i++) {
2N/A /* Already verified to be alive */
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A return (rval);
2N/A
2N/Arollback:
2N/A rval = -1;
2N/A
2N/A /* level 6 */
2N/A if (rb_level > 5) {
2N/A /*
2N/A * For each node being deleted, set DEL flag and
2N/A * reset OK flag on that node first.
2N/A * Until a node has turned off its own
2N/A * rpc.metad's NODE_OK flag, that node could be
2N/A * considered for master during a reconfig.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, handle this in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set flags for del host to DEL on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_DEL, NULL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A if (dd != NULL) {
2N/A /* Reset master on newly added node */
2N/A if (clnt_mnsetmaster(node_v[i], sp, "",
2N/A MD_MN_INVALID_NID, &xep))
2N/A mdclrerror(&xep);
2N/A /* Withdraw set on newly added node */
2N/A if (clnt_withdrawset(node_v[i], sp, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A /*
2N/A * Turn off owner flag in nodes to be deleted
2N/A * if there are drives in the set.
2N/A * Also, turn off NODE_OK and turn on NODE_DEL
2N/A * for nodes to be deleted.
2N/A * These flags are used to set the node
2N/A * record flags in all nodes in the set.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0) {
2N/A if (dd != NULL) {
2N/A nd->nd_flags &= ~MD_MN_NODE_OWN;
2N/A }
2N/A nd->nd_flags |= MD_MN_NODE_DEL;
2N/A nd->nd_flags &= ~MD_MN_NODE_OK;
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Now, reset owner and set delete flags for the deleted
2N/A * nodes on all nodes.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_SET, NULL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /*
2N/A * On each node being deleted, set the set record
2N/A * to be in DEL state.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* level 5 */
2N/A if (rb_level > 4) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A if (clnt_delhosts(nd->nd_nodename, sp, node_c,
2N/A node_v, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Send reinit command to mdcommd which forces it to get
2N/A * fresh set description. Then send resume.
2N/A * Nodelist contains all nodes (existing + added).
2N/A */
2N/A if (suspendall_flag) {
2N/A /* Send reinit */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A /* Send reinit to nodes in nodelist before addhosts call */
2N/A while (nd) {
2N/A /*
2N/A * Skip nodes being added if remote sets were not
2N/A * created since rpc.mdcommd may not be running
2N/A * on the remote nodes.
2N/A */
2N/A if ((remote_sets_created == 0) &&
2N/A (strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A /* Send resume */
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A /*
2N/A * Skip nodes being added if remote sets were not
2N/A * created since rpc.mdcommd may not be running
2N/A * on the remote nodes.
2N/A */
2N/A if ((remote_sets_created == 0) &&
2N/A (strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /*
2N/A * Resume all classes but class 1 so that lock is held
2N/A * against meta* commands.
2N/A * Send resume_all_but_1 to nodes in nodelist
2N/A * before addhosts call.
2N/A */
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_DONT_RESUME_CLASS1,
2N/A &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A
2N/A /* level 4 */
2N/A /* Nodelist may or may not contain nodes being added. */
2N/A if (rb_level > 3 && dd != NULL) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being added */
2N/A if (!strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (del_db_sidenms(sp, nd->nd_nodeid, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /* level 3 */
2N/A /* Nodelist may or may not contain nodes being added. */
2N/A if (rb_level > 2 && dd != NULL) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being added */
2N/A if (!strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (del_md_sidenms(sp, nd->nd_nodeid, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A if (dd != NULL) {
2N/A /* delete the drive records */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_deldrvs(node_v[i], sp, dd, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* delete the set record */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_delset(node_v[i], sp, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 0 */
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A /* Don't test lock flag since guaranteed to be set if in rollback */
2N/A /* Nodelist may or may not contain nodes being added. */
2N/A /*
2N/A * Unlock diskset by resuming messages across the diskset.
2N/A * Just resume all classes so that resume is the same whether
2N/A * just one class was locked or all classes were locked.
2N/A */
2N/A if ((suspend1_flag) || (suspendall_flag)) {
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * Skip nodes being added since remote sets
2N/A * were either created and then deleted or
2N/A * were never created. Either way - rpc.mdcommd
2N/A * may not be running on the remote node.
2N/A */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_RESUME, sp, MD_MSG_CLASS0,
2N/A MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE */
2N/A while (nd) {
2N/A /* Skip hosts we get in the next loop */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_unlock_set(nd->nd_nodename, cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++)
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A cl_set_setkey(NULL);
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * Add host(s) to the traditional diskset provided in sp.
2N/A * - create set if non-existent.
2N/A */
2N/Astatic int
2N/Ameta_traditional_set_addhosts(
2N/A mdsetname_t *sp,
2N/A int multi_node,
2N/A int node_c,
2N/A char **node_v,
2N/A int auto_take,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_set_desc *sd;
2N/A md_drive_desc *dd, *p;
2N/A med_rec_t medr;
2N/A med_rec_t rb_medr;
2N/A int rval = 0;
2N/A int bool;
2N/A int nodeindex;
2N/A int i;
2N/A int has_set;
2N/A int numsides;
2N/A sigset_t oldsigs;
2N/A md_setkey_t *cl_sk;
2N/A int rb_level = 0;
2N/A md_error_t xep = mdnullerror;
2N/A int max_meds;
2N/A
2N/A if (nodesuniq(sp, node_c, node_v, ep))
2N/A return (-1);
2N/A
2N/A if (validate_nodes(sp, node_c, node_v, ep))
2N/A return (-1);
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL) {
2N/A if (! mdiserror(ep, MDE_NO_SET))
2N/A return (-1);
2N/A mdclrerror(ep);
2N/A return (create_set(sp, multi_node, node_c, node_v, auto_take,
2N/A ep));
2N/A }
2N/A
2N/A /* The auto_take behavior is inconsistent with multiple hosts. */
2N/A if (auto_take || sd->sd_flags & MD_SR_AUTO_TAKE) {
2N/A (void) mddserror(ep, MDE_DS_SINGLEHOST, sp->setno, NULL, NULL,
2N/A sp->setname);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * We already have the set.
2N/A */
2N/A
2N/A /* Make sure we own the set */
2N/A if (meta_check_ownership(sp, ep) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Perform the required checks for new hosts
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (getnodeside(node_v[i], sd) != MD_SIDEWILD)
2N/A return (mddserror(ep, MDE_DS_NODEINSET, sp->setno,
2N/A node_v[i], NULL, sp->setname));
2N/A
2N/A /* Make sure this set name is not used on the other hosts */
2N/A has_set = nodehasset(sp, node_v[i], NHS_N_EQ, ep);
2N/A if (has_set < 0) {
2N/A if (! mdiserror(ep, MDE_NO_SET))
2N/A return (-1);
2N/A /* Keep on truck'n */
2N/A mdclrerror(ep);
2N/A } else if (has_set)
2N/A return (mddserror(ep, MDE_DS_NODEHASSET, sp->setno,
2N/A node_v[i], NULL, sp->setname));
2N/A
2N/A if (clnt_setnumbusy(node_v[i], sp->setno, &bool, ep) == -1)
2N/A return (-1);
2N/A
2N/A if (bool == TRUE)
2N/A return (mddserror(ep, MDE_DS_SETNUMBUSY, sp->setno,
2N/A node_v[i], NULL, sp->setname));
2N/A
2N/A if (clnt_setnameok(node_v[i], sp, &bool, ep) == -1)
2N/A return (-1);
2N/A
2N/A if (bool == FALSE)
2N/A return (mddserror(ep, MDE_DS_SETNAMEBUSY, sp->setno,
2N/A node_v[i], NULL, sp->setname));
2N/A
2N/A if (check_setdrvs_againstnode(sp, node_v[i], ep))
2N/A return (-1);
2N/A }
2N/A
2N/A /* Count the number of occupied slots */
2N/A numsides = 0;
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Count occupied slots */
2N/A if (sd->sd_nodes[i][0] != '\0')
2N/A numsides++;
2N/A }
2N/A
2N/A /* Make sure the we have space to add the new sides */
2N/A if ((numsides + node_c) > MD_MAXSIDES) {
2N/A (void) mddserror(ep, MDE_DS_SIDENUMNOTAVAIL, sp->setno, NULL,
2N/A NULL, sp->setname);
2N/A return (-1);
2N/A }
2N/A
2N/A /* Get drive descriptors for the set */
2N/A if ((dd = metaget_drivedesc(sp, MD_FULLNAME_ONLY, ep)) == NULL)
2N/A if (! mdisok(ep))
2N/A return (-1);
2N/A
2N/A /* Setup the mediator record roll-back structure */
2N/A (void) memset(&rb_medr, '\0', sizeof (med_rec_t));
2N/A rb_medr.med_rec_mag = MED_REC_MAGIC;
2N/A rb_medr.med_rec_rev = MED_REC_REV;
2N/A rb_medr.med_rec_fl = 0;
2N/A rb_medr.med_rec_sn = sp->setno;
2N/A (void) strcpy(rb_medr.med_rec_snm, sp->setname);
2N/A for (i = 0; i < MD_MAXSIDES; i++)
2N/A (void) strcpy(rb_medr.med_rec_nodes[i], sd->sd_nodes[i]);
2N/A rb_medr.med_rec_meds = sd->sd_med; /* structure assigment */
2N/A (void) memset(&rb_medr.med_rec_data, '\0', sizeof (med_data_t));
2N/A rb_medr.med_rec_foff = 0;
2N/A crcgen(&rb_medr, &rb_medr.med_rec_cks, sizeof (med_rec_t), NULL);
2N/A
2N/A if ((max_meds = get_max_meds(ep)) == 0)
2N/A return (-1);
2N/A
2N/A /* END CHECK CODE */
2N/A
2N/A md_rb_sig_handling_on();
2N/A
2N/A /* Lock the set on current set members */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_lock_set(sd->sd_nodes[i], sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* Lock the set on new set members */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_lock_set(node_v[i], sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(1, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 1; /* level 1 */
2N/A
2N/A RB_TEST(2, "addhosts", ep)
2N/A
2N/A /*
2N/A * Add the new hosts to the existing set record on the existing hosts
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_addhosts(sd->sd_nodes[i], sp, node_c, node_v, ep))
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A RB_TEST(3, "addhosts", ep);
2N/A
2N/A /* Merge the new entries into the set with the existing sides */
2N/A nodeindex = 0;
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip full slots */
2N/A if (sd->sd_nodes[i][0] != '\0')
2N/A continue;
2N/A
2N/A (void) strcpy(sd->sd_nodes[i], node_v[nodeindex++]);
2N/A if (nodeindex == node_c)
2N/A break;
2N/A }
2N/A
2N/A /* If we have drives */
2N/A if (dd != NULL) {
2N/A /*
2N/A * For all the hosts being added, create a sidename structure
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being added */
2N/A if (! strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A for (p = dd; p != NULL; p = p->dd_next) {
2N/A if (make_sideno_sidenm(sp, p->dd_dnp, i,
2N/A ep) != 0)
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Add the new sidename for each drive to the existing hosts
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes being added */
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_add_drv_sidenms(sd->sd_nodes[i], mynode(), sp,
2N/A sd, node_c, node_v, ep)) {
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(4, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 3; /* level 3 */
2N/A
2N/A RB_TEST(5, "addhosts", ep)
2N/A
2N/A if (add_db_sidenms(sp, ep)) {
2N/A goto rollback;
2N/A }
2N/A
2N/A } else {
2N/A RB_PREEMPT;
2N/A rb_level = 3;
2N/A }
2N/A
2N/A RB_TEST(6, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 4; /* level 4 */
2N/A
2N/A RB_TEST(7, "addhosts", ep)
2N/A
2N/A
2N/A /* create the set on the new nodes, this adds the drives as well */
2N/A if (create_set_on_hosts(sp, multi_node, node_c, node_v, 0, ep)) {
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(8, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 5; /* level 5 */
2N/A
2N/A RB_TEST(9, "addhosts", ep)
2N/A
2N/A if (dd != NULL) {
2N/A
2N/A /*
2N/A * Add the device entries for the new sides into the namespace.
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being added */
2N/A if (! strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (add_md_sidenms(sp, i, MD_SIDEWILD, ep))
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(10, "addhosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 6; /* level 6 */
2N/A
2N/A RB_TEST(11, "addhosts", ep);
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * Mark the drives MD_DR_OK.
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_upd_dr_flags(sd->sd_nodes[i], sp, dd,
2N/A MD_DR_OK, ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A }
2N/A }
2N/A
2N/A RB_TEST(12, "addhosts", ep)
2N/A
2N/A /* Bring the mediator record up to date with the set record */
2N/A medr = rb_medr; /* structure assignment */
2N/A for (i = 0; i < MD_MAXSIDES; i++)
2N/A (void) strcpy(medr.med_rec_nodes[i], sd->sd_nodes[i]);
2N/A crcgen(&medr, &medr.med_rec_cks, sizeof (med_rec_t), NULL);
2N/A
2N/A /* Inform the mediator hosts of the new node list */
2N/A for (i = 0; i < max_meds; i++) {
2N/A if (sd->sd_med.n_lst[i].a_cnt == 0)
2N/A continue;
2N/A
2N/A if (clnt_med_upd_rec(&sd->sd_med.n_lst[i], sp, &medr, ep))
2N/A goto rollback;
2N/A }
2N/A
2N/A /* Add the mediator information to all hosts in the set */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_updmeds(sd->sd_nodes[i], sp, &sd->sd_med, ep))
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(13, "addhosts", ep)
2N/A
2N/A /*
2N/A * Mark the set record MD_SR_OK
2N/A */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_upd_sr_flags(sd->sd_nodes[i], sp, MD_SR_OK, ep))
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(14, "addhosts", ep)
2N/A
2N/Aout:
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip hosts we get in the next loop */
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A
2N/A if (rval == 0) {
2N/A for (i = 0; i < node_c; i++)
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A
2N/A return (rval);
2N/A
2N/Arollback:
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A rval = -1;
2N/A
2N/A /* level 6 */
2N/A if (rb_level > 5) {
2N/A for (i = 0; i < max_meds; i++) {
2N/A if (sd->sd_med.n_lst[i].a_cnt == 0)
2N/A continue;
2N/A
2N/A if (clnt_med_upd_rec(&sd->sd_med.n_lst[i], sp,
2N/A &rb_medr, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A if (dd != NULL) {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being added */
2N/A if (! strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (del_md_sidenms(sp, i, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* level 5 */
2N/A if (rb_level > 4) {
2N/A if (dd != NULL) {
2N/A /* delete the drive records */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_deldrvs(node_v[i], sp, dd, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A /* delete the set record on the 'new' hosts */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (clnt_delset(node_v[i], sp, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 4 */
2N/A if (rb_level > 3 && dd != NULL) {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being added */
2N/A if (! strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (del_db_sidenms(sp, i, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 3 */
2N/A if (rb_level > 2 && dd != NULL) {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being added */
2N/A if (! strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_del_drv_sidenms(sd->sd_nodes[i], sp,
2N/A &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 2 */
2N/A if (rb_level > 1) {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_delhosts(sd->sd_nodes[i], sp, node_c, node_v,
2N/A &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip hosts we get in the next loop */
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++)
2N/A if (clnt_unlock_set(node_v[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A cl_set_setkey(NULL);
2N/A }
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * Add host(s) to the diskset provided in sp.
2N/A * - create set if non-existent.
2N/A */
2N/Aint
2N/Ameta_set_addhosts(
2N/A mdsetname_t *sp,
2N/A int multi_node,
2N/A int node_c,
2N/A char **node_v,
2N/A int auto_take,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (multi_node)
2N/A return (meta_multinode_set_addhosts(sp, multi_node, node_c,
2N/A node_v, auto_take, ep));
2N/A else
2N/A return (meta_traditional_set_addhosts(sp, multi_node, node_c,
2N/A node_v, auto_take, ep));
2N/A}
2N/A
2N/A/*
2N/A * Delete host(s) from the diskset provided in sp.
2N/A * - destroy set if last host in set is removed.
2N/A */
2N/Aint
2N/Ameta_set_deletehosts(
2N/A mdsetname_t *sp,
2N/A int node_c,
2N/A char **node_v,
2N/A int forceflg,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_set_desc *sd;
2N/A md_drive_desc *dd;
2N/A med_rec_t medr;
2N/A med_rec_t rb_medr;
2N/A int i, j;
2N/A int has_set;
2N/A int numsides = 0;
2N/A int oha = FALSE;
2N/A sigset_t oldsigs;
2N/A mhd_mhiargs_t mhiargs;
2N/A md_replicalist_t *rlp = NULL;
2N/A md_setkey_t *cl_sk;
2N/A ulong_t max_genid = 0;
2N/A int rval = 0;
2N/A int rb_level = 0;
2N/A int max_meds = 0;
2N/A md_error_t xep = mdnullerror;
2N/A md_mnnode_desc *nd;
2N/A md_mnnode_record *nr;
2N/A int delete_master = 0;
2N/A int suspendall_flag = 0, suspendall_flag_rb = 0;
2N/A int suspend1_flag = 0;
2N/A int lock_flag = 0;
2N/A int stale_flag = 0;
2N/A int *node_id_list = NULL;
2N/A int remote_sets_deleted = 0;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Verify that list of nodes being deleted contains no
2N/A * duplicates.
2N/A */
2N/A if (nodesuniq(sp, node_c, node_v, ep))
2N/A return (-1);
2N/A
2N/A /* Make sure we own the set */
2N/A if (meta_check_ownership(sp, ep) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * The drive and node records are stored in the local mddbs of each
2N/A * node in the diskset. Each node's rpc.metad daemon reads in the set,
2N/A * drive and node records from that node's local mddb and caches them
2N/A * internally. Any process needing diskset information contacts its
2N/A * local rpc.metad to get this information. Since each node in the
2N/A * diskset is independently reading the set information from its local
2N/A * mddb, the set, drive and node records in the local mddbs must stay
2N/A * in-sync, so that all nodes have a consistent view of the diskset.
2N/A *
2N/A * For a multinode diskset, explicitly verify that all nodes in the
2N/A * diskset are ALIVE (i.e. are in the API membership list) if the
2N/A * forceflag is FALSE. (The case of forceflag being TRUE is handled
2N/A * in OHA check above.)
2N/A *
2N/A * If forceflag is FALSE and a node in the diskset is not in
2N/A * the membership list, then fail this operation since all nodes must
2N/A * be ALIVE in order to delete the node record from their local mddb.
2N/A * If a panic of this node leaves the local mddbs set, node and drive
2N/A * records out-of-sync, the reconfig cycle will fix the local mddbs
2N/A * and force them back into synchronization.
2N/A */
2N/A if ((forceflg == FALSE) && (MD_MNSET_DESC(sd))) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A return (mddserror(ep, MDE_DS_NOTINMEMBERLIST,
2N/A sp->setno, nd->nd_nodename,
2N/A NULL, sp->setname));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Lock the set on current set members.
2N/A * Set locking done much earlier for MN diskset than for traditional
2N/A * diskset since lock_set and SUSPEND are used to protect against
2N/A * other meta* commands running on the other nodes.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_lock_set(nd->nd_nodename, sp, ep)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A lock_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Lock out other meta* commands by suspending
2N/A * class 1 messages across the diskset.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1,
2N/A MD_MSCF_NO_FLAGS, ep)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A suspend1_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A for (i = 0; i < node_c; i++)
2N/A if (getnodeside(node_v[i], sd) == MD_SIDEWILD) {
2N/A (void) mddserror(ep, MDE_DS_NODENOTINSET, sp->setno,
2N/A node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * Count the number of nodes currently in the set.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A numsides++;
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++)
2N/A /* Count full slots */
2N/A if (sd->sd_nodes[i][0] != '\0')
2N/A numsides++;
2N/A }
2N/A
2N/A /*
2N/A * OHA mode == -f -h <hostname>
2N/A * OHA is One Host Administration that occurs when the forceflag (-f)
2N/A * is set and at least one host in the diskset isn't responding
2N/A * to RPC requests.
2N/A *
2N/A * When in OHA mode, a node cannot delete itself from a diskset.
2N/A * When in OHA mode, a node can delete a list of nodes from a diskset
2N/A * even if some of the nodes in the diskset are unresponsive.
2N/A *
2N/A * For multinode diskset, only allow OHA mode when the nodes that
2N/A * aren't responding in the diskset are not in the membership list
2N/A * (i.e. nodes that aren't responding are not marked ALIVE).
2N/A * Nodes that aren't in the membership list will be rejoining
2N/A * the diskset through a reconfig cycle and the local mddb set
2N/A * and node records can be reconciled during the reconfig cycle.
2N/A *
2N/A * If a node isn't responding, but is still in the membership list,
2N/A * fail the request since the node may not be responding because
2N/A * rpc.metad died and is restarting. In this case, no reconfig
2N/A * cycle will be started, so there's no way to recover if
2N/A * the host delete operation was allowed.
2N/A *
2N/A * NOTE: if nodes that weren't in the membership when the OHA host
2N/A * delete occurred are now the only nodes in membership list,
2N/A * those nodes will see the old view of the diskset. As soon as
2N/A * a node re-enters the cluster that was present in the cluster
2N/A * during the host deletion, the diskset will reflect the host
2N/A * deletion on all nodes presently in the cluster.
2N/A */
2N/A if (forceflg == TRUE) {
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * If a node isn't ALIVE (in member list),
2N/A * then allow a force-able delete in OHA mode.
2N/A */
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A oha = TRUE;
2N/A break;
2N/A }
2N/A /*
2N/A * Don't test for clnt_nullproc since already
2N/A * tested the RPC connections by clnt_lock_set.
2N/A */
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_nullproc(sd->sd_nodes[i], ep) == -1) {
2N/A /*
2N/A * If we timeout to at least one
2N/A * client, then we can allow OHA mode,
2N/A * otherwise, we are in normal mode.
2N/A */
2N/A if (mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A if (strinlst(sd->sd_nodes[i],
2N/A node_c, node_v)) {
2N/A oha = TRUE;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Don't allow this for MN diskset since meta_set_destroy of 1 node
2N/A * does NOT remove this node's node record from the other node's set
2N/A * records in their local mddb. This leaves a MN diskset in a very
2N/A * messed up state.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A /* Destroy set */
2N/A if (forceflg == TRUE && node_c == 1 &&
2N/A strcmp(mynode(), node_v[0]) == 0) {
2N/A /* Can return since !MN diskset so nothing to unlock */
2N/A return (meta_set_destroy(sp, TRUE, ep));
2N/A }
2N/A }
2N/A
2N/A
2N/A /*
2N/A * In multinode diskset, can only delete self if this
2N/A * is the last node in the set or if all nodes in
2N/A * the set are being deleted. The traditional diskset code
2N/A * allows a node to delete itself (when there are other nodes
2N/A * in the diskset) when using the force flag, but that code
2N/A * path doesn't have the node remove itself from
2N/A * the set node list on the other nodes. Since this isn't
2N/A * satisfactory for the multinode diskset, just don't
2N/A * allow this operation.
2N/A */
2N/A if (MD_MNSET_DESC(sd) && (numsides > 1) && (node_c != numsides) &&
2N/A strinlst(mynode(), node_c, node_v)) {
2N/A (void) mddserror(ep, MDE_DS_MNCANTDELSELF, sp->setno,
2N/A mynode(), NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * In multinode diskset, don't allow deletion of master node unless
2N/A * this is the only node left or unless all nodes are being
2N/A * deleted since there is no way to switch
2N/A * master ownership (unless via a cluster reconfig cycle).
2N/A */
2N/A delete_master = strinlst(sd->sd_mn_master_nodenm, node_c, node_v);
2N/A if (MD_MNSET_DESC(sd) && (numsides > 1) && (node_c != numsides) &&
2N/A delete_master) {
2N/A (void) mddserror(ep, MDE_DS_CANTDELMASTER, sp->setno,
2N/A sd->sd_mn_master_nodenm, NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A
2N/A /* Deleting self w/o forceflg */
2N/A if (forceflg == FALSE && numsides > 1 &&
2N/A strinlst(mynode(), node_c, node_v)) {
2N/A (void) mddserror(ep, MDE_DS_CANTDELSELF, sp->setno,
2N/A mynode(), NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * Setup the mediator record roll-back structure for a trad diskset.
2N/A *
2N/A * For a MN diskset, the deletion of a host in the diskset
2N/A * does not cause an update of the mediator record. If the
2N/A * host deletion will cause the diskset to be removed (this is
2N/A * the last host being removed or all hosts are being removed)
2N/A * then the mediator record must have already been removed by the
2N/A * user or this delete host operation will fail (a check for
2N/A * this is done later in this routine).
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A (void) memset(&rb_medr, '\0', sizeof (med_rec_t));
2N/A rb_medr.med_rec_mag = MED_REC_MAGIC;
2N/A rb_medr.med_rec_rev = MED_REC_REV;
2N/A rb_medr.med_rec_fl = 0;
2N/A rb_medr.med_rec_sn = sp->setno;
2N/A (void) strcpy(rb_medr.med_rec_snm, sp->setname);
2N/A for (i = 0; i < MD_MAXSIDES; i++)
2N/A (void) strcpy(rb_medr.med_rec_nodes[i],
2N/A sd->sd_nodes[i]);
2N/A rb_medr.med_rec_meds = sd->sd_med; /* structure assigment */
2N/A (void) memset(&rb_medr.med_rec_data, '\0', sizeof (med_data_t));
2N/A rb_medr.med_rec_foff = 0;
2N/A crcgen(&rb_medr, &rb_medr.med_rec_cks,
2N/A sizeof (med_rec_t), NULL);
2N/A
2N/A /* Bring the mediator record up to date with the set record */
2N/A medr = rb_medr; /* structure assignment */
2N/A
2N/A if ((max_meds = get_max_meds(ep)) == 0) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * For traditional diskset:
2N/A * Check to see if all the hosts we are trying to delete the set from
2N/A * have a set "setname" that is the same as ours, i.e. - same name,
2N/A * same time stamp, same genid. We only do this if forceflg is not
2N/A * specified or we are in OHA mode.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd)) && (forceflg == FALSE || oha == TRUE)) {
2N/A int fix_node_v = FALSE;
2N/A int j;
2N/A
2N/A for (i = 0; i < node_c; i++) {
2N/A /* We skip this side */
2N/A if (strcmp(mynode(), node_v[i]) == 0)
2N/A continue;
2N/A
2N/A has_set = nodehasset(sp, node_v[i], NHS_NSTG_EQ, ep);
2N/A
2N/A if (has_set < 0) {
2N/A char *anode[1];
2N/A
2N/A /*
2N/A * Can't talk to the host only allowed in OHA
2N/A * mode.
2N/A */
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * We got an error we do not, or are not,
2N/A * prepared to handle.
2N/A */
2N/A if (! mdiserror(ep, MDE_NO_SET) &&
2N/A ! mdismddberror(ep, MDE_DB_NODB)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A mdclrerror(ep);
2N/A
2N/A /*
2N/A * If we got here: both hosts are up; a host in
2N/A * our set record does not have the set. So we
2N/A * delete the host from our set and invalidate
2N/A * the node.
2N/A */
2N/A anode[0] = Strdup(node_v[i]);
2N/A
2N/A rval = del_host_noset(sp, anode, ep);
2N/A
2N/A /*
2N/A * If we delete a host, make sure the mediator
2N/A * hosts are made aware of this.
2N/A */
2N/A for (j = 0; j < MD_MAXSIDES; j++) {
2N/A if (strcmp(medr.med_rec_nodes[j],
2N/A node_v[i]) != 0)
2N/A continue;
2N/A (void) memset(&medr.med_rec_nodes[j],
2N/A '\0', sizeof (md_node_nm_t));
2N/A }
2N/A crcgen(&medr, &medr.med_rec_cks,
2N/A sizeof (med_rec_t), NULL);
2N/A
2N/A rb_medr = medr; /* struct assignment */
2N/A
2N/A Free(anode[0]);
2N/A
2N/A if (rval == -1)
2N/A goto out2;
2N/A
2N/A node_v[i][0] = '\0';
2N/A fix_node_v = TRUE;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * If we can talk to the host, and they do not have the
2N/A * exact set, then we disallow the operation.
2N/A */
2N/A if (has_set == FALSE) {
2N/A (void) mddserror(ep, MDE_DS_NODENOSET,
2N/A sp->setno, node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Here we prune the node_v's that were invalidated above.
2N/A */
2N/A if (fix_node_v == TRUE) {
2N/A i = 0;
2N/A while (i < node_c) {
2N/A if (node_v[i][0] == '\0') {
2N/A for (j = i; (j + 1) < node_c; j++)
2N/A node_v[j] = node_v[j + 1];
2N/A node_c--;
2N/A }
2N/A i++;
2N/A }
2N/A /*
2N/A * If we are left with no nodes, then we have
2N/A * compeleted the operation.
2N/A */
2N/A if (node_c == 0) {
2N/A /*
2N/A * Inform the mediator hosts of the new node
2N/A * list
2N/A */
2N/A for (i = 0; i < max_meds; i++) {
2N/A if (sd->sd_med.n_lst[i].a_cnt == 0)
2N/A continue;
2N/A
2N/A if (clnt_med_upd_rec(
2N/A &sd->sd_med.n_lst[i], sp, &medr,
2N/A ep))
2N/A mdclrerror(ep);
2N/A }
2N/A rval = 0;
2N/A goto out2;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * For multinode diskset:
2N/A * If forceflag is FALSE then check to see if all the hosts we
2N/A * are trying to delete the set from have a set "setname" that
2N/A * is the same as ours, i.e. - same name, same time stamp, same genid.
2N/A * If forceflag is TRUE, then we don't care if the hosts being
2N/A * deleted have the same set information or not since user is forcing
2N/A * those hosts to be deleted.
2N/A */
2N/A if ((MD_MNSET_DESC(sd)) && (forceflg == FALSE)) {
2N/A for (i = 0; i < node_c; i++) {
2N/A /* We skip this node since comparing against it */
2N/A if (strcmp(mynode(), node_v[i]) == 0)
2N/A continue;
2N/A
2N/A has_set = nodehasset(sp, node_v[i], NHS_NSTG_EQ, ep);
2N/A
2N/A if (has_set < 0) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * If we can talk to the host, and they do not have the
2N/A * exact set, then we disallow the operation.
2N/A */
2N/A if (has_set == FALSE) {
2N/A (void) mddserror(ep, MDE_DS_NODENOSET,
2N/A sp->setno, node_v[i], NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * For traditional diskset:
2N/A * Can't allow user to delete their node (without deleting all nodes)
2N/A * out of a set in OHA mode, would leave a real mess.
2N/A * This action was already failed above for a MN diskset.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd)) && (oha == TRUE) &&
2N/A strinlst(mynode(), node_c, node_v)) {
2N/A /* Can directly return since !MN diskset; nothing to unlock */
2N/A return (mddserror(ep, MDE_DS_OHACANTDELSELF, sp->setno,
2N/A mynode(), NULL, sp->setname));
2N/A }
2N/A
2N/A
2N/A /* Get the drive descriptors for this set */
2N/A if ((dd = metaget_drivedesc(sp, (MD_BASICNAME_OK | PRINT_FAST),
2N/A ep)) == NULL) {
2N/A if (! mdisok(ep)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * We have been asked to delete all the hosts in the set, i.e. - delete
2N/A * the whole set.
2N/A */
2N/A if (node_c == numsides) {
2N/A /*
2N/A * This is only a valid operation if all drives have been
2N/A * removed first.
2N/A */
2N/A
2N/A if (dd != NULL) {
2N/A (void) mddserror(ep, MDE_DS_HASDRIVES, sp->setno,
2N/A NULL, NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * If a mediator is currently associated with this set,
2N/A * fail the deletion of the last host(s).
2N/A */
2N/A if (sd->sd_med.n_cnt != 0) {
2N/A (void) mddserror(ep, MDE_DS_HASMED, sp->setno,
2N/A NULL, NULL, sp->setname);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A if (! mdisok(ep)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A rval = del_set_nodrives(sp, node_c, node_v, oha, ep);
2N/A remote_sets_deleted = 1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * Get timeout values in case we need to roll back
2N/A */
2N/A (void) memset(&mhiargs, '\0', sizeof (mhiargs));
2N/A if (clnt_gtimeout(mynode(), sp, &mhiargs, ep) != 0) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * We need this around for re-adding DB side names later.
2N/A */
2N/A if (metareplicalist(sp, MD_BASICNAME_OK, &rlp, ep) < 0) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A
2N/A /*
2N/A * Alloc nodeid list if drives are present in diskset.
2N/A * nodeid list is used to reset mirror owners if the
2N/A * owner is a deleted node.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A node_id_list = Zalloc(sizeof (int) * node_c);
2N/A }
2N/A }
2N/A
2N/A /* Lock the set on current set members */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A md_rb_sig_handling_on();
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_lock_set(sd->sd_nodes[i], sp, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A lock_flag = 1;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(1, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 1; /* level 1 */
2N/A
2N/A RB_TEST(2, "deletehosts", ep)
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A md_mnnode_desc *saved_nd_next;
2N/A mddb_config_t c;
2N/A
2N/A if (dd != NULL) {
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Start by suspending rpc.mdcommd (which drains it of
2N/A * all messages), then change the nodelist followed
2N/A * by a reinit and resume.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_SUSPEND, sp,
2N/A MD_MSG_CLASS0,
2N/A MD_MSCF_NO_FLAGS, ep)) {
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A suspendall_flag = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Is current set STALE?
2N/A * Need to know this if delete host fails and node
2N/A * is re-joined to diskset.
2N/A */
2N/A (void) memset(&c, 0, sizeof (c));
2N/A c.c_id = 0;
2N/A c.c_setno = sp->setno;
2N/A if (metaioctl(MD_DB_GETDEV, &c, &c.c_mde, NULL) != 0) {
2N/A (void) mdstealerror(ep, &c.c_mde);
2N/A rval = -1;
2N/A goto out2;
2N/A }
2N/A if (c.c_flags & MDDB_C_STALE) {
2N/A stale_flag = MNSET_IS_STALE;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * For each node being deleted, set DEL flag and
2N/A * reset OK flag on that node first.
2N/A * Until a node has turned off its own
2N/A * rpc.metad's NODE_OK flag, that node could be
2N/A * considered for master during a reconfig.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, handle this in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A /* If node_id_list is alloc'd, fill in for later use */
2N/A if (node_id_list)
2N/A node_id_list[i] = nd->nd_nodeid;
2N/A
2N/A /* All nodes are guaranteed to be ALIVE unless OHA */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A continue;
2N/A }
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set flags for del host to DEL on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_DEL, NULL, ep)) {
2N/A nd->nd_next = saved_nd_next;
2N/A goto rollback;
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * Turn off owner flag in nodes to be deleted
2N/A * if this node has been joined.
2N/A * Also, turn off NODE_OK and turn on NODE_DEL
2N/A * for nodes to be deleted.
2N/A * These flags are used to set the node
2N/A * record flags in all nodes in the set.
2N/A * Only withdraw nodes that are joined.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * Don't communicate with non-ALIVE node if
2N/A * in OHA - but set flags in master list so
2N/A * alive nodes are updated correctly.
2N/A */
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0) {
2N/A if ((oha == TRUE) && (!(nd->nd_flags &
2N/A MD_MN_NODE_ALIVE))) {
2N/A nd->nd_flags |= MD_MN_NODE_DEL;
2N/A nd->nd_flags &= ~MD_MN_NODE_OK;
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (nd->nd_flags & MD_MN_NODE_OWN) {
2N/A /*
2N/A * Going to set locally cached
2N/A * node flags to rollback join
2N/A * so in case of error, the
2N/A * rollback code knows which
2N/A * nodes to re-join. rpc.metad
2N/A * ignores the RB_JOIN flag.
2N/A */
2N/A nd->nd_flags |=
2N/A MD_MN_NODE_RB_JOIN;
2N/A nd->nd_flags &= ~MD_MN_NODE_OWN;
2N/A
2N/A /*
2N/A * Be careful in ordering of
2N/A * following steps so that
2N/A * recovery from a panic
2N/A * between the steps is viable.
2N/A * Only reset master info in
2N/A * rpc.metad - don't reset
2N/A * local cached info which will
2N/A * be used to set master info
2N/A * back if failure (rollback).
2N/A */
2N/A if (clnt_withdrawset(
2N/A nd->nd_nodename, sp, ep))
2N/A goto rollback;
2N/A
2N/A /*
2N/A * Reset master on deleted node
2N/A */
2N/A if (clnt_mnsetmaster(node_v[i],
2N/A sp, "", MD_MN_INVALID_NID,
2N/A ep))
2N/A goto rollback;
2N/A }
2N/A
2N/A nd->nd_flags |= MD_MN_NODE_DEL;
2N/A nd->nd_flags &= ~MD_MN_NODE_OK;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Now, reset owner and set delete flags for the
2N/A * deleted nodes on all nodes.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip non-ALIVE node if in OHA */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_SET, NULL, ep)) {
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Send reinit command to mdcommd which forces it to get
2N/A * fresh set description.
2N/A */
2N/A if (suspendall_flag) {
2N/A /* Send reinit */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_REINIT, sp, NULL,
2N/A MD_MSCF_NO_FLAGS, ep)) {
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Send resume */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_RESUME, sp, MD_MSG_CLASS0,
2N/A MD_MSCF_DONT_RESUME_CLASS1, ep)) {
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A goto rollback;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Mark the set record MD_SR_DEL on the hosts we are deleting
2N/A * If a MN diskset and OHA mode, don't issue RPC to nodes that
2N/A * are not ALIVE.
2N/A * If a MN diskset and not in OHA mode, then all nodes must respond
2N/A * to RPC (be alive) or this routine will return failure.
2N/A * If a traditional diskset, all RPC failures if in OHA mode.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A
2N/A RB_TEST(3, "deletehosts", ep)
2N/A
2N/A if ((MD_MNSET_DESC(sd)) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i]) == 0) {
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL) {
2N/A (void) mddserror(ep, MDE_DS_NODENOTINSET,
2N/A sp->setno, node_v[i], NULL, sp->setname);
2N/A goto rollback;
2N/A } else if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A /* Skip non-ALIVE node if in OHA mode */
2N/A continue;
2N/A } else {
2N/A if (clnt_upd_sr_flags(node_v[i], sp,
2N/A MD_SR_DEL, ep)) {
2N/A goto rollback;
2N/A }
2N/A }
2N/A } else if ((MD_MNSET_DESC(sd)) && (oha == FALSE)) {
2N/A /*
2N/A * All nodes should be alive in non-oha mode.
2N/A */
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, ep)) {
2N/A goto rollback;
2N/A }
2N/A } else {
2N/A /*
2N/A * For traditional diskset, issue the RPC and
2N/A * ignore RPC failure if in OHA mode.
2N/A */
2N/A if (clnt_upd_sr_flags(node_v[i], sp, MD_SR_DEL, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A }
2N/A
2N/A RB_TEST(4, "deletehosts", ep)
2N/A }
2N/A
2N/A RB_TEST(5, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 2; /* level 2 */
2N/A
2N/A RB_TEST(6, "deletehosts", ep)
2N/A
2N/A /* Delete the set on the hosts we are deleting */
2N/A if (del_set_on_hosts(sp, sd, dd, node_c, node_v, oha, ep)) {
2N/A if (node_id_list)
2N/A Free(node_id_list);
2N/A /*
2N/A * Failure during del_set_on_hosts would have recreated
2N/A * the diskset on the remote hosts, but for multi-owner
2N/A * disksets need to set node flags properly and REINIT and
2N/A * RESUME rpc.mdcommd, so just let the rollback code
2N/A * do this.
2N/A */
2N/A if (MD_MNSET_DESC(sd))
2N/A goto rollback;
2N/A return (-1);
2N/A }
2N/A remote_sets_deleted = 1;
2N/A
2N/A RB_TEST(19, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 3; /* level 3 */
2N/A
2N/A RB_TEST(20, "deletehosts", ep)
2N/A
2N/A /* Delete the host from sets on hosts not being deleted */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes are guaranteed to be ALIVE unless in oha mode */
2N/A while (nd) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* Skip nodes being deleted */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_delhosts(nd->nd_nodename, sp, node_c, node_v,
2N/A ep) == -1) {
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(21, "deletehosts", ep)
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes being deleted */
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_delhosts(sd->sd_nodes[i], sp, node_c, node_v,
2N/A ep) == -1) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(21, "deletehosts", ep)
2N/A }
2N/A }
2N/A
2N/A /* We have drives */
2N/A if (dd != NULL) {
2N/A RB_TEST(22, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 4; /* level 4 */
2N/A
2N/A RB_TEST(23, "deletehosts", ep)
2N/A
2N/A /*
2N/A * Delete the old sidename for each drive on all the hosts.
2N/A * If a multi-node diskset, each host only stores
2N/A * the side information for itself. So, a multi-node
2N/A * diskset doesn't delete the old sidename for
2N/A * an old host.
2N/A *
2N/A * If a MN diskset, reset owners of mirrors that are
2N/A * owned by the deleted nodes.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes being deleted */
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A if (clnt_del_drv_sidenms(sd->sd_nodes[i], sp,
2N/A ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A metaflushsetname(sp);
2N/A goto rollback;
2N/A }
2N/A
2N/A RB_TEST(24, "deletehosts", ep)
2N/A }
2N/A } else {
2N/A nd = sd->sd_nodelist;
2N/A /* All nodes guaranteed ALIVE unless in oha mode */
2N/A while (nd) {
2N/A /*
2N/A * If mirror owner was set to a deleted node,
2N/A * then each existing node resets mirror owner
2N/A * to NULL.
2N/A *
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* Skip nodes being deleted */
2N/A if (strinlst(nd->nd_nodename, node_c, node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * If mirror owner is a deleted node, reset
2N/A * mirror owners to NULL. If an error occurs,
2N/A * print a warning and continue. Don't fail
2N/A * metaset because of mirror owner reset
2N/A * problem since next node to grab mirror
2N/A * will resolve this issue. Before next node
2N/A * grabs mirrors, metaset will show the deleted
2N/A * node as owner which is why an attempt to
2N/A * reset the mirror owner is made.
2N/A */
2N/A if (clnt_reset_mirror_owner(nd->nd_nodename, sp,
2N/A node_c, &node_id_list[0], &xep) == -1) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reset mirror owner on"
2N/A " node %s\n"), nd->nd_nodename);
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A RB_TEST(21, "deletehosts", ep)
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A }
2N/A
2N/A RB_TEST(25, "deletehosts", ep)
2N/A
2N/A RB_PREEMPT;
2N/A rb_level = 4; /* level 4 */
2N/A
2N/A RB_TEST(26, "deletehosts", ep)
2N/A
2N/A /*
2N/A * Bring the mediator record up to date with the set record for
2N/A * traditional diskset.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A medr = rb_medr; /* structure assignment */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A if (strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A (void) memset(&medr.med_rec_nodes[i],
2N/A '\0', sizeof (md_node_nm_t));
2N/A else
2N/A (void) strcpy(medr.med_rec_nodes[i],
2N/A sd->sd_nodes[i]);
2N/A }
2N/A crcgen(&medr, &medr.med_rec_cks, sizeof (med_rec_t), NULL);
2N/A
2N/A /* Inform the mediator hosts of the new node list */
2N/A for (i = 0; i < max_meds; i++) {
2N/A if (sd->sd_med.n_lst[i].a_cnt == 0)
2N/A continue;
2N/A
2N/A if (clnt_med_upd_rec(&sd->sd_med.n_lst[i], sp,
2N/A &medr, ep)) {
2N/A if (oha == TRUE && mdanyrpcerror(ep)) {
2N/A mdclrerror(ep);
2N/A continue;
2N/A }
2N/A goto rollback;
2N/A }
2N/A }
2N/A }
2N/A
2N/A RB_TEST(27, "deletehosts", ep)
2N/A
2N/A /*
2N/A * For traditional diskset:
2N/A * We are deleting ourselves out of the set and we have drives to
2N/A * consider; so we need to halt the set, release the drives and
2N/A * reset the timeout. **** THIS IS A ONE WAY TICKET, NO ROLL BACK
2N/A * IS POSSIBLE AS SOON AS THE HALT SET COMPLETES, SO THIS IS DONE
2N/A * WITH ALL SIGNALS BLOCKED AND LAST ****
2N/A *
2N/A * This situation cannot occur in a MN diskset since a node can't
2N/A * delete itself unless all nodes are being deleted and a diskset
2N/A * cannot contain any drives if all nodes are being deleted.
2N/A * So, don't even test for this if a MN diskset.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd)) && (dd != NULL) &&
2N/A strinlst(mynode(), node_c, node_v)) {
2N/A /* Make sure we are blocking all signals */
2N/A if (procsigs(TRUE, &oldsigs, ep) < 0) {
2N/A rval = -1;
2N/A goto out1;
2N/A }
2N/A
2N/A if (halt_set(sp, ep)) {
2N/A rval = -1;
2N/A goto out1;
2N/A }
2N/A
2N/A if (rel_own_bydd(sp, dd, FALSE, ep))
2N/A rval = -1;
2N/A
2N/Aout1:
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A
2N/Aout2:
2N/A /*
2N/A * Unlock diskset by resuming messages across the diskset.
2N/A * Just resume all classes so that resume is the same whether
2N/A * just one class was locked or all classes were locked.
2N/A */
2N/A if ((suspend1_flag) || (suspendall_flag)) {
2N/A /* Send resume */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /*
2N/A * Skip nodes being deleted if remote set
2N/A * was deleted since rpc.mdcommd may no longer
2N/A * be running on remote node.
2N/A */
2N/A if ((remote_sets_deleted == 1) &&
2N/A (strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A if (lock_flag) {
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_unlock_set(nd->nd_nodename,
2N/A cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_unlock_set(sd->sd_nodes[i],
2N/A cl_sk, &xep)) {
2N/A if (oha == TRUE &&
2N/A mdanyrpcerror(&xep)) {
2N/A mdclrerror(&xep);
2N/A continue;
2N/A }
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/Aout3:
2N/A metafreereplicalist(rlp);
2N/A if (node_id_list)
2N/A Free(node_id_list);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A } else {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A
2N/A return (rval);
2N/A
2N/Arollback:
2N/A /* all signals already blocked for MN disket */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A if (procsigs(TRUE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A rval = -1;
2N/A
2N/A max_genid = sd->sd_genid;
2N/A
2N/A
2N/A /*
2N/A * Send reinit command to rpc.mdcommd which forces it to get
2N/A * fresh set description and resume all classes but class 0.
2N/A * Don't send any commands to rpc.mdcommd if set on that node
2N/A * has been removed.
2N/A */
2N/A if (suspendall_flag) {
2N/A /* Send reinit */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /*
2N/A * If the remote set was deleted, rpc.mdcommd
2N/A * may no longer be running so send nothing to it.
2N/A */
2N/A if ((remote_sets_deleted == 1) &&
2N/A (strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Send resume */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /*
2N/A * If the remote set was deleted, rpc.mdcommd
2N/A * may no longer be running so send nothing to it.
2N/A */
2N/A if ((remote_sets_deleted == 1) &&
2N/A (strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_DONT_RESUME_CLASS1,
2N/A &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A
2N/A /* level 2 */
2N/A if (rb_level > 1) {
2N/A md_set_record *sr;
2N/A md_replicalist_t *rl;
2N/A
2N/A recreate_set(sp, sd);
2N/A
2N/A /*
2N/A * Lock out other meta* commands on nodes with the newly
2N/A * re-created sets by suspending class 1 messages
2N/A * across the diskset.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being deleted */
2N/A if (!(strinlst(nd->nd_nodename, node_c, node_v))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Suspend commd on nodes with re-created sets */
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_SUSPEND, sp, MD_MSG_CLASS1,
2N/A MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to suspend rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A
2N/A max_genid++;
2N/A
2N/A /*
2N/A * See if we have to re-add the drives specified.
2N/A */
2N/A for (i = 0; i < node_c; i++) {
2N/A if (MD_MNSET_DESC(sd) && (oha == TRUE)) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i])
2N/A == 0) {
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == 0)
2N/A continue;
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE))
2N/A continue;
2N/A }
2N/A
2N/A /* Don't care if set record is MN or not */
2N/A if (clnt_getset(node_v[i], sp->setname, MD_SET_BAD, &sr,
2N/A &xep) == -1) {
2N/A mdclrerror(&xep);
2N/A continue;
2N/A }
2N/A
2N/A /* Drive already added, skip to next node */
2N/A if (sr->sr_drivechain != NULL) {
2N/A /*
2N/A * Set record structure was allocated from RPC
2N/A * routine getset so this structure is only of
2N/A * size md_set_record even if the MN flag is
2N/A * set. So, clear the flag so that the free
2N/A * code doesn't attempt to free a structure
2N/A * the size of md_mnset_record.
2N/A */
2N/A sr->sr_flags &= ~MD_SR_MN;
2N/A free_sr(sr);
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_adddrvs(node_v[i], sp, dd, sr->sr_ctime,
2N/A sr->sr_genid, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A
2N/A if (clnt_upd_dr_flags(node_v[i], sp, dd, MD_DR_OK,
2N/A &xep) == -1)
2N/A mdclrerror(&xep);
2N/A
2N/A /*
2N/A * Set record structure was allocated from RPC routine
2N/A * getset so this structure is only of size
2N/A * md_set_record even if the MN flag is set. So,
2N/A * clear the flag so that the free code doesn't
2N/A * attempt to free a structure the size of
2N/A * md_mnset_record.
2N/A */
2N/A sr->sr_flags &= ~MD_SR_MN;
2N/A free_sr(sr);
2N/A }
2N/A max_genid += 3;
2N/A
2N/A for (rl = rlp; rl != NULL; rl = rl->rl_next) {
2N/A md_replica_t *r = rl->rl_repp;
2N/A /*
2N/A * This is not the first replica being added to the
2N/A * diskset so call with ADDSIDENMS_BCAST. If this
2N/A * is a traditional diskset, the bcast flag is ignored
2N/A * since traditional disksets don't use the rpc.mdcommd.
2N/A */
2N/A if (meta_db_addsidenms(sp, r->r_namep, r->r_blkno,
2N/A DB_ADDSIDENMS_BCAST, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A /*
2N/A * Add the device names for the new sides into the namespace,
2N/A * on all hosts not being deleted.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Find a node that is not being deleted */
2N/A if (!strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A j = nd->nd_nodeid;
2N/A break;
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (j = 0; j < MD_MAXSIDES; j++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[j][0] == '\0')
2N/A continue;
2N/A
2N/A /* Find a node that is not being deleted */
2N/A if (!strinlst(sd->sd_nodes[j], node_c, node_v))
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /* Skip nodes not being deleted */
2N/A if (!strinlst(nd->nd_nodename, node_c,
2N/A node_v)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* this side was just created, add the names */
2N/A if (add_md_sidenms(sp, nd->nd_nodeid, j, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being deleted */
2N/A if (!strinlst(sd->sd_nodes[i], node_c, node_v))
2N/A continue;
2N/A
2N/A /* this side was just created, add the names */
2N/A if (add_md_sidenms(sp, i, j, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* level 4 */
2N/A if (rb_level > 3 && dd != NULL) {
2N/A /*
2N/A * Add the new sidename for each drive to all the hosts
2N/A * Multi-node disksets only store the sidename for
2N/A * that host, so there is nothing to re-add.
2N/A */
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A for (j = 0; j < MD_MAXSIDES; j++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[j][0] == '\0')
2N/A continue;
2N/A
2N/A /* Skip nodes not being deleted */
2N/A if (!strinlst(sd->sd_nodes[j], node_c, node_v))
2N/A break;
2N/A }
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_add_drv_sidenms(sd->sd_nodes[i],
2N/A sd->sd_nodes[j], sp, sd, node_c, node_v,
2N/A &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A }
2N/A
2N/A /* level 5 */
2N/A if ((rb_level > 4) && (!(MD_MNSET_DESC(sd)))) {
2N/A /* rollback the mediator record */
2N/A for (i = 0; i < max_meds; i++) {
2N/A if (sd->sd_med.n_lst[i].a_cnt == 0)
2N/A continue;
2N/A
2N/A if (clnt_med_upd_rec(&sd->sd_med.n_lst[i], sp,
2N/A &rb_medr, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* level 3 */
2N/A if (rb_level > 2) {
2N/A md_set_record *sr;
2N/A md_mnset_record *mnsr;
2N/A
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A while (nd) {
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A /* Record should be for a multi-node diskset */
2N/A if (clnt_mngetset(nd->nd_nodename, sp->setname,
2N/A MD_SET_BAD, &mnsr, &xep) == -1) {
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A has_set = 1;
2N/A
2N/A nr = mnsr->sr_nodechain;
2N/A while (nr) {
2N/A if (nd->nd_nodeid == nr->nr_nodeid) {
2N/A break;
2N/A }
2N/A nr = nr->nr_next;
2N/A }
2N/A if (nr == NULL)
2N/A has_set = 0;
2N/A
2N/A free_sr((struct md_set_record *)mnsr);
2N/A if (has_set) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A if (clnt_addhosts(nd->nd_nodename, sp, node_c,
2N/A node_v, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A /* Record should be for a non-multi-node set */
2N/A if (clnt_getset(sd->sd_nodes[i], sp->setname,
2N/A MD_SET_BAD, &sr, &xep) == -1) {
2N/A mdclrerror(&xep);
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Set record structure was allocated from RPC
2N/A * routine getset so this structure is only of
2N/A * size md_set_record even if the MN flag is
2N/A * set. So, clear the flag so that the free
2N/A * code doesn't attempt to free a structure
2N/A * the size of md_mnset_record.
2N/A */
2N/A if (MD_MNSET_REC(sr)) {
2N/A sr->sr_flags &= ~MD_SR_MN;
2N/A free_sr(sr);
2N/A continue;
2N/A }
2N/A
2N/A has_set = 1;
2N/A for (j = 0; j < MD_MAXSIDES; j++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[j][0] == '\0')
2N/A continue;
2N/A
2N/A if (sr->sr_nodes[j][0] == '\0') {
2N/A has_set = 0;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A free_sr(sr);
2N/A if (has_set)
2N/A continue;
2N/A
2N/A if (clnt_addhosts(sd->sd_nodes[i], sp, node_c,
2N/A node_v, &xep) == -1)
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A max_genid++;
2N/A }
2N/A
2N/A /* level 1 */
2N/A if (rb_level > 0) {
2N/A max_genid++;
2N/A /* Sets MD_SR_OK on given nodes. */
2N/A resync_genid(sp, sd, max_genid, node_c, node_v);
2N/A
2N/A /*
2N/A * For MN diskset:
2N/A * On each newly re-added node, set the node record for that
2N/A * node to OK. Then set all node records for the newly added
2N/A * nodes on all nodes to ok.
2N/A *
2N/A * By setting a node's own node record to ok first, even if
2N/A * the node re-adding the hosts panics, the rest of the nodes
2N/A * can determine the same node list during the choosing of the
2N/A * master during reconfig. So, only nodes considered for
2N/A * mastership are nodes that have both MD_MN_NODE_OK and
2N/A * MD_SR_OK set on that node's rpc.metad. If all nodes have
2N/A * MD_SR_OK set, but no node has its own MD_MN_NODE_OK set,
2N/A * then the set will be removed during reconfig since a panic
2N/A * occurred during the re-creation of the deletion of
2N/A * the initial diskset.
2N/A */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A md_mnnode_desc *saved_nd_next;
2N/A if (dd != NULL) {
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a
2N/A * nodelist change. Start by suspending
2N/A * rpc.mdcommd (which drains it of all
2N/A * messages), then change the nodelist
2N/A * followed by a reinit and resume.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags &
2N/A MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename,
2N/A COMMDCTL_SUSPEND, sp,
2N/A MD_MSG_CLASS0,
2N/A MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Unable to suspend "
2N/A "rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A suspendall_flag_rb = 1;
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i])
2N/A == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A /* Something wrong, finish this in next loop */
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A continue;
2N/A }
2N/A
2N/A if (dd != NULL) {
2N/A /* Set master on re-joining node. */
2N/A if (clnt_mnsetmaster(node_v[i], sp,
2N/A sd->sd_mn_master_nodenm,
2N/A sd->sd_mn_master_nodeid, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A
2N/A /*
2N/A * Re-join set to same state as
2N/A * before - stale or non-stale.
2N/A */
2N/A if (clnt_joinset(node_v[i], sp,
2N/A stale_flag, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A /* Only changing my local cache of node list */
2N/A saved_nd_next = nd->nd_next;
2N/A nd->nd_next = NULL;
2N/A
2N/A /* Set record for host to ok on that host */
2N/A if (clnt_upd_nr_flags(node_v[i], sp,
2N/A nd, MD_NR_OK, NULL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A nd->nd_next = saved_nd_next;
2N/A }
2N/A
2N/A /* Now set all node records on all nodes to be ok */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_upd_nr_flags(nd->nd_nodename, sp,
2N/A sd->sd_nodelist, MD_NR_OK, NULL, &xep)) {
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Notify rpc.mdcommd on all nodes of a nodelist change.
2N/A * Send reinit command to mdcommd which forces it to get
2N/A * fresh set description.
2N/A */
2N/A if (suspendall_flag_rb) {
2N/A /* Send reinit */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A
2N/A /* Class is ignored for REINIT */
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_REINIT,
2N/A sp, NULL, MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to reinit rpc.mdcommd.\n"));
2N/A mdclrerror(&xep);
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Unlock diskset by resuming messages across the diskset.
2N/A * Just resume all classes so that resume is the same whether
2N/A * just one class was locked or all classes were locked.
2N/A */
2N/A if ((suspend1_flag) || (suspendall_flag) || (suspendall_flag_rb)) {
2N/A /* Send resume */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (!(nd->nd_flags & MD_MN_NODE_ALIVE)) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_mdcommdctl(nd->nd_nodename, COMMDCTL_RESUME,
2N/A sp, MD_MSG_CLASS0, MD_MSCF_NO_FLAGS, &xep)) {
2N/A mde_perror(&xep, dgettext(TEXT_DOMAIN,
2N/A "Unable to resume rpc.mdcommd.\n"));
2N/A }
2N/A nd = nd->nd_next;
2N/A }
2N/A meta_ping_mnset(sp->setno);
2N/A }
2N/A
2N/A /*
2N/A * Start a resync thread on the re-added nodes
2N/A * if set is not stale. Also start a thread to update the
2N/A * abr state of all soft partitions
2N/A */
2N/A if (stale_flag != MNSET_IS_STALE) {
2N/A for (i = 0; i < node_c; i++) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A if (strcmp(nd->nd_nodename, node_v[i])
2N/A == 0)
2N/A break;
2N/A nd = nd->nd_next;
2N/A }
2N/A if (nd == NULL)
2N/A continue;
2N/A
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A continue;
2N/A }
2N/A
2N/A if (dd != 0) {
2N/A if (clnt_mn_mirror_resync_all(node_v[i],
2N/A sp->setno, &xep)) {
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to start resync "
2N/A "thread.\n"));
2N/A }
2N/A if (clnt_mn_sp_update_abr(node_v[i],
2N/A sp->setno, &xep)) {
2N/A mde_perror(ep, dgettext(TEXT_DOMAIN,
2N/A "Unable to start sp update "
2N/A "thread.\n"));
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* level 0 */
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A /* Don't test lock flag since guaranteed to be set if in rollback */
2N/A if (MD_MNSET_DESC(sd)) {
2N/A nd = sd->sd_nodelist;
2N/A while (nd) {
2N/A /*
2N/A * During OHA mode, don't issue RPCs to
2N/A * non-alive nodes since there is no reason to
2N/A * wait for RPC timeouts.
2N/A */
2N/A if ((oha == TRUE) &&
2N/A (!(nd->nd_flags & MD_MN_NODE_ALIVE))) {
2N/A nd = nd->nd_next;
2N/A continue;
2N/A }
2N/A if (clnt_unlock_set(nd->nd_nodename, cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A nd = nd->nd_next;
2N/A }
2N/A } else {
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A /* Skip empty slots */
2N/A if (sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (clnt_unlock_set(sd->sd_nodes[i], cl_sk, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A /* release signals back to what they were on entry */
2N/A if (procsigs(FALSE, &oldsigs, &xep) < 0)
2N/A mdclrerror(&xep);
2N/A
2N/A metafreereplicalist(rlp);
2N/A if (node_id_list)
2N/A Free(node_id_list);
2N/A
2N/A metaflushsetname(sp);
2N/A
2N/A if (!(MD_MNSET_DESC(sd))) {
2N/A md_rb_sig_handling_off(md_got_sig(), md_which_sig());
2N/A }
2N/A
2N/A return (rval);
2N/A}
2N/A
2N/Aint
2N/Ameta_set_auto_take(
2N/A mdsetname_t *sp,
2N/A int take_val,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int i;
2N/A md_set_desc *sd;
2N/A int rval = 0;
2N/A md_setkey_t *cl_sk;
2N/A md_error_t xep = mdnullerror;
2N/A char *hostname;
2N/A md_drive_desc *dd;
2N/A
2N/A if ((sd = metaget_setdesc(sp, ep)) == NULL)
2N/A return (-1);
2N/A
2N/A /* Make sure we own the set */
2N/A if (meta_check_ownership(sp, ep) != 0)
2N/A return (-1);
2N/A
2N/A hostname = mynode();
2N/A
2N/A /* Lock the set on our side */
2N/A if (clnt_lock_set(hostname, sp, ep)) {
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (take_val) {
2N/A /* enable auto_take but only if it is not already set */
2N/A if (! (sd->sd_flags & MD_SR_AUTO_TAKE)) {
2N/A /* verify that we're the only host in the set */
2N/A for (i = 0; i < MD_MAXSIDES; i++) {
2N/A if (sd->sd_nodes[i] == NULL ||
2N/A sd->sd_nodes[i][0] == '\0')
2N/A continue;
2N/A
2N/A if (strcmp(sd->sd_nodes[i], hostname) != 0) {
2N/A (void) mddserror(ep, MDE_DS_SINGLEHOST,
2N/A sp->setno, NULL, NULL, sp->setname);
2N/A rval = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A if (clnt_enable_sr_flags(hostname, sp,
2N/A MD_SR_AUTO_TAKE, ep))
2N/A rval = -1;
2N/A
2N/A /* Disable SCSI reservations */
2N/A if (sd->sd_flags & MD_SR_MB_DEVID)
2N/A dd = metaget_drivedesc(sp, MD_BASICNAME_OK |
2N/A PRINT_FAST, &xep);
2N/A else
2N/A dd = metaget_drivedesc(sp, MD_BASICNAME_OK,
2N/A &xep);
2N/A
2N/A if (! mdisok(&xep))
2N/A mdclrerror(&xep);
2N/A
2N/A if (dd != NULL) {
2N/A if (rel_own_bydd(sp, dd, TRUE, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A }
2N/A
2N/A } else {
2N/A /* disable auto_take, if set, or error */
2N/A if (sd->sd_flags & MD_SR_AUTO_TAKE) {
2N/A if (clnt_disable_sr_flags(hostname, sp,
2N/A MD_SR_AUTO_TAKE, ep))
2N/A rval = -1;
2N/A
2N/A /* Enable SCSI reservations */
2N/A if (sd->sd_flags & MD_SR_MB_DEVID)
2N/A dd = metaget_drivedesc(sp, MD_BASICNAME_OK |
2N/A PRINT_FAST, &xep);
2N/A else
2N/A dd = metaget_drivedesc(sp, MD_BASICNAME_OK,
2N/A &xep);
2N/A
2N/A if (! mdisok(&xep))
2N/A mdclrerror(&xep);
2N/A
2N/A if (dd != NULL) {
2N/A mhd_mhiargs_t mhiargs = defmhiargs;
2N/A
2N/A if (tk_own_bydd(sp, dd, &mhiargs, TRUE, &xep))
2N/A mdclrerror(&xep);
2N/A }
2N/A } else {
2N/A (void) mddserror(ep, MDE_DS_AUTONOTSET, sp->setno,
2N/A NULL, NULL, sp->setname);
2N/A rval = -1;
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A cl_sk = cl_get_setkey(sp->setno, sp->setname);
2N/A if (clnt_unlock_set(hostname, cl_sk, &xep)) {
2N/A if (rval == 0)
2N/A (void) mdstealerror(ep, &xep);
2N/A rval = -1;
2N/A }
2N/A cl_set_setkey(NULL);
2N/A
2N/A return (rval);
2N/A}