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 uint8_t status; 142 int error, fd; 143 144 if (ac != 1) { 145 warnx("show battery: extra arguments"); 146 return (EINVAL); 147 } 148 149 fd = mfi_open(mfi_unit); 150 if (fd < 0) { 151 error = errno; 152 warn("mfi_open"); 153 return (error); 154 } 155 156 if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_CAPACITY_INFO, &cap, 157 sizeof(cap), NULL, 0, &status) < 0) { 158 if (status == MFI_STAT_NO_HW_PRESENT) { 159 printf("mfi%d: No battery present\n", mfi_unit); 160 return (0); 161 } 162 error = errno; 163 warn("Failed to get capacity info"); 164 return (error); 165 } 166 167 if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_DESIGN_INFO, &design, 168 sizeof(design), NULL, 0, NULL) < 0) { 169 error = errno; 170 warn("Failed to get design info"); 171 return (error); 172 } 173 174 printf("mfi%d: Battery State:\n", mfi_unit); 175 printf(" Manufacture Date: %d/%d/%d\n", design.mfg_date >> 5 & 0x0f, 176 design.mfg_date & 0x1f, design.mfg_date >> 9 & 0xffff); 177 printf(" Serial Number: %d\n", design.serial_number); 178 printf(" Manufacturer: %s\n", design.mfg_name); 179 printf(" Model: %s\n", design.device_name); 180 printf(" Chemistry: %s\n", design.device_chemistry); 181 printf(" Design Capacity: %d mAh\n", design.design_capacity); 182 printf(" Design Voltage: %d mV\n", design.design_voltage); 183 printf(" Current Charge: %d%%\n", cap.relative_charge); 184 185 close(fd); 186 187 return (0); 188 } 189 MFI_COMMAND(show, battery, show_battery); 190 191 static void 192 print_ld(struct mfi_ld_info *info, int state_len) 193 { 194 struct mfi_ld_params *params = &info->ld_config.params; 195 const char *level; 196 char size[6], stripe[5]; 197 198 humanize_number(size, sizeof(size), info->size * 512, 199 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 200 format_stripe(stripe, sizeof(stripe), 201 info->ld_config.params.stripe_size); 202 level = mfi_raid_level(params->primary_raid_level, 203 params->secondary_raid_level); 204 if (state_len > 0) 205 printf("(%6s) %-8s %6s %-*s", size, level, stripe, state_len, 206 mfi_ldstate(params->state)); 207 else 208 printf("(%s) %s %s %s", size, level, stripe, 209 mfi_ldstate(params->state)); 210 } 211 212 static void 213 print_pd(struct mfi_pd_info *info, int state_len, int location) 214 { 215 const char *s; 216 char buf[6]; 217 218 humanize_number(buf, sizeof(buf), info->raw_size * 512, "", 219 HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 220 printf("(%6s) ", buf); 221 if (state_len > 0) 222 printf("%-*s", state_len, mfi_pdstate(info->fw_state)); 223 else 224 printf("%s", mfi_pdstate(info->fw_state)); 225 s = mfi_pd_inq_string(info); 226 if (s != NULL) 227 printf(" %s", s); 228 if (!location) 229 return; 230 if (info->encl_device_id == 0xffff) 231 printf(" slot %d", info->slot_number); 232 else if (info->encl_device_id == info->ref.v.device_id) 233 printf(" enclosure %d", info->encl_index); 234 else 235 printf(" enclosure %d, slot %d", info->encl_index, 236 info->slot_number); 237 } 238 239 static int 240 show_config(int ac, char **av) 241 { 242 struct mfi_config_data *config; 243 struct mfi_array *ar; 244 struct mfi_ld_config *ld; 245 struct mfi_spare *sp; 246 struct mfi_ld_info linfo; 247 struct mfi_pd_info pinfo; 248 uint16_t device_id; 249 char *p; 250 int error, fd, i, j; 251 252 if (ac != 1) { 253 warnx("show config: extra arguments"); 254 return (EINVAL); 255 } 256 257 fd = mfi_open(mfi_unit); 258 if (fd < 0) { 259 error = errno; 260 warn("mfi_open"); 261 return (error); 262 } 263 264 /* Get the config from the controller. */ 265 if (mfi_config_read(fd, &config) < 0) { 266 error = errno; 267 warn("Failed to get config"); 268 return (error); 269 } 270 271 /* Dump out the configuration. */ 272 printf("mfi%d Configuration: %d arrays, %d volumes, %d spares\n", 273 mfi_unit, config->array_count, config->log_drv_count, 274 config->spares_count); 275 p = (char *)config->array; 276 277 for (i = 0; i < config->array_count; i++) { 278 ar = (struct mfi_array *)p; 279 printf(" array %u of %u drives:\n", ar->array_ref, 280 ar->num_drives); 281 for (j = 0; j < ar->num_drives; j++) { 282 device_id = ar->pd[j].ref.v.device_id; 283 if (device_id == 0xffff) 284 printf(" drive MISSING\n"); 285 else { 286 printf(" drive %u ", device_id); 287 if (mfi_pd_get_info(fd, device_id, &pinfo, 288 NULL) < 0) 289 printf("%s", 290 mfi_pdstate(ar->pd[j].fw_state)); 291 else 292 print_pd(&pinfo, -1, 1); 293 printf("\n"); 294 } 295 } 296 p += config->array_size; 297 } 298 299 for (i = 0; i < config->log_drv_count; i++) { 300 ld = (struct mfi_ld_config *)p; 301 printf(" volume %s ", 302 mfi_volume_name(fd, ld->properties.ld.v.target_id)); 303 if (mfi_ld_get_info(fd, ld->properties.ld.v.target_id, &linfo, 304 NULL) < 0) { 305 printf("%s %s", 306 mfi_raid_level(ld->params.primary_raid_level, 307 ld->params.secondary_raid_level), 308 mfi_ldstate(ld->params.state)); 309 } else 310 print_ld(&linfo, -1); 311 if (ld->properties.name[0] != '\0') 312 printf(" <%s>", ld->properties.name); 313 printf(" spans:\n"); 314 for (j = 0; j < ld->params.span_depth; j++) 315 printf(" array %u\n", ld->span[j].array_ref); 316 p += config->log_drv_size; 317 } 318 319 for (i = 0; i < config->spares_count; i++) { 320 sp = (struct mfi_spare *)p; 321 printf(" %s spare %u ", 322 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 323 "global", sp->ref.v.device_id); 324 if (mfi_pd_get_info(fd, sp->ref.v.device_id, &pinfo, NULL) < 0) 325 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 326 else 327 print_pd(&pinfo, -1, 1); 328 if (sp->spare_type & MFI_SPARE_DEDICATED) { 329 printf(" backs:\n"); 330 for (j = 0; j < sp->array_count; j++) 331 printf(" array %u\n", sp->array_ref[j]); 332 } else 333 printf("\n"); 334 p += config->spares_size; 335 } 336 close(fd); 337 338 return (0); 339 } 340 MFI_COMMAND(show, config, show_config); 341 342 static int 343 show_volumes(int ac, char **av) 344 { 345 struct mfi_ld_list list; 346 struct mfi_ld_info info; 347 int error, fd; 348 u_int i, len, state_len; 349 350 if (ac != 1) { 351 warnx("show volumes: extra arguments"); 352 return (EINVAL); 353 } 354 355 fd = mfi_open(mfi_unit); 356 if (fd < 0) { 357 error = errno; 358 warn("mfi_open"); 359 return (error); 360 } 361 362 /* Get the logical drive list from the controller. */ 363 if (mfi_ld_get_list(fd, &list, NULL) < 0) { 364 error = errno; 365 warn("Failed to get volume list"); 366 return (error); 367 } 368 369 /* List the volumes. */ 370 printf("mfi%d Volumes:\n", mfi_unit); 371 state_len = strlen("State"); 372 for (i = 0; i < list.ld_count; i++) { 373 len = strlen(mfi_ldstate(list.ld_list[i].state)); 374 if (len > state_len) 375 state_len = len; 376 } 377 printf(" Id Size Level Stripe "); 378 len = state_len - strlen("State"); 379 for (i = 0; i < (len + 1) / 2; i++) 380 printf(" "); 381 printf("State"); 382 for (i = 0; i < len / 2; i++) 383 printf(" "); 384 printf(" Cache Name\n"); 385 for (i = 0; i < list.ld_count; i++) { 386 if (mfi_ld_get_info(fd, list.ld_list[i].ld.v.target_id, &info, 387 NULL) < 0) { 388 error = errno; 389 warn("Failed to get info for volume %d", 390 list.ld_list[i].ld.v.target_id); 391 return (error); 392 } 393 printf("%6s ", 394 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 395 print_ld(&info, state_len); 396 switch (info.ld_config.properties.current_cache_policy & 397 (MR_LD_CACHE_ALLOW_WRITE_CACHE | 398 MR_LD_CACHE_ALLOW_READ_CACHE)) { 399 case 0: 400 printf(" Disabled"); 401 break; 402 case MR_LD_CACHE_ALLOW_READ_CACHE: 403 printf(" Reads "); 404 break; 405 case MR_LD_CACHE_ALLOW_WRITE_CACHE: 406 printf(" Writes "); 407 break; 408 case MR_LD_CACHE_ALLOW_WRITE_CACHE | 409 MR_LD_CACHE_ALLOW_READ_CACHE: 410 printf(" Enabled "); 411 break; 412 } 413 if (info.ld_config.properties.name[0] != '\0') 414 printf(" <%s>", info.ld_config.properties.name); 415 printf("\n"); 416 } 417 close(fd); 418 419 return (0); 420 } 421 MFI_COMMAND(show, volumes, show_volumes); 422 423 static int 424 show_drives(int ac, char **av) 425 { 426 struct mfi_pd_list *list; 427 struct mfi_pd_info info; 428 u_int i, len, state_len; 429 int error, fd; 430 431 if (ac != 1) { 432 warnx("show drives: extra arguments"); 433 return (EINVAL); 434 } 435 436 fd = mfi_open(mfi_unit); 437 if (fd < 0) { 438 error = errno; 439 warn("mfi_open"); 440 return (error); 441 } 442 443 if (mfi_pd_get_list(fd, &list, NULL) < 0) { 444 error = errno; 445 warn("Failed to get drive list"); 446 return (error); 447 } 448 449 /* Walk the list of drives to determine width of state column. */ 450 state_len = 0; 451 for (i = 0; i < list->count; i++) { 452 if (list->addr[i].scsi_dev_type != 0) 453 continue; 454 455 if (mfi_pd_get_info(fd, list->addr[i].device_id, &info, 456 NULL) < 0) { 457 error = errno; 458 warn("Failed to fetch info for drive %u", 459 list->addr[i].device_id); 460 return (error); 461 } 462 len = strlen(mfi_pdstate(info.fw_state)); 463 if (len > state_len) 464 state_len = len; 465 } 466 467 /* List the drives. */ 468 printf("mfi%d Physical Drives:\n", mfi_unit); 469 for (i = 0; i < list->count; i++) { 470 471 /* Skip non-hard disks. */ 472 if (list->addr[i].scsi_dev_type != 0) 473 continue; 474 475 /* Fetch details for this drive. */ 476 if (mfi_pd_get_info(fd, list->addr[i].device_id, &info, 477 NULL) < 0) { 478 error = errno; 479 warn("Failed to fetch info for drive %u", 480 list->addr[i].device_id); 481 return (error); 482 } 483 484 print_pd(&info, state_len, 1); 485 printf("\n"); 486 } 487 close(fd); 488 489 return (0); 490 } 491 MFI_COMMAND(show, drives, show_drives); 492 493 int fw_name_width, fw_version_width, fw_date_width, fw_time_width; 494 495 static void 496 scan_firmware(struct mfi_info_component *comp) 497 { 498 int len; 499 500 len = strlen(comp->name); 501 if (fw_name_width < len) 502 fw_name_width = len; 503 len = strlen(comp->version); 504 if (fw_version_width < len) 505 fw_version_width = len; 506 len = strlen(comp->build_date); 507 if (fw_date_width < len) 508 fw_date_width = len; 509 len = strlen(comp->build_time); 510 if (fw_time_width < len) 511 fw_time_width = len; 512 } 513 514 static void 515 display_firmware(struct mfi_info_component *comp, const char *tag) 516 { 517 518 printf("%-*s %-*s %-*s %-*s %s\n", fw_name_width, comp->name, 519 fw_version_width, comp->version, fw_date_width, comp->build_date, 520 fw_time_width, comp->build_time, tag); 521 } 522 523 static int 524 show_firmware(int ac, char **av) 525 { 526 struct mfi_ctrl_info info; 527 struct mfi_info_component header; 528 int error, fd; 529 u_int i; 530 531 if (ac != 1) { 532 warnx("show drives: extra arguments"); 533 return (EINVAL); 534 } 535 536 fd = mfi_open(mfi_unit); 537 if (fd < 0) { 538 error = errno; 539 warn("mfi_open"); 540 return (error); 541 } 542 543 if (mfi_ctrl_get_info(fd, &info, NULL) < 0) { 544 error = errno; 545 warn("Failed to get controller info"); 546 return (error); 547 } 548 549 if (info.package_version[0] != '\0') 550 printf("mfi%d Firmware Package Version: %s\n", mfi_unit, 551 info.package_version); 552 printf("mfi%d Firmware Images:\n", mfi_unit); 553 strcpy(header.name, "Name"); 554 strcpy(header.version, "Version"); 555 strcpy(header.build_date, "Date"); 556 strcpy(header.build_time, "Time"); 557 scan_firmware(&header); 558 if (info.image_component_count > 8) 559 info.image_component_count = 8; 560 for (i = 0; i < info.image_component_count; i++) 561 scan_firmware(&info.image_component[i]); 562 if (info.pending_image_component_count > 8) 563 info.pending_image_component_count = 8; 564 for (i = 0; i < info.pending_image_component_count; i++) 565 scan_firmware(&info.pending_image_component[i]); 566 display_firmware(&header, "Status"); 567 for (i = 0; i < info.image_component_count; i++) 568 display_firmware(&info.image_component[i], "active"); 569 for (i = 0; i < info.pending_image_component_count; i++) 570 display_firmware(&info.pending_image_component[i], "pending"); 571 572 close(fd); 573 574 return (0); 575 } 576 MFI_COMMAND(show, firmware, show_firmware); 577