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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * initialize metadevices 31 */ 32 33 #include <meta.h> 34 #include <sys/lvm/mdio.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 and return 'key' 213 * -1 Error. <ep> contains error reason 214 */ 215 mdkey_t 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 mdkey_t rval = 0; 225 char *p; 226 int len = strlen(uname); 227 228 (void) memset(¶ms, 0, sizeof (params)); 229 MD_SETDRIVERNAME(¶ms, "md", (*spp)->setno); 230 231 /* 232 * This ioctl call causes kernel to allocate a unit number 233 * and populate /devices for the named metadevice 234 */ 235 if (metaioctl(MD_IOCMAKE_DEV, ¶ms, ¶ms.mde, NULL) != 0) { 236 return (mdstealerror(ep, ¶ms.mde)); 237 } 238 239 /* 240 * Now we have minor number so add it to the namespace 241 * and return the key 242 */ 243 if ((rval = add_self_name(*spp, uname, ¶ms, ep)) <= 0) { 244 return (mderror(ep, MDE_UNIT_NOT_FOUND, NULL)); 245 } 246 247 /* 248 * Wait until device appears in namespace. di_devlink_init() returns 249 * once the /dev links have been created. If NULL is returned the 250 * link operation failed and we haven't got a device to use. 251 * NOTE: This will take a _long_ time for large numbers of metadevices. 252 * Change to use the enhanced di_devlink_init() interface when 253 * available. 254 */ 255 hdl = di_devlink_init("md", DI_MAKE_LINK); 256 if (hdl != NULL) { 257 (void) di_devlink_fini(&hdl); 258 } else { 259 /* 260 * Delete name entry we just created 261 */ 262 (void) del_self_name(*spp, rval, ep); 263 p = Malloc(len + 3); 264 (void) snprintf(p, len + 3, "\"%s\"", uname); 265 rval = mderror(ep, MDE_UNIT_NOT_FOUND, p); 266 Free(p); 267 } 268 return (rval); 269 } 270 271 /* 272 * FUNCTION: is_metadb_cmd() 273 * INPUT: argc - number of command line arguments 274 * argv - pointer to array of command line arguments 275 * OUTPUT: none 276 * RETURNS: TRUE if a metadb is to be created, FALSE otherwise 277 * PURPOSE: parses enough of the command line to determine if a metadb 278 * create is being attempted 279 */ 280 static boolean_t 281 is_metadb_cmd( 282 int argc, 283 char *argv[] 284 ) 285 { 286 ulong_t num; 287 int len; 288 289 /* look for match */ 290 if (argc > 0 && (sscanf(argv[0], "mddb%lu%n", &num, &len) == 1) && 291 (strlen(argv[0]) == len) && ((long)num >= 0)) { 292 return (B_TRUE); 293 } 294 295 return (B_FALSE); 296 } 297 298 /* 299 * FUNCTION: is_stripe_cmd() 300 * INPUT: argc - number of command line arguments 301 * argv - pointer to array of command line arguments 302 * OUTPUT: none 303 * RETURNS: TRUE if a stripe is to be created, FALSE otherwise 304 * PURPOSE: parses enough of the command line to determine if a stripe 305 * create is being attempted 306 */ 307 static boolean_t 308 is_stripe_cmd( 309 int argc, 310 char *argv[] 311 ) 312 { 313 uint_t nrow; 314 315 if (argc > 1 && (sscanf(argv[1], "%u", &nrow) != 1) || ((int)nrow < 0)) 316 return (B_FALSE); 317 318 return (B_TRUE); 319 } 320 321 /* 322 * FUNCTION: meta_get_init_type() 323 * INPUT: argc - number of command line arguments 324 * argv - pointer to array of command line arguments 325 * OUTPUT: none 326 * RETURNS: type of metadevice or hot spare pools being initialized 327 * PURPOSE: parses enough of the command line to determine what type 328 * of metainit is being attempted 329 */ 330 mdinittypes_t 331 meta_get_init_type( 332 int argc, 333 char *argv[] 334 ) 335 { 336 char *arg = argv[1]; 337 mdinittypes_t init_type; 338 339 if (argc == 1) /* must be a hot spare pool w/o devices */ 340 return (TAB_HSP); 341 342 init_type = TAB_UNKNOWN; 343 if (arg != NULL) { 344 if (strcmp(arg, "-m") == 0) { 345 init_type = TAB_MIRROR; 346 } else if (strcmp(arg, "-r") == 0) { 347 init_type = TAB_RAID; 348 } else if (strcmp(arg, "-p") == 0) { 349 init_type = TAB_SP; 350 } else if (strcmp(arg, "-t") == 0) { 351 init_type = TAB_TRANS; 352 } else if (is_metadb_cmd(argc, argv)) { 353 init_type = TAB_MDDB; 354 } else if (is_stripe_cmd(argc, argv)) { 355 init_type = TAB_STRIPE; 356 } else { /* assume that it is a hsp */ 357 init_type = TAB_HSP; 358 } 359 } 360 return (init_type); 361 } 362 363 /* 364 * initialize named device or hotspare pool 365 */ 366 int 367 meta_init_name( 368 mdsetname_t **spp, 369 int argc, 370 char *argv[], 371 char *cname, /* canonical name */ 372 mdcmdopts_t options, 373 md_error_t *ep 374 ) 375 { 376 mdinittypes_t init_type; 377 char *p; 378 int rval; 379 char *uname = argv[0]; 380 mdkey_t key = MD_KEYWILD; 381 minor_t mnum; 382 md_error_t t_e = mdnullerror; 383 384 assert(argc > 0); 385 assert(*spp != NULL); 386 387 /* determine type of metadevice or hot spare pool being created */ 388 init_type = meta_get_init_type(argc, argv); 389 390 /* 391 * Metatrans is eof 392 */ 393 if (init_type == TAB_TRANS) 394 return (mderror(ep, MDE_EOF_TRANS, NULL)); 395 396 /* hotspare pool */ 397 if (init_type == TAB_HSP) 398 return (meta_init_hsp(spp, argc, argv, options, ep)); 399 400 /* 401 * We are creating metadevice so make sure the name 402 * has not been used 403 */ 404 if (is_existing_meta_hsp(*spp, cname)) { 405 /* 406 * The name has been used by hsp 407 */ 408 if (is_existing_hsp(*spp, cname)) { 409 return (mderror(ep, MDE_NAME_IN_USE, cname)); 410 } 411 412 /* 413 * If path exists but unit is not created 414 * then meta_init_make_device will correct 415 * that. If unit also exists then it 416 * will return a conflict error 417 */ 418 if (init_type != TAB_UNKNOWN) { 419 /* Create device node */ 420 if ((key = meta_init_make_device(spp, uname, 421 &t_e)) <= 0) { 422 return (mdstealerror(ep, &t_e)); 423 } 424 } 425 } 426 427 /* metadevice */ 428 if (argc >= 2 && init_type != TAB_UNKNOWN) { 429 /* 430 * We need to create the device node if the specified metadevice 431 * does not already exist in the database. The actual creation 432 * is undertaken by the md driver and the links propagated by 433 * devfsadm. 434 */ 435 if (key == MD_KEYWILD) { 436 if ((key = meta_init_make_device(spp, uname, 437 &t_e)) <= 0) 438 return (mdstealerror(ep, &t_e)); 439 } 440 441 switch (init_type) { 442 case TAB_MIRROR: 443 rval = meta_init_mirror(spp, argc, argv, options, ep); 444 break; 445 case TAB_RAID: 446 rval = meta_init_raid(spp, argc, argv, options, ep); 447 break; 448 case TAB_SP: 449 rval = meta_init_sp(spp, argc, argv, options, ep); 450 break; 451 case TAB_STRIPE: 452 rval = meta_init_stripe(spp, argc, argv, options, ep); 453 break; 454 } 455 456 if (rval == -1 || !(options & MDCMD_DOIT)) { 457 /* 458 * Remove the device node created before 459 */ 460 if ((meta_getnmentbykey((*spp)->setno, MD_SIDEWILD, 461 key, NULL, &mnum, NULL, ep) != NULL) && 462 MD_MIN2UNIT(mnum) < MD_MAXUNITS) { 463 (void) metaioctl(MD_IOCREM_DEV, &mnum, &t_e, NULL); 464 } 465 466 /* 467 * Del what we added before 468 */ 469 (void) del_self_name(*spp, key, &t_e); 470 } 471 return (rval); 472 } 473 474 /* unknown type */ 475 p = Malloc(1 + strlen(uname) + 1 + 1); 476 (void) strcpy(p, "\""); 477 (void) strcat(p, uname); 478 (void) strcat(p, "\""); 479 rval = mderror(ep, MDE_SYNTAX, p); 480 Free(p); 481 return (rval); 482 } 483