meta_mirror.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Just in case we're not in a build environment, make sure that
* TEXT_DOMAIN gets set to something.
*/
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*
* mirror operations
*/
#include <meta.h>
#include <sys/lvm/md_mirror.h>
#include <sys/lvm/md_convert.h>
#include <ctype.h>
#include <stddef.h>
/*
* FUNCTION: meta_get_mirror_names()
* INPUT: sp - the set name to get mirrors from
* options - options from the command line
* OUTPUT: nlpp - list of all mirror names
* ep - return error pointer
* RETURNS: int - -1 if error, 0 success
* PURPOSE: returns a list of all mirrors in the metadb
* for all devices in the specified set
*/
int
meta_get_mirror_names(
mdsetname_t *sp,
mdnamelist_t **nlpp,
int options,
md_error_t *ep
)
{
return (meta_get_names(MD_MIRROR, sp, nlpp, options, ep));
}
/*
* free mirror unit
*/
void
meta_free_mirror(
md_mirror_t *mirrorp
)
{
Free(mirrorp);
}
/*
* get mirror unit
*/
static md_mirror_t *
meta_get_mirror_common(
mdsetname_t *sp,
mdname_t *mirnp,
int fast,
md_error_t *ep
)
{
mddrivename_t *dnp = mirnp->drivenamep;
char *miscname;
mm_unit_t *mm;
md_mirror_t *mirrorp;
uint_t smi, nsm;
md_resync_ioctl_t ri;
/* must have set */
assert(sp != NULL);
/* short circuit */
if (dnp->unitp != NULL) {
assert(dnp->unitp->type == MD_METAMIRROR);
return ((md_mirror_t *)dnp->unitp);
}
/* get miscname and unit */
if ((miscname = metagetmiscname(mirnp, ep)) == NULL)
return (NULL);
if (strcmp(miscname, MD_MIRROR) != 0) {
(void) mdmderror(ep, MDE_NOT_MM, meta_getminor(mirnp->dev),
mirnp->cname);
return (NULL);
}
if ((mm = (mm_unit_t *)meta_get_mdunit(sp, mirnp, ep)) == NULL)
return (NULL);
assert(mm->c.un_type == MD_METAMIRROR);
/* allocate mirror */
mirrorp = Zalloc(sizeof (*mirrorp));
/* get common info */
mirrorp->common.namep = mirnp;
mirrorp->common.type = mm->c.un_type;
mirrorp->common.state = mm->c.un_status;
mirrorp->common.capabilities = mm->c.un_capabilities;
mirrorp->common.parent = mm->c.un_parent;
mirrorp->common.size = mm->c.un_total_blocks;
mirrorp->common.user_flags = mm->c.un_user_flags;
mirrorp->common.revision = mm->c.un_revision;
/* get options */
mirrorp->read_option = mm->un_read_option;
mirrorp->write_option = mm->un_write_option;
mirrorp->pass_num = mm->un_pass_num;
/* get submirrors */
for (smi = 0, nsm = 0; (smi < NMIRROR); ++smi) {
mm_submirror_t *mmsp = &mm->un_sm[smi];
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
/* get submirror state */
mdsp->state = mmsp->sm_state;
if (mdsp->state == SMS_UNUSED)
continue;
++nsm;
/* get submirror time of last state change */
mdsp->timestamp = mmsp->sm_timestamp;
/* get submirror flags */
mdsp->flags = mmsp->sm_flags;
/* get submirror name */
mdsp->submirnamep = metakeyname(&sp, mmsp->sm_key, fast, ep);
if (mdsp->submirnamep == NULL)
goto out;
}
assert(nsm == mm->un_nsm);
/* get resync info */
(void) memset(&ri, 0, sizeof (ri));
ri.ri_mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&ri, MD_MIRROR, sp->setno);
if (metaioctl(MD_IOCGETSYNC, &ri, &ri.mde, mirnp->cname) != 0) {
(void) mdstealerror(ep, &ri.mde);
goto out;
}
mirrorp->percent_done = ri.ri_percent_done;
mirrorp->percent_dirty = ri.ri_percent_dirty;
/* cleanup, return success */
Free(mm);
dnp->unitp = (md_common_t *)mirrorp;
return (mirrorp);
/* cleanup, return error */
out:
Free(mm);
meta_free_mirror(mirrorp);
return (NULL);
}
/*
* get mirror unit
*/
md_mirror_t *
meta_get_mirror(
mdsetname_t *sp,
mdname_t *mirnp,
md_error_t *ep
)
{
return (meta_get_mirror_common(sp, mirnp, 0, ep));
}
/*
* check mirror for dev
*/
static int
in_mirror(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *np,
diskaddr_t slblk,
diskaddr_t nblks,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
/* should be in the same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* get unit */
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
/* look in submirrors */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
/* skip unused submirrors */
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* check overlap */
if (metaismeta(submirnp))
continue;
if (meta_check_overlap(mirnp->cname, np, slblk, nblks,
submirnp, 0, -1, ep) != 0)
return (-1);
}
/* return success */
return (0);
}
/*
* check to see if we're in a mirror
*/
int
meta_check_inmirror(
mdsetname_t *sp,
mdname_t *np,
diskaddr_t slblk,
diskaddr_t nblks,
md_error_t *ep
)
{
mdnamelist_t *mirrornlp = NULL;
mdnamelist_t *p;
int rval = 0;
/* should have a set */
assert(sp != NULL);
/* for each mirror */
if (meta_get_mirror_names(sp, &mirrornlp, 0, ep) < 0)
return (-1);
for (p = mirrornlp; (p != NULL); p = p->next) {
mdname_t *mirnp = p->namep;
/* check mirror */
if (in_mirror(sp, mirnp, np, slblk, nblks, ep) != 0) {
rval = -1;
break;
}
}
/* cleanup, return success */
metafreenamelist(mirrornlp);
return (rval);
}
/*
* Check to see if the primary mirror is built on top of a
* root slice which is mounted. This check is primarily to
* account for this case -
*
* # metainit -f d1 1 1 <root slice>
* # metainit d0 -m d1
* # metainit d2 1 1 ctds
* # metattach d0 d2
*
* The metattach here needs to fail if the root slice is
* being mirrored; otherwise there is a potential for
* data corruption.
*/
static int
meta_check_primary_mirror(
mdsetname_t *sp,
mdname_t *mirnp,
md_error_t *ep
)
{
int smi;
char *curroot;
mdname_t *rootnp;
md_mirror_t *mirrorp;
md_stripe_t *stripep;
md_row_t *rp;
md_comp_t *cp;
if ((curroot = meta_get_current_root(ep)) == NULL)
return (-1);
/*
* Get device name of current root metadevice. If root
* is net mounted as happens if we're part of the
* install process, rootnp will be set to NULL and we
* return success.
*/
if ((rootnp = metaname(&sp, curroot, ep)) == NULL)
return (0);
/*
* If the currently mounted root slice is not a
* ctds, we don't bother checking
*/
if ((!metaismeta(rootnp)) && metaismeta(mirnp)) {
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
for (smi = 0; (smi < NMIRROR); ++smi) {
/* Check all submirrors */
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnamep = mdsp->submirnamep;
/* skip unused submirrors */
if (submirnamep == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* check if submirror is a stripe or not */
if (strcmp(metagetmiscname(submirnamep, ep), MD_STRIPE)
!= 0)
return (-1);
if ((stripep = meta_get_stripe(sp, submirnamep, ep))
== NULL)
return (-1);
/*
* Examine the first component of the first row and
* check to see if it has a mounted root slice
*/
rp = &stripep->rows.rows_val[0];
cp = &rp->comps.comps_val[0];
/*
* we just care about the component built on
* top of a raw device
*/
if (!metaismeta(cp->compnamep)) {
/*
* If root device is the 1st component of
* the stripe, then fail.
*/
if (strcmp(rootnp->cname, cp->compnamep->cname)
== 0) {
(void) mduseerror(ep, MDE_IS_MOUNTED,
rootnp->dev, "/", rootnp->cname);
return (-1);
}
}
}
}
/* return success */
return (0);
}
/*
* check submirror
*/
int
meta_check_submirror(
mdsetname_t *sp,
mdname_t *np,
mdname_t *mirnp,
int force,
md_error_t *ep
)
{
mdchkopts_t options = 0;
md_common_t *mdp;
/* make sure we have a metadevice disk */
if (metachkmeta(np, ep) != 0)
return (-1);
/*
* Check to see if the primary mirror consists of a root
* mounted device
*/
if (mirnp && (!force) && ((meta_check_primary_mirror(sp, mirnp, ep)
!= 0)))
return (-1);
/* check to ensure that it is not already in use */
if ((! force) &&
(meta_check_inuse(sp, np, MDCHK_INUSE, ep) != 0)) {
return (-1);
}
/* make sure it is in the set */
if (meta_check_inset(sp, np, ep) != 0)
return (-1);
/* make sure its not in a metadevice */
if (! metaismeta(np)) { /* Non-metadevices */
if (meta_check_inmeta(sp, np, options, 0, -1, ep) != 0)
return (-1);
} else { /* Metadevices only! */
/* make sure it can be parented */
if ((mdp = meta_get_unit(sp, np, ep)) == NULL)
return (-1);
if ((! (mdp->capabilities & MD_CAN_PARENT)) ||
(! (mdp->capabilities & MD_CAN_SUB_MIRROR)) ||
(mdp->parent != MD_NO_PARENT)) {
return (mdmderror(ep, MDE_INVAL_UNIT,
meta_getminor(np->dev), np->cname));
}
}
/* return success */
return (0);
}
/*
* convert read options
*/
char *
rd_opt_to_name(
mm_rd_opt_t opt
)
{
switch (opt) {
case RD_LOAD_BAL:
return ("roundrobin");
case RD_GEOMETRY:
return ("geometric");
case RD_FIRST:
return ("first");
default:
assert(0);
return (dgettext(TEXT_DOMAIN, "invalid"));
}
}
static char *
rd_opt_to_opt(
mm_rd_opt_t opt
)
{
switch (opt) {
case RD_LOAD_BAL:
return (NULL); /* default */
case RD_GEOMETRY:
return ("-g");
case RD_FIRST:
return ("-r");
default:
assert(0);
return (dgettext(TEXT_DOMAIN, "invalid"));
}
}
int
name_to_rd_opt(
char *uname,
char *name,
mm_rd_opt_t *optp,
md_error_t *ep
)
{
if (strcasecmp(name, "roundrobin") == 0) {
*optp = RD_LOAD_BAL;
return (0);
}
if (strcasecmp(name, "geometric") == 0) {
*optp = RD_GEOMETRY;
return (0);
}
if (strcasecmp(name, "first") == 0) {
*optp = RD_FIRST;
return (0);
}
return (meta_cook_syntax(ep, MDE_BAD_RD_OPT, uname, 1, &name));
}
/*
* convert write options
*/
char *
wr_opt_to_name(
mm_wr_opt_t opt
)
{
switch (opt) {
case WR_PARALLEL:
return ("parallel");
case WR_SERIAL:
return ("serial");
default:
assert(0);
return (dgettext(TEXT_DOMAIN, "invalid"));
}
}
static char *
wr_opt_to_opt(
mm_wr_opt_t opt
)
{
switch (opt) {
case WR_PARALLEL:
return (NULL); /* default */
case WR_SERIAL:
return ("-S");
default:
assert(0);
return (dgettext(TEXT_DOMAIN, "invalid"));
}
}
int
name_to_wr_opt(
char *uname,
char *name,
mm_wr_opt_t *optp,
md_error_t *ep
)
{
if (strcasecmp(name, "parallel") == 0) {
*optp = WR_PARALLEL;
return (0);
}
if (strcasecmp(name, "serial") == 0) {
*optp = WR_SERIAL;
return (0);
}
return (meta_cook_syntax(ep, MDE_BAD_WR_OPT, uname, 1, &name));
}
/*
* convert pass numbers
*/
int
name_to_pass_num(
char *uname,
char *name,
mm_pass_num_t *passp,
md_error_t *ep
)
{
if ((sscanf(name, "%hd", passp) != 1) ||
(*passp < 0) || (*passp > MD_PASS_MAX)) {
return (meta_cook_syntax(ep, MDE_BAD_PASS_NUM,
uname, 1, &name));
}
return (0);
}
/*
* convert resync option
*/
static char *
resync_opt_to_name(
uint_t tstate
)
{
if (tstate & MD_ABR_CAP)
return (dgettext(TEXT_DOMAIN, "application based"));
else
return (dgettext(TEXT_DOMAIN, "optimized resync"));
}
/*
* print mirror
*/
static int
mirror_print(
md_mirror_t *mirrorp,
char *fname,
FILE *fp,
mdprtopts_t options,
md_error_t *ep
)
{
uint_t smi;
char *p;
int rval = -1;
if (options & PRINT_LARGEDEVICES) {
if (mirrorp->common.revision != MD_64BIT_META_DEV) {
rval = 0;
goto out;
}
}
/* print name and -m */
if (fprintf(fp, "%s -m", mirrorp->common.namep->cname) == EOF)
goto out;
/* print submirrors */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnamep = mdsp->submirnamep;
/* skip unused submirrors */
if (submirnamep == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* print submirror */
if (fprintf(fp, " %s", submirnamep->cname) == EOF)
goto out;
}
/* print options */
if ((p = rd_opt_to_opt(mirrorp->read_option)) != NULL) {
if (fprintf(fp, " %s", p) == EOF)
goto out;
}
if ((p = wr_opt_to_opt(mirrorp->write_option)) != NULL) {
if (fprintf(fp, " %s", p) == EOF)
goto out;
}
if (fprintf(fp, " %u\n", mirrorp->pass_num) == EOF)
goto out;
/* success */
rval = 0;
/* cleanup, return error */
out:
if (rval != 0)
(void) mdsyserror(ep, errno, fname);
return (rval);
}
/*
* convert submirror state to name
*/
char *
sm_state_to_name(
md_submirror_t *mdsp,
md_status_t mirror_status,
md_timeval32_t *tvp,
uint_t tstate
)
{
static char state_to_str[100];
sm_state_t state = mdsp->state;
uint_t is_target = mdsp->flags & MD_SM_RESYNC_TARGET;
/* grab time */
if (tvp != NULL)
*tvp = mdsp->timestamp;
/*
* Only return Unavailable if there is no flagged error on the
* submirror. If the mirror has received any writes since the submirror
* went into Unavailable state a resync is required. To alert the
* administrator to this we return a 'Needs maintenance' message.
*/
if ((tstate != 0) && (state & SMS_RUNNING)) {
return (dgettext(TEXT_DOMAIN, "Unavailable"));
}
/* all is well */
if (state & SMS_RUNNING) {
if (!(mirror_status & MD_UN_OPT_NOT_DONE) ||
((mirror_status & MD_UN_OPT_NOT_DONE) && !is_target)) {
return (dgettext(TEXT_DOMAIN, "Okay"));
}
}
/* resyncing, needs repair */
if ((state & (SMS_COMP_RESYNC | SMS_ATTACHED_RESYNC |
SMS_OFFLINE_RESYNC)) ||
(mirror_status & MD_UN_OPT_NOT_DONE)) {
if (mirror_status & MD_UN_RESYNC_ACTIVE) {
return (dgettext(TEXT_DOMAIN, "Resyncing"));
}
if (mirror_status & MD_UN_RESYNC_CANCEL) {
return (dgettext(TEXT_DOMAIN, "Resync cancelled"));
}
return (dgettext(TEXT_DOMAIN, "Needs maintenance"));
}
/* needs repair */
if (state & (SMS_COMP_ERRED | SMS_ATTACHED | SMS_OFFLINE)) {
if (mirror_status & MD_UN_RESYNC_CANCEL) {
return (dgettext(TEXT_DOMAIN, "Resync cancelled"));
}
return (dgettext(TEXT_DOMAIN, "Needs maintenance"));
}
/* unknown */
assert(0);
(void) sprintf(state_to_str, "0x%x", state);
return (state_to_str);
}
/*
* convert submirror state to repair action
*/
int
sm_state_to_action(
mdsetname_t *sp,
md_submirror_t *mdsp,
md_status_t mirror_status,
md_mirror_t *mirrorp,
char **actionp,
md_error_t *ep
)
{
static char buf[1024];
mdname_t *submirnamep = mdsp->submirnamep;
sm_state_t state = mdsp->state;
char *miscname;
/* all is well */
*actionp = NULL;
if (mirror_status & MD_UN_RESYNC_ACTIVE)
return (0);
if ((state == SMS_RUNNING) && !(mirror_status & MD_UN_OPT_NOT_DONE))
return (0);
/* complete cancelled resync */
if (mirror_status & MD_UN_RESYNC_CANCEL) {
(void) snprintf(buf, sizeof (buf),
dgettext(TEXT_DOMAIN, "metasync %s"),
mirrorp->common.namep->cname);
*actionp = buf;
return (0);
}
/* replace stripe component */
if ((metaismeta(submirnamep)) && (state & SMS_COMP_ERRED)) {
if ((miscname = metagetmiscname(submirnamep, ep)) == NULL)
return (-1);
if (strcmp(miscname, MD_STRIPE) == 0) {
mdname_t *compnamep;
comp_state_t compstate;
if (meta_find_erred_comp(sp, submirnamep,
&compnamep, &compstate, ep) != 0) {
return (-1);
}
if (compstate != CS_LAST_ERRED)
(void) snprintf(buf, sizeof (buf),
"metareplace %s %s <%s>",
mirrorp->common.namep->cname,
compnamep->cname,
dgettext(TEXT_DOMAIN, "new device"));
else
(void) snprintf(buf, sizeof (buf),
dgettext(TEXT_DOMAIN,
"after replacing \"Maintenance\" "
"components:\n"
"\t\tmetareplace %s %s <new device>"),
mirrorp->common.namep->cname,
compnamep->cname);
*actionp = buf;
return (0);
}
}
/* resync mirror */
if ((state & (SMS_ATTACHED_RESYNC | SMS_OFFLINE_RESYNC |
SMS_COMP_RESYNC | SMS_ATTACHED)) ||
(mirror_status & MD_UN_OPT_NOT_DONE)) {
(void) snprintf(buf, sizeof (buf), "metasync %s",
mirrorp->common.namep->cname);
*actionp = buf;
return (0);
}
/* online submirror */
if (state & SMS_OFFLINE) {
(void) snprintf(buf, sizeof (buf), "metaonline %s %s",
mirrorp->common.namep->cname, submirnamep->cname);
*actionp = buf;
return (0);
}
/* unknown action */
*actionp = dgettext(TEXT_DOMAIN, "???");
return (0);
}
/*
* print mirror options
*/
int
meta_print_mirror_options(
mm_rd_opt_t read_option,
mm_wr_opt_t write_option,
mm_pass_num_t pass_num,
uint_t tstate,
char *fname,
mdsetname_t *sp,
FILE *fp,
md_error_t *ep
)
{
char *p;
int rval = -1;
/* print options */
if (fprintf(fp, dgettext(TEXT_DOMAIN, " Pass: %u\n"),
pass_num) == EOF) {
goto out;
}
if ((p = rd_opt_to_opt(read_option)) == NULL)
p = dgettext(TEXT_DOMAIN, "default");
if (fprintf(fp, dgettext(TEXT_DOMAIN, " Read option: %s (%s)\n"),
rd_opt_to_name(read_option), p) == EOF) {
goto out;
}
if ((p = wr_opt_to_opt(write_option)) == NULL)
p = dgettext(TEXT_DOMAIN, "default");
if (fprintf(fp, dgettext(TEXT_DOMAIN, " Write option: %s (%s)\n"),
wr_opt_to_name(write_option), p) == EOF) {
goto out;
}
/* Display resync option for mirror, if MultiNode set */
if (meta_is_mn_set(sp, ep)) {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync option: %s\n"),
resync_opt_to_name(tstate)) == EOF) {
goto out;
}
}
/* success */
rval = 0;
/* cleanup, return error */
out:
if (rval != 0)
(void) mdsyserror(ep, errno, fname);
return (rval);
}
static char *
get_node_name(uint_t nid, md_error_t *ep)
{
mndiskset_membershiplist_t *nl, *p;
int n;
char *node_nm;
/* get the known membership list */
if (meta_read_nodelist(&n, &nl, ep)) {
return (NULL);
}
/* find the matching node and return the name */
for (p = nl; (p != NULL); p = p->next) {
if (nid == p->msl_node_id) {
/* match found */
node_nm = Strdup(p->msl_node_name);
goto out;
}
}
/* match not found */
node_nm = Strdup(dgettext(TEXT_DOMAIN, "None"));
out:
meta_free_nodelist(nl);
return (node_nm);
}
/*
* report mirror
*/
static int
mirror_report(
mdsetname_t *sp,
md_mirror_t *mirrorp,
mdnamelist_t **nlpp,
char *fname,
FILE *fp,
mdprtopts_t options,
md_error_t *ep
)
{
md_status_t status = mirrorp->common.state;
uint_t smi;
char *p;
int rval = -1;
uint_t tstate = 0;
/*
* check for the -B option. If -B and the metadevice is
* a 64 bit device, get the dev for relocation information
* printout. If not a 64 bit device, just don't print this
* information out but you need to go down to the subdevice
* level and print there if appropriate.
*/
if (options & PRINT_LARGEDEVICES) {
if (mirrorp->common.revision != MD_64BIT_META_DEV) {
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp =
&mirrorp->submirrors[smi];
mdname_t *submirnamep =
mdsp->submirnamep;
if (submirnamep == NULL) {
continue;
}
if ((metaismeta(submirnamep)) &&
(meta_print_name(sp, submirnamep, nlpp,
fname, fp, options | PRINT_SUBDEVS, NULL,
ep) != 0)) {
return (-1);
}
}
rval = 0;
goto out;
} else {
if (meta_getdevs(sp, mirrorp->common.namep,
nlpp, ep) != 0)
goto out;
}
}
/* print header */
if (options & PRINT_HEADER) {
if (fprintf(fp, dgettext(TEXT_DOMAIN, "%s: Mirror\n"),
mirrorp->common.namep->cname) == EOF) {
goto out;
}
}
/* print submirrors, adjust status */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnamep = mdsp->submirnamep;
char *sm_state;
md_timeval32_t tv;
char *timep;
/* skip unused submirrors */
if (submirnamep == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
if (mdsp->state & SMS_OFFLINE)
status &= ~MD_UN_OPT_NOT_DONE;
/* print submirror */
if (fprintf(fp, dgettext(TEXT_DOMAIN, " Submirror %u: %s\n"),
smi, submirnamep->cname) == EOF) {
goto out;
}
/* print state */
if (metaismeta(mdsp->submirnamep)) {
if (meta_get_tstate(mdsp->submirnamep->dev, &tstate,
ep) != 0)
return (-1);
}
sm_state = sm_state_to_name(mdsp, status, &tv,
tstate & MD_DEV_ERRORED);
if (options & PRINT_TIMES) {
timep = meta_print_time(&tv);
} else {
timep = "";
}
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" State: %-12s %s\n"),
sm_state, timep) == EOF) {
goto out;
}
}
/* print resync status */
if (status & MD_UN_RESYNC_CANCEL) {
/* Resync was cancelled but is restartable */
if (mirrorp->common.revision == MD_64BIT_META_DEV) {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync cancelled: %2d.%1d %% done\n"),
mirrorp->percent_done/10,
mirrorp->percent_done%10) == EOF) {
goto out;
}
} else {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync cancelled: %d %% done\n"),
mirrorp->percent_done) == EOF) {
goto out;
}
}
} else if (status & MD_UN_RESYNC_ACTIVE) {
if (mirrorp->common.revision == MD_64BIT_META_DEV) {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync in progress: %2d.%1d %% done\n"),
mirrorp->percent_done/10,
mirrorp->percent_done%10) == EOF) {
goto out;
}
} else {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync in progress: %d %% done\n"),
mirrorp->percent_done) == EOF) {
goto out;
}
}
}
/* print options */
if (meta_get_tstate(mirrorp->common.namep->dev, &tstate, ep) != 0)
return (-1);
if (meta_print_mirror_options(mirrorp->read_option,
mirrorp->write_option, mirrorp->pass_num,
tstate, fname, sp, fp, ep) != 0)
return (-1);
/* print mirror owner for multi-node metadevice */
if (meta_is_mn_set(sp, ep)) {
md_set_mmown_params_t ownpar;
mdname_t *mirnp = mirrorp->common.namep;
char *node_name;
(void) memset(&ownpar, 0, sizeof (ownpar));
ownpar.d.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&ownpar, MD_MIRROR, sp->setno);
if (metaioctl(MD_MN_GET_MM_OWNER, &ownpar, ep,
"MD_MN_GET_MM_OWNER") != 0) {
return (-1);
}
node_name = get_node_name(ownpar.d.owner, ep);
if (node_name == NULL)
return (-1);
else if (fprintf(fp, dgettext(TEXT_DOMAIN, " Owner: %s\n"),
node_name) == EOF) {
Free(node_name);
goto out;
}
Free(node_name);
}
/* print size */
if (fprintf(fp, dgettext(TEXT_DOMAIN, " Size: %lld blocks (%s)\n"),
mirrorp->common.size,
meta_number_to_string(mirrorp->common.size, DEV_BSIZE))
== EOF) {
goto out;
}
/* MD_DEBUG stuff */
if (options & PRINT_DEBUG) {
mdname_t *mirnp = mirrorp->common.namep;
mm_unit_t *mm;
mddb_optloc_t optloc;
uint_t i;
/* get real mirror unit */
if ((mm = (mm_unit_t *)meta_get_mdunit(sp, mirnp, ep))
== NULL) {
return (-1);
}
assert(mm->c.un_type == MD_METAMIRROR);
/* print dirty regions */
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Regions which are dirty: %d%% (blksize %d num %d)\n"),
mirrorp->percent_dirty, mm->un_rrd_blksize,
mm->un_rrd_num) == EOF) {
Free(mm);
goto out;
}
/* print optimized resync record locations */
(void) memset(&optloc, 0, sizeof (optloc));
optloc.recid = mm->un_rr_dirty_recid;
if (metaioctl(MD_DB_GETOPTLOC, &optloc, ep,
"MD_DB_GETOPTLOC") != 0) {
Free(mm);
return (-1);
}
for (i = 0; (i < ((sizeof optloc.li) / sizeof (optloc.li[0])));
++i) {
mddb_config_t dbconf;
char *devname;
(void) memset(&dbconf, 0, sizeof (dbconf));
dbconf.c_id = optloc.li[i];
dbconf.c_setno = sp->setno;
dbconf.c_subcmd = MDDB_CONFIG_ABS;
/* Don't need device id information from this ioctl */
dbconf.c_locator.l_devid = (uint64_t)0;
dbconf.c_locator.l_devid_flags = 0;
if (metaioctl(MD_DB_ENDDEV, &dbconf, &dbconf.c_mde,
"MD_DB_ENDDEV") != 0) {
Free(mm);
return (mdstealerror(ep, &dbconf.c_mde));
}
if ((devname = splicename(&dbconf.c_devname))
== NULL) {
devname = Strdup(dgettext(TEXT_DOMAIN,
"unknown"));
}
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" Resync record[%u]: %d (%s %d %d)\n"), i,
optloc.li[i], devname, dbconf.c_locator.l_blkno,
(dbconf.c_dbend - dbconf.c_locator.l_blkno + 1))
== EOF) {
Free(mm);
Free(devname);
goto out;
}
Free(devname);
}
Free(mm);
}
/* print submirror details */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnamep = mdsp->submirnamep;
char *sm_state;
md_timeval32_t tv;
char *timep;
/* skip unused submirrors */
if (submirnamep == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* add extra line */
if (fprintf(fp, "\n") == EOF)
goto out;
/* print submirror */
if (fprintf(fp, dgettext(TEXT_DOMAIN,
"%s: Submirror of %s\n"),
submirnamep->cname,
mirrorp->common.namep->cname) == EOF) {
goto out;
}
/* print state */
if (metaismeta(mdsp->submirnamep)) {
if (meta_get_tstate(mdsp->submirnamep->dev, &tstate, ep)
!= 0)
return (-1);
}
sm_state = sm_state_to_name(mdsp, status, &tv, NULL);
if (options & PRINT_TIMES) {
timep = meta_print_time(&tv);
} else {
timep = "";
}
if ((tstate & MD_DEV_ERRORED) == 0) {
if (fprintf(fp, dgettext(TEXT_DOMAIN,
" State: %-12s %s\n"),
sm_state, timep) == EOF) {
goto out;
}
/* print what to do */
if (sm_state_to_action(sp, mdsp, status,
mirrorp, &p, ep) != 0)
return (-1);
if ((p != NULL) &&
(fprintf(fp, dgettext(TEXT_DOMAIN,
" Invoke: %s\n"), p) == EOF)) {
goto out;
}
}
/* print underlying metadevice */
if ((metaismeta(submirnamep)) &&
(meta_print_name(sp, submirnamep, nlpp, fname, fp,
((options & ~PRINT_HEADER) | PRINT_SUBDEVS),
NULL, ep) != 0)) {
return (-1);
}
}
/* add extra line */
if (fprintf(fp, "\n") == EOF)
goto out;
/* success */
rval = 0;
/* cleanup, return error */
out:
if (rval != 0)
(void) mdsyserror(ep, errno, fname);
return (rval);
}
/*
* print/report mirror
*/
int
meta_mirror_print(
mdsetname_t *sp,
mdname_t *mirnp,
mdnamelist_t **nlpp,
char *fname,
FILE *fp,
mdprtopts_t options,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
/* should have same set */
assert(sp != NULL);
assert((mirnp == NULL) ||
(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev))));
/* print all mirrors */
if (mirnp == NULL) {
mdnamelist_t *nlp = NULL;
mdnamelist_t *p;
int cnt;
int rval = 0;
/* get list */
if ((cnt = meta_get_mirror_names(sp, &nlp, options, ep)) < 0)
return (-1);
else if (cnt == 0)
return (0);
/* recurse */
for (p = nlp; (p != NULL); p = p->next) {
mdname_t *np = p->namep;
if (meta_mirror_print(sp, np, nlpp, fname, fp,
options, ep) != 0)
rval = -1;
}
/* cleanup, return success */
metafreenamelist(nlp);
return (rval);
}
/* get unit structure */
if ((mirrorp = meta_get_mirror_common(sp, mirnp,
((options & PRINT_FAST) ? 1 : 0), ep)) == NULL)
return (-1);
/* check for parented */
if ((! (options & PRINT_SUBDEVS)) &&
(MD_HAS_PARENT(mirrorp->common.parent))) {
return (0);
}
/* print appropriate detail */
if (options & PRINT_SHORT) {
/* print mirror */
if (mirror_print(mirrorp, fname, fp, options, ep) != 0)
return (-1);
/* print underlying metadevices */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnamep = mdsp->submirnamep;
/* skip unused submirrors */
if (submirnamep == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* print submirror */
if (metaismeta(submirnamep)) {
if (meta_print_name(sp, submirnamep, nlpp,
fname, fp, (options | PRINT_SUBDEVS), NULL,
ep) != 0) {
return (-1);
}
}
}
/* return success */
return (0);
} else {
return (mirror_report(sp, mirrorp, nlpp, fname, fp,
options, ep));
}
}
/*
* online submirror
*/
int
meta_mirror_online(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *submirnp,
mdcmdopts_t options,
md_error_t *ep
)
{
md_i_off_on_t mio;
md_mirror_t *mirrorp;
md_set_desc *sd;
uint_t tstate;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
/* Only valid for mirror without ABR set */
if (meta_get_tstate(mirrorp->common.namep->dev, &tstate, ep) != 0)
return (-1);
if (tstate & MD_ABR_CAP) {
(void) mderror(ep, MDE_ABR_SET, NULL);
return (-1);
}
/*
* In a MN set, the master always executes the online command first.
* Before the master executes the IOC_ONLINE ioctl,
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_ONLINE ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the online command, each slave will
* call the IOC_ONLINE ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if ((MD_MNSET_DESC(sd)) && sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
/* online submirror */
(void) memset(&mio, 0, sizeof (mio));
mio.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&mio, MD_MIRROR, sp->setno);
mio.submirror = submirnp->dev;
if (metaioctl(MD_IOCONLINE, &mio, &mio.mde, NULL) != 0)
return (mdstealerror(ep, &mio.mde));
/* clear cache */
meta_invalidate_name(mirnp);
meta_invalidate_name(submirnp);
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: submirror %s is onlined\n"),
mirnp->cname, submirnp->cname);
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* offline submirror
*/
int
meta_mirror_offline(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *submirnp,
mdcmdopts_t options,
md_error_t *ep
)
{
int force = ((options & MDCMD_FORCE) ? 1 : 0);
md_i_off_on_t mio;
md_mirror_t *mirrorp;
md_set_desc *sd;
uint_t tstate;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
/* Only valid for mirror without ABR set */
if (meta_get_tstate(mirrorp->common.namep->dev, &tstate, ep) != 0)
return (-1);
if (tstate & MD_ABR_CAP) {
(void) mderror(ep, MDE_ABR_SET, NULL);
return (-1);
}
/*
* In a MN set, the master always executes the offline command first.
* Before the master executes the IOC_OFFLINE ioctl,
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_OFFLINE ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the offline command, each slave will
* call the IOC_OFFLINE ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if ((MD_MNSET_DESC(sd)) && sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
/* offline submirror */
(void) memset(&mio, 0, sizeof (mio));
mio.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&mio, MD_MIRROR, sp->setno);
mio.submirror = submirnp->dev;
mio.force_offline = force;
if (metaioctl(MD_IOCOFFLINE, &mio, &mio.mde, NULL) != 0)
return (mdstealerror(ep, &mio.mde));
/* clear cache */
meta_invalidate_name(mirnp);
meta_invalidate_name(submirnp);
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: submirror %s is offlined\n"),
mirnp->cname, submirnp->cname);
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* attach submirror to mirror
* we actually never have to worry about crossing a thresh hold here.
* 2 cases 1) attach and the only way the mirror can be 64 bit is if
* one of the submirrors already is. 2) grow and the only way the mirror
* is 64 bit is if one of the submirror's already is.
*/
int
meta_mirror_attach(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *submirnp,
mdcmdopts_t options,
md_error_t *ep
)
{
md_att_struct_t att;
md_set_desc *sd;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* just grow */
if (submirnp == NULL) {
return (meta_concat_generic(sp, mirnp, NULL, ep));
}
/* check submirror */
if (meta_check_submirror(sp, submirnp, mirnp, 0, ep) != 0)
return (-1);
/* In dryrun mode (DOIT not set) we must not alter the mddb */
if (options & MDCMD_DOIT) {
/* store name in namespace */
if (add_key_name(sp, submirnp, NULL, ep) != 0)
return (-1);
}
/*
* In a MN set, the master always executes the attach command first.
* Before the master executes the IOC_ATTACH ioctl, in non-DRYRUN mode
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_ATTACH ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the attach command, each slave will
* call the IOC_ATTACH ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if ((MD_MNSET_DESC(sd)) && (options & MDCMD_DOIT) &&
sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
/* attach submirror */
(void) memset(&att, 0, sizeof (att));
att.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&att, MD_MIRROR, sp->setno);
att.submirror = submirnp->dev;
att.key = submirnp->key;
/* if the comamnd was issued with -n option, use dryrun mode */
if ((options & MDCMD_DOIT) == 0) {
att.options = MDIOCTL_DRYRUN;
}
if (metaioctl(MD_IOCATTACH, &att, &att.mde, NULL) != 0) {
/* In dryrun mode (DOIT not set) we must not alter the mddb */
if (options & MDCMD_DOIT) {
(void) del_key_name(sp, submirnp, ep);
}
return (mdstealerror(ep, &att.mde));
}
/* In dryrun mode (DOIT not set) we must not alter the mddb */
if (options & MDCMD_DOIT) {
/* clear cache */
meta_invalidate_name(mirnp);
meta_invalidate_name(submirnp);
}
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: submirror %s %s\n"), mirnp->cname, submirnp->cname,
(options & MDCMD_DOIT) ? "is attached" : "would attach");
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* detach submirror
*/
int
meta_mirror_detach(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *submirnp,
mdcmdopts_t options,
md_error_t *ep
)
{
int force = ((options & MDCMD_FORCE) ? 1 : 0);
md_detach_params_t detach;
md_set_desc *sd;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/*
* In a MN set, the master always executes the detach command first.
* Before the master executes the IOC_DETACH ioctl,
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_DETACH ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the detach command, each slave will
* call the IOC_DETACH ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if ((MD_MNSET_DESC(sd)) && sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
/* detach submirror */
(void) memset(&detach, 0, sizeof (detach));
detach.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&detach, MD_MIRROR, sp->setno);
detach.submirror = submirnp->dev;
detach.force_detach = force;
if (metaioctl(MD_IOCDETACH, &detach, &detach.mde, NULL) != 0)
return (mdstealerror(ep, &detach.mde));
/* clear cache */
meta_invalidate_name(mirnp);
meta_invalidate_name(submirnp);
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: submirror %s is detached\n"),
mirnp->cname, submirnp->cname);
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* get mirror parameters
*/
int
meta_mirror_get_params(
mdsetname_t *sp,
mdname_t *mirnp,
mm_params_t *paramsp,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
/* should have a set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* get unit */
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
/* return parameters */
(void) memset(paramsp, 0, sizeof (*paramsp));
paramsp->read_option = mirrorp->read_option;
paramsp->write_option = mirrorp->write_option;
paramsp->pass_num = mirrorp->pass_num;
return (0);
}
/*
* set mirror parameters
*/
int
meta_mirror_set_params(
mdsetname_t *sp,
mdname_t *mirnp,
mm_params_t *paramsp,
md_error_t *ep
)
{
md_mirror_params_t mmp;
/* should have a set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* set parameters */
(void) memset(&mmp, 0, sizeof (mmp));
MD_SETDRIVERNAME(&mmp, MD_MIRROR, sp->setno);
mmp.mnum = meta_getminor(mirnp->dev);
mmp.params = *paramsp;
if (metaioctl(MD_IOCCHANGE, &mmp, &mmp.mde, mirnp->cname) != 0)
return (mdstealerror(ep, &mmp.mde));
/* clear cache */
meta_invalidate_name(mirnp);
/* return success */
return (0);
}
/*
* invalidate submirror names
*/
static int
invalidate_submirrors(
mdsetname_t *sp,
mdname_t *mirnp,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
meta_invalidate_name(submirnp);
}
return (0);
}
/*
* replace mirror component
*/
int
meta_mirror_replace(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *oldnp,
mdname_t *newnp,
mdcmdopts_t options,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
replace_params_t params;
diskaddr_t size, label, start_blk;
md_dev64_t old_dev, new_dev;
diskaddr_t new_start_blk, new_end_blk;
int rebind;
md_set_desc *sd;
char *new_devidp = NULL;
int ret;
md_error_t xep = mdnullerror;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* save new binding incase this is a rebind where oldnp==newnp */
new_dev = newnp->dev;
new_start_blk = newnp->start_blk;
new_end_blk = newnp->end_blk;
/* invalidate, then get the mirror (fill in oldnp from metadb) */
meta_invalidate_name(mirnp);
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
if (! metaismeta(submirnp))
continue;
meta_invalidate_name(submirnp);
if (meta_get_unit(sp, submirnp, ep) == NULL)
return (-1);
}
/* the old device binding is now established */
if ((old_dev = oldnp->dev) == NODEV64)
return (mdsyserror(ep, ENODEV, oldnp->cname));
/*
* check for the case where oldnp and newnp indicate the same
* device, but the dev_t of the device has changed between old
* and new. This is called a rebind. On entry the dev_t
* represents the new device binding determined from the
* filesystem (meta_getdev). After calling meta_get_unit
* oldnp (and maybe newnp if this is a rebind) is updated based
* to the old binding from the metadb (done by metakeyname).
*/
if ((strcmp(oldnp->rname, newnp->rname) == 0) &&
(old_dev != new_dev)) {
rebind = 1;
} else {
rebind = 0;
}
if (rebind) {
newnp->dev = new_dev;
newnp->start_blk = new_start_blk;
newnp->end_blk = new_end_blk;
}
/*
* Save a copy of the devid associated with the new disk, the reason
* is that if we are rebinding then the call to meta_check_component()
* will cause the devid of the disk to be overwritten with what is in
* the replica namespace. The function that actually overwrites the
* devid is dr2drivedesc().
*/
if (newnp->drivenamep->devid != NULL)
new_devidp = Strdup(newnp->drivenamep->devid);
/* if it's a multi-node diskset clear new_devidp */
if (!metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if (MD_MNSET_DESC(sd))
new_devidp = NULL;
}
/* check it out (dup on rebind is ok) */
if (meta_check_component(sp, newnp, 0, ep) != 0) {
if ((! rebind) || (! mdisuseerror(ep, MDE_ALREADY))) {
Free(new_devidp);
return (-1);
}
mdclrerror(ep);
}
if ((size = metagetsize(newnp, ep)) == MD_DISKADDR_ERROR) {
Free(new_devidp);
return (-1);
}
if ((label = metagetlabel(newnp, ep)) == MD_DISKADDR_ERROR) {
Free(new_devidp);
return (-1);
}
if ((start_blk = metagetstart(sp, newnp, ep)) == MD_DISKADDR_ERROR) {
Free(new_devidp);
return (-1);
}
if (start_blk >= size) {
(void) mdsyserror(ep, ENOSPC, newnp->cname);
Free(new_devidp);
return (-1);
}
/*
* Copy back the saved devid.
*/
Free(newnp->drivenamep->devid);
if (new_devidp != NULL) {
newnp->drivenamep->devid = Strdup(new_devidp);
Free(new_devidp);
}
/* store name in namespace, allocate new key */
if (add_key_name(sp, newnp, NULL, ep) != 0)
return (-1);
/*
* In a MN set, the master always executes the replace command first.
* Before the master executes the IOC_REPLACE ioctl, in non-DRYRUN mode
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_REPLACE ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the replace command, each slave will
* call the IOC_REPLACE ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((MD_MNSET_DESC(sd)) && (options & MDCMD_DOIT) &&
sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
if (rebind && !metaislocalset(sp)) {
/*
* We are 'rebind'ing a disk that is in a diskset so as well
* as updating the diskset's namespace the local set needs
* to be updated because it also contains a reference to
* the disk in question.
*/
ret = meta_fixdevid(sp, DEV_UPDATE|DEV_LOCAL_SET,
newnp->cname, ep);
if (ret != METADEVADM_SUCCESS) {
(void) del_key_name(sp, newnp, &xep);
return (-1);
}
}
/* replace component */
(void) memset(&params, 0, sizeof (params));
params.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&params, MD_MIRROR, sp->setno);
params.cmd = REPLACE_COMP;
params.old_dev = old_dev;
params.new_dev = new_dev;
params.start_blk = start_blk;
params.has_label = ((label > 0) ? 1 : 0);
params.number_blks = size;
params.new_key = newnp->key;
/* Is this just a dryrun ? */
if ((options & MDCMD_DOIT) == 0) {
params.options |= MDIOCTL_DRYRUN;
}
if (metaioctl(MD_IOCREPLACE, &params, &params.mde, NULL) != 0) {
(void) del_key_name(sp, newnp, ep);
return (mdstealerror(ep, &params.mde));
}
/* clear cache */
meta_invalidate_name(oldnp);
meta_invalidate_name(newnp);
if (invalidate_submirrors(sp, mirnp, ep) != 0) {
meta_invalidate_name(mirnp);
return (-1);
}
meta_invalidate_name(mirnp);
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: device %s is replaced with %s\n"),
mirnp->cname, oldnp->cname, newnp->cname);
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* enable mirror component
*/
int
meta_mirror_enable(
mdsetname_t *sp,
mdname_t *mirnp,
mdname_t *compnp,
mdcmdopts_t options,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
replace_params_t params;
diskaddr_t size, label, start_blk;
md_dev64_t fs_dev;
md_set_desc *sd;
int ret;
/* should have same set */
assert(sp != NULL);
assert(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev)));
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* get the file_system dev binding */
if (meta_getdev(sp, compnp, ep) != 0)
return (-1);
fs_dev = compnp->dev;
/* get the mirror unit (fill in compnp->dev with metadb version) */
meta_invalidate_name(mirnp);
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
if (! metaismeta(submirnp))
continue;
meta_invalidate_name(submirnp);
if (meta_get_unit(sp, submirnp, ep) == NULL)
return (-1);
}
/* the metadb device binding is now established */
if (compnp->dev == NODEV64)
return (mdsyserror(ep, ENODEV, compnp->cname));
/*
* check for the case where the dev_t has changed between the
* filesystem and the metadb. This is called a rebind, and
* is handled by meta_mirror_replace.
*/
if (fs_dev != compnp->dev) {
/* establish file system binding with invalid start/end */
compnp->dev = fs_dev;
compnp->start_blk = -1;
compnp->end_blk = -1;
return (meta_mirror_replace(sp, mirnp,
compnp, compnp, options, ep));
}
/* setup mirror info */
(void) memset(&params, 0, sizeof (params));
params.mnum = meta_getminor(mirnp->dev);
MD_SETDRIVERNAME(&params, MD_MIRROR, sp->setno);
params.cmd = ENABLE_COMP;
/* check it out */
if (meta_check_component(sp, compnp, 0, ep) != 0) {
if (! mdisuseerror(ep, MDE_ALREADY))
return (-1);
mdclrerror(ep);
}
if ((size = metagetsize(compnp, ep)) == MD_DISKADDR_ERROR)
return (-1);
if ((label = metagetlabel(compnp, ep)) == MD_DISKADDR_ERROR)
return (-1);
if ((start_blk = metagetstart(sp, compnp, ep)) == MD_DISKADDR_ERROR)
return (-1);
if (start_blk >= size) {
(void) mdsyserror(ep, ENOSPC, compnp->cname);
return (-1);
}
/*
* In a MN set, the master always executes the replace command first.
* Before the master executes the IOC_REPLACE ioctl, in non-DRYRUN mode
* the master sends a message to all nodes to suspend writes to
* this mirror. Then the master executes the IOC_REPLACE ioctl
* which resumes writes to this mirror from the master node.
* As each slave executes the replace command, each slave will
* call the IOC_REPLACE ioctl which will resume writes to this mirror
* from that slave node.
*/
if (! metaislocalset(sp)) {
if ((sd = metaget_setdesc(sp, ep)) == NULL)
return (-1);
if ((MD_MNSET_DESC(sd)) && (options & MDCMD_DOIT) &&
sd->sd_mn_am_i_master)
if (meta_mn_send_suspend_writes(
meta_getminor(mirnp->dev), ep) != 0)
return (-1);
}
/* enable component */
params.old_dev = compnp->dev;
params.new_dev = compnp->dev;
params.start_blk = start_blk;
params.has_label = ((label > 0) ? 1 : 0);
params.number_blks = size;
/* Is this just a dryrun ? */
if ((options & MDCMD_DOIT) == 0) {
params.options |= MDIOCTL_DRYRUN;
}
if (metaioctl(MD_IOCREPLACE, &params, &params.mde, NULL) != 0)
return (mdstealerror(ep, &params.mde));
/*
* Are we dealing with a non-local set? If so need to update the
* local namespace so that the disk record has the correct devid.
*/
if (!metaislocalset(sp)) {
ret = meta_fixdevid(sp, DEV_UPDATE|DEV_LOCAL_SET, compnp->cname,
ep);
if (ret != METADEVADM_SUCCESS) {
/*
* Failed to update the local set. Nothing to do here
* apart from report the error. The namespace is
* most likely broken and some form of remedial
* recovery is going to be required.
*/
mde_perror(ep, "");
mdclrerror(ep);
}
}
/* clear cache */
meta_invalidate_name(compnp);
if (invalidate_submirrors(sp, mirnp, ep) != 0) {
meta_invalidate_name(mirnp);
return (-1);
}
meta_invalidate_name(mirnp);
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: device %s is enabled\n"),
mirnp->cname, compnp->cname);
(void) fflush(stdout);
}
/* return success */
return (0);
}
/*
* check for dups in the mirror itself
*/
static int
check_twice(
md_mirror_t *mirrorp,
uint_t smi,
md_error_t *ep
)
{
mdname_t *mirnp = mirrorp->common.namep;
mdname_t *thisnp;
uint_t s;
thisnp = mirrorp->submirrors[smi].submirnamep;
for (s = 0; (s < smi); ++s) {
md_submirror_t *mdsp = &mirrorp->submirrors[s];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL)
continue;
if (meta_check_overlap(mirnp->cname, thisnp, 0, -1,
submirnp, 0, -1, ep) != 0) {
return (-1);
}
}
return (0);
}
/*
* check mirror
*/
int
meta_check_mirror(
mdsetname_t *sp,
md_mirror_t *mirrorp,
mdcmdopts_t options,
md_error_t *ep
)
{
mdname_t *mirnp = mirrorp->common.namep;
int force = ((options & MDCMD_FORCE) ? 1 : 0);
int doit = ((options & MDCMD_DOIT) ? 1 : 0);
uint_t nsm = 0;
uint_t smi;
/* check submirrors */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL)
continue;
++nsm;
}
if (nsm < 1) {
return (mdmderror(ep, MDE_BAD_MIRROR,
meta_getminor(mirnp->dev), mirnp->cname));
}
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
diskaddr_t size;
/* skip unused submirrors */
if (submirnp == NULL) {
if (mdsp->state != SMS_UNUSED) {
return (mdmderror(ep, MDE_BAD_MIRROR,
meta_getminor(mirnp->dev), mirnp->cname));
}
continue;
}
/* check submirror */
if (doit) {
if (meta_check_submirror(sp, submirnp, NULL, force,
ep) != 0)
return (-1);
if ((size = metagetsize(submirnp, ep)) ==
MD_DISKADDR_ERROR) {
return (-1);
} else if (size == 0) {
return (mdsyserror(ep, ENOSPC,
submirnp->cname));
}
}
/* check this mirror too */
if (check_twice(mirrorp, smi, ep) != 0)
return (-1);
}
/* check read option */
switch (mirrorp->read_option) {
case RD_LOAD_BAL:
case RD_GEOMETRY:
case RD_FIRST:
break;
default:
return (mderror(ep, MDE_BAD_RD_OPT, mirnp->cname));
}
/* check write option */
switch (mirrorp->write_option) {
case WR_PARALLEL:
case WR_SERIAL:
break;
default:
return (mderror(ep, MDE_BAD_WR_OPT, mirnp->cname));
}
/* check pass number */
if ((mirrorp->pass_num < 0) || (mirrorp->pass_num > MD_PASS_MAX))
return (mderror(ep, MDE_BAD_PASS_NUM, mirnp->cname));
/* return success */
return (0);
}
/*
* setup mirror geometry
*/
static int
mirror_geom(
md_mirror_t *mirrorp,
mm_unit_t *mm,
md_error_t *ep
)
{
uint_t write_reinstruct = 0;
uint_t read_reinstruct = 0;
uint_t round_cyl = 1;
mdname_t *smnp = NULL;
uint_t smi;
mdgeom_t *geomp;
/* get worst reinstructs */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
if (submirnp == NULL)
continue;
if ((geomp = metagetgeom(submirnp, ep)) == NULL)
return (-1);
if (geomp->write_reinstruct > write_reinstruct)
write_reinstruct = geomp->write_reinstruct;
if (geomp->read_reinstruct > read_reinstruct)
read_reinstruct = geomp->read_reinstruct;
if (smnp == NULL)
smnp = submirnp;
}
/* setup geometry from first submirror */
assert(smnp != NULL);
if ((geomp = metagetgeom(smnp, ep)) == NULL)
return (-1);
if (meta_setup_geom((md_unit_t *)mm, mirrorp->common.namep, geomp,
write_reinstruct, read_reinstruct, round_cyl, ep) != 0)
return (-1);
/* return success */
return (0);
}
/*
* create mirror
*/
int
meta_create_mirror(
mdsetname_t *sp,
md_mirror_t *mirrorp,
mdcmdopts_t options,
md_error_t *ep
)
{
mdname_t *mirnp = mirrorp->common.namep;
mm_unit_t *mm;
diskaddr_t submir_size = MD_DISKADDR_ERROR;
ushort_t nsm = 0;
uint_t smi;
mdnamelist_t *keynlp = NULL;
md_set_params_t set_params;
int rval = -1;
md_timeval32_t creation_time;
int create_flag = MD_CRO_32BIT;
/* validate mirror */
if (meta_check_mirror(sp, mirrorp, options, ep) != 0)
return (-1);
/* allocate mirror unit */
mm = Zalloc(sizeof (*mm));
if (meta_gettimeofday(&creation_time) == -1)
return (mdsyserror(ep, errno, NULL));
/* do submirrors */
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
mm_submirror_t *mmsp = &mm->un_sm[smi];
diskaddr_t size;
/* skip unused submirrors */
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
++nsm;
/* get size */
if ((size = metagetsize(submirnp, ep)) == MD_DISKADDR_ERROR)
goto out;
assert(size > 0);
/* adjust for smallest submirror */
if (submir_size == MD_DISKADDR_ERROR) {
submir_size = size;
} else if (size < submir_size) {
submir_size = size;
}
if (options & MDCMD_DOIT) {
/* store name in namespace */
if (add_key_name(sp, submirnp, &keynlp, ep) != 0)
goto out;
}
/* setup submirror */
mmsp->sm_key = submirnp->key;
mmsp->sm_dev = submirnp->dev;
mmsp->sm_state = SMS_RUNNING;
mmsp->sm_timestamp = creation_time;
}
/* setup unit */
mm->c.un_type = MD_METAMIRROR;
MD_SID(mm) = meta_getminor(mirnp->dev);
mm->c.un_actual_tb = submir_size;
mm->c.un_size = offsetof(mm_unit_t, un_smic);
mm->un_nsm = nsm;
mm->un_read_option = mirrorp->read_option;
mm->un_write_option = mirrorp->write_option;
mm->un_pass_num = mirrorp->pass_num;
if (mirror_geom(mirrorp, mm, ep) != 0)
goto out;
/* fill in the size of the mirror */
if (options & MDCMD_UPDATE) {
mirrorp->common.size = mm->c.un_total_blocks;
}
/* if we're not doing anything, return success */
if (! (options & MDCMD_DOIT)) {
rval = 0; /* success */
goto out;
}
/* create mirror */
(void) memset(&set_params, 0, sizeof (set_params));
/* did the user tell us to generate a large device? */
create_flag = meta_check_devicesize(mm->c.un_total_blocks);
if (create_flag == MD_CRO_64BIT) {
mm->c.un_revision = MD_64BIT_META_DEV;
set_params.options = MD_CRO_64BIT;
} else {
mm->c.un_revision = MD_32BIT_META_DEV;
set_params.options = MD_CRO_32BIT;
}
set_params.mnum = MD_SID(mm);
set_params.size = mm->c.un_size;
set_params.mdp = (uintptr_t)mm;
MD_SETDRIVERNAME(&set_params, MD_MIRROR, MD_MIN2SET(set_params.mnum));
if (metaioctl(MD_IOCSET, &set_params, &set_params.mde,
mirnp->cname) != 0) {
(void) mdstealerror(ep, &set_params.mde);
goto out;
}
rval = 0; /* success */
/* cleanup, return success */
out:
Free(mm);
if (rval != 0) {
(void) del_key_names(sp, keynlp, NULL);
}
metafreenamelist(keynlp);
if ((rval == 0) && (options & MDCMD_DOIT)) {
if (invalidate_submirrors(sp, mirnp, ep) != 0)
rval = -1;
meta_invalidate_name(mirnp);
}
return (rval);
}
/*
* initialize mirror
* NOTE: this functions is metainit(1m)'s command line parser!
*/
int
meta_init_mirror(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
char *uname = argv[0];
mdname_t *mirnp = NULL;
int old_optind;
int c;
md_mirror_t *mirrorp = NULL;
uint_t smi;
int rval = -1;
/* get mirror name */
assert(argc > 0);
if (argc < 1)
goto syntax;
if ((mirnp = metaname(spp, uname, ep)) == NULL)
goto out;
assert(*spp != NULL);
uname = mirnp->cname;
if (metachkmeta(mirnp, ep) != 0)
goto out;
if (!(options & MDCMD_NOLOCK)) {
/* grab set lock */
if (meta_lock(*spp, TRUE, ep) != 0)
goto out;
if (meta_check_ownership(*spp, ep) != 0)
goto out;
}
/* see if it exists already */
if (metagetmiscname(mirnp, ep) != NULL) {
(void) mdmderror(ep, MDE_UNIT_ALREADY_SETUP,
meta_getminor(mirnp->dev), uname);
goto out;
} else if (! mdismderror(ep, MDE_UNIT_NOT_SETUP)) {
goto out;
} else {
mdclrerror(ep);
}
--argc, ++argv;
/* grab -m */
if ((argc < 1) || (strcmp(argv[0], "-m") != 0))
goto syntax;
--argc, ++argv;
if (argc == 0)
goto syntax;
/* parse general options */
optind = 0;
opterr = 0;
if (getopt(argc, argv, "") != -1)
goto options;
/* allocate mirror */
mirrorp = Zalloc(sizeof (*mirrorp));
/* setup common */
mirrorp->common.namep = mirnp;
mirrorp->common.type = MD_METAMIRROR;
/* parse submirrors */
for (smi = 0; ((argc > 0) && (argv[0][0] != '-') &&
(! isdigit(argv[0][0]))); ++smi) {
md_submirror_t *mdsm = &mirrorp->submirrors[smi];
mdname_t *submirnamep;
/* check for room */
if (smi >= NMIRROR) {
(void) mdmderror(ep, MDE_MIRROR_FULL,
meta_getminor(mirnp->dev), uname);
goto out;
}
/* parse submirror name */
if ((submirnamep = metaname(spp, argv[0], ep)) == NULL)
goto out;
mdsm->submirnamep = submirnamep;
--argc, ++argv;
}
if (smi == 0) {
(void) mdmderror(ep, MDE_NSUBMIRS, meta_getminor(mirnp->dev),
uname);
goto out;
}
/* dangerous n-way mirror creation */
if ((smi > 1) && (options & MDCMD_PRINT)) {
md_eprintf(dgettext(TEXT_DOMAIN,
"%s: WARNING: This form of metainit is not recommended.\n"
"The submirrors may not have the same data.\n"
"Please see ERRORS in metainit(1M) for additional information.\n"),
uname);
}
/* parse mirror options */
mirrorp->read_option = RD_LOAD_BAL;
mirrorp->write_option = WR_PARALLEL;
mirrorp->pass_num = MD_PASS_DEFAULT;
old_optind = optind = 0;
opterr = 0;
while ((c = getopt(argc, argv, "grS")) != -1) {
switch (c) {
case 'g':
if (mirrorp->read_option != RD_LOAD_BAL) {
(void) mderror(ep, MDE_BAD_RD_OPT, uname);
goto out;
}
mirrorp->read_option = RD_GEOMETRY;
break;
case 'r':
if (mirrorp->read_option != RD_LOAD_BAL) {
(void) mderror(ep, MDE_BAD_RD_OPT, uname);
goto out;
}
mirrorp->read_option = RD_FIRST;
break;
case 'S':
if (mirrorp->write_option != WR_PARALLEL) {
(void) mderror(ep, MDE_BAD_WR_OPT, uname);
goto out;
}
mirrorp->write_option = WR_SERIAL;
break;
default:
argc -= old_optind;
argv += old_optind;
goto options;
}
old_optind = optind;
}
argc -= optind;
argv += optind;
/* parse pass number */
if ((argc > 0) && (isdigit(argv[0][0]))) {
if (name_to_pass_num(uname, argv[0],
&mirrorp->pass_num, ep) != 0) {
goto out;
}
--argc, ++argv;
}
/* we should be at the end */
if (argc != 0)
goto syntax;
/* create mirror */
if (meta_create_mirror(*spp, mirrorp, options, ep) != 0)
goto out;
rval = 0; /* success */
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: Mirror is setup\n"),
uname);
(void) fflush(stdout);
}
goto out;
/* syntax error */
syntax:
rval = meta_cook_syntax(ep, MDE_SYNTAX, uname, argc, argv);
goto out;
/* options error */
options:
rval = meta_cook_syntax(ep, MDE_OPTION, uname, argc, argv);
goto out;
/* cleanup, return error */
out:
if (mirrorp != NULL)
meta_free_mirror(mirrorp);
return (rval);
}
/*
* reset mirrors
*/
int
meta_mirror_reset(
mdsetname_t *sp,
mdname_t *mirnp,
mdcmdopts_t options,
md_error_t *ep
)
{
md_mirror_t *mirrorp;
uint_t smi;
int rval = -1;
/* should have same set */
assert(sp != NULL);
assert((mirnp == NULL) ||
(sp->setno == MD_MIN2SET(meta_getminor(mirnp->dev))));
/* reset all mirrors */
if (mirnp == NULL) {
mdnamelist_t *mirrornlp = NULL;
mdnamelist_t *p;
/* for each mirror */
rval = 0;
if (meta_get_mirror_names(sp, &mirrornlp, 0, ep) < 0)
return (-1);
for (p = mirrornlp; (p != NULL); p = p->next) {
/* reset mirror */
mirnp = p->namep;
/*
* If this is a multi-node set, we send a series
* of individual metaclear commands.
*/
if (meta_is_mn_set(sp, ep)) {
if (meta_mn_send_metaclear_command(sp,
mirnp->cname, options, 0, ep) != 0) {
rval = -1;
break;
}
} else {
if (meta_mirror_reset(sp, mirnp, options,
ep) != 0) {
rval = -1;
break;
}
}
}
/* cleanup return success */
metafreenamelist(mirrornlp);
return (rval);
}
/* check name */
if (metachkmeta(mirnp, ep) != 0)
return (-1);
/* get unit structure */
if ((mirrorp = meta_get_mirror(sp, mirnp, ep)) == NULL)
return (-1);
/* make sure nobody owns us */
if (MD_HAS_PARENT(mirrorp->common.parent)) {
return (mdmderror(ep, MDE_IN_USE, meta_getminor(mirnp->dev),
mirnp->cname));
}
/* clear subdevices cache */
if (invalidate_submirrors(sp, mirnp, ep) != 0)
return (-1);
/* clear metadevice */
if (meta_reset(sp, mirnp, options, ep) != 0)
goto out;
rval = 0; /* success */
/* let em know */
if (options & MDCMD_PRINT) {
(void) printf(dgettext(TEXT_DOMAIN,
"%s: Mirror is cleared\n"), mirnp->cname);
(void) fflush(stdout);
}
/* clear subdevices */
if (! (options & MDCMD_RECURSE))
goto out;
for (smi = 0; (smi < NMIRROR); ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
mdname_t *submirnp = mdsp->submirnamep;
/* skip unused submirrors */
if (submirnp == NULL) {
assert(mdsp->state == SMS_UNUSED);
continue;
}
/* make sure we have a metadevice */
if (! metaismeta(submirnp))
continue;
/* clear submirror */
if (meta_reset_by_name(sp, submirnp, options, ep) != 0)
rval = -1;
}
/* cleanup, return success */
out:
meta_invalidate_name(mirnp);
return (rval);
}
/*
* reports TRUE if any mirror component is in error
*/
int
meta_mirror_anycomp_is_err(mdsetname_t *sp, mdnamelist_t *mirror_names)
{
mdnamelist_t *nlp;
md_error_t status = mdnullerror;
md_error_t *ep = &status;
int any_errs = FALSE;
for (nlp = mirror_names; nlp; nlp = nlp->next) {
md_mirror_t *mirrorp;
int smi;
if ((mirrorp = meta_get_mirror(sp, nlp->namep, ep)) == NULL) {
any_errs |= TRUE;
goto out;
}
for (smi = 0; smi < NMIRROR; ++smi) {
md_submirror_t *mdsp = &mirrorp->submirrors[smi];
if (mdsp->state &
(SMS_COMP_ERRED|SMS_ATTACHED|SMS_OFFLINE)) {
any_errs |= TRUE;
goto out;
}
}
}
out:
if (!mdisok(ep))
mdclrerror(ep);
return (any_errs);
}