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