/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2007 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 #include #include #include #include /* * 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 * # 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; char *temproot; 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); /* * We need to take the canonical name here otherwise the call to * metaname will add a bad entry to the drivelistp cache and * things will get nasty later on. * However we also need to trap the case where we have a logical * device name and meta_canonicalize returns NULL. */ temproot = meta_canonicalize(sp, curroot); if (temproot != NULL) { curroot = Strdup(temproot); Free(temproot); } /* * 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. * * Since curroot should be a complete path, we only * need to check whether the device is a logical device. * The metaname below returns NULL if curroot is not a logical * device. */ if ((rootnp = metaname(&sp, curroot, LOGICAL_DEVICE, ep)) == NULL) return (0); /* * If we're here, the curroot is a mounted on a logical device. * Make sure this mirror is not on the root logical device. */ if (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) == 0) { rval = 0; goto out; } } if (options & PRINT_FN) { if ((mirrorp->common.revision & MD_FN_META_DEV) == 0) { 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->rname) == 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 "), 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) == 0) { 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; } } /* * check for the -D option. If -D and the name is * a descriptive name, get the dev for relocation information * printout. If not a descriptive name, don't print this * information out but you need to go down to the subdevice * level and print there if appropriate. */ if (options & PRINT_FN) { if ((mirrorp->common.revision & MD_FN_META_DEV) == 0) { 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; md_stripe_t *stripep; /* skip unused submirrors */ if (submirnamep == NULL) { assert(mdsp->state == SMS_UNUSED); continue; } if (options & PRINT_FN) { /* get unit structure */ if ((stripep = meta_get_stripe_common(sp, submirnamep, ((options & PRINT_FAST) ? 1 : 0), ep)) == NULL) goto out; if ((stripep->common.revision & MD_FN_META_DEV) == 0) 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(¶ms, 0, sizeof (params)); params.mnum = meta_getminor(mirnp->dev); MD_SETDRIVERNAME(¶ms, 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, ¶ms, ¶ms.mde, NULL) != 0) { (void) del_key_name(sp, newnp, ep); return (mdstealerror(ep, ¶ms.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(¶ms, 0, sizeof (params)); params.mnum = meta_getminor(mirnp->dev); MD_SETDRIVERNAME(¶ms, 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, ¶ms, ¶ms.mde, NULL) != 0) return (mdstealerror(ep, ¶ms.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_64BIT_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, META_DEVICE, 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], META_DEVICE, 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); }