/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* get dev_t list
*/
#include <meta.h>
#include <sys/mhd.h>
#include <strings.h>
/*
* private version of minor(), able to handle 64 bit and 32 bit devices.
* print a warning out in case a 32 bit dev is specified.
*/
minor_t
meta_getminor(md_dev64_t dev64)
{
/* check if it's a real 64 bit dev */
if ((dev64 >> NBITSMAJOR64) > 0) {
return ((minor_t)(dev64 & MAXMIN64));
} else {
if (getenv("META_DEBUG"))
(void) printf(
"meta_getminor called with 32 bit dev: 0x%llx\n",
dev64);
return ((minor_t)(dev64 & MAXMIN32));
}
}
/*
* private version of major(), able to handle 64 bit and 32 bit devices.
* print a warning out in case a 32 bit dev is specified.
*/
major_t
meta_getmajor(md_dev64_t dev64)
{
/* check if it's a real 64 bit dev */
if ((dev64 >> NBITSMAJOR64) > 0) {
return ((major_t)((dev64 >> NBITSMINOR64) & MAXMAJ64));
} else {
if (getenv("META_DEBUG"))
(void) printf(
"meta_getmajor called with 32 bit dev: 0x%llx\n",
dev64);
return ((major_t)((dev64 >> NBITSMINOR32) & MAXMAJ32));
}
}
/*
* private version of cmpldev(), able to handle 64 bit and 32 bit devices.
*/
dev32_t
meta_cmpldev(md_dev64_t dev64)
{
minor_t minor;
major_t major;
major = (major_t)(dev64 >> NBITSMAJOR64);
if (major == 0) {
return ((dev32_t)dev64);
}
minor = (dev32_t)dev64 & MAXMIN32;
return ((major << NBITSMINOR32) | minor);
}
/*
* private version of expldev(), able to handle 64 bit and 32 bit devices.
*/
md_dev64_t
meta_expldev(md_dev64_t dev64)
{
minor_t minor;
major_t major;
major = (major_t)(dev64 >> NBITSMAJOR64);
if (major > 0) { /* a 64 bit device was given, return unchanged */
return (dev64);
}
minor = (minor_t)(dev64) & MAXMIN32;
major = ((major_t)dev64 >> NBITSMINOR32) & MAXMAJ32;
return (((md_dev64_t)major << NBITSMINOR64) | minor);
}
/*
* get underlying devices (recursively)
*/
int
meta_getdevs(
mdsetname_t *sp,
mdname_t *namep,
mdnamelist_t **nlpp,
md_error_t *ep
)
{
char *miscname;
md_dev64_t *mydevs = NULL;
md_getdevs_params_t mgd;
size_t i;
int rval = -1;
md_sys_error_t *ip;
/* must have local set */
assert(sp != NULL);
/* if no valid name then return an error */
if (namep == NULL)
return (-1);
/* just add regular devices */
if (! metaismeta(namep)) {
mdnamelist_t *p;
/*
* If the dev_t is in the array already
* then let's continue.
*/
for (p = *nlpp; (p != NULL); p = p->next) {
if (strcmp(namep->bname, p->namep->bname) == 0) {
rval = 0;
goto out;
}
}
/* add to list */
(void) metanamelist_append(nlpp, namep);
rval = 0;
goto out;
}
/* get MD misc module */
if ((miscname = metagetmiscname(namep, ep)) == NULL)
goto out;
/* get count of underlying devices */
(void) memset(&mgd, '\0', sizeof (mgd));
MD_SETDRIVERNAME(&mgd, miscname, sp->setno);
mgd.mnum = meta_getminor(namep->dev);
mgd.cnt = 0;
mgd.devs = NULL;
if (metaioctl(MD_IOCGET_DEVS, &mgd, &mgd.mde, namep->cname) != 0) {
if (mgd.mde.info.errclass == MDEC_SYS) {
ip = &mgd.mde.info.md_error_info_t_u.sys_error;
if (ip->errnum == ENODEV) {
rval = 0;
goto out;
}
}
(void) mdstealerror(ep, &mgd.mde);
goto out;
} else if (mgd.cnt <= 0) {
assert(mgd.cnt >= 0);
rval = 0;
goto out;
}
/* get underlying devices */
mydevs = Zalloc(sizeof (*mydevs) * mgd.cnt);
mgd.devs = (uintptr_t)mydevs;
if (metaioctl(MD_IOCGET_DEVS, &mgd, &mgd.mde, namep->cname) != 0) {
if (mgd.mde.info.errclass == MDEC_SYS) {
ip = &mgd.mde.info.md_error_info_t_u.sys_error;
if (ip->errnum == ENODEV) {
rval = 0;
goto out;
}
}
(void) mdstealerror(ep, &mgd.mde);
goto out;
} else if (mgd.cnt <= 0) {
assert(mgd.cnt >= 0);
rval = 0;
goto out;
}
/* recurse */
for (i = 0; (i < mgd.cnt); ++i) {
mdname_t *devnp;
if (mydevs[i] == NODEV64) {
continue;
}
if ((devnp = metadevname(&sp, mydevs[i], ep)) == NULL) {
if (mdissyserror(ep, ENOENT)) {
mdclrerror(ep);
/*
* If the device doesn't exist, it could be
* that we have a wrong dev_t/name
* combination in the namespace, so
* meta_fix_compnames try to check this
* with the unit structure and fix this.
*/
if (meta_fix_compnames(sp, namep,
mydevs[i], ep) == 0)
continue;
}
goto out;
}
if (meta_getdevs(sp, devnp, nlpp, ep) != 0)
goto out;
}
/* success */
rval = 0;
/* cleanup, return error */
out:
if (mydevs != NULL)
Free(mydevs);
return (rval);
}
/*
* get all dev_t for a set
*/
int
meta_getalldevs(
mdsetname_t *sp, /* set to look in */
mdnamelist_t **nlpp, /* returned devices */
int check_db,
md_error_t *ep
)
{
md_replicalist_t *rlp, *rp;
mdnamelist_t *nlp, *np;
mdhspnamelist_t *hspnlp, *hspp;
int rval = 0;
assert(sp != NULL);
/*
* Get a replica namelist,
* and then get all the devs within the replicas.
*/
if (check_db == TRUE) {
rlp = NULL;
if (metareplicalist(sp, MD_BASICNAME_OK, &rlp, ep) < 0)
rval = -1;
for (rp = rlp; (rp != NULL); rp = rp->rl_next) {
if (meta_getdevs(sp, rp->rl_repp->r_namep,
nlpp, ep) != 0)
rval = -1;
}
metafreereplicalist(rlp);
}
/*
* Get a stripe namelist,
* and then get all the devs within the stripes.
*/
nlp = NULL;
if (meta_get_stripe_names(sp, &nlp, 0, ep) < 0)
rval = -1;
for (np = nlp; (np != NULL); np = np->next) {
if (meta_getdevs(sp, np->namep, nlpp, ep) != 0)
rval = -1;
}
metafreenamelist(nlp);
/*
* Get a mirror namelist,
* and then get all the devs within the mirrors.
*/
nlp = NULL;
if (meta_get_mirror_names(sp, &nlp, 0, ep) < 0)
rval = -1;
for (np = nlp; (np != NULL); np = np->next) {
if (meta_getdevs(sp, np->namep, nlpp, ep) != 0)
rval = -1;
}
metafreenamelist(nlp);
/*
* Get a trans namelist,
* and then get all the devs within the trans.
*/
nlp = NULL;
if (meta_get_trans_names(sp, &nlp, 0, ep) < 0)
rval = -1;
for (np = nlp; (np != NULL); np = np->next) {
if (meta_getdevs(sp, np->namep, nlpp, ep) != 0)
rval = -1;
}
metafreenamelist(nlp);
/*
* Get a hot spare pool namelist,
* and then get all the devs within the hot spare pools.
*/
hspnlp = NULL;
if (meta_get_hsp_names(sp, &hspnlp, 0, ep) < 0)
rval = -1;
for (hspp = hspnlp; (hspp != NULL); hspp = hspp->next) {
md_hsp_t *hsp;
uint_t i;
if ((hsp = meta_get_hsp(sp, hspp->hspnamep, ep)) == NULL)
rval = -1;
else for (i = 0; (i < hsp->hotspares.hotspares_len); ++i) {
md_hs_t *hs = &hsp->hotspares.hotspares_val[i];
if (meta_getdevs(sp, hs->hsnamep, nlpp, ep) != 0)
rval = -1;
}
}
metafreehspnamelist(hspnlp);
/*
* Get a raid namelist,
* and then get all the devs within the raids.
*/
nlp = NULL;
if (meta_get_raid_names(sp, &nlp, 0, ep) < 0)
rval = -1;
for (np = nlp; (np != NULL); np = np->next) {
if (meta_getdevs(sp, np->namep, nlpp, ep) != 0)
rval = -1;
}
metafreenamelist(nlp);
/*
* Get a soft partition namelist,
* and then get all the devs within the softpartitions
*/
nlp = NULL;
if (meta_get_sp_names(sp, &nlp, 0, ep) < 0)
rval = -1;
for (np = nlp; (np != NULL); np = np->next) {
if (meta_getdevs(sp, np->namep, nlpp, ep) != 0)
rval = -1;
}
metafreenamelist(nlp);
return (rval);
}
/*
* get vtoc from a device already opened.
* returns
* 0 on success,
* -1 on error. If the error was ENOTSUP, partno will be set to
* VT_ENOTSUP if possible.
*/
int
meta_getvtoc(
int fd, /* fd for named device */
char *devname, /* name of device */
struct extvtoc *vtocbufp, /* vtoc buffer to fill */
int *partno, /* return partno here */
md_error_t *ep
)
{
int part;
(void) memset(vtocbufp, 0, sizeof (*vtocbufp));
if ((part = read_extvtoc(fd, vtocbufp)) < 0) {
int err = errno;
if (ioctl(fd, MHIOCSTATUS, NULL) == 1)
err = EACCES;
else if (part == VT_EINVAL)
err = EINVAL;
else if (part == VT_EIO)
err = EIO;
else if (part == VT_ENOTSUP) {
if (partno) {
*partno = VT_ENOTSUP;
return (-1);
}
}
return (mdsyserror(ep, err, devname));
}
/* Slice number for *p0 partition (whole disk on x86) is 16 */
if (part >= V_NUMPAR)
return (mdsyserror(ep, EINVAL, devname));
/* Slice number for *p0 partition (whole disk on x86) is 16 */
if (part >= V_NUMPAR)
return (mdsyserror(ep, EINVAL, devname));
if (partno)
*partno = part;
return (0);
}
/*
* set mdvtoc for a meta devices
*/
int
meta_setmdvtoc(
int fd, /* fd for named device */
char *devname, /* name of device */
mdvtoc_t *mdvtocp, /* mdvtoc buffer to fill */
md_error_t *ep
)
{
uint_t i;
/*
* Sanity-check the mdvtoc
*/
if (mdvtocp->nparts > V_NUMPAR) {
return (-1);
}
/*
* since many drivers won't allow opening a device make sure
* all partitions aren't being set to zero. If all are zero then
* we have no way to set them to something else
*/
for (i = 0; i < mdvtocp->nparts; i++)
if (mdvtocp->parts[i].size > 0)
break;
if (i == mdvtocp->nparts)
return (-1);
/*
* Write the mdvtoc
*/
if (ioctl(fd, DKIOCSVTOC, (caddr_t)mdvtocp) == -1) {
return (mdsyserror(ep, errno, devname));
}
return (0);
}
/*
* set vtoc
*/
int
meta_setvtoc(
int fd, /* fd for named device */
char *devname, /* name of device */
struct extvtoc *vtocbufp, /* vtoc buffer to fill */
md_error_t *ep
)
{
int part;
int err;
if ((part = write_extvtoc(fd, vtocbufp)) < 0) {
if (part == VT_EINVAL)
err = EINVAL;
else if (part == VT_EIO)
err = EIO;
else
err = errno;
return (mdsyserror(ep, err, devname));
}
return (0);
}
/*
* FUNCTION: meta_get_names()
* INPUT: drivername - char string containing the driver name
* sp - the set name to get soft partitions from
* options - options from the command line
* OUTPUT: nlpp - list of all soft partition names
* ep - return error pointer
* RETURNS: int - -1 if error, 0 success
* PURPOSE: returns a list of all specified devices in the metadb
* for all devices in the specified set
*/
int
meta_get_names(
char *drivername,
mdsetname_t *sp,
mdnamelist_t **nlpp,
mdprtopts_t options,
md_error_t *ep
)
{
md_i_getnum_t gn; /* MD_IOCGET_NUM params */
mdnamelist_t **tailpp = nlpp;
minor_t *minors = NULL;
minor_t *m_ptr;
int i;
(void) memset(&gn, '\0', sizeof (gn));
MD_SETDRIVERNAME(&gn, drivername, sp->setno);
/* get number of devices */
if (metaioctl(MD_IOCGET_NUM, &gn, &gn.mde, NULL) != 0) {
if (mdiserror(&gn.mde, MDE_UNIT_NOT_FOUND)) {
mdclrerror(&gn.mde);
} else {
(void) mdstealerror(ep, &gn.mde);
return (-1);
}
}
if (gn.size > 0) {
/* malloc minor number buffer to be filled by ioctl */
if ((minors = (minor_t *)malloc(
gn.size * sizeof (minor_t))) == 0) {
return (ENOMEM);
}
gn.minors = (uintptr_t)minors;
if (metaioctl(MD_IOCGET_NUM, &gn, &gn.mde, NULL) != 0) {
(void) mdstealerror(ep, &gn.mde);
free(minors);
return (-1);
}
m_ptr = minors;
for (i = 0; i < gn.size; i++) {
mdname_t *np;
/* get name */
np = metamnumname(&sp, *m_ptr,
((options & PRINT_FAST) ? 1 : 0), ep);
/*
* np can be NULL if the /dev/md namespace entries
* do not exist. This could have happened due to
* devfsadmd not having created them.
* Therefore assume devfsadmd has not run and tell
* it to run for the specific device that is missing.
* Ignore any error return from meta_update_devtree
* as a failure to create the device nodes will be
* picked up in the metamnumname() call. Note that
* the call to meta_update_devtree should not return
* until the /dev/md links have been created or if
* there has been a failure of some sort.
*/
if (np == NULL) {
(void) meta_update_devtree(*m_ptr);
np = metamnumname(&sp, *m_ptr,
((options & PRINT_FAST) ? 1 : 0), ep);
}
if (np == NULL)
goto out;
tailpp = meta_namelist_append_wrapper(tailpp, np);
/* next device */
m_ptr++;
}
free(minors);
}
return (gn.size);
out:
if (minors != NULL)
free(minors);
metafreenamelist(*nlpp);
*nlpp = NULL;
return (-1);
}
/*
* Wrap lib/libdevid/devid_deviceid_to_nmlist. We want to take the
* results from that function and filter out the c[t]dp style names that
* we typically see on x86 so that we never see them.
*/
int
meta_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name,
devid_nmlist_t **retlist)
{
int res;
devid_nmlist_t *dp;
devid_nmlist_t *tmp_retlist;
int i = 1;
devid_nmlist_t *rp;
res = devid_deviceid_to_nmlist(search_path, devid, minor_name, retlist);
if (res != 0) {
return (res);
}
/* first count the number of non c[t]dp items in retlist */
for (dp = *retlist; dp->dev != NODEV; dp++) {
uint_t s;
/* Check if this is a c[t]dp style name. */
if (parse_ctd(basename(dp->devname), &s) != 1) {
i++;
}
}
/* create an array to hold the non c[t]dp items */
tmp_retlist = Malloc(sizeof (devid_nmlist_t) * i);
/* copy the non c[t]dp items to the array */
for (dp = *retlist, rp = tmp_retlist; dp->dev != NODEV; dp++) {
uint_t s;
/* Check if this is a c[t]dp style name. */
if (parse_ctd(basename(dp->devname), &s) != 1) {
/* nope, so copy and go to the next */
rp->dev = dp->dev;
rp->devname = Strdup(dp->devname);
rp++;
}
/* if it is c[t]dp, just skip the element */
}
/* copy the list terminator */
rp->dev = NODEV;
rp->devname = NULL;
devid_free_nmlist (*retlist);
*retlist = tmp_retlist;
return (res);
}
/*
* Check each real device that makes up a metadevice so that
* un_dev entries can be matched against the entries in the
* namespace.
*
* RETURN:
* -1 error
* 0 success
*/
int
meta_fix_compnames(
mdsetname_t *sp,
mdname_t *namep,
md_dev64_t dev,
md_error_t *ep
)
{
int ret = 0;
char *miscname;
/* get miscname and unit */
if ((miscname = metagetmiscname(namep, ep)) == NULL)
return (-1);
if (strcmp(miscname, MD_STRIPE) == 0) {
if (meta_stripe_check_component(sp, namep, dev, ep) < 0) {
ret = -1;
}
} else if (strcmp(miscname, MD_SP) == 0) {
if (meta_sp_check_component(sp, namep, ep) < 0) {
ret = -1;
}
} else if (strcmp(miscname, MD_RAID) == 0) {
if (meta_raid_check_component(sp, namep, dev, ep) < 0) {
ret = -1;
}
} else {
(void) mdmderror(ep, MDE_INVAL_UNIT, 0, namep->cname);
return (-1);
}
return (ret);
}