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 * Copyright 2003 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 #include "mhd_local.h" 30 31 /* 32 * manipulate set list 33 */ 34 35 /* 36 * global set list 37 */ 38 static mutex_t mhd_set_mx = DEFAULTMUTEX; 39 static uint_t mhd_nset = 0; 40 static mhd_drive_set_t **mhd_sets = NULL; 41 42 /* 43 * add drive to set 44 */ 45 void 46 mhd_add_drive_to_set( 47 mhd_drive_set_t *sp, 48 mhd_drive_t *dp 49 ) 50 { 51 mhd_drive_list_t *dlp = &sp->sr_drives; 52 53 /* check locks */ 54 assert(MUTEX_HELD(&mhd_set_mx)); 55 assert(MUTEX_HELD(&sp->sr_mx)); 56 assert(DRIVE_IS_IDLE(dp)); 57 58 /* add to set */ 59 mhd_add_drive(dlp, dp); 60 61 /* adjust backlink */ 62 dp->dr_sp = sp; 63 } 64 65 /* 66 * delete drive from set 67 */ 68 void 69 mhd_del_drive_from_set( 70 mhd_drive_t *dp 71 ) 72 { 73 mhd_drive_set_t *sp = dp->dr_sp; 74 mhd_drive_list_t *dlp = &sp->sr_drives; 75 76 /* check locks */ 77 assert(MUTEX_HELD(&mhd_set_mx)); 78 assert(MUTEX_HELD(&sp->sr_mx)); 79 assert(DRIVE_IS_IDLE(dp)); 80 81 /* delete from set */ 82 mhd_del_drive(dlp, dp); 83 84 /* adjust backlink */ 85 dp->dr_sp = NULL; 86 } 87 88 /* 89 * find set in list 90 */ 91 static mhd_drive_set_t * 92 mhd_find_set( 93 char *setname 94 ) 95 { 96 uint_t i; 97 98 /* check lock */ 99 assert(MUTEX_HELD(&mhd_set_mx)); 100 101 /* look for set */ 102 for (i = 0; (i < mhd_nset); ++i) { 103 mhd_drive_set_t *sp = mhd_sets[i]; 104 105 if (strcmp(setname, sp->sr_name) == 0) 106 return (sp); 107 } 108 109 /* not found */ 110 return (NULL); 111 } 112 113 /* 114 * wait for operation to complete 115 */ 116 static void 117 mhd_wait_set( 118 mhd_drive_set_t *sp, 119 mhd_drive_list_t *dlp, 120 mhd_state_t state 121 ) 122 { 123 /* check lock */ 124 assert(MUTEX_HELD(&mhd_set_mx)); 125 assert(MUTEX_HELD(&sp->sr_mx)); 126 127 /* wait for complete */ 128 for (;;) { 129 uint_t cnt = 0; 130 uint_t i; 131 132 /* kick threads */ 133 for (i = 0; (i < dlp->dl_ndrive); ++i) { 134 mhd_drive_t *dp = dlp->dl_drives[i]; 135 136 /* IDLE or ERRORED */ 137 if (state == DRIVE_IDLE) { 138 if (DRIVE_IS_IDLE(dp)) 139 continue; 140 } 141 142 /* operation complete */ 143 else { 144 if (! (dp->dr_state & state)) 145 continue; 146 } 147 148 /* kick thread */ 149 mhd_cv_broadcast(&dp->dr_cv); 150 ++cnt; 151 } 152 153 /* if complete, quit */ 154 if (cnt == 0) 155 break; 156 157 /* wait for something to happen */ 158 (void) mhd_cv_wait(&sp->sr_cv, &sp->sr_mx); 159 } 160 } 161 162 /* 163 * idle set 164 */ 165 static int 166 mhd_idle_set( 167 mhd_drive_set_t *sp, 168 mhd_drive_list_t *dlp, 169 mhd_error_t *mhep 170 ) 171 { 172 uint_t i; 173 174 /* check lock */ 175 assert(MUTEX_HELD(&mhd_set_mx)); 176 assert(MUTEX_HELD(&sp->sr_mx)); 177 178 /* disarm any failfast */ 179 if (dlp->dl_ndrive >= sp->sr_drives.dl_ndrive) { 180 if (mhd_ff_disarm(sp, mhep) != 0) 181 return (-1); 182 } 183 184 /* set IDLING */ 185 for (i = 0; (i < dlp->dl_ndrive); ++i) { 186 mhd_drive_t *dp = dlp->dl_drives[i]; 187 188 if (! DRIVE_IS_IDLE(dp)) { 189 if (mhd_state(dp, DRIVE_IDLING, mhep) != 0) 190 return (-1); 191 } 192 } 193 194 /* wait for IDLE */ 195 mhd_wait_set(sp, dlp, DRIVE_IDLE); 196 197 /* return success */ 198 return (0); 199 } 200 201 /* 202 * create or update new set 203 */ 204 mhd_drive_set_t * 205 mhd_create_set( 206 mhd_set_t *mhsp, 207 mhd_opts_t options, 208 mhd_drive_list_t *dlp, 209 mhd_error_t *mhep 210 ) 211 { 212 char *setname; 213 mhd_drive_set_t *sp; 214 mhd_drive_list_t *sp_dlp; 215 mhd_drive_set_t *null_sp; 216 uint_t i; 217 218 /* check locks */ 219 assert(MUTEX_HELD(&mhd_set_mx)); 220 221 /* get setname */ 222 if (mhsp == NULL) 223 setname = ""; 224 else 225 setname = mhsp->setname; 226 227 /* find or create set */ 228 if ((sp = mhd_find_set(setname)) == NULL) { 229 /* allocate and initialize set */ 230 sp = Zalloc(sizeof (*sp)); 231 sp->sr_name = Strdup(setname); 232 mhd_mx_init(&sp->sr_mx); 233 mhd_cv_init(&sp->sr_cv); 234 sp->sr_ff = -1; 235 236 /* append to set list */ 237 ++mhd_nset; 238 mhd_sets = Realloc(mhd_sets, (mhd_nset * sizeof (*mhd_sets))); 239 mhd_sets[mhd_nset - 1] = sp; 240 } 241 sp_dlp = &sp->sr_drives; 242 243 /* if just grabbing null set, return */ 244 if (mhsp == NULL) 245 return (sp); 246 assert(strcmp(setname, "") != 0); 247 assert(mhep != NULL); 248 249 /* get null set */ 250 null_sp = mhd_create_set(NULL, 0, NULL, NULL); 251 assert(null_sp != NULL); 252 assert(sp != null_sp); 253 254 /* grab set lock */ 255 mhd_mx_lock(&sp->sr_mx); 256 257 /* save options */ 258 if (options & MHD_SERIAL) 259 sp->sr_options |= MHD_SERIAL; 260 else 261 sp->sr_options &= ~MHD_SERIAL; 262 263 /* move drives no longer in set to null set */ 264 if (! (options & MHD_PARTIAL_SET)) { 265 for (i = 0; (i < sp_dlp->dl_ndrive); /* void */) { 266 mhd_drive_t *dp = sp_dlp->dl_drives[i]; 267 uint_t j; 268 269 /* check still there */ 270 for (j = 0; (j < mhsp->drives.drives_len); ++j) { 271 mhd_drivename_t mhdp; 272 273 mhdp = mhsp->drives.drives_val[j]; 274 if (strcmp(dp->dr_rname, mhdp) == 0) 275 break; 276 } 277 if (j < mhsp->drives.drives_len) { 278 ++i; 279 continue; 280 } 281 282 /* idle the drive */ 283 if (mhd_idle(dp, mhep) != 0) 284 mhd_clrerror(mhep); 285 286 /* move to null set */ 287 mhd_del_drive_from_set(dp); 288 mhd_mx_unlock(&sp->sr_mx); 289 mhd_mx_lock(&null_sp->sr_mx); 290 mhd_add_drive_to_set(null_sp, dp); 291 mhd_mx_unlock(&null_sp->sr_mx); 292 mhd_mx_lock(&sp->sr_mx); 293 } 294 } 295 296 /* add new drives to lists */ 297 for (i = 0; (i < mhsp->drives.drives_len); ++i) { 298 mhd_drivename_t mhdp = mhsp->drives.drives_val[i]; 299 uint_t j; 300 mhd_drive_t *dp; 301 302 /* check already there */ 303 for (j = 0; (j < dlp->dl_ndrive); ++j) { 304 dp = dlp->dl_drives[j]; 305 if (strcmp(mhdp, dp->dr_rname) == 0) 306 break; 307 } 308 if (j < dlp->dl_ndrive) { 309 mhd_add_drive(dlp, dp); 310 continue; 311 } 312 313 /* add drive to set */ 314 if ((dp = mhd_create_drive(sp, mhdp, NULL, mhep)) == NULL) { 315 mhde_perror(mhep, "mhd_create_drive: %s", mhdp); 316 continue; 317 } 318 mhd_add_drive(dlp, dp); 319 } 320 321 /* debug */ 322 #ifdef MHD_DEBUG 323 if (mhd_debug > 0) { 324 for (i = 0; (i < mhd_nset); ++i) { 325 mhd_drive_set_t *sp = mhd_sets[i]; 326 mhd_drive_list_t *dlp = &sp->sr_drives; 327 char buf[10240]; 328 uint_t j; 329 330 (void) snprintf(buf, sizeof (buf), "set '%s':", 331 sp->sr_name); 332 for (j = 0; (j < dlp->dl_ndrive); ++j) { 333 mhd_drive_t *dp = dlp->dl_drives[j]; 334 char *p; 335 336 if ((p = strrchr(dp->dr_rname, '/')) != NULL) 337 ++p; 338 else 339 p = dp->dr_rname; 340 (void) strncat(buf, " ", sizeof (buf)); 341 (void) strncat(buf, p, sizeof (buf)); 342 } 343 buf[sizeof (buf) - 1] = '\0'; 344 mhd_eprintf("%s\n", buf); 345 } 346 } 347 #endif /* MHD_DEBUG */ 348 349 /* unlock, return set */ 350 mhd_mx_unlock(&sp->sr_mx); 351 return (sp); 352 } 353 354 /* 355 * find drive 356 */ 357 mhd_drive_t * 358 mhd_find_drive( 359 char *rname 360 ) 361 { 362 uint_t i; 363 364 /* check locks */ 365 assert(MUTEX_HELD(&mhd_set_mx)); 366 367 /* for each set */ 368 for (i = 0; (i < mhd_nset); ++i) { 369 mhd_drive_set_t *sp = mhd_sets[i]; 370 mhd_drive_list_t *dlp = &sp->sr_drives; 371 uint_t j; 372 373 /* for each drive */ 374 for (j = 0; (j < dlp->dl_ndrive); ++j) { 375 mhd_drive_t *dp = dlp->dl_drives[j]; 376 377 if (strcmp(rname, dp->dr_rname) == 0) 378 return (dp); 379 } 380 } 381 382 /* not found */ 383 return (NULL); 384 } 385 386 /* 387 * list all the drives 388 */ 389 int 390 mhd_list_drives( 391 char *path, 392 mhd_did_flags_t flags, 393 mhd_list_res_t *resultsp, 394 mhd_error_t *mhep 395 ) 396 { 397 mhd_state_t state; 398 uint_t ndrive, i, j, c; 399 400 /* grab lock */ 401 mhd_mx_lock(&mhd_set_mx); 402 403 /* add path to list */ 404 if (mhd_create_drives(path, mhep) != 0) { 405 mhd_mx_unlock(&mhd_set_mx); 406 return (-1); 407 } 408 409 /* get what we want */ 410 state = 0; 411 if (flags & MHD_DID_SERIAL) 412 state |= DRIVE_SERIALING; 413 if (flags & MHD_DID_TIME) 414 state |= DRIVE_VTOCING; 415 if (flags & MHD_DID_CINFO) 416 state |= DRIVE_CINFOING; 417 418 /* ident and count drives */ 419 for (ndrive = 0, i = 0; (i < mhd_nset); ++i) { 420 mhd_drive_set_t *sp = mhd_sets[i]; 421 mhd_drive_list_t *dlp = &sp->sr_drives; 422 423 /* count drives */ 424 ndrive += dlp->dl_ndrive; 425 426 /* ident drives */ 427 if (state != 0) { 428 mhd_mx_lock(&sp->sr_mx); 429 for (j = 0; (j < dlp->dl_ndrive); ++j) { 430 mhd_drive_t *dp = dlp->dl_drives[j]; 431 432 if (mhd_state_set(dp, state, mhep) != 0) { 433 mhd_mx_unlock(&sp->sr_mx); 434 mhd_mx_unlock(&mhd_set_mx); 435 return (-1); 436 } 437 } 438 mhd_wait_set(sp, dlp, state); 439 mhd_mx_unlock(&sp->sr_mx); 440 } 441 } 442 443 /* build list */ 444 assert(resultsp->results.mhd_drive_info_list_t_len == 0); 445 assert(resultsp->results.mhd_drive_info_list_t_val == NULL); 446 resultsp->results.mhd_drive_info_list_t_len = ndrive; 447 resultsp->results.mhd_drive_info_list_t_val = Zalloc( 448 ndrive * sizeof (*resultsp->results.mhd_drive_info_list_t_val)); 449 for (c = 0, i = 0; (i < mhd_nset); ++i) { 450 mhd_drive_set_t *sp = mhd_sets[i]; 451 mhd_drive_list_t *dlp = &sp->sr_drives; 452 453 mhd_mx_lock(&sp->sr_mx); 454 for (j = 0; (j < dlp->dl_ndrive); ++j) { 455 mhd_drive_t *dp = dlp->dl_drives[j]; 456 mhd_drive_info_t *ip = 457 &resultsp->results.mhd_drive_info_list_t_val[c++]; 458 459 ip->dif_name = Strdup(dp->dr_rname); 460 ip->dif_id = dp->dr_drive_id; 461 } 462 mhd_mx_unlock(&sp->sr_mx); 463 } 464 assert(c == ndrive); 465 466 /* unlock, return count */ 467 mhd_mx_unlock(&mhd_set_mx); 468 return (ndrive); 469 } 470 471 /* 472 * release drives 473 */ 474 static int 475 mhd_release_set( 476 mhd_drive_set_t *sp, 477 mhd_drive_list_t *dlp, 478 mhd_error_t *mhep 479 ) 480 { 481 uint_t i; 482 483 /* check locks */ 484 assert(MUTEX_HELD(&mhd_set_mx)); 485 assert(MUTEX_HELD(&sp->sr_mx)); 486 487 /* idle set */ 488 if (mhd_idle_set(sp, dlp, mhep) != 0) 489 return (-1); 490 491 /* release drives */ 492 for (i = 0; (i < dlp->dl_ndrive); i++) { 493 mhd_drive_t *dp = dlp->dl_drives[i]; 494 495 if (mhd_state(dp, DRIVE_RELEASING, mhep) != 0) 496 return (-1); 497 } 498 mhd_wait_set(sp, dlp, DRIVE_IDLE); 499 500 /* return success */ 501 return (0); 502 } 503 504 /* 505 * release drives in set 506 */ 507 int 508 mhd_release_drives( 509 mhd_set_t *mhsp, 510 mhd_opts_t options, 511 mhd_error_t *mhep 512 ) 513 { 514 mhd_drive_list_t dl = mhd_null_list; 515 mhd_drive_set_t *sp; 516 int rval; 517 518 /* grab global lock */ 519 mhd_mx_lock(&mhd_set_mx); 520 521 /* create or update set */ 522 if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) { 523 mhd_mx_unlock(&mhd_set_mx); 524 mhd_free_list(&dl); 525 return (-1); 526 } 527 528 /* lock set */ 529 mhd_mx_lock(&sp->sr_mx); 530 531 /* release drives */ 532 rval = mhd_release_set(sp, &dl, mhep); 533 534 /* unlock, return success */ 535 out: 536 mhd_mx_unlock(&sp->sr_mx); 537 mhd_mx_unlock(&mhd_set_mx); 538 mhd_free_list(&dl); 539 return (rval); 540 } 541 542 /* 543 * reserve drives 544 */ 545 static int 546 mhd_reserve_set( 547 mhd_drive_set_t *sp, 548 mhd_drive_list_t *dlp, 549 mhd_error_t *mhep 550 ) 551 { 552 mhd_msec_t ff = sp->sr_timeouts.mh_ff; 553 uint_t retry, i, ok; 554 int rval = 0; 555 556 /* check locks */ 557 assert(MUTEX_HELD(&mhd_set_mx)); 558 assert(MUTEX_HELD(&sp->sr_mx)); 559 560 /* idle set, idle everyone if cancelling failfast */ 561 if (ff == 0) { 562 if (mhd_idle_set(sp, &sp->sr_drives, mhep) != 0) 563 return (-1); 564 } else { 565 if (mhd_idle_set(sp, dlp, mhep) != 0) 566 return (-1); 567 } 568 569 /* 570 * Try to take ownership of the drives twice. This helps 571 * to avoid the situation where the other machine retakes 572 * ownership of a majority drives back, but then kills itself 573 * leaving no owners. 574 */ 575 for (retry = 0; (retry < 2); ++retry) { 576 for (i = 0; (i < dlp->dl_ndrive); i++) { 577 mhd_drive_t *dp = dlp->dl_drives[i]; 578 579 if ((retry == 0) || 580 ((dp->dr_state == DRIVE_ERRORED) && 581 (dp->dr_errnum == EACCES))) { 582 if (mhd_state(dp, DRIVE_RESERVING, mhep) != 0) 583 return (-1); 584 } 585 } 586 mhd_wait_set(sp, dlp, DRIVE_IDLE); 587 } 588 589 /* 590 * Did the take ownership succeed on a majority of the drives? 591 */ 592 ok = 0; 593 for (i = 0; (i < dlp->dl_ndrive); ++i) { 594 mhd_drive_t *dp = dlp->dl_drives[i]; 595 596 if (dp->dr_state == DRIVE_IDLE) 597 ++ok; 598 } 599 600 /* 601 * Let the replica majority be the deciding factor, if able to get 602 * at least a single drive reserved. 603 */ 604 if (ok == 0) { 605 rval = mhd_error(mhep, MHD_E_MAJORITY, sp->sr_name); 606 goto out; 607 } 608 609 /* 610 * Enable the failfast probes if we haven't given up yet. 611 */ 612 switch (sp->sr_ff_mode) { 613 614 /* do nothing */ 615 default: 616 assert(0); 617 /* FALLTHROUGH */ 618 case MHD_FF_NONE: 619 goto out; 620 621 /* old style per drive failfast */ 622 case MHD_FF_DRIVER: 623 for (i = 0; (i < dlp->dl_ndrive); i++) { 624 mhd_drive_t *dp = dlp->dl_drives[i]; 625 626 if (dp->dr_state != DRIVE_ERRORED) { 627 if (mhd_state(dp, DRIVE_FAILFASTING, 628 mhep) != 0) { 629 rval = -1; 630 goto out; 631 } 632 } 633 } 634 mhd_wait_set(sp, dlp, DRIVE_IDLE); 635 break; 636 637 /* failfast probe threads */ 638 case MHD_FF_DEBUG: 639 case MHD_FF_HALT: 640 case MHD_FF_PANIC: 641 if (ff != 0) { 642 if (mhd_ff_open(sp, mhep) != 0) { 643 rval = -1; 644 goto out; 645 } 646 for (i = 0; (i < dlp->dl_ndrive); i++) { 647 mhd_drive_t *dp = dlp->dl_drives[i]; 648 649 if (mhd_state_set(dp, DRIVE_PROBING, 650 mhep) != 0) { 651 rval = -1; 652 goto out; 653 } 654 dp->dr_time = mhd_time(); 655 } 656 (void) mhd_ff_rearm(sp, mhep); 657 } 658 break; 659 } 660 661 /* cleanup, return success */ 662 out: 663 if (rval != 0) { 664 mhd_error_t status = mhd_null_error; 665 666 (void) mhd_release_set(sp, dlp, &status); 667 mhd_clrerror(&status); 668 } 669 return (rval); 670 } 671 672 /* 673 * reserve drives in set 674 */ 675 int 676 mhd_reserve_drives( 677 mhd_set_t *mhsp, 678 mhd_mhiargs_t *timeoutp, 679 mhd_ff_mode_t ff_mode, 680 mhd_opts_t options, 681 mhd_error_t *mhep 682 ) 683 { 684 mhd_drive_list_t dl = mhd_null_list; 685 mhd_drive_set_t *sp; 686 int rval; 687 688 /* grab global lock */ 689 mhd_mx_lock(&mhd_set_mx); 690 691 /* create or update set */ 692 if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) { 693 mhd_mx_unlock(&mhd_set_mx); 694 mhd_free_list(&dl); 695 return (-1); 696 } 697 698 /* lock set */ 699 mhd_mx_lock(&sp->sr_mx); 700 701 /* can't change mode or timeouts of partial set */ 702 if ((dl.dl_ndrive != sp->sr_drives.dl_ndrive) && 703 (options & MHD_PARTIAL_SET)) { 704 if (ff_mode != sp->sr_ff_mode) { 705 mhd_eprintf("%s: invalid ff_mode %d now %d\n", 706 sp->sr_name, ff_mode, sp->sr_ff_mode); 707 ff_mode = sp->sr_ff_mode; 708 } 709 if (timeoutp->mh_ff < sp->sr_timeouts.mh_ff) { 710 mhd_eprintf("%s: invalid mh_ff %d now %d\n", 711 sp->sr_name, timeoutp->mh_ff, 712 sp->sr_timeouts.mh_ff); 713 timeoutp->mh_ff = sp->sr_timeouts.mh_ff; 714 } 715 } 716 717 /* save timouts and mode */ 718 sp->sr_timeouts = *timeoutp; 719 sp->sr_ff_mode = ff_mode; 720 721 /* reserve drives */ 722 rval = mhd_reserve_set(sp, &dl, mhep); 723 724 /* unlock, return success */ 725 out: 726 mhd_mx_unlock(&sp->sr_mx); 727 mhd_mx_unlock(&mhd_set_mx); 728 mhd_free_list(&dl); 729 return (rval); 730 } 731 732 /* 733 * status drives 734 */ 735 static int 736 mhd_status_set( 737 mhd_drive_set_t *sp, 738 mhd_drive_list_t *dlp, 739 mhd_error_t *mhep 740 ) 741 { 742 uint_t i; 743 744 /* check locks */ 745 assert(MUTEX_HELD(&mhd_set_mx)); 746 assert(MUTEX_HELD(&sp->sr_mx)); 747 748 /* status drives */ 749 for (i = 0; (i < dlp->dl_ndrive); i++) { 750 mhd_drive_t *dp = dlp->dl_drives[i]; 751 752 if (mhd_state_set(dp, DRIVE_STATUSING, mhep) != 0) 753 return (-1); 754 } 755 mhd_wait_set(sp, dlp, DRIVE_STATUSING); 756 757 /* return success */ 758 return (0); 759 } 760 761 /* 762 * status drives in set 763 */ 764 int 765 mhd_status_drives( 766 mhd_set_t *mhsp, 767 mhd_opts_t options, 768 mhd_drive_status_t **status, 769 mhd_error_t *mhep 770 ) 771 { 772 mhd_drive_list_t dl = mhd_null_list; 773 mhd_drive_list_t *dlp = &dl; 774 mhd_drive_set_t *sp; 775 uint_t i; 776 int rval = 0; 777 778 /* grab global lock */ 779 mhd_mx_lock(&mhd_set_mx); 780 781 /* create or update set */ 782 if ((sp = mhd_create_set(mhsp, options, &dl, mhep)) == NULL) { 783 mhd_mx_unlock(&mhd_set_mx); 784 mhd_free_list(&dl); 785 return (-1); 786 } 787 788 /* lock set */ 789 mhd_mx_lock(&sp->sr_mx); 790 791 /* status drives */ 792 if (mhd_status_set(sp, &dl, mhep) != 0) { 793 rval = -1; 794 goto out; 795 } 796 797 /* build list */ 798 *status = Zalloc(dlp->dl_ndrive * sizeof (**status)); 799 for (i = 0; (i < dlp->dl_ndrive); ++i) { 800 mhd_drive_t *dp = dlp->dl_drives[i]; 801 mhd_drive_status_t *statusp = &(*status)[i]; 802 803 statusp->drive = Strdup(dp->dr_rname); 804 statusp->errnum = dp->dr_errnum; 805 } 806 assert(i == dlp->dl_ndrive); 807 rval = dlp->dl_ndrive; 808 809 /* unlock, return count */ 810 out: 811 mhd_mx_unlock(&sp->sr_mx); 812 mhd_mx_unlock(&mhd_set_mx); 813 mhd_free_list(&dl); 814 return (rval); 815 } 816