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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "mhd_local.h" 27 28 #include <ftw.h> 29 #include <libgen.h> 30 #include <sys/mhd.h> 31 #include <sys/scsi/impl/uscsi.h> 32 #include <sys/scsi/generic/commands.h> 33 #include <sys/scsi/generic/inquiry.h> 34 35 /* 36 * manipulate drives 37 */ 38 39 /* 40 * null list constant 41 */ 42 const mhd_drive_list_t mhd_null_list = MHD_NULL_LIST; 43 44 /* 45 * add drive to list 46 */ 47 void 48 mhd_add_drive( 49 mhd_drive_list_t *dlp, 50 mhd_drive_t *dp 51 ) 52 { 53 /* add drive to list */ 54 if (dlp->dl_ndrive >= dlp->dl_alloc) { 55 dlp->dl_alloc += 10; 56 dlp->dl_drives = Realloc(dlp->dl_drives, 57 (dlp->dl_alloc * sizeof (*dlp->dl_drives))); 58 } 59 dlp->dl_drives[dlp->dl_ndrive++] = dp; 60 } 61 62 /* 63 * delete drive from list 64 */ 65 void 66 mhd_del_drive( 67 mhd_drive_list_t *dlp, 68 mhd_drive_t *dp 69 ) 70 { 71 uint_t i; 72 73 /* delete drive from list */ 74 for (i = 0; (i < dlp->dl_ndrive); ++i) { 75 if (dlp->dl_drives[i] == dp) 76 break; 77 } 78 assert(dlp->dl_drives[i] == dp); 79 for (/* void */; (i < dlp->dl_ndrive); ++i) 80 dlp->dl_drives[i] = dlp->dl_drives[i + 1]; 81 dlp->dl_ndrive--; 82 } 83 84 /* 85 * free drive list 86 */ 87 void 88 mhd_free_list( 89 mhd_drive_list_t *dlp 90 ) 91 { 92 if (dlp->dl_drives != NULL) 93 Free(dlp->dl_drives); 94 (void) memset(dlp, 0, sizeof (*dlp)); 95 } 96 97 /* 98 * manipulate drive state 99 */ 100 int 101 mhd_state( 102 mhd_drive_t *dp, 103 mhd_state_t new_state, 104 mhd_error_t *mhep 105 ) 106 { 107 mhd_drive_set_t *sp = dp->dr_sp; 108 mhd_state_t old_state = dp->dr_state; 109 110 /* check lock */ 111 assert(MUTEX_HELD(&sp->sr_mx)); 112 113 /* set state and kick thread */ 114 MHDPRINTF2(("%s: state 0x%x now 0x%x\n", 115 dp->dr_rname, dp->dr_state, new_state)); 116 dp->dr_state = new_state; 117 mhd_cv_broadcast(&dp->dr_cv); 118 119 /* if this is the last PROBING drive, disable any failfast */ 120 if ((old_state & DRIVE_PROBING) && (! (new_state & DRIVE_PROBING))) { 121 mhd_drive_list_t *dlp = &sp->sr_drives; 122 uint_t cnt, i; 123 124 for (cnt = 0, i = 0; (i < dlp->dl_ndrive); ++i) { 125 if (dlp->dl_drives[i]->dr_state & DRIVE_PROBING) 126 ++cnt; 127 } 128 if (cnt == 0) { 129 mhd_error_t status = mhd_null_error; 130 131 if (mhep == NULL) 132 mhep = &status; 133 if (mhd_ff_disarm(sp, mhep) != 0) { 134 if (mhep == &status) { 135 mhde_perror(mhep, dp->dr_rname); 136 mhd_clrerror(mhep); 137 } 138 return (-1); 139 } 140 } 141 } 142 143 /* return success */ 144 return (0); 145 } 146 147 int 148 mhd_state_set( 149 mhd_drive_t *dp, 150 mhd_state_t new_state, 151 mhd_error_t *mhep 152 ) 153 { 154 return (mhd_state(dp, (dp->dr_state | new_state), mhep)); 155 } 156 157 static int 158 mhd_state_clr( 159 mhd_drive_t *dp, 160 mhd_state_t new_state, 161 mhd_error_t *mhep 162 ) 163 { 164 return (mhd_state(dp, (dp->dr_state & ~new_state), mhep)); 165 } 166 167 /* 168 * idle a drive 169 */ 170 int 171 mhd_idle( 172 mhd_drive_t *dp, 173 mhd_error_t *mhep 174 ) 175 { 176 mhd_drive_set_t *sp = dp->dr_sp; 177 178 /* check lock */ 179 assert(MUTEX_HELD(&sp->sr_mx)); 180 181 /* wait for thread to idle */ 182 for (;;) { 183 if (DRIVE_IS_IDLE(dp)) 184 return (0); 185 if (mhd_state(dp, DRIVE_IDLING, mhep) != 0) 186 return (-1); 187 (void) mhd_cv_wait(&sp->sr_cv, &sp->sr_mx); 188 } 189 } 190 191 /* 192 * reserve the drive 193 */ 194 static int 195 mhd_reserve( 196 mhd_drive_t *dp 197 ) 198 { 199 mhd_drive_set_t *sp = dp->dr_sp; 200 int serial = (sp->sr_options & MHD_SERIAL); 201 mhd_mhioctkown_t *tkp = &sp->sr_timeouts.mh_tk; 202 struct mhioctkown tkown; 203 int err; 204 205 /* check locks */ 206 assert(MUTEX_HELD(&sp->sr_mx)); 207 assert(dp->dr_fd >= 0); 208 assert(dp->dr_state == DRIVE_RESERVING); 209 210 /* setup timeouts */ 211 (void) memset(&tkown, 0, sizeof (tkown)); 212 tkown.reinstate_resv_delay = tkp->reinstate_resv_delay; 213 tkown.min_ownership_delay = tkp->min_ownership_delay; 214 tkown.max_ownership_delay = tkp->max_ownership_delay; 215 216 /* reserve drive */ 217 if (! serial) 218 mhd_mx_unlock(&sp->sr_mx); 219 err = ioctl(dp->dr_fd, MHIOCTKOWN, &tkown); 220 if (! serial) 221 mhd_mx_lock(&sp->sr_mx); 222 if (err != 0) { 223 mhd_perror("%s: MHIOCTKOWN", dp->dr_rname); 224 (void) mhd_state(dp, DRIVE_ERRORED, NULL); 225 dp->dr_errnum = errno; 226 return (-1); 227 } 228 229 /* return success */ 230 MHDPRINTF(("%s: MHIOCTKOWN: succeeded\n", dp->dr_rname)); 231 (void) mhd_state(dp, DRIVE_IDLE, NULL); 232 return (0); 233 } 234 235 /* 236 * failfast the drive 237 */ 238 static int 239 mhd_failfast( 240 mhd_drive_t *dp 241 ) 242 { 243 mhd_drive_set_t *sp = dp->dr_sp; 244 int serial = (sp->sr_options & MHD_SERIAL); 245 int ff = sp->sr_timeouts.mh_ff; 246 char *release = ((ff == 0) ? " (release)" : ""); 247 int err; 248 249 /* check locks */ 250 assert(MUTEX_HELD(&sp->sr_mx)); 251 assert(dp->dr_fd >= 0); 252 assert(dp->dr_state == DRIVE_FAILFASTING); 253 254 /* failfast drive */ 255 if (! serial) 256 mhd_mx_unlock(&sp->sr_mx); 257 err = ioctl(dp->dr_fd, MHIOCENFAILFAST, &ff); 258 if (! serial) 259 mhd_mx_lock(&sp->sr_mx); 260 if (err != 0) { 261 mhd_perror("%s: MHIOCENFAILFAST%s", dp->dr_rname, release); 262 (void) mhd_state(dp, DRIVE_ERRORED, NULL); 263 dp->dr_errnum = errno; 264 return (-1); 265 } 266 267 /* return success */ 268 MHDPRINTF(("%s: MHIOCENFAILFAST%s: succeeded\n", 269 dp->dr_rname, release)); 270 (void) mhd_state(dp, DRIVE_IDLE, NULL); 271 return (0); 272 } 273 274 /* 275 * release the drive 276 */ 277 static int 278 mhd_release( 279 mhd_drive_t *dp 280 ) 281 { 282 mhd_drive_set_t *sp = dp->dr_sp; 283 int serial = (sp->sr_options & MHD_SERIAL); 284 int ff = 0; /* disable failfast */ 285 int err; 286 287 /* check locks */ 288 assert(MUTEX_HELD(&sp->sr_mx)); 289 assert(dp->dr_fd >= 0); 290 assert(dp->dr_state == DRIVE_RELEASING); 291 292 /* disable failfast */ 293 if (! serial) 294 mhd_mx_unlock(&sp->sr_mx); 295 err = ioctl(dp->dr_fd, MHIOCENFAILFAST, &ff); 296 if (! serial) 297 mhd_mx_lock(&sp->sr_mx); 298 if (err != 0) { 299 mhd_perror("%s: MHIOCENFAILFAST (release)", dp->dr_rname); 300 (void) mhd_state(dp, DRIVE_ERRORED, NULL); 301 dp->dr_errnum = errno; 302 return (-1); 303 } 304 MHDPRINTF(("%s: MHIOCENFAILFAST (release): succeeded\n", 305 dp->dr_rname)); 306 307 /* release drive */ 308 if (! serial) 309 mhd_mx_unlock(&sp->sr_mx); 310 err = ioctl(dp->dr_fd, MHIOCRELEASE, NULL); 311 if (! serial) 312 mhd_mx_lock(&sp->sr_mx); 313 if (err != 0) { 314 mhd_perror("%s: MHIOCRELEASE", dp->dr_rname); 315 (void) mhd_state(dp, DRIVE_ERRORED, NULL); 316 dp->dr_errnum = errno; 317 return (-1); 318 } 319 320 /* return success */ 321 MHDPRINTF(("%s: MHIOCRELEASE: succeeded\n", dp->dr_rname)); 322 (void) mhd_state(dp, DRIVE_IDLE, NULL); 323 return (0); 324 } 325 326 /* 327 * probe the drive 328 */ 329 static int 330 mhd_probe( 331 mhd_drive_t *dp 332 ) 333 { 334 mhd_drive_set_t *sp = dp->dr_sp; 335 int serial = (sp->sr_options & MHD_SERIAL); 336 int err; 337 mhd_msec_t now; 338 339 /* check locks */ 340 assert(MUTEX_HELD(&sp->sr_mx)); 341 assert(dp->dr_fd >= 0); 342 assert(dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING)); 343 344 /* get status (we may get dumped from PROBING here) */ 345 if (! serial) 346 mhd_mx_unlock(&sp->sr_mx); 347 err = ioctl(dp->dr_fd, MHIOCSTATUS, NULL); 348 now = mhd_time(); 349 if (! serial) 350 mhd_mx_lock(&sp->sr_mx); 351 if (! (dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING))) 352 return (0); 353 354 /* update status */ 355 if (dp->dr_state & DRIVE_STATUSING) { 356 if (err == 1) { 357 MHDPRINTF(("%s: MHIOCSTATUS: reserved\n", 358 dp->dr_rname)); 359 dp->dr_errnum = MHD_E_RESERVED; 360 } else if (err != 0) { 361 mhd_perror("%s: MHIOCSTATUS", dp->dr_rname); 362 dp->dr_errnum = errno; 363 } else { 364 MHDPRINTF(("%s: MHIOCSTATUS: available\n", 365 dp->dr_rname)); 366 dp->dr_errnum = 0; 367 } 368 (void) mhd_state_clr(dp, DRIVE_STATUSING, NULL); 369 } 370 371 /* update time or die */ 372 if (dp->dr_state & DRIVE_PROBING) { 373 /* check our drive */ 374 if (err == 0) { 375 dp->dr_time = now; 376 } else if (err == 1) { 377 mhd_eprintf("%s: %s: reservation conflict\n", 378 sp->sr_name, dp->dr_rname); 379 mhd_ff_die(sp); 380 } 381 382 /* check other drives */ 383 mhd_ff_check(sp); 384 } 385 386 /* return success */ 387 return (0); 388 } 389 390 /* 391 * cached controller map 392 */ 393 typedef struct { 394 char *regexpr1; 395 uint_t tray; 396 uint_t bus; 397 char *regexpr2; 398 char *scan; 399 } mhd_ctlrmap_t; 400 401 static rwlock_t ctlr_rw = DEFAULTRWLOCK; 402 static time_t ctlr_mtime = 0; 403 static size_t ctlr_num = 0; 404 static mhd_ctlrmap_t *ctlr_map = NULL; 405 406 /* 407 * free up controller map 408 */ 409 static void 410 free_map() 411 { 412 size_t i; 413 414 assert(RW_WRITE_HELD(&ctlr_rw)); 415 416 for (i = 0; (i < ctlr_num); ++i) { 417 mhd_ctlrmap_t *cmp = &ctlr_map[i]; 418 419 if (cmp->regexpr1 != NULL) 420 Free(cmp->regexpr1); 421 if (cmp->regexpr2 != NULL) 422 Free(cmp->regexpr2); 423 if (cmp->scan != NULL) 424 Free(cmp->scan); 425 } 426 if (ctlr_map != NULL) 427 Free(ctlr_map); 428 ctlr_num = 0; 429 ctlr_map = NULL; 430 } 431 432 /* 433 * unlock controller map 434 */ 435 static void 436 unlock_map() 437 { 438 assert(RW_WRITE_HELD(&ctlr_rw) | RW_READ_HELD(&ctlr_rw)); 439 440 mhd_rw_unlock(&ctlr_rw); 441 } 442 443 /* 444 * update controller map and lock it 445 */ 446 static int 447 update_map() 448 { 449 struct stat statbuf; 450 FILE *fp; 451 char line[256], expr1[256], expr2[256], scan[256]; 452 unsigned tray, bus; 453 int rval = -1; 454 455 /* see if map file has changed */ 456 mhd_rw_rdlock(&ctlr_rw); 457 if (stat(METACTLRMAP, &statbuf) != 0) { 458 mhd_perror(METACTLRMAP); 459 goto out; 460 } 461 if (statbuf.st_mtime == ctlr_mtime) { 462 rval = 0; 463 goto out; 464 } 465 466 /* trade up to writer lock, check again */ 467 mhd_rw_unlock(&ctlr_rw); 468 mhd_rw_wrlock(&ctlr_rw); 469 if (statbuf.st_mtime == ctlr_mtime) { 470 rval = 0; 471 goto out; 472 } 473 if (ctlr_mtime != 0) 474 mhd_eprintf("updating controller map\n"); 475 ctlr_mtime = statbuf.st_mtime; 476 477 /* toss existing cache */ 478 free_map(); 479 480 /* parse md.ctlrmap */ 481 if ((fp = fopen(METACTLRMAP, "r")) == NULL) { 482 mhd_perror(METACTLRMAP); 483 goto out; 484 } 485 clearerr(fp); 486 while (fgets(line, sizeof (line), fp) != NULL) { 487 char *regexpr1 = NULL; 488 char *regexpr2 = NULL; 489 mhd_ctlrmap_t *cmp; 490 491 /* skip blank lines and comments */ 492 if ((line[0] == '\0') || (line[0] == '\n') || (line[0] == '#')) 493 continue; 494 495 /* parse line */ 496 if (((sscanf(line, "\"%[^\"]\" %u %u \"%[^\"]\" \"%[^\"]\"", 497 expr1, &tray, &bus, expr2, scan)) != 5) || 498 ((regexpr1 = regcmp(expr1, 0)) == NULL) || 499 ((regexpr2 = regcmp(expr2, 0)) == NULL)) { 500 mhd_eprintf("%s: bad regex(es) '%s'\n", 501 METACTLRMAP, line); 502 if (regexpr1 != NULL) 503 Free(regexpr1); 504 if (regexpr2 != NULL) 505 Free(regexpr2); 506 continue; 507 } 508 509 /* add to cache */ 510 ctlr_map = Realloc(ctlr_map, 511 ((ctlr_num + 1) * sizeof (*ctlr_map))); 512 cmp = &ctlr_map[ctlr_num++]; 513 cmp->regexpr1 = regexpr1; 514 cmp->tray = tray; 515 cmp->bus = bus; 516 cmp->regexpr2 = regexpr2; 517 cmp->scan = Strdup(scan); 518 } 519 if (ferror(fp)) { 520 mhd_perror(METACTLRMAP); 521 (void) fclose(fp); 522 goto out; 523 } 524 if (fclose(fp) != 0) { 525 mhd_perror(METACTLRMAP); 526 goto out; 527 } 528 529 /* success */ 530 rval = 0; 531 532 /* return success */ 533 out: 534 if (rval != 0) { 535 mhd_rw_unlock(&ctlr_rw); 536 return (-1); 537 } 538 return (0); 539 } 540 541 static char * 542 get_pln_ctlr_name( 543 char *path 544 ) 545 { 546 char *devicesname, *p; 547 char retval[MAXPATHLEN]; 548 549 devicesname = Strdup(path); 550 if ((p = strrchr(devicesname, '/')) == NULL) { 551 Free(devicesname); 552 return (NULL); 553 } 554 555 /* strip off the "ssd@..." portion of the devices name */ 556 *p = '\0'; 557 558 /* strip off the "../../" in front of "devices" */ 559 if ((p = strstr(devicesname, "/devices/")) == NULL) { 560 Free(devicesname); 561 return (NULL); 562 } 563 564 (void) snprintf(retval, sizeof (retval), "%s:ctlr", p); 565 Free(devicesname); 566 return (Strdup(retval)); 567 } 568 569 struct pln_cache { 570 char *pln_name; 571 enum mhd_ctlrtype_t ctype; 572 struct pln_cache *next; 573 }; 574 575 static struct pln_cache *pln_cache_anchor = NULL; 576 static mutex_t mhd_pln_mx = DEFAULTMUTEX; 577 578 /* singled threaded by caller */ 579 static void 580 add_pln_cache( 581 char *pln_name, 582 enum mhd_ctlrtype_t ctype 583 584 ) 585 { 586 struct pln_cache *p; 587 588 p = Malloc(sizeof (*p)); 589 590 p->pln_name = pln_name; 591 p->ctype = ctype; 592 p->next = pln_cache_anchor; 593 pln_cache_anchor = p; 594 } 595 596 /* singled threaded by caller */ 597 static int 598 find_pln_cache( 599 char *pln_name, 600 enum mhd_ctlrtype_t *ctype_ret 601 ) 602 { 603 struct pln_cache *p; 604 605 for (p = pln_cache_anchor; p != NULL; p = p->next) { 606 if (strcmp(pln_name, p->pln_name) == 0) { 607 *ctype_ret = p->ctype; 608 return (1); 609 } 610 } 611 return (0); 612 } 613 614 static void 615 free_pln_cache(void) 616 { 617 struct pln_cache *p, *n = NULL; 618 619 mutex_lock(&mhd_pln_mx); 620 for (p = pln_cache_anchor; p != NULL; p = n) { 621 n = p->next; 622 Free(p->pln_name); 623 Free(p); 624 } 625 626 pln_cache_anchor = NULL; 627 mutex_unlock(&mhd_pln_mx); 628 } 629 630 /* 631 * match on SSA Model 200. 632 */ 633 static void 634 match_SSA200( 635 mhd_drive_t *dp, 636 char *path 637 ) 638 { 639 mhd_cinfo_t *cinfop = &dp->dr_drive_id.did_cinfo; 640 struct uscsi_cmd ucmd; 641 union scsi_cdb cdb; 642 struct scsi_inquiry inq; 643 int fd; 644 char *pln_ctlr_name; 645 enum mhd_ctlrtype_t ctype; 646 char *p; 647 648 if ((pln_ctlr_name = get_pln_ctlr_name(path)) == NULL) 649 return; 650 651 mutex_lock(&mhd_pln_mx); 652 if (find_pln_cache(pln_ctlr_name, &ctype) == 1) { 653 mutex_unlock(&mhd_pln_mx); 654 if (ctype != MHD_CTLR_SSA200) 655 return; 656 657 /* over-ride for SSA200 */ 658 cinfop->mhc_ctype = ctype; 659 cinfop->mhc_tray = cinfop->mhc_bus; 660 return; 661 } 662 663 if ((fd = open(pln_ctlr_name, (O_RDONLY|O_NDELAY), 0)) < 0) { 664 mutex_unlock(&mhd_pln_mx); 665 Free(pln_ctlr_name); 666 return; 667 } 668 669 (void) memset(&ucmd, 0, sizeof (ucmd)); 670 (void) memset(&cdb, 0, sizeof (cdb)); 671 (void) memset(&inq, 0, sizeof (inq)); 672 cdb.scc_cmd = SCMD_INQUIRY; 673 cdb.g0_count0 = sizeof (inq); 674 ucmd.uscsi_cdb = (caddr_t)&cdb; 675 ucmd.uscsi_cdblen = CDB_GROUP0; 676 ucmd.uscsi_bufaddr = (caddr_t)&inq; 677 ucmd.uscsi_buflen = sizeof (inq); 678 ucmd.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE; 679 ucmd.uscsi_timeout = 30; 680 if (ioctl(fd, USCSICMD, &ucmd)) { 681 mutex_unlock(&mhd_pln_mx); 682 (void) close(fd); 683 MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): failed errno %d\n", 684 pln_ctlr_name, errno)); 685 Free(pln_ctlr_name); 686 return; 687 } 688 689 (void) close(fd); 690 MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): success\n", pln_ctlr_name)); 691 692 /* Make all trailing spaces be null char */ 693 for (p = inq.inq_pid + sizeof (inq.inq_pid) - 1; p != inq.inq_pid; 694 p--) { 695 if (*p == '\0') 696 continue; 697 if (!isspace(*p)) 698 break; 699 *p = '\0'; 700 } 701 702 if (strncmp(inq.inq_pid, META_SSA200_PID, sizeof (inq.inq_pid)) != 0) 703 goto out; 704 705 /* over-ride the ctype, and tray */ 706 cinfop->mhc_ctype = MHD_CTLR_SSA200; 707 cinfop->mhc_tray = cinfop->mhc_bus; 708 709 out: 710 add_pln_cache(pln_ctlr_name, cinfop->mhc_ctype); 711 mutex_unlock(&mhd_pln_mx); 712 } 713 714 /* 715 * get controller info 716 */ 717 static void 718 match_SSA100( 719 mhd_drive_t *dp, 720 char *path 721 ) 722 { 723 mhd_cinfo_t *cinfop = &dp->dr_drive_id.did_cinfo; 724 uint_t i; 725 char *p; 726 lloff_t wwn; 727 const char *fmt; 728 729 /* update and lock controller map */ 730 if (update_map() != 0) 731 return; /* give up */ 732 assert(RW_WRITE_HELD(&ctlr_rw) || RW_READ_HELD(&ctlr_rw)); 733 734 /* look for match in cache */ 735 for (i = 0; (i < ctlr_num); ++i) { 736 mhd_ctlrmap_t *cmp = &ctlr_map[i]; 737 738 fmt = cmp->scan; 739 if ((regex(cmp->regexpr1, path) != NULL) && 740 ((p = regex(cmp->regexpr2, path)) != NULL) && 741 (sscanf(p, fmt, 742 (ulong_t *)&wwn._p._u, (ulong_t *)&wwn._p._l) == 2)) { 743 cinfop->mhc_ctype = MHD_CTLR_SSA100; 744 cinfop->mhc_tray = cmp->tray; 745 cinfop->mhc_bus = cmp->bus; 746 cinfop->mhc_wwn = wwn._f; 747 match_SSA200(dp, path); 748 break; 749 } 750 } 751 752 /* unlock controller map */ 753 unlock_map(); 754 } 755 756 /* 757 * get unique drive ID 758 */ 759 static int 760 mhd_ident( 761 mhd_drive_t *dp 762 ) 763 { 764 mhd_drive_set_t *sp = dp->dr_sp; 765 int serial = (sp->sr_options & MHD_SERIAL); 766 struct uscsi_cmd ucmd; 767 union scsi_cdb cdb; 768 struct scsi_inquiry inq; 769 struct extvtoc vtoc_buf; 770 char path[MAXPATHLEN + 1]; 771 int len; 772 int err; 773 774 /* check locks */ 775 assert(MUTEX_HELD(&sp->sr_mx)); 776 assert(dp->dr_fd >= 0); 777 assert(dp->dr_state & DRIVE_IDENTING); 778 779 /* reset ID */ 780 (void) memset(&dp->dr_drive_id, 0, sizeof (dp->dr_drive_id)); 781 782 /* get serial number */ 783 if (dp->dr_state & DRIVE_SERIALING) { 784 if (! serial) 785 mhd_mx_unlock(&sp->sr_mx); 786 (void) memset(&ucmd, 0, sizeof (ucmd)); 787 (void) memset(&cdb, 0, sizeof (cdb)); 788 (void) memset(&inq, 0, sizeof (inq)); 789 cdb.scc_cmd = SCMD_INQUIRY; 790 cdb.g0_count0 = sizeof (inq); 791 ucmd.uscsi_cdb = (caddr_t)&cdb; 792 ucmd.uscsi_cdblen = CDB_GROUP0; 793 ucmd.uscsi_bufaddr = (caddr_t)&inq; 794 ucmd.uscsi_buflen = sizeof (inq); 795 ucmd.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE; 796 ucmd.uscsi_timeout = 30; 797 err = ioctl(dp->dr_fd, USCSICMD, &ucmd); 798 if (! serial) 799 mhd_mx_lock(&sp->sr_mx); 800 if (err != 0) { 801 MHDPRINTF(( 802 "%s: USCSICMD(SCMD_INQUIRY): failed errno %d\n", 803 dp->dr_rname, errno)); 804 dp->dr_drive_id.did_flags &= ~MHD_DID_SERIAL; 805 } else { 806 char *p, *e; 807 uint_t i; 808 809 MHDPRINTF(("%s: USCSICMD(SCMD_INQUIRY): success\n", 810 dp->dr_rname)); 811 dp->dr_drive_id.did_flags |= MHD_DID_SERIAL; 812 p = dp->dr_drive_id.did_serial; 813 e = p + sizeof (dp->dr_drive_id.did_serial); 814 for (i = 0; 815 ((i < sizeof (inq.inq_vid)) && (p < e)); ++i) 816 *p++ = inq.inq_vid[i]; 817 for (i = 0; 818 ((i < sizeof (inq.inq_pid)) && (p < e)); ++i) 819 *p++ = inq.inq_pid[i]; 820 for (i = 0; 821 ((i < sizeof (inq.inq_revision)) && (p < e)); ++i) 822 *p++ = inq.inq_revision[i]; 823 for (i = 0; 824 ((i < sizeof (inq.inq_serial)) && (p < e)); ++i) 825 *p++ = inq.inq_serial[i]; 826 assert(p == e); 827 for (p = dp->dr_drive_id.did_serial; (p < e); ++p) { 828 if (*p == '\0') 829 *p = ' '; 830 } 831 } 832 } else { 833 dp->dr_drive_id.did_flags &= ~MHD_DID_SERIAL; 834 } 835 836 /* get VTOC */ 837 if (dp->dr_state & DRIVE_VTOCING) { 838 if (! serial) 839 mhd_mx_unlock(&sp->sr_mx); 840 (void) memset(&vtoc_buf, 0, sizeof (vtoc_buf)); 841 err = read_extvtoc(dp->dr_fd, &vtoc_buf); 842 if (! serial) 843 mhd_mx_lock(&sp->sr_mx); 844 if (err < 0) { 845 MHDPRINTF(("%s: read_extvtoc: failed errno %d\n", 846 dp->dr_rname, errno)); 847 dp->dr_drive_id.did_flags &= ~MHD_DID_TIME; 848 } else { 849 MHDPRINTF(("%s: read_extvtoc: success\n", 850 dp->dr_rname)); 851 dp->dr_drive_id.did_flags |= MHD_DID_TIME; 852 dp->dr_drive_id.did_time = vtoc_buf.timestamp[0]; 853 } 854 } else { 855 dp->dr_drive_id.did_flags &= ~MHD_DID_TIME; 856 } 857 858 /* get controller info */ 859 if (dp->dr_state & DRIVE_CINFOING) { 860 if (! serial) 861 mhd_mx_unlock(&sp->sr_mx); 862 len = readlink(dp->dr_rname0, path, (sizeof (path) - 1)); 863 if (! serial) 864 mhd_mx_lock(&sp->sr_mx); 865 if (len >= sizeof (path)) { 866 len = -1; 867 errno = ENAMETOOLONG; 868 } 869 if (len < 0) { 870 MHDPRINTF(("%s: readlink: failed errno %d\n", 871 dp->dr_rname0, errno)); 872 dp->dr_drive_id.did_flags &= ~MHD_DID_CINFO; 873 } else { 874 MHDPRINTF(("%s: readlink: success\n", 875 dp->dr_rname0)); 876 dp->dr_drive_id.did_flags |= MHD_DID_CINFO; 877 (void) memset(&dp->dr_drive_id.did_cinfo, 0, 878 sizeof (dp->dr_drive_id.did_cinfo)); 879 match_SSA100(dp, path); 880 } 881 } else { 882 dp->dr_drive_id.did_flags &= ~MHD_DID_CINFO; 883 } 884 885 /* return success */ 886 (void) mhd_state_clr(dp, DRIVE_IDENTING, NULL); 887 return (0); 888 } 889 890 /* 891 * disk thread 892 */ 893 static void 894 mhd_drive_thread( 895 mhd_drive_t *dp 896 ) 897 { 898 mhd_drive_set_t *sp = dp->dr_sp; 899 900 /* wait for dp->dr_thread to be filled in */ 901 assert(sp != NULL); 902 mhd_mx_lock(&sp->sr_mx); 903 904 /* forever */ 905 for (;;) { 906 /* check locks */ 907 assert(MUTEX_HELD(&sp->sr_mx)); 908 assert(dp->dr_thread == thr_self()); 909 910 /* check for changed set */ 911 if (sp != dp->dr_sp) { 912 MHDPRINTF2(("%s: changed from set '%s' to '%s'\n", 913 dp->dr_rname, sp->sr_name, dp->dr_sp->sr_name)); 914 915 mhd_mx_unlock(&sp->sr_mx); 916 sp = dp->dr_sp; 917 mhd_mx_lock(&sp->sr_mx); 918 } 919 920 /* open drive, if necessary */ 921 if ((dp->dr_fd < 0) && (! (DRIVE_IS_IDLE(dp) || 922 (dp->dr_state == DRIVE_IDLING)))) { 923 int serial = (sp->sr_options & MHD_SERIAL); 924 925 if (! serial) 926 mhd_mx_unlock(&sp->sr_mx); 927 dp->dr_fd = open(dp->dr_rname0, (O_RDWR|O_NDELAY), 0); 928 if (! serial) 929 mhd_mx_lock(&sp->sr_mx); 930 if (dp->dr_fd < 0) { 931 mhd_perror("%s: open", dp->dr_rname); 932 (void) mhd_state(dp, DRIVE_ERRORED, NULL); 933 dp->dr_errnum = errno; 934 } 935 continue; 936 } 937 938 /* dispatch */ 939 switch (dp->dr_state) { 940 case DRIVE_IDLE: 941 MHDPRINTF1(("%s: IDLE\n", dp->dr_rname)); 942 break; 943 944 case DRIVE_ERRORED: 945 MHDPRINTF1(("%s: ERRORED %d\n", 946 dp->dr_rname, dp->dr_errnum)); 947 break; 948 949 case DRIVE_IDLING: 950 (void) mhd_state(dp, DRIVE_IDLE, NULL); 951 continue; 952 953 case DRIVE_RESERVING: 954 MHDPRINTF1(("%s: RESERVING\n", dp->dr_rname)); 955 (void) mhd_reserve(dp); 956 assert(DRIVE_IS_IDLE(dp)); 957 continue; 958 959 case DRIVE_FAILFASTING: 960 MHDPRINTF1(("%s: FAILFASTING\n", dp->dr_rname)); 961 (void) mhd_failfast(dp); 962 assert(DRIVE_IS_IDLE(dp)); 963 continue; 964 965 case DRIVE_RELEASING: 966 MHDPRINTF1(("%s: RELEASING\n", dp->dr_rname)); 967 (void) mhd_release(dp); 968 assert(DRIVE_IS_IDLE(dp)); 969 continue; 970 971 /* non-exclusive states */ 972 default: 973 assert(! (dp->dr_state & 974 (DRIVE_EXCLUSIVE_STATES & ~DRIVE_ERRORED))); 975 if (dp->dr_state & (DRIVE_PROBING | DRIVE_STATUSING)) { 976 MHDPRINTF1(("%s: PROBING\n", dp->dr_rname)); 977 (void) mhd_probe(dp); 978 assert(! (dp->dr_state & DRIVE_STATUSING)); 979 } 980 if (dp->dr_state & DRIVE_IDENTING) { 981 MHDPRINTF1(("%s: IDENTING\n", dp->dr_rname)); 982 (void) mhd_ident(dp); 983 assert(! (dp->dr_state & DRIVE_IDENTING)); 984 continue; /* in case we're probing */ 985 } 986 break; 987 } 988 989 /* close drive, if possible */ 990 if ((dp->dr_fd >= 0) && (DRIVE_IS_IDLE(dp))) { 991 int serial = (sp->sr_options & MHD_SERIAL); 992 993 if (! serial) 994 mhd_mx_unlock(&sp->sr_mx); 995 (void) close(dp->dr_fd); /* sd/ssd bug */ 996 if (! serial) 997 mhd_mx_lock(&sp->sr_mx); 998 dp->dr_fd = -1; 999 } 1000 1001 /* wake up anybody waiting */ 1002 mhd_cv_broadcast(&sp->sr_cv); 1003 1004 /* see if anything happened */ 1005 if (! DRIVE_IS_IDLE(dp)) 1006 continue; 1007 1008 /* wait for something to happen */ 1009 if (! (dp->dr_state & DRIVE_PROBING)) { 1010 mhd_cv_wait(&dp->dr_cv, &sp->sr_mx); 1011 } else { 1012 mhd_cv_timedwait(&dp->dr_cv, &sp->sr_mx, 1013 (sp->sr_timeouts.mh_ff / 2)); 1014 } 1015 } 1016 } 1017 1018 /* 1019 * kick off drive thread 1020 */ 1021 static int 1022 mhd_thread_create( 1023 mhd_drive_t *dp, 1024 mhd_error_t *mhep 1025 ) 1026 { 1027 mhd_drive_set_t *sp = dp->dr_sp; 1028 thread_t thread = NULL; 1029 int rval = 0; 1030 1031 /* check lock and thread */ 1032 assert(MUTEX_HELD(&sp->sr_mx)); 1033 assert(dp->dr_thread == NULL); 1034 1035 /* create thread */ 1036 if (thr_create(NULL, 0, (void *(*)(void *))mhd_drive_thread, 1037 (void *)dp, (THR_DETACHED | THR_BOUND), &thread) != 0) { 1038 rval = mhd_error(mhep, errno, "thr_create"); 1039 } else { 1040 assert(thread != NULL); 1041 dp->dr_thread = thread; 1042 } 1043 1044 /* return success */ 1045 return (rval); 1046 } 1047 1048 /* 1049 * peel off s%u from name 1050 */ 1051 static char * 1052 diskname( 1053 const char *sname 1054 ) 1055 { 1056 char *dname; 1057 char *p, *e; 1058 1059 /* duplicate name */ 1060 if ((dname = Strdup(sname)) == NULL) 1061 return (NULL); 1062 1063 /* gobble number and 's' */ 1064 p = e = dname + strlen(dname) - 1; 1065 for (; (p > dname); --p) { 1066 if (!isdigit(*p)) 1067 break; 1068 } 1069 if ((p == e) || (p <= dname)) { 1070 Free(dname); 1071 return (NULL); 1072 } 1073 if (*p-- != 's') { 1074 Free(dname); 1075 return (NULL); 1076 } 1077 if ((p <= dname) || (!isdigit(*p))) { 1078 Free(dname); 1079 return (NULL); 1080 } 1081 *(++p) = '\0'; 1082 return (dname); 1083 } 1084 1085 /* 1086 * create new drive 1087 */ 1088 mhd_drive_t * 1089 mhd_create_drive( 1090 mhd_drive_set_t *sp, /* new set */ 1091 char *rname, /* raw drive name */ 1092 int *fdp, /* open device or -1 */ 1093 mhd_error_t *mhep /* returned error */ 1094 ) 1095 { 1096 mhd_drive_t *dp = NULL; 1097 char *rname0 = NULL; 1098 1099 /* check locks */ 1100 assert(MUTEX_HELD(&sp->sr_mx)); 1101 1102 /* if drive already exists */ 1103 if ((dp = mhd_find_drive(rname)) != NULL) { 1104 mhd_drive_set_t *oldsp = dp->dr_sp; 1105 1106 /* if set has changed, move drive */ 1107 if (oldsp != sp) { 1108 mhd_mx_unlock(&sp->sr_mx); 1109 mhd_mx_lock(&oldsp->sr_mx); 1110 if (mhd_idle(dp, mhep) != 0) { 1111 mhd_mx_unlock(&oldsp->sr_mx); 1112 mhd_mx_lock(&sp->sr_mx); 1113 return (NULL); 1114 } 1115 mhd_del_drive_from_set(dp); 1116 mhd_mx_unlock(&oldsp->sr_mx); 1117 mhd_mx_lock(&sp->sr_mx); 1118 mhd_add_drive_to_set(sp, dp); 1119 } 1120 1121 /* return drive */ 1122 return (dp); 1123 } 1124 1125 /* build slice0 */ 1126 rname0 = Malloc(strlen(rname) + strlen("s0") + 1); 1127 (void) strcpy(rname0, rname); 1128 (void) strcat(rname0, "s0"); 1129 1130 /* allocate and initialize drive */ 1131 dp = Zalloc(sizeof (*dp)); 1132 dp->dr_sp = sp; 1133 dp->dr_rname = Strdup(rname); 1134 dp->dr_rname0 = rname0; 1135 mhd_cv_init(&dp->dr_cv); 1136 dp->dr_thread = NULL; 1137 dp->dr_fd = -1; 1138 dp->dr_state = DRIVE_IDLE; 1139 1140 /* steal open drive */ 1141 if ((fdp != NULL) && (*fdp >= 0)) { 1142 dp->dr_fd = *fdp; 1143 *fdp = -1; 1144 } 1145 1146 /* add to set */ 1147 mhd_add_drive_to_set(sp, dp); 1148 1149 /* kick off drive thread */ 1150 if (mhd_thread_create(dp, mhep) != 0) { 1151 Free(dp->dr_rname0); 1152 Free(dp->dr_rname); 1153 Free(dp); 1154 return (NULL); 1155 } 1156 1157 /* return drive */ 1158 return (dp); 1159 } 1160 1161 /* 1162 * find or create drive in any set 1163 */ 1164 static mhd_drive_t * 1165 mhd_create_drive_anyset( 1166 char *rname, 1167 int *fdp, 1168 mhd_error_t *mhep 1169 ) 1170 { 1171 mhd_drive_set_t *null_sp = mhd_create_set(NULL, 0, NULL, NULL); 1172 mhd_drive_t *dp; 1173 1174 /* check locks */ 1175 assert(null_sp != NULL); 1176 1177 /* drive already exists */ 1178 if ((dp = mhd_find_drive(rname)) != NULL) 1179 return (dp); 1180 1181 /* add to null set */ 1182 mhd_mx_lock(&null_sp->sr_mx); 1183 dp = mhd_create_drive(null_sp, rname, fdp, mhep); 1184 mhd_mx_unlock(&null_sp->sr_mx); 1185 1186 /* return drive */ 1187 return (dp); 1188 } 1189 1190 /* 1191 * process a file in the tree walk 1192 */ 1193 static int 1194 do_disk( 1195 const char *path, 1196 const struct stat *statp, 1197 int type 1198 ) 1199 { 1200 char *dname = NULL; 1201 int fd = -1; 1202 struct dk_cinfo cinfo; 1203 mhd_error_t status = mhd_null_error; 1204 1205 /* skip all but character devices */ 1206 if ((type != FTW_F) || (! S_ISCHR(statp->st_mode)) || 1207 ((dname = diskname(path)) == NULL)) { 1208 return (0); 1209 } 1210 1211 /* see if drive already exists */ 1212 if (mhd_find_drive(dname) != NULL) 1213 return (0); 1214 1215 /* see if device is a disk */ 1216 if ((fd = open(path, (O_RDONLY|O_NDELAY), 0)) < 0) 1217 goto out; 1218 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 1219 switch (errno) { 1220 case EINVAL: 1221 case ENOTTY: 1222 break; 1223 default: 1224 mhd_perror("DKIOCINFO: %s", path); 1225 break; 1226 } 1227 goto out; 1228 } 1229 1230 /* skip CDROMs */ 1231 if (cinfo.dki_ctype == DKC_CDROM) { 1232 (void) close(fd); 1233 Free(dname); 1234 return (0); 1235 } 1236 1237 /* put disk on list */ 1238 if (mhd_create_drive_anyset(dname, &fd, &status) == NULL) { 1239 mhde_perror(&status, ""); 1240 goto out; 1241 } 1242 1243 /* cleanup, return success (no matter what) */ 1244 out: 1245 if (dname != NULL) 1246 Free(dname); 1247 if (fd >= 0) 1248 (void) close(fd); 1249 mhd_clrerror(&status); 1250 return (0); 1251 } 1252 1253 /* 1254 * find or create all the drives under a given directory 1255 */ 1256 int 1257 mhd_create_drives( 1258 char *path, 1259 mhd_error_t *mhep 1260 ) 1261 { 1262 /* default */ 1263 if ((path == NULL) || (*path == '\0')) 1264 path = "/dev/rdsk"; 1265 1266 free_pln_cache(); 1267 1268 /* walk the directory, adding disks */ 1269 if (ftw(path, do_disk, 5) != 0) 1270 return (mhd_error(mhep, errno, path)); 1271 1272 /* return success */ 1273 return (0); 1274 } 1275