1 /*- 2 * Copyright (c) 2008, 2009 Yahoo!, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <err.h> 35 #include <libutil.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include "mfiutil.h" 41 42 MFI_TABLE(top, show); 43 44 static void 45 format_stripe(char *buf, size_t buflen, uint8_t stripe) 46 { 47 48 humanize_number(buf, buflen, (1 << stripe) * 512, "", HN_AUTOSCALE, 49 HN_B | HN_NOSPACE); 50 } 51 52 static int 53 show_adapter(int ac, char **av) 54 { 55 struct mfi_ctrl_info info; 56 char stripe[5]; 57 int error, fd, comma; 58 59 if (ac != 1) { 60 warnx("show adapter: extra arguments"); 61 return (EINVAL); 62 } 63 64 fd = mfi_open(mfi_unit); 65 if (fd < 0) { 66 error = errno; 67 warn("mfi_open"); 68 return (error); 69 } 70 71 if (mfi_ctrl_get_info(fd, &info, NULL) < 0) { 72 error = errno; 73 warn("Failed to get controller info"); 74 return (error); 75 } 76 printf("mfi%d Adapter:\n", mfi_unit); 77 printf(" Product Name: %.80s\n", info.product_name); 78 printf(" Serial Number: %.32s\n", info.serial_number); 79 if (info.package_version[0] != '\0') 80 printf(" Firmware: %s\n", info.package_version); 81 printf(" RAID Levels:"); 82 #ifdef DEBUG 83 printf(" (%#x)", info.raid_levels); 84 #endif 85 comma = 0; 86 if (info.raid_levels & MFI_INFO_RAID_0) { 87 printf(" JBOD, RAID0"); 88 comma = 1; 89 } 90 if (info.raid_levels & MFI_INFO_RAID_1) { 91 printf("%s RAID1", comma ? "," : ""); 92 comma = 1; 93 } 94 if (info.raid_levels & MFI_INFO_RAID_5) { 95 printf("%s RAID5", comma ? "," : ""); 96 comma = 1; 97 } 98 if (info.raid_levels & MFI_INFO_RAID_1E) { 99 printf("%s RAID1E", comma ? "," : ""); 100 comma = 1; 101 } 102 if (info.raid_levels & MFI_INFO_RAID_6) { 103 printf("%s RAID6", comma ? "," : ""); 104 comma = 1; 105 } 106 if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) == 107 (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) { 108 printf("%s RAID10", comma ? "," : ""); 109 comma = 1; 110 } 111 if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) == 112 (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) { 113 printf("%s RAID50", comma ? "," : ""); 114 comma = 1; 115 } 116 printf("\n"); 117 printf(" Battery Backup: "); 118 if (info.hw_present & MFI_INFO_HW_BBU) 119 printf("present\n"); 120 else 121 printf("not present\n"); 122 if (info.hw_present & MFI_INFO_HW_NVRAM) 123 printf(" NVRAM: %uK\n", info.nvram_size); 124 printf(" Onboard Memory: %uM\n", info.memory_size); 125 format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.min); 126 printf(" Minimum Stripe: %s\n", stripe); 127 format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.max); 128 printf(" Maximum Stripe: %s\n", stripe); 129 130 close(fd); 131 132 return (0); 133 } 134 MFI_COMMAND(show, adapter, show_adapter); 135 136 static int 137 show_battery(int ac, char **av) 138 { 139 struct mfi_bbu_capacity_info cap; 140 struct mfi_bbu_design_info design; 141 struct mfi_bbu_status stat; 142 uint8_t status; 143 int comma, error, fd; 144 145 if (ac != 1) { 146 warnx("show battery: extra arguments"); 147 return (EINVAL); 148 } 149 150 fd = mfi_open(mfi_unit); 151 if (fd < 0) { 152 error = errno; 153 warn("mfi_open"); 154 return (error); 155 } 156 157 if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_CAPACITY_INFO, &cap, 158 sizeof(cap), NULL, 0, &status) < 0) { 159 if (status == MFI_STAT_NO_HW_PRESENT) { 160 printf("mfi%d: No battery present\n", mfi_unit); 161 return (0); 162 } 163 error = errno; 164 warn("Failed to get capacity info"); 165 return (error); 166 } 167 168 if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_DESIGN_INFO, &design, 169 sizeof(design), NULL, 0, NULL) < 0) { 170 error = errno; 171 warn("Failed to get design info"); 172 return (error); 173 } 174 175 if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_STATUS, &stat, sizeof(stat), 176 NULL, 0, NULL) < 0) { 177 error = errno; 178 warn("Failed to get status"); 179 return (error); 180 } 181 182 printf("mfi%d: Battery State:\n", mfi_unit); 183 printf(" Manufacture Date: %d/%d/%d\n", design.mfg_date >> 5 & 0x0f, 184 design.mfg_date & 0x1f, design.mfg_date >> 9 & 0xffff); 185 printf(" Serial Number: %d\n", design.serial_number); 186 printf(" Manufacturer: %s\n", design.mfg_name); 187 printf(" Model: %s\n", design.device_name); 188 printf(" Chemistry: %s\n", design.device_chemistry); 189 printf(" Design Capacity: %d mAh\n", design.design_capacity); 190 printf(" Full Charge Capacity: %d mAh\n", cap.full_charge_capacity); 191 printf(" Current Capacity: %d mAh\n", cap.remaining_capacity); 192 printf(" Charge Cycles: %d\n", cap.cycle_count); 193 printf(" Current Charge: %d%%\n", cap.relative_charge); 194 printf(" Design Voltage: %d mV\n", design.design_voltage); 195 printf(" Current Voltage: %d mV\n", stat.voltage); 196 printf(" Temperature: %d C\n", stat.temperature); 197 printf(" Status:"); 198 comma = 0; 199 if (stat.fw_status & MFI_BBU_STATE_PACK_MISSING) { 200 printf(" PACK_MISSING"); 201 comma = 1; 202 } 203 if (stat.fw_status & MFI_BBU_STATE_VOLTAGE_LOW) { 204 printf("%s VOLTAGE_LOW", comma ? "," : ""); 205 comma = 1; 206 } 207 if (stat.fw_status & MFI_BBU_STATE_TEMPERATURE_HIGH) { 208 printf("%s TEMPERATURE_HIGH", comma ? "," : ""); 209 comma = 1; 210 } 211 if (stat.fw_status & MFI_BBU_STATE_CHARGE_ACTIVE) { 212 printf("%s CHARGING", comma ? "," : ""); 213 comma = 1; 214 } 215 if (stat.fw_status & MFI_BBU_STATE_DISCHARGE_ACTIVE) { 216 printf("%s DISCHARGING", comma ? "," : ""); 217 } 218 if (!comma) 219 printf(" normal"); 220 printf("\n"); 221 switch (stat.battery_type) { 222 case MFI_BBU_TYPE_BBU: 223 printf(" State of Health: %s\n", 224 stat.detail.bbu.is_SOH_good ? "good" : "bad"); 225 break; 226 } 227 228 close(fd); 229 230 return (0); 231 } 232 MFI_COMMAND(show, battery, show_battery); 233 234 static void 235 print_ld(struct mfi_ld_info *info, int state_len) 236 { 237 struct mfi_ld_params *params = &info->ld_config.params; 238 const char *level; 239 char size[6], stripe[5]; 240 241 humanize_number(size, sizeof(size), info->size * 512, 242 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 243 format_stripe(stripe, sizeof(stripe), 244 info->ld_config.params.stripe_size); 245 level = mfi_raid_level(params->primary_raid_level, 246 params->secondary_raid_level); 247 if (state_len > 0) 248 printf("(%6s) %-8s %6s %-*s", size, level, stripe, state_len, 249 mfi_ldstate(params->state)); 250 else 251 printf("(%s) %s %s %s", size, level, stripe, 252 mfi_ldstate(params->state)); 253 } 254 255 static void 256 print_pd(struct mfi_pd_info *info, int state_len, int location) 257 { 258 const char *s; 259 char buf[6]; 260 261 humanize_number(buf, sizeof(buf), info->raw_size * 512, "", 262 HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 263 printf("(%6s) ", buf); 264 if (state_len > 0) 265 printf("%-*s", state_len, mfi_pdstate(info->fw_state)); 266 else 267 printf("%s", mfi_pdstate(info->fw_state)); 268 s = mfi_pd_inq_string(info); 269 if (s != NULL) 270 printf(" %s", s); 271 if (!location) 272 return; 273 if (info->encl_device_id == 0xffff) 274 printf(" slot %d", info->slot_number); 275 else if (info->encl_device_id == info->ref.v.device_id) 276 printf(" enclosure %d", info->encl_index); 277 else 278 printf(" enclosure %d, slot %d", info->encl_index, 279 info->slot_number); 280 } 281 282 static int 283 show_config(int ac, char **av) 284 { 285 struct mfi_config_data *config; 286 struct mfi_array *ar; 287 struct mfi_ld_config *ld; 288 struct mfi_spare *sp; 289 struct mfi_ld_info linfo; 290 struct mfi_pd_info pinfo; 291 uint16_t device_id; 292 char *p; 293 int error, fd, i, j; 294 295 if (ac != 1) { 296 warnx("show config: extra arguments"); 297 return (EINVAL); 298 } 299 300 fd = mfi_open(mfi_unit); 301 if (fd < 0) { 302 error = errno; 303 warn("mfi_open"); 304 return (error); 305 } 306 307 /* Get the config from the controller. */ 308 if (mfi_config_read(fd, &config) < 0) { 309 error = errno; 310 warn("Failed to get config"); 311 return (error); 312 } 313 314 /* Dump out the configuration. */ 315 printf("mfi%d Configuration: %d arrays, %d volumes, %d spares\n", 316 mfi_unit, config->array_count, config->log_drv_count, 317 config->spares_count); 318 p = (char *)config->array; 319 320 for (i = 0; i < config->array_count; i++) { 321 ar = (struct mfi_array *)p; 322 printf(" array %u of %u drives:\n", ar->array_ref, 323 ar->num_drives); 324 for (j = 0; j < ar->num_drives; j++) { 325 device_id = ar->pd[j].ref.v.device_id; 326 if (device_id == 0xffff) 327 printf(" drive MISSING\n"); 328 else { 329 printf(" drive %u ", device_id); 330 if (mfi_pd_get_info(fd, device_id, &pinfo, 331 NULL) < 0) 332 printf("%s", 333 mfi_pdstate(ar->pd[j].fw_state)); 334 else 335 print_pd(&pinfo, -1, 1); 336 printf("\n"); 337 } 338 } 339 p += config->array_size; 340 } 341 342 for (i = 0; i < config->log_drv_count; i++) { 343 ld = (struct mfi_ld_config *)p; 344 printf(" volume %s ", 345 mfi_volume_name(fd, ld->properties.ld.v.target_id)); 346 if (mfi_ld_get_info(fd, ld->properties.ld.v.target_id, &linfo, 347 NULL) < 0) { 348 printf("%s %s", 349 mfi_raid_level(ld->params.primary_raid_level, 350 ld->params.secondary_raid_level), 351 mfi_ldstate(ld->params.state)); 352 } else 353 print_ld(&linfo, -1); 354 if (ld->properties.name[0] != '\0') 355 printf(" <%s>", ld->properties.name); 356 printf(" spans:\n"); 357 for (j = 0; j < ld->params.span_depth; j++) 358 printf(" array %u\n", ld->span[j].array_ref); 359 p += config->log_drv_size; 360 } 361 362 for (i = 0; i < config->spares_count; i++) { 363 sp = (struct mfi_spare *)p; 364 printf(" %s spare %u ", 365 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 366 "global", sp->ref.v.device_id); 367 if (mfi_pd_get_info(fd, sp->ref.v.device_id, &pinfo, NULL) < 0) 368 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 369 else 370 print_pd(&pinfo, -1, 1); 371 if (sp->spare_type & MFI_SPARE_DEDICATED) { 372 printf(" backs:\n"); 373 for (j = 0; j < sp->array_count; j++) 374 printf(" array %u\n", sp->array_ref[j]); 375 } else 376 printf("\n"); 377 p += config->spares_size; 378 } 379 close(fd); 380 381 return (0); 382 } 383 MFI_COMMAND(show, config, show_config); 384 385 static int 386 show_volumes(int ac, char **av) 387 { 388 struct mfi_ld_list list; 389 struct mfi_ld_info info; 390 int error, fd; 391 u_int i, len, state_len; 392 393 if (ac != 1) { 394 warnx("show volumes: extra arguments"); 395 return (EINVAL); 396 } 397 398 fd = mfi_open(mfi_unit); 399 if (fd < 0) { 400 error = errno; 401 warn("mfi_open"); 402 return (error); 403 } 404 405 /* Get the logical drive list from the controller. */ 406 if (mfi_ld_get_list(fd, &list, NULL) < 0) { 407 error = errno; 408 warn("Failed to get volume list"); 409 return (error); 410 } 411 412 /* List the volumes. */ 413 printf("mfi%d Volumes:\n", mfi_unit); 414 state_len = strlen("State"); 415 for (i = 0; i < list.ld_count; i++) { 416 len = strlen(mfi_ldstate(list.ld_list[i].state)); 417 if (len > state_len) 418 state_len = len; 419 } 420 printf(" Id Size Level Stripe "); 421 len = state_len - strlen("State"); 422 for (i = 0; i < (len + 1) / 2; i++) 423 printf(" "); 424 printf("State"); 425 for (i = 0; i < len / 2; i++) 426 printf(" "); 427 printf(" Cache Name\n"); 428 for (i = 0; i < list.ld_count; i++) { 429 if (mfi_ld_get_info(fd, list.ld_list[i].ld.v.target_id, &info, 430 NULL) < 0) { 431 error = errno; 432 warn("Failed to get info for volume %d", 433 list.ld_list[i].ld.v.target_id); 434 return (error); 435 } 436 printf("%6s ", 437 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 438 print_ld(&info, state_len); 439 switch (info.ld_config.properties.current_cache_policy & 440 (MR_LD_CACHE_ALLOW_WRITE_CACHE | 441 MR_LD_CACHE_ALLOW_READ_CACHE)) { 442 case 0: 443 printf(" Disabled"); 444 break; 445 case MR_LD_CACHE_ALLOW_READ_CACHE: 446 printf(" Reads "); 447 break; 448 case MR_LD_CACHE_ALLOW_WRITE_CACHE: 449 printf(" Writes "); 450 break; 451 case MR_LD_CACHE_ALLOW_WRITE_CACHE | 452 MR_LD_CACHE_ALLOW_READ_CACHE: 453 printf(" Enabled "); 454 break; 455 } 456 if (info.ld_config.properties.name[0] != '\0') 457 printf(" <%s>", info.ld_config.properties.name); 458 printf("\n"); 459 } 460 close(fd); 461 462 return (0); 463 } 464 MFI_COMMAND(show, volumes, show_volumes); 465 466 static int 467 show_drives(int ac, char **av) 468 { 469 struct mfi_pd_list *list; 470 struct mfi_pd_info info; 471 u_int i, len, state_len; 472 int error, fd; 473 474 if (ac != 1) { 475 warnx("show drives: extra arguments"); 476 return (EINVAL); 477 } 478 479 fd = mfi_open(mfi_unit); 480 if (fd < 0) { 481 error = errno; 482 warn("mfi_open"); 483 return (error); 484 } 485 486 if (mfi_pd_get_list(fd, &list, NULL) < 0) { 487 error = errno; 488 warn("Failed to get drive list"); 489 return (error); 490 } 491 492 /* Walk the list of drives to determine width of state column. */ 493 state_len = 0; 494 for (i = 0; i < list->count; i++) { 495 if (list->addr[i].scsi_dev_type != 0) 496 continue; 497 498 if (mfi_pd_get_info(fd, list->addr[i].device_id, &info, 499 NULL) < 0) { 500 error = errno; 501 warn("Failed to fetch info for drive %u", 502 list->addr[i].device_id); 503 return (error); 504 } 505 len = strlen(mfi_pdstate(info.fw_state)); 506 if (len > state_len) 507 state_len = len; 508 } 509 510 /* List the drives. */ 511 printf("mfi%d Physical Drives:\n", mfi_unit); 512 for (i = 0; i < list->count; i++) { 513 514 /* Skip non-hard disks. */ 515 if (list->addr[i].scsi_dev_type != 0) 516 continue; 517 518 /* Fetch details for this drive. */ 519 if (mfi_pd_get_info(fd, list->addr[i].device_id, &info, 520 NULL) < 0) { 521 error = errno; 522 warn("Failed to fetch info for drive %u", 523 list->addr[i].device_id); 524 return (error); 525 } 526 527 print_pd(&info, state_len, 1); 528 printf("\n"); 529 } 530 close(fd); 531 532 return (0); 533 } 534 MFI_COMMAND(show, drives, show_drives); 535 536 int fw_name_width, fw_version_width, fw_date_width, fw_time_width; 537 538 static void 539 scan_firmware(struct mfi_info_component *comp) 540 { 541 int len; 542 543 len = strlen(comp->name); 544 if (fw_name_width < len) 545 fw_name_width = len; 546 len = strlen(comp->version); 547 if (fw_version_width < len) 548 fw_version_width = len; 549 len = strlen(comp->build_date); 550 if (fw_date_width < len) 551 fw_date_width = len; 552 len = strlen(comp->build_time); 553 if (fw_time_width < len) 554 fw_time_width = len; 555 } 556 557 static void 558 display_firmware(struct mfi_info_component *comp, const char *tag) 559 { 560 561 printf("%-*s %-*s %-*s %-*s %s\n", fw_name_width, comp->name, 562 fw_version_width, comp->version, fw_date_width, comp->build_date, 563 fw_time_width, comp->build_time, tag); 564 } 565 566 static int 567 show_firmware(int ac, char **av) 568 { 569 struct mfi_ctrl_info info; 570 struct mfi_info_component header; 571 int error, fd; 572 u_int i; 573 574 if (ac != 1) { 575 warnx("show firmware: extra arguments"); 576 return (EINVAL); 577 } 578 579 fd = mfi_open(mfi_unit); 580 if (fd < 0) { 581 error = errno; 582 warn("mfi_open"); 583 return (error); 584 } 585 586 if (mfi_ctrl_get_info(fd, &info, NULL) < 0) { 587 error = errno; 588 warn("Failed to get controller info"); 589 return (error); 590 } 591 592 if (info.package_version[0] != '\0') 593 printf("mfi%d Firmware Package Version: %s\n", mfi_unit, 594 info.package_version); 595 printf("mfi%d Firmware Images:\n", mfi_unit); 596 strcpy(header.name, "Name"); 597 strcpy(header.version, "Version"); 598 strcpy(header.build_date, "Date"); 599 strcpy(header.build_time, "Time"); 600 scan_firmware(&header); 601 if (info.image_component_count > 8) 602 info.image_component_count = 8; 603 for (i = 0; i < info.image_component_count; i++) 604 scan_firmware(&info.image_component[i]); 605 if (info.pending_image_component_count > 8) 606 info.pending_image_component_count = 8; 607 for (i = 0; i < info.pending_image_component_count; i++) 608 scan_firmware(&info.pending_image_component[i]); 609 display_firmware(&header, "Status"); 610 for (i = 0; i < info.image_component_count; i++) 611 display_firmware(&info.image_component[i], "active"); 612 for (i = 0; i < info.pending_image_component_count; i++) 613 display_firmware(&info.pending_image_component[i], "pending"); 614 615 close(fd); 616 617 return (0); 618 } 619 MFI_COMMAND(show, firmware, show_firmware); 620 621 static int 622 show_progress(int ac, char **av) 623 { 624 struct mfi_ld_list llist; 625 struct mfi_pd_list *plist; 626 struct mfi_ld_info linfo; 627 struct mfi_pd_info pinfo; 628 int busy, error, fd; 629 u_int i; 630 631 uint16_t device_id; 632 uint8_t target_id; 633 634 if (ac != 1) { 635 warnx("show progress: extra arguments"); 636 return (EINVAL); 637 } 638 639 fd = mfi_open(mfi_unit); 640 if (fd < 0) { 641 error = errno; 642 warn("mfi_open"); 643 return (error); 644 } 645 busy = 0; 646 647 if (mfi_ld_get_list(fd, &llist, NULL) < 0) { 648 error = errno; 649 warn("Failed to get volume list"); 650 return (error); 651 } 652 if (mfi_pd_get_list(fd, &plist, NULL) < 0) { 653 error = errno; 654 warn("Failed to get drive list"); 655 return (error); 656 } 657 658 for (i = 0; i < llist.ld_count; i++) { 659 target_id = llist.ld_list[i].ld.v.target_id; 660 if (mfi_ld_get_info(fd, target_id, &linfo, NULL) < 0) { 661 error = errno; 662 warn("Failed to get info for volume %s", 663 mfi_volume_name(fd, target_id)); 664 return (error); 665 } 666 if (linfo.progress.active & MFI_LD_PROGRESS_CC) { 667 printf("volume %s ", mfi_volume_name(fd, target_id)); 668 mfi_display_progress("Consistency Check", 669 &linfo.progress.cc); 670 busy = 1; 671 } 672 if (linfo.progress.active & MFI_LD_PROGRESS_BGI) { 673 printf("volume %s ", mfi_volume_name(fd, target_id)); 674 mfi_display_progress("Background Init", 675 &linfo.progress.bgi); 676 busy = 1; 677 } 678 if (linfo.progress.active & MFI_LD_PROGRESS_FGI) { 679 printf("volume %s ", mfi_volume_name(fd, target_id)); 680 mfi_display_progress("Foreground Init", 681 &linfo.progress.fgi); 682 busy = 1; 683 } 684 if (linfo.progress.active & MFI_LD_PROGRESS_RECON) { 685 printf("volume %s ", mfi_volume_name(fd, target_id)); 686 mfi_display_progress("Reconstruction", 687 &linfo.progress.recon); 688 busy = 1; 689 } 690 } 691 692 for (i = 0; i < plist->count; i++) { 693 if (plist->addr[i].scsi_dev_type != 0) 694 continue; 695 696 device_id = plist->addr[i].device_id; 697 if (mfi_pd_get_info(fd, device_id, &pinfo, NULL) < 0) { 698 error = errno; 699 warn("Failed to fetch info for drive %u", device_id); 700 return (error); 701 } 702 703 if (pinfo.prog_info.active & MFI_PD_PROGRESS_REBUILD) { 704 printf("drive %u ", device_id); 705 mfi_display_progress("Rebuild", &pinfo.prog_info.rbld); 706 busy = 1; 707 } 708 if (pinfo.prog_info.active & MFI_PD_PROGRESS_PATROL) { 709 printf("drive %u ", device_id); 710 mfi_display_progress("Patrol Read", 711 &pinfo.prog_info.patrol); 712 busy = 1; 713 } 714 if (pinfo.prog_info.active & MFI_PD_PROGRESS_CLEAR) { 715 printf("drive %u ", device_id); 716 mfi_display_progress("Clear", &pinfo.prog_info.clear); 717 busy = 1; 718 } 719 } 720 721 close(fd); 722 723 if (!busy) 724 printf("No activity in progress for adapter mfi%d\n", mfi_unit); 725 726 return (0); 727 } 728 MFI_COMMAND(show, progress, show_progress); 729