meta_init.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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* initialize metadevices
*/
#include <meta.h>
#include <libdevinfo.h>
int
parse_interlace(
char *uname, /* Meta Device name (eg d0) */
char *str, /* String to Parse */
diskaddr_t *interlacep,
md_error_t *ep
)
{
diskaddr_t num;
char c;
int cnt;
/* parse interlace */
if ((cnt = sscanf(str, "%llu%c", &num, &c)) < 1) {
return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
uname, 1, &str));
} else if (cnt == 1) {
if (num & (DEV_BSIZE - 1)) {
return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
uname, 1, &str));
}
num = lbtodb(num);
} else switch (c) {
case 'b':
case 'B':
num *= DEV_BSIZE / DEV_BSIZE;
break;
case 'k':
case 'K':
num *= 1024 / DEV_BSIZE;
break;
case 'm':
case 'M':
num *= 1024 * 1024 / DEV_BSIZE;
break;
default:
return (meta_cook_syntax(ep, MDE_BAD_INTERLACE,
NULL, 1, &str));
}
/* return success */
*interlacep = num;
return (0);
}
/*
* cook up syntax error
*/
int
meta_cook_syntax(
md_error_t *ep,
md_void_errno_t errcode,
char *uname,
int argc,
char *argv[]
)
{
int rval;
/* if we have a token, concat it to uname */
if ((argc > 0) && (argv[0] != NULL) && (argv[0][0] != '\0')) {
char *p;
if ((uname != NULL) && (uname[0] != '\0')) {
p = Malloc(strlen(uname) + 2
+ 1 + strlen(argv[0]) + 1 + 1);
(void) strcpy(p, uname);
(void) strcat(p, ": ");
} else {
p = Malloc(1 + strlen(argv[0]) + 1 + 1);
p[0] = '\0';
}
(void) strcat(p, "\"");
(void) strcat(p, argv[0]);
(void) strcat(p, "\"");
rval = mderror(ep, errcode, p);
Free(p);
} else {
rval = mderror(ep, errcode, uname);
}
return (rval);
}
int
meta_check_devicesize(
diskaddr_t total_blocks
)
{
int rval = MD_CRO_32BIT;
if (total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) {
rval = MD_CRO_64BIT;
}
return (rval);
}
/*
* setup metadevice geometry
*/
/*ARGSUSED*/
int
meta_setup_geom(
md_unit_t *md,
mdname_t *np,
mdgeom_t *geomp,
uint_t write_reinstruct,
uint_t read_reinstruct,
uint_t round_cyl,
md_error_t *ep
)
{
diskaddr_t cylsize = geomp->nhead * geomp->nsect;
diskaddr_t total_blocks;
if (round_cyl) {
total_blocks = rounddown(md->c.un_actual_tb, cylsize);
} else {
total_blocks = md->c.un_actual_tb;
}
md->c.un_total_blocks = total_blocks;
md->c.un_nhead = geomp->nhead;
md->c.un_nsect = geomp->nsect;
md->c.un_rpm = geomp->rpm;
md->c.un_wr_reinstruct = write_reinstruct;
md->c.un_rd_reinstruct = read_reinstruct;
return (0);
}
/*
* adjust metadevice geometry
*/
/*ARGSUSED*/
int
meta_adjust_geom(
md_unit_t *md,
mdname_t *np,
uint_t write_reinstruct,
uint_t read_reinstruct,
uint_t round_cyl,
md_error_t *ep
)
{
diskaddr_t cylsize = md->c.un_nhead * md->c.un_nsect;
diskaddr_t total_blocks;
if (round_cyl) {
total_blocks = rounddown(md->c.un_actual_tb, cylsize);
} else {
total_blocks = md->c.un_actual_tb;
}
md->c.un_total_blocks = total_blocks;
if (write_reinstruct > md->c.un_wr_reinstruct)
md->c.un_wr_reinstruct = write_reinstruct;
if (read_reinstruct > md->c.un_rd_reinstruct)
md->c.un_rd_reinstruct = read_reinstruct;
return (0);
}
/*
* Function: meta_init_make_device
* Purpose:
* Create the device node <uname> by constructing the necessary
* md_mkdev_params_t structure. We have to handle relative names
* (e.g. "d80") and fully-qualified names (e.g. "/dev/md/red/dsk/d80").
* The field that we need is the unit number of the metadevice (80 in
* the above examples).
* Input: spp set structure
* uname unit-name (fully qualified or relative)
* Output: ep error return structure
* Returns: 0 success
* -1 Error. <ep> contains error reason
*/
int
meta_init_make_device(
mdsetname_t **spp,
char *uname,
md_error_t *ep
)
{
di_devlink_handle_t hdl;
md_mkdev_params_t params;
int rval = 0;
char *p, *e = uname;
size_t len = strlen(uname);
e += len;
(void) memset(&params, 0, sizeof (params));
MD_SETDRIVERNAME(&params, "md", (*spp)->setno);
/*
* Find the start of the unit within <uname>.
*/
p = strrchr(uname, '/');
if (p == NULL) {
/* Relative name (e.g. d80) */
p = &uname[1];
} else {
/* qualified name (e.g. /dev/md/dsk/d80) */
p += 2;
if (p >= e) {
/* Invalid drive name */
p = Malloc(len + 3);
(void) snprintf(p, len + 3, "\"%s\"", uname);
rval = mderror(ep, MDE_NOT_DRIVENAME, p);
Free(p);
return (rval);
}
}
e = NULL;
params.mnum = strtoul(p, &e, 10);
if (e == p) {
/* Invalid drive name */
p = Malloc(len + 3);
(void) snprintf(p, len + 3, "\"%s\"", uname);
rval = mderror(ep, MDE_NOT_DRIVENAME, p);
Free(p);
return (rval);
}
if (metaioctl(MD_IOCMAKE_DEV, &params, &params.mde, NULL) != 0) {
return (mdstealerror(ep, &params.mde));
}
/*
* Wait until device appears in namespace. di_devlink_init() returns
* once the /dev links have been created. If NULL is returned the
* link operation failed and we haven't got a device to use.
* NOTE: This will take a _long_ time for large numbers of metadevices.
* Change to use the enhanced di_devlink_init() interface when
* available.
*/
hdl = di_devlink_init("md", DI_MAKE_LINK);
if (hdl != NULL) {
(void) di_devlink_fini(&hdl);
} else {
p = Malloc(len + 3);
(void) snprintf(p, len + 3, "\"%s\"", uname);
rval = mderror(ep, MDE_UNIT_NOT_FOUND, p);
Free(p);
}
return (rval);
}
/*
* FUNCTION: is_metadb_cmd()
* INPUT: argc - number of command line arguments
* argv - pointer to array of command line arguments
* OUTPUT: none
* RETURNS: TRUE if a metadb is to be created, FALSE otherwise
* PURPOSE: parses enough of the command line to determine if a metadb
* create is being attempted
*/
static boolean_t
is_metadb_cmd(
int argc,
char *argv[]
)
{
ulong_t num;
int len;
/* look for match */
if (argc > 0 && (sscanf(argv[0], "mddb%lu%n", &num, &len) == 1) &&
(strlen(argv[0]) == len) && ((long)num >= 0)) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* FUNCTION: is_stripe_cmd()
* INPUT: argc - number of command line arguments
* argv - pointer to array of command line arguments
* OUTPUT: none
* RETURNS: TRUE if a stripe is to be created, FALSE otherwise
* PURPOSE: parses enough of the command line to determine if a stripe
* create is being attempted
*/
static boolean_t
is_stripe_cmd(
int argc,
char *argv[]
)
{
uint_t nrow;
if (argc > 1 && (sscanf(argv[1], "%u", &nrow) != 1) || ((int)nrow < 0))
return (B_FALSE);
return (B_TRUE);
}
/*
* FUNCTION: meta_get_init_type()
* INPUT: argc - number of command line arguments
* argv - pointer to array of command line arguments
* OUTPUT: none
* RETURNS: type of metadevice or hot spare pools being initialized
* PURPOSE: parses enough of the command line to determine what type
* of metainit is being attempted
*/
mdinittypes_t
meta_get_init_type(
int argc,
char *argv[]
)
{
char *arg = argv[1];
mdinittypes_t init_type;
if (argc == 1) /* must be a hot spare pool w/o devices */
return (TAB_HSP);
init_type = TAB_UNKNOWN;
if (arg != NULL) {
if (strcmp(arg, "-m") == 0) {
init_type = TAB_MIRROR;
} else if (strcmp(arg, "-r") == 0) {
init_type = TAB_RAID;
} else if (strcmp(arg, "-p") == 0) {
init_type = TAB_SP;
} else if (strcmp(arg, "-t") == 0) {
init_type = TAB_TRANS;
} else if (is_metadb_cmd(argc, argv)) {
init_type = TAB_MDDB;
} else if (is_stripe_cmd(argc, argv)) {
init_type = TAB_STRIPE;
} else { /* assume that it is a hsp */
init_type = TAB_HSP;
}
}
return (init_type);
}
/*
* initialize named device or hotspare pool
*/
int
meta_init_name(
mdsetname_t **spp,
int argc,
char *argv[],
mdcmdopts_t options,
md_error_t *ep
)
{
mdinittypes_t init_type;
char *p;
int rval;
char *uname = argv[0];
assert(argc > 0);
/* determine type of metadevice or hot spare pool being created */
init_type = meta_get_init_type(argc, argv);
/* hotspare pool */
if (init_type == TAB_HSP)
return (meta_init_hsp(spp, argc, argv, options, ep));
/* metadevice */
if (argc >= 2 && init_type != TAB_UNKNOWN) {
md_error_t t_e = mdnullerror;
char *cname;
/*
* We need to create the device node if the specified metadevice
* does not already exist in the database. The actual creation
* is undertaken by the md driver and the links propagated by
* devfsadm.
*/
/* initialize the spp properly */
if ((cname = meta_name_getname(spp, uname, &t_e)) != NULL)
Free(cname);
if (! mdisok(&t_e))
return (mdstealerror(ep, &t_e));
/* Create device node */
if (meta_init_make_device(spp, uname, &t_e) != 0) {
return (mdstealerror(ep, &t_e));
}
switch (init_type) {
case TAB_MIRROR:
return (meta_init_mirror(spp, argc, argv, options, ep));
break;
case TAB_RAID:
return (meta_init_raid(spp, argc, argv, options, ep));
break;
case TAB_SP:
return (meta_init_sp(spp, argc, argv, options, ep));
break;
case TAB_TRANS:
return (mderror(ep, MDE_EOF_TRANS, NULL));
break;
case TAB_STRIPE:
return (meta_init_stripe(spp, argc, argv, options, ep));
break;
}
}
/* unknown type */
p = Malloc(1 + strlen(uname) + 1 + 1);
(void) strcpy(p, "\"");
(void) strcat(p, uname);
(void) strcat(p, "\"");
rval = mderror(ep, MDE_SYNTAX, p);
Free(p);
return (rval);
}