/* * 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 #include 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 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. 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(¶ms, 0, sizeof (params)); MD_SETDRIVERNAME(¶ms, "md", (*spp)->setno); /* * Find the start of the unit within . */ 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, ¶ms, ¶ms.mde, NULL) != 0) { return (mdstealerror(ep, ¶ms.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); }