/* * resync raid */ int meta_raid_resync( mdsetname_t *sp, mdname_t *raidnp, daddr_t size, md_error_t *ep ) { char *miscname; md_resync_ioctl_t ri; /* should have a set */ assert(sp != NULL); assert(sp->setno == MD_MIN2SET(meta_getminor(raidnp->dev))); /* make sure we have a raid */ if ((miscname = metagetmiscname(raidnp, ep)) == NULL) return (-1); if (strcmp(miscname, MD_RAID) != 0) { return (mdmderror(ep, MDE_NOT_RAID, meta_getminor(raidnp->dev), raidnp->cname)); } /* start resync */ (void) memset(&ri, 0, sizeof (ri)); MD_SETDRIVERNAME(&ri, MD_RAID, sp->setno); ri.ri_mnum = meta_getminor(raidnp->dev); ri.ri_copysize = size; if (metaioctl(MD_IOCSETSYNC, &ri, &ri.mde, raidnp->cname) != 0) return (mdstealerror(ep, &ri.mde)); /* return success */ return (0); }
/* * Return the current root filesystem block device name. This is only valid * when root is either a slice, a stripe or a mirror. */ mdname_t * meta_get_current_root_dev( mdsetname_t *sp, md_error_t *ep ) { md_stripe_t *stripep; md_mirror_t *mirrorp; md_row_t *rp; md_comp_t *cp; mdname_t *rootnp; void *curroot; char *miscname; int smi; if ((curroot = meta_get_current_root(ep)) == NULL) return (NULL); if ((rootnp = metaname(&sp, curroot, UNKNOWN, ep)) == NULL) return (NULL); if (metaismeta(rootnp)) { if ((miscname = metagetmiscname(rootnp, ep)) == NULL) return (NULL); if ((strcmp(miscname, MD_MIRROR) == 0) && ((mirrorp = meta_get_mirror(sp, rootnp, ep)) != NULL)) { for (smi = 0; smi < NMIRROR; smi++) { md_submirror_t *mdsp = &mirrorp->submirrors[smi]; rootnp = mdsp->submirnamep; /* skip unused submirrors */ if (rootnp == NULL) { assert(mdsp->state == SMS_UNUSED); continue; } if ((miscname = metagetmiscname(rootnp, ep)) == NULL) { (void) mdmderror(ep, MDE_UNKNOWN_TYPE, meta_getminor(rootnp->dev), rootnp->cname); return (NULL); } break; } } if ((strcmp(miscname, MD_STRIPE) == 0) && ((stripep = meta_get_stripe(sp, rootnp, ep)) != NULL)) { rp = &stripep->rows.rows_val[0]; cp = &rp->comps.comps_val[0]; if (metachkcomp(cp->compnamep, ep) == 0) return (cp->compnamep); } /* Root is not a single stripe metadevice */ (void) mddeverror(ep, MDE_INV_ROOT, rootnp->dev, rootnp->cname); return (NULL); } else return (rootnp); }
int md_probe_ioctl(mdnamelist_t *nlp, int ndevs, char *drvname, boolean_e verbose) { mdnamelist_t *p; mdname_t *np; md_probedev_t probe_ioc, *iocp; int i, retval = 0; /* * Allocate space for all the metadevices and fill in * the minor numbers. */ memset(&probe_ioc, 0, sizeof (probe_ioc)); iocp = &probe_ioc; if ((iocp->mnum_list = (uintptr_t)calloc(ndevs, sizeof (minor_t))) == 0) { monitord_print(0, "md_probe_ioctl: calloc"); return (-1); } (void) strcpy(iocp->test_name, MD_PROBE_OPEN_T); MD_SETDRIVERNAME(&probe_ioc, drvname, sp->setno); if (verbose == True) { monitord_print(6, "\n\nmd_probe_ioctl: %s: %s\n", (strcmp(sp->setname, MD_LOCAL_NAME) == 0) ? gettext("local_set") : sp->setname, iocp->md_driver.md_drivername); } iocp->nmdevs = ndevs; if (verbose == True) monitord_print(6, "...ndevs 0x%x\n", ndevs); for (p = nlp, i = 0; p; p = p->next, i++) { np = p->namep; ((minor_t *)(uintptr_t)iocp->mnum_list)[i] = meta_getminor(np->dev); if (verbose == True) monitord_print(6, "...%s 0x%lx\n", np->cname, ((minor_t *)(uintptr_t)iocp->mnum_list)[i]); } if (issue_ioctl == True) { if (metaioctl(MD_IOCPROBE_DEV, iocp, &(iocp->mde), NULL) != 0) retval = -1; } return (retval); }
/* * grow generic device */ int meta_concat_generic( mdsetname_t *sp, mdname_t *namep, u_longlong_t big_or_little, md_error_t *ep ) { md_grow_params_t mgp; char *miscname; /* should have a set */ assert(sp != NULL); assert(sp->setno == MD_MIN2SET(meta_getminor(namep->dev))); /* get type */ if ((miscname = metagetmiscname(namep, ep)) == NULL) return (-1); /* grow device */ (void) memset(&mgp, 0, sizeof (mgp)); if (big_or_little & MD_64BIT_META_DEV) mgp.options = MD_CRO_64BIT; else mgp.options = MD_CRO_32BIT; mgp.mnum = meta_getminor(namep->dev); MD_SETDRIVERNAME(&mgp, miscname, sp->setno); if (metaioctl(MD_IOCGROW, &mgp, &mgp.mde, namep->cname) != 0) return (mdstealerror(ep, &mgp.mde)); /* clear cache */ meta_invalidate_name(namep); /* return success */ return (0); }
/* * grow the parent of a device */ int meta_concat_parent( mdsetname_t *sp, mdname_t *childnp, md_error_t *ep ) { md_common_t *mdp; mdname_t *parentnp; md_unit_t *mup; /* should have a set */ assert(sp != NULL); assert(sp->setno == MD_MIN2SET(meta_getminor(childnp->dev))); /* get parent */ if ((mdp = meta_get_unit(sp, childnp, ep)) == NULL) return (-1); if (! MD_HAS_PARENT(mdp->parent)) return (0); if (mdp->parent == MD_MULTI_PARENT) return (0); /* single parent */ if ((parentnp = metamnumname(&sp, mdp->parent, 0, ep)) == NULL) return (-1); /* don't grow non-metadevices or soft partitions */ if (! metaismeta(parentnp) || meta_sp_issp(sp, parentnp, ep) == 0) return (0); if ((mup = meta_get_mdunit(sp, childnp, ep)) == NULL) return (-1); /* grow parent */ if (meta_concat_generic(sp, parentnp, mup->c.un_revision, ep) != 0) return (-1); /* recursively check for parents of parents */ return (meta_concat_parent(sp, parentnp, ep)); }
char * obj2devname(uint32_t tag, set_t setno, md_dev64_t dev) { char *setname; char *uname; char name[MD_MAX_CTDLEN]; mdsetname_t *sp; md_error_t status = mdnullerror; md_set_record *md_sr; minor_t mnum = meta_getminor(dev); int rtn = 0; setname = NULL; if ((setno != MD_SET_BAD) && ((sp = metasetnosetname(setno, &status)) != NULL)) { setname = sp->setname; } name[0] = '\0'; switch (tag) { case SVM_TAG_HS: case SVM_TAG_METADEVICE: case SVM_TAG_MIRROR: case SVM_TAG_RAID5: case SVM_TAG_STRIPE: case SVM_TAG_TRANS: uname = get_mdname(sp, mnum); if (uname == NULL) return (NULL); (void) strcpy(name, uname); break; case SVM_TAG_HSP: uname = get_hspname(sp, mnum); if (uname == NULL) return (NULL); (void) strcpy(name, uname); break; case SVM_TAG_DRIVE: (void) sprintf(name, "drive"); break; case SVM_TAG_HOST: md_sr = NULL; if (setname != NULL) { md_sr = getsetbyname(setname, &status); } if ((md_sr != NULL) && (md_sr->sr_nodes[mnum] != NULL)) { /* * Get the host data from the node array. */ rtn = snprintf(name, sizeof (name), "%s", md_sr->sr_nodes[mnum]); } if ((name[0] == '\0') || (rtn >= sizeof (name))) { (void) sprintf(name, "host"); rtn = 0; } break; case SVM_TAG_SET: if (setname == NULL) { (void) sprintf(name, "diskset"); } else { rtn = snprintf(name, sizeof (name), "%s", setname); } break; default: if ((setname = get_devname(setno, dev)) != NULL) { rtn = snprintf(name, sizeof (name), "%s", setname); } break; } mdclrerror(&status); /* Check if we got any rubbish for any of the snprintf's */ if ((name[0] == '\0') || (rtn >= sizeof (name))) { return (NULL); } return (strdup(name)); }
/* * FUNCTION: meta_repartition_drive() * INPUT: sp - the set name for the device to check * dnp - the name of the drive to partition * options - options (see NOTES) * OUTPUT: vtocp - pointer to an mdvtoc_t structure in which * to return the new VTOC to the caller * ep - pointer to an md_error_t structure in which * to return errors to the caller * RETURNS: int - 0 - drive was or can be repartitioned * -1 - drive could not or should not be * repartitioned * PURPOSE: Repartition a disk for use in a disk set or in order * to create soft partitions on it. Alternatively, * return the VTOC that the disk would have if it were * repartitioned without actually repartitioning it. * * NOTES: * * This routine will repartition a drive to make it suitable for * inclusion in a diskset. Specifically, it will create a * proposed VTOC that specifies a replica slice that begins at the * first valid lba, is large enough to hold a label and a metadb * replica, does not overlap any other slices, and is unmountable. * If the current replica slice already satisfies those criteria, * the routine will neither create a proposed VTOC nor repartition * the drive unless the MD_REPART_FORCE flag is passed into the * routine in the options argument. If the routine does create a * proposed VTOC, it will return the proposed VTOC in *vtocp if * vtocp isn't NULL. * * The slice to be used as the replica slice is determined by the * function meta_replicaslice(). * * If the replica slice does not satisfy the above criteria or the * MD_REPART_FORCE flag is set, the proposed VTOC will specify a * replica slice that satisfies the above criteria, a slice zero * that contains the remaining space on the disk, and no other * slices. If that repartitioning would cause the replica slice * to move or shrink, and the MD_REPART_LEAVE_REP option is set, * the routine will return -1 without creating or returning a * proposed vtoc, and without repartitioning the disk. Otherwise * the routine will repartition the disk unless the * MD_REPART_DONT_LABEL flag is set in the options argument. * * If the MD_REPART_DONT_LABEL flag is set in the options argument, * but the routine would otherwise repartition the drive, the * routine won't repartition the drive, but will create a proposed * VTOC that satisfies the criteria defined above and return it * it in *vtocp if vtocp isn't NULL, The MD_REPART_DONT_LABEL * option allows calling routines to determine what the contents of * the drive's VTOC would be if the drive were repartitioned without * actually repartitioning the drive. */ int meta_repartition_drive( mdsetname_t *sp, mddrivename_t *dnp, int options, mdvtoc_t *vtocp, md_error_t *ep ) { uint_t replicaslice; diskaddr_t first_lba, last_lba; int round_sizes = 1; unsigned long long cylsize; unsigned long long drvsize; int i; mdgeom_t *mdgp; mdvtoc_t *mdvp; mdvtoc_t proposed_vtoc; uint_t reservedcyl; ushort_t resflag; mdname_t *resnp; unsigned long long ressize; md_set_desc *sd; daddr_t dbsize; diskaddr_t replica_start; diskaddr_t replica_size; diskaddr_t replica_end; diskaddr_t data_start; diskaddr_t data_size; if (meta_replicaslice(dnp, &replicaslice, ep) != 0) { return (-1); } /* Don't round for EFI disks */ if (replicaslice == MD_SLICE6) round_sizes = 0; /* * We took as argument a drive name pointer, but we need a * slice name pointer to retrieve vtoc information. So get * the name pointer for slice zero first, then use it to get * the vtoc info for the disk. */ if ((resnp = metaslicename(dnp, MD_SLICE0, ep)) == NULL) return (-1); if ((mdvp = metagetvtoc(resnp, FALSE, NULL, ep)) == NULL) return (-1); /* * Determine the metadb size. */ dbsize = MD_DBSIZE; if (!metaislocalset(sp)) { if ((sd = metaget_setdesc(sp, ep)) == NULL) return (-1); if (MD_MNSET_DESC(sd)) dbsize = MD_MN_DBSIZE; } /* If we've got an efi disk, we better have lba info */ first_lba = mdvp->first_lba; last_lba = mdvp->last_lba; ASSERT((round_sizes != 0) || (last_lba > 0)); /* * At this point, ressize is used as a minimum value. Later * it will be rounded up to a cylinder boundary if * appropriate. ressize is in units of disk sectors. */ ressize = dbsize + VTOC_SIZE; resflag = V_UNMNT; /* * If we're forcing the repartition, we can skip the replica * slice and overlap tests. */ if (options & MD_REPART_FORCE) { goto do_repartition; } /* * Replica slice tests: it must begin at first_lba, be long * enough, have the right flags, and not overlap any other * slices. If any of these conditions is violated, we need to * repartition the disk. */ if (mdvp->parts[replicaslice].start != first_lba) { goto do_repartition; } if (mdvp->parts[replicaslice].size < ressize) { goto do_repartition; } if (mdvp->parts[replicaslice].flag != resflag) { goto do_repartition; } /* * Check for overlap: this test should use the actual size of * the replica slice, as contained in the vtoc, and NOT the * minimum size calculated above. */ replica_end = first_lba + mdvp->parts[replicaslice].size; for (i = 0; i < mdvp->nparts; i++) { if (i != replicaslice) { if ((mdvp->parts[i].size > 0) && (mdvp->parts[i].start < replica_end)) { goto do_repartition; } } } /* * If we passed the above tests, then the disk is already * partitioned appropriately, and we're not being told to * force a change. */ return (0); do_repartition: /* Retrieve disk geometry info and round to cylinder sizes */ if (round_sizes != 0) { if ((mdgp = metagetgeom(resnp, ep)) == NULL) return (-1); /* * Both cylsize and drvsize are in units of disk * sectors. * * The intended results are of type unsigned long * long. Since each operand of the first * multiplication is of type unsigned int, we risk * overflow by multiplying and then converting the * result. Therefore we explicitly cast (at least) * one of the operands, forcing conversion BEFORE * multiplication, and avoiding overflow. The second * assignment is OK, since one of the operands is * already of the desired type. */ cylsize = ((unsigned long long)mdgp->nhead) * mdgp->nsect; drvsize = cylsize * mdgp->ncyl; /* * How many cylinders must we reserve for the replica * slice to ensure that it meets the previously * calculated minimum size? */ reservedcyl = (ressize + cylsize - 1) / cylsize; ressize = reservedcyl * cylsize; } else { drvsize = last_lba - first_lba; } /* Would this require a forbidden change? */ if (options & MD_REPART_LEAVE_REP) { if ((mdvp->parts[replicaslice].start != first_lba) || (mdvp->parts[replicaslice].size < ressize)) { return (mddeverror(ep, MDE_REPART_REPLICA, resnp->dev, NULL)); } } /* * It seems unlikely that someone would pass us too small a * disk, but it's still worth checking for... */ if (((round_sizes != 0) && (reservedcyl >= (int)mdgp->ncyl)) || ((round_sizes == 0) && (ressize + first_lba >= last_lba))) { return (mdmddberror(ep, MDE_DB_TOOSMALL, meta_getminor(resnp->dev), sp->setno, 0, NULL)); } replica_start = first_lba; replica_size = ressize; data_start = first_lba + ressize; data_size = drvsize - ressize; /* * Create the proposed VTOC. First copy the current VTOC * into the proposed VTOC to duplicate the values that don't * need to change. Then change the partition table and set * the flag value for the replica slice to resflag to reserve it * for metadata. */ proposed_vtoc = *mdvp; /* We need at least replicaslice partitions in the proposed vtoc */ if (replicaslice >= proposed_vtoc.nparts) { proposed_vtoc.nparts = replicaslice + 1; } for (i = 0; i < proposed_vtoc.nparts; i++) { /* don't change the reserved partition of an EFI device */ if (proposed_vtoc.parts[i].tag == V_RESERVED) data_size = proposed_vtoc.parts[i].start - data_start; else (void) memset(&proposed_vtoc.parts[i], '\0', sizeof (proposed_vtoc.parts[i])); } proposed_vtoc.parts[MD_SLICE0].start = data_start; proposed_vtoc.parts[MD_SLICE0].size = data_size; proposed_vtoc.parts[MD_SLICE0].tag = V_USR; proposed_vtoc.parts[replicaslice].start = replica_start; proposed_vtoc.parts[replicaslice].size = replica_size; proposed_vtoc.parts[replicaslice].flag = resflag; proposed_vtoc.parts[replicaslice].tag = V_USR; if (!(options & MD_REPART_DONT_LABEL)) { /* * Label the disk with the proposed VTOC. */ *mdvp = proposed_vtoc; if (metasetvtoc(resnp, ep) != 0) { return (-1); } } if (vtocp != NULL) { /* * Return the proposed VTOC. */ *vtocp = proposed_vtoc; } return (0); }