1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * initialize metadevices 32 */ 33 34 #include <meta.h> 35 #include <libdevinfo.h> 36 37 38 int 39 parse_interlace( 40 char *uname, /* Meta Device name (eg d0) */ 41 char *str, /* String to Parse */ 42 diskaddr_t *interlacep, 43 md_error_t *ep 44 ) 45 { 46 diskaddr_t num; 47 char c; 48 int cnt; 49 50 /* parse interlace */ 51 if ((cnt = sscanf(str, "%llu%c", &num, &c)) < 1) { 52 return (meta_cook_syntax(ep, MDE_BAD_INTERLACE, 53 uname, 1, &str)); 54 } else if (cnt == 1) { 55 if (num & (DEV_BSIZE - 1)) { 56 return (meta_cook_syntax(ep, MDE_BAD_INTERLACE, 57 uname, 1, &str)); 58 } 59 num = lbtodb(num); 60 } else switch (c) { 61 case 'b': 62 case 'B': 63 num *= DEV_BSIZE / DEV_BSIZE; 64 break; 65 case 'k': 66 case 'K': 67 num *= 1024 / DEV_BSIZE; 68 break; 69 case 'm': 70 case 'M': 71 num *= 1024 * 1024 / DEV_BSIZE; 72 break; 73 default: 74 return (meta_cook_syntax(ep, MDE_BAD_INTERLACE, 75 NULL, 1, &str)); 76 } 77 78 /* return success */ 79 *interlacep = num; 80 return (0); 81 } 82 83 /* 84 * cook up syntax error 85 */ 86 int 87 meta_cook_syntax( 88 md_error_t *ep, 89 md_void_errno_t errcode, 90 char *uname, 91 int argc, 92 char *argv[] 93 ) 94 { 95 int rval; 96 97 /* if we have a token, concat it to uname */ 98 if ((argc > 0) && (argv[0] != NULL) && (argv[0][0] != '\0')) { 99 char *p; 100 101 if ((uname != NULL) && (uname[0] != '\0')) { 102 p = Malloc(strlen(uname) + 2 103 + 1 + strlen(argv[0]) + 1 + 1); 104 (void) strcpy(p, uname); 105 (void) strcat(p, ": "); 106 } else { 107 p = Malloc(1 + strlen(argv[0]) + 1 + 1); 108 p[0] = '\0'; 109 } 110 (void) strcat(p, "\""); 111 (void) strcat(p, argv[0]); 112 (void) strcat(p, "\""); 113 rval = mderror(ep, errcode, p); 114 Free(p); 115 } else { 116 rval = mderror(ep, errcode, uname); 117 } 118 119 return (rval); 120 } 121 122 int 123 meta_check_devicesize( 124 diskaddr_t total_blocks 125 ) 126 { 127 int rval = MD_CRO_32BIT; 128 129 130 if (total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) { 131 rval = MD_CRO_64BIT; 132 } 133 return (rval); 134 } 135 136 137 /* 138 * setup metadevice geometry 139 */ 140 /*ARGSUSED*/ 141 int 142 meta_setup_geom( 143 md_unit_t *md, 144 mdname_t *np, 145 mdgeom_t *geomp, 146 uint_t write_reinstruct, 147 uint_t read_reinstruct, 148 uint_t round_cyl, 149 md_error_t *ep 150 ) 151 { 152 diskaddr_t cylsize = geomp->nhead * geomp->nsect; 153 diskaddr_t total_blocks; 154 155 if (round_cyl) { 156 total_blocks = rounddown(md->c.un_actual_tb, cylsize); 157 } else { 158 total_blocks = md->c.un_actual_tb; 159 } 160 161 md->c.un_total_blocks = total_blocks; 162 md->c.un_nhead = geomp->nhead; 163 md->c.un_nsect = geomp->nsect; 164 md->c.un_rpm = geomp->rpm; 165 md->c.un_wr_reinstruct = write_reinstruct; 166 md->c.un_rd_reinstruct = read_reinstruct; 167 return (0); 168 } 169 170 /* 171 * adjust metadevice geometry 172 */ 173 /*ARGSUSED*/ 174 int 175 meta_adjust_geom( 176 md_unit_t *md, 177 mdname_t *np, 178 uint_t write_reinstruct, 179 uint_t read_reinstruct, 180 uint_t round_cyl, 181 md_error_t *ep 182 ) 183 { 184 diskaddr_t cylsize = md->c.un_nhead * md->c.un_nsect; 185 diskaddr_t total_blocks; 186 187 if (round_cyl) { 188 total_blocks = rounddown(md->c.un_actual_tb, cylsize); 189 } else { 190 total_blocks = md->c.un_actual_tb; 191 } 192 193 md->c.un_total_blocks = total_blocks; 194 if (write_reinstruct > md->c.un_wr_reinstruct) 195 md->c.un_wr_reinstruct = write_reinstruct; 196 if (read_reinstruct > md->c.un_rd_reinstruct) 197 md->c.un_rd_reinstruct = read_reinstruct; 198 return (0); 199 } 200 201 /* 202 * Function: meta_init_make_device 203 * Purpose: 204 * Create the device node <uname> by constructing the necessary 205 * md_mkdev_params_t structure. We have to handle relative names 206 * (e.g. "d80") and fully-qualified names (e.g. "/dev/md/red/dsk/d80"). 207 * The field that we need is the unit number of the metadevice (80 in 208 * the above examples). 209 * Input: spp set structure 210 * uname unit-name (fully qualified or relative) 211 * Output: ep error return structure 212 * Returns: 0 success 213 * -1 Error. <ep> contains error reason 214 */ 215 int 216 meta_init_make_device( 217 mdsetname_t **spp, 218 char *uname, 219 md_error_t *ep 220 ) 221 { 222 di_devlink_handle_t hdl; 223 md_mkdev_params_t params; 224 int rval = 0; 225 char *p, *e = uname; 226 size_t len = strlen(uname); 227 228 e += len; 229 (void) memset(¶ms, 0, sizeof (params)); 230 MD_SETDRIVERNAME(¶ms, "md", (*spp)->setno); 231 232 /* 233 * Find the start of the unit within <uname>. 234 */ 235 p = strrchr(uname, '/'); 236 if (p == NULL) { 237 /* Relative name (e.g. d80) */ 238 p = &uname[1]; 239 } else { 240 /* qualified name (e.g. /dev/md/dsk/d80) */ 241 p += 2; 242 if (p >= e) { 243 /* Invalid drive name */ 244 p = Malloc(len + 3); 245 (void) snprintf(p, len + 3, "\"%s\"", uname); 246 rval = mderror(ep, MDE_NOT_DRIVENAME, p); 247 Free(p); 248 return (rval); 249 } 250 } 251 e = NULL; 252 params.mnum = strtoul(p, &e, 10); 253 if (e == p) { 254 /* Invalid drive name */ 255 p = Malloc(len + 3); 256 (void) snprintf(p, len + 3, "\"%s\"", uname); 257 rval = mderror(ep, MDE_NOT_DRIVENAME, p); 258 Free(p); 259 return (rval); 260 } 261 262 if (metaioctl(MD_IOCMAKE_DEV, ¶ms, ¶ms.mde, NULL) != 0) { 263 return (mdstealerror(ep, ¶ms.mde)); 264 } 265 /* 266 * Wait until device appears in namespace. di_devlink_init() returns 267 * once the /dev links have been created. If NULL is returned the 268 * link operation failed and we haven't got a device to use. 269 * NOTE: This will take a _long_ time for large numbers of metadevices. 270 * Change to use the enhanced di_devlink_init() interface when 271 * available. 272 */ 273 hdl = di_devlink_init("md", DI_MAKE_LINK); 274 if (hdl != NULL) { 275 (void) di_devlink_fini(&hdl); 276 } else { 277 p = Malloc(len + 3); 278 (void) snprintf(p, len + 3, "\"%s\"", uname); 279 rval = mderror(ep, MDE_UNIT_NOT_FOUND, p); 280 Free(p); 281 } 282 return (rval); 283 } 284 285 /* 286 * FUNCTION: is_metadb_cmd() 287 * INPUT: argc - number of command line arguments 288 * argv - pointer to array of command line arguments 289 * OUTPUT: none 290 * RETURNS: TRUE if a metadb is to be created, FALSE otherwise 291 * PURPOSE: parses enough of the command line to determine if a metadb 292 * create is being attempted 293 */ 294 static boolean_t 295 is_metadb_cmd( 296 int argc, 297 char *argv[] 298 ) 299 { 300 ulong_t num; 301 int len; 302 303 /* look for match */ 304 if (argc > 0 && (sscanf(argv[0], "mddb%lu%n", &num, &len) == 1) && 305 (strlen(argv[0]) == len) && ((long)num >= 0)) { 306 return (B_TRUE); 307 } 308 309 return (B_FALSE); 310 } 311 312 /* 313 * FUNCTION: is_stripe_cmd() 314 * INPUT: argc - number of command line arguments 315 * argv - pointer to array of command line arguments 316 * OUTPUT: none 317 * RETURNS: TRUE if a stripe is to be created, FALSE otherwise 318 * PURPOSE: parses enough of the command line to determine if a stripe 319 * create is being attempted 320 */ 321 static boolean_t 322 is_stripe_cmd( 323 int argc, 324 char *argv[] 325 ) 326 { 327 uint_t nrow; 328 329 if (argc > 1 && (sscanf(argv[1], "%u", &nrow) != 1) || ((int)nrow < 0)) 330 return (B_FALSE); 331 332 return (B_TRUE); 333 } 334 335 /* 336 * FUNCTION: meta_get_init_type() 337 * INPUT: argc - number of command line arguments 338 * argv - pointer to array of command line arguments 339 * OUTPUT: none 340 * RETURNS: type of metadevice or hot spare pools being initialized 341 * PURPOSE: parses enough of the command line to determine what type 342 * of metainit is being attempted 343 */ 344 mdinittypes_t 345 meta_get_init_type( 346 int argc, 347 char *argv[] 348 ) 349 { 350 char *arg = argv[1]; 351 mdinittypes_t init_type; 352 353 if (argc == 1) /* must be a hot spare pool w/o devices */ 354 return (TAB_HSP); 355 356 init_type = TAB_UNKNOWN; 357 if (arg != NULL) { 358 if (strcmp(arg, "-m") == 0) { 359 init_type = TAB_MIRROR; 360 } else if (strcmp(arg, "-r") == 0) { 361 init_type = TAB_RAID; 362 } else if (strcmp(arg, "-p") == 0) { 363 init_type = TAB_SP; 364 } else if (strcmp(arg, "-t") == 0) { 365 init_type = TAB_TRANS; 366 } else if (is_metadb_cmd(argc, argv)) { 367 init_type = TAB_MDDB; 368 } else if (is_stripe_cmd(argc, argv)) { 369 init_type = TAB_STRIPE; 370 } else { /* assume that it is a hsp */ 371 init_type = TAB_HSP; 372 } 373 } 374 return (init_type); 375 } 376 377 /* 378 * initialize named device or hotspare pool 379 */ 380 int 381 meta_init_name( 382 mdsetname_t **spp, 383 int argc, 384 char *argv[], 385 mdcmdopts_t options, 386 md_error_t *ep 387 ) 388 { 389 mdinittypes_t init_type; 390 char *p; 391 int rval; 392 char *uname = argv[0]; 393 394 assert(argc > 0); 395 396 /* determine type of metadevice or hot spare pool being created */ 397 init_type = meta_get_init_type(argc, argv); 398 399 /* hotspare pool */ 400 if (init_type == TAB_HSP) 401 return (meta_init_hsp(spp, argc, argv, options, ep)); 402 403 /* metadevice */ 404 if (argc >= 2 && init_type != TAB_UNKNOWN) { 405 md_error_t t_e = mdnullerror; 406 char *cname; 407 408 /* 409 * We need to create the device node if the specified metadevice 410 * does not already exist in the database. The actual creation 411 * is undertaken by the md driver and the links propagated by 412 * devfsadm. 413 */ 414 415 /* initialize the spp properly */ 416 if ((cname = meta_name_getname(spp, uname, &t_e)) != NULL) 417 Free(cname); 418 if (! mdisok(&t_e)) 419 return (mdstealerror(ep, &t_e)); 420 421 /* Create device node */ 422 if (meta_init_make_device(spp, uname, &t_e) != 0) { 423 return (mdstealerror(ep, &t_e)); 424 } 425 426 switch (init_type) { 427 case TAB_MIRROR: 428 return (meta_init_mirror(spp, argc, argv, options, ep)); 429 break; 430 case TAB_RAID: 431 return (meta_init_raid(spp, argc, argv, options, ep)); 432 break; 433 case TAB_SP: 434 return (meta_init_sp(spp, argc, argv, options, ep)); 435 break; 436 case TAB_TRANS: 437 return (mderror(ep, MDE_EOF_TRANS, NULL)); 438 break; 439 case TAB_STRIPE: 440 return (meta_init_stripe(spp, argc, argv, options, ep)); 441 break; 442 } 443 } 444 445 /* unknown type */ 446 p = Malloc(1 + strlen(uname) + 1 + 1); 447 (void) strcpy(p, "\""); 448 (void) strcat(p, uname); 449 (void) strcat(p, "\""); 450 rval = mderror(ep, MDE_SYNTAX, p); 451 Free(p); 452 return (rval); 453 } 454