1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2008 Yahoo!, Inc. 5 * All rights reserved. 6 * Written by: John Baldwin <jhb@FreeBSD.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/errno.h> 35 #include <err.h> 36 #include <libutil.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include "mptutil.h" 42 43 MPT_TABLE(top, show); 44 45 #define STANDALONE_STATE "ONLINE" 46 47 static void 48 format_stripe(char *buf, size_t buflen, U32 stripe) 49 { 50 51 humanize_number(buf, buflen, stripe * 512, "", HN_AUTOSCALE, 52 HN_B | HN_NOSPACE); 53 } 54 55 static void 56 display_stripe_map(const char *label, U32 StripeMap) 57 { 58 char stripe[5]; 59 int comma, i; 60 61 comma = 0; 62 printf("%s: ", label); 63 for (i = 0; StripeMap != 0; i++, StripeMap >>= 1) 64 if (StripeMap & 1) { 65 format_stripe(stripe, sizeof(stripe), 1 << i); 66 if (comma) 67 printf(", "); 68 printf("%s", stripe); 69 comma = 1; 70 } 71 printf("\n"); 72 } 73 74 static int 75 show_adapter(int ac, char **av) 76 { 77 CONFIG_PAGE_MANUFACTURING_0 *man0; 78 CONFIG_PAGE_IOC_2 *ioc2; 79 CONFIG_PAGE_IOC_6 *ioc6; 80 U16 IOCStatus; 81 int comma, error, fd; 82 83 if (ac != 1) { 84 warnx("show adapter: extra arguments"); 85 return (EINVAL); 86 } 87 88 fd = mpt_open(mpt_unit); 89 if (fd < 0) { 90 error = errno; 91 warn("mpt_open"); 92 return (error); 93 } 94 95 man0 = mpt_read_man_page(fd, 0, NULL); 96 if (man0 == NULL) { 97 error = errno; 98 warn("Failed to get controller info"); 99 close(fd); 100 return (error); 101 } 102 if (man0->Header.PageLength < sizeof(*man0) / 4) { 103 warnx("Invalid controller info"); 104 free(man0); 105 close(fd); 106 return (EINVAL); 107 } 108 printf("mpt%d Adapter:\n", mpt_unit); 109 printf(" Board Name: %.16s\n", man0->BoardName); 110 printf(" Board Assembly: %.16s\n", man0->BoardAssembly); 111 printf(" Chip Name: %.16s\n", man0->ChipName); 112 printf(" Chip Revision: %.16s\n", man0->ChipRevision); 113 114 free(man0); 115 116 ioc2 = mpt_read_ioc_page(fd, 2, &IOCStatus); 117 if (ioc2 != NULL) { 118 printf(" RAID Levels:"); 119 comma = 0; 120 if (ioc2->CapabilitiesFlags & 121 MPI_IOCPAGE2_CAP_FLAGS_IS_SUPPORT) { 122 printf(" RAID0"); 123 comma = 1; 124 } 125 if (ioc2->CapabilitiesFlags & 126 MPI_IOCPAGE2_CAP_FLAGS_IM_SUPPORT) { 127 printf("%s RAID1", comma ? "," : ""); 128 comma = 1; 129 } 130 if (ioc2->CapabilitiesFlags & 131 MPI_IOCPAGE2_CAP_FLAGS_IME_SUPPORT) { 132 printf("%s RAID1E", comma ? "," : ""); 133 comma = 1; 134 } 135 if (ioc2->CapabilitiesFlags & 136 MPI_IOCPAGE2_CAP_FLAGS_RAID_5_SUPPORT) { 137 printf("%s RAID5", comma ? "," : ""); 138 comma = 1; 139 } 140 if (ioc2->CapabilitiesFlags & 141 MPI_IOCPAGE2_CAP_FLAGS_RAID_6_SUPPORT) { 142 printf("%s RAID6", comma ? "," : ""); 143 comma = 1; 144 } 145 if (ioc2->CapabilitiesFlags & 146 MPI_IOCPAGE2_CAP_FLAGS_RAID_10_SUPPORT) { 147 printf("%s RAID10", comma ? "," : ""); 148 comma = 1; 149 } 150 if (ioc2->CapabilitiesFlags & 151 MPI_IOCPAGE2_CAP_FLAGS_RAID_50_SUPPORT) { 152 printf("%s RAID50", comma ? "," : ""); 153 comma = 1; 154 } 155 if (!comma) 156 printf(" none"); 157 printf("\n"); 158 free(ioc2); 159 } else if ((IOCStatus & MPI_IOCSTATUS_MASK) != 160 MPI_IOCSTATUS_CONFIG_INVALID_PAGE) 161 warnx("mpt_read_ioc_page(2): %s", mpt_ioc_status(IOCStatus)); 162 163 ioc6 = mpt_read_ioc_page(fd, 6, &IOCStatus); 164 if (ioc6 != NULL) { 165 display_stripe_map(" RAID0 Stripes", 166 ioc6->SupportedStripeSizeMapIS); 167 display_stripe_map(" RAID1E Stripes", 168 ioc6->SupportedStripeSizeMapIME); 169 printf(" RAID0 Drives/Vol: %u", ioc6->MinDrivesIS); 170 if (ioc6->MinDrivesIS != ioc6->MaxDrivesIS) 171 printf("-%u", ioc6->MaxDrivesIS); 172 printf("\n"); 173 printf(" RAID1 Drives/Vol: %u", ioc6->MinDrivesIM); 174 if (ioc6->MinDrivesIM != ioc6->MaxDrivesIM) 175 printf("-%u", ioc6->MaxDrivesIM); 176 printf("\n"); 177 printf("RAID1E Drives/Vol: %u", ioc6->MinDrivesIME); 178 if (ioc6->MinDrivesIME != ioc6->MaxDrivesIME) 179 printf("-%u", ioc6->MaxDrivesIME); 180 printf("\n"); 181 free(ioc6); 182 } else if ((IOCStatus & MPI_IOCSTATUS_MASK) != 183 MPI_IOCSTATUS_CONFIG_INVALID_PAGE) 184 warnx("mpt_read_ioc_page(6): %s", mpt_ioc_status(IOCStatus)); 185 186 /* TODO: Add an ioctl to fetch IOC_FACTS and print firmware version. */ 187 188 close(fd); 189 190 return (0); 191 } 192 MPT_COMMAND(show, adapter, show_adapter); 193 194 static void 195 print_vol(CONFIG_PAGE_RAID_VOL_0 *info, int state_len) 196 { 197 uint64_t size; 198 const char *level, *state; 199 char buf[6], stripe[5]; 200 201 size = ((uint64_t)info->MaxLBAHigh << 32) | info->MaxLBA; 202 humanize_number(buf, sizeof(buf), (size + 1) * 512, "", HN_AUTOSCALE, 203 HN_B | HN_NOSPACE | HN_DECIMAL); 204 if (info->VolumeType == MPI_RAID_VOL_TYPE_IM) 205 stripe[0] = '\0'; 206 else 207 format_stripe(stripe, sizeof(stripe), info->StripeSize); 208 level = mpt_raid_level(info->VolumeType); 209 state = mpt_volstate(info->VolumeStatus.State); 210 if (state_len > 0) 211 printf("(%6s) %-8s %6s %-*s", buf, level, stripe, state_len, 212 state); 213 else if (stripe[0] != '\0') 214 printf("(%s) %s %s %s", buf, level, stripe, state); 215 else 216 printf("(%s) %s %s", buf, level, state); 217 } 218 219 static void 220 print_pd(CONFIG_PAGE_RAID_PHYS_DISK_0 *info, int state_len, int location) 221 { 222 const char *inq, *state; 223 char buf[6]; 224 225 humanize_number(buf, sizeof(buf), ((uint64_t)info->MaxLBA + 1) * 512, 226 "", HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 227 state = mpt_pdstate(info); 228 if (state_len > 0) 229 printf("(%6s) %-*s", buf, state_len, state); 230 else 231 printf("(%s) %s", buf, state); 232 inq = mpt_pd_inq_string(info); 233 if (inq != NULL) 234 printf(" %s", inq); 235 if (!location) 236 return; 237 printf(" bus %d id %d", info->PhysDiskBus, info->PhysDiskID); 238 } 239 240 static void 241 print_standalone(struct mpt_standalone_disk *disk, int state_len, int location) 242 { 243 char buf[6]; 244 245 humanize_number(buf, sizeof(buf), (disk->maxlba + 1) * 512, 246 "", HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 247 if (state_len > 0) 248 printf("(%6s) %-*s", buf, state_len, STANDALONE_STATE); 249 else 250 printf("(%s) %s", buf, STANDALONE_STATE); 251 if (disk->inqstring[0] != '\0') 252 printf(" %s", disk->inqstring); 253 if (!location) 254 return; 255 printf(" bus %d id %d", disk->bus, disk->target); 256 } 257 258 static void 259 print_spare_pools(U8 HotSparePool) 260 { 261 int i; 262 263 if (HotSparePool == 0) { 264 printf("none"); 265 return; 266 } 267 for (i = 0; HotSparePool != 0; i++) { 268 if (HotSparePool & 1) { 269 printf("%d", i); 270 if (HotSparePool == 1) 271 break; 272 printf(", "); 273 } 274 HotSparePool >>= 1; 275 } 276 } 277 278 static int 279 show_config(int ac, char **av) 280 { 281 CONFIG_PAGE_IOC_2 *ioc2; 282 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 283 CONFIG_PAGE_IOC_5 *ioc5; 284 IOC_5_HOT_SPARE *spare; 285 CONFIG_PAGE_RAID_VOL_0 *vinfo; 286 RAID_VOL0_PHYS_DISK *disk; 287 CONFIG_PAGE_RAID_VOL_1 *vnames; 288 CONFIG_PAGE_RAID_PHYS_DISK_0 *pinfo; 289 struct mpt_standalone_disk *sdisks; 290 int error, fd, i, j, nsdisks; 291 292 if (ac != 1) { 293 warnx("show config: extra arguments"); 294 return (EINVAL); 295 } 296 297 fd = mpt_open(mpt_unit); 298 if (fd < 0) { 299 error = errno; 300 warn("mpt_open"); 301 return (error); 302 } 303 304 /* Get the config from the controller. */ 305 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 306 ioc5 = mpt_read_ioc_page(fd, 5, NULL); 307 if (ioc2 == NULL || ioc5 == NULL) { 308 error = errno; 309 warn("Failed to get config"); 310 free(ioc2); 311 close(fd); 312 return (error); 313 } 314 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) < 0) { 315 error = errno; 316 warn("Failed to get standalone drive list"); 317 free(ioc5); 318 free(ioc2); 319 close(fd); 320 return (error); 321 } 322 323 /* Dump out the configuration. */ 324 printf("mpt%d Configuration: %d volumes, %d drives\n", 325 mpt_unit, ioc2->NumActiveVolumes, ioc2->NumActivePhysDisks + 326 nsdisks); 327 vol = ioc2->RaidVolume; 328 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 329 printf(" volume %s ", mpt_volume_name(vol->VolumeBus, 330 vol->VolumeID)); 331 vinfo = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL); 332 if (vinfo == NULL) { 333 printf("%s UNKNOWN", mpt_raid_level(vol->VolumeType)); 334 } else 335 print_vol(vinfo, -1); 336 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 337 if (vnames != NULL) { 338 if (vnames->Name[0] != '\0') 339 printf(" <%s>", vnames->Name); 340 free(vnames); 341 } 342 if (vinfo == NULL) { 343 printf("\n"); 344 continue; 345 } 346 printf(" spans:\n"); 347 disk = vinfo->PhysDisk; 348 for (j = 0; j < vinfo->NumPhysDisks; disk++, j++) { 349 printf(" drive %u ", disk->PhysDiskNum); 350 pinfo = mpt_pd_info(fd, disk->PhysDiskNum, NULL); 351 if (pinfo != NULL) { 352 print_pd(pinfo, -1, 0); 353 free(pinfo); 354 } 355 printf("\n"); 356 } 357 if (vinfo->VolumeSettings.HotSparePool != 0) { 358 printf(" spare pools: "); 359 print_spare_pools(vinfo->VolumeSettings.HotSparePool); 360 printf("\n"); 361 } 362 free(vinfo); 363 } 364 365 spare = ioc5->HotSpare; 366 for (i = 0; i < ioc5->NumHotSpares; spare++, i++) { 367 printf(" spare %u ", spare->PhysDiskNum); 368 pinfo = mpt_pd_info(fd, spare->PhysDiskNum, NULL); 369 if (pinfo != NULL) { 370 print_pd(pinfo, -1, 0); 371 free(pinfo); 372 } 373 printf(" backs pool %d\n", ffs(spare->HotSparePool) - 1); 374 } 375 for (i = 0; i < nsdisks; i++) { 376 printf(" drive %s ", sdisks[i].devname); 377 print_standalone(&sdisks[i], -1, 0); 378 printf("\n"); 379 } 380 free(ioc2); 381 free(ioc5); 382 free(sdisks); 383 close(fd); 384 385 return (0); 386 } 387 MPT_COMMAND(show, config, show_config); 388 389 static int 390 show_volumes(int ac, char **av) 391 { 392 CONFIG_PAGE_IOC_2 *ioc2; 393 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 394 CONFIG_PAGE_RAID_VOL_0 **volumes; 395 CONFIG_PAGE_RAID_VOL_1 *vnames; 396 int error, fd, i, len, state_len; 397 398 if (ac != 1) { 399 warnx("show volumes: extra arguments"); 400 return (EINVAL); 401 } 402 403 fd = mpt_open(mpt_unit); 404 if (fd < 0) { 405 error = errno; 406 warn("mpt_open"); 407 return (error); 408 } 409 410 /* Get the volume list from the controller. */ 411 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 412 if (ioc2 == NULL) { 413 error = errno; 414 warn("Failed to get volume list"); 415 return (error); 416 } 417 418 /* 419 * Go ahead and read the info for all the volumes and figure 420 * out the maximum width of the state field. 421 */ 422 volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 423 state_len = strlen("State"); 424 vol = ioc2->RaidVolume; 425 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 426 volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 427 NULL); 428 if (volumes[i] == NULL) 429 len = strlen("UNKNOWN"); 430 else 431 len = strlen(mpt_volstate( 432 volumes[i]->VolumeStatus.State)); 433 if (len > state_len) 434 state_len = len; 435 } 436 printf("mpt%d Volumes:\n", mpt_unit); 437 printf(" Id Size Level Stripe "); 438 len = state_len - strlen("State"); 439 for (i = 0; i < (len + 1) / 2; i++) 440 printf(" "); 441 printf("State"); 442 for (i = 0; i < len / 2; i++) 443 printf(" "); 444 printf(" Write-Cache Name\n"); 445 vol = ioc2->RaidVolume; 446 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 447 printf("%6s ", mpt_volume_name(vol->VolumeBus, vol->VolumeID)); 448 if (volumes[i] != NULL) 449 print_vol(volumes[i], state_len); 450 else 451 printf(" %-8s %-*s", 452 mpt_raid_level(vol->VolumeType), state_len, 453 "UNKNOWN"); 454 if (volumes[i] != NULL) { 455 if (volumes[i]->VolumeSettings.Settings & 456 MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE) 457 printf(" Enabled "); 458 else 459 printf(" Disabled "); 460 } else 461 printf(" "); 462 free(volumes[i]); 463 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 464 if (vnames != NULL) { 465 if (vnames->Name[0] != '\0') 466 printf(" <%s>", vnames->Name); 467 free(vnames); 468 } 469 printf("\n"); 470 } 471 free(volumes); 472 free(ioc2); 473 close(fd); 474 475 return (0); 476 } 477 MPT_COMMAND(show, volumes, show_volumes); 478 479 static int 480 show_drives(int ac, char **av) 481 { 482 struct mpt_drive_list *list; 483 struct mpt_standalone_disk *sdisks; 484 int error, fd, i, len, nsdisks, state_len; 485 486 if (ac != 1) { 487 warnx("show drives: extra arguments"); 488 return (EINVAL); 489 } 490 491 fd = mpt_open(mpt_unit); 492 if (fd < 0) { 493 error = errno; 494 warn("mpt_open"); 495 return (error); 496 } 497 498 /* Get the drive list. */ 499 list = mpt_pd_list(fd); 500 if (list == NULL) { 501 error = errno; 502 close(fd); 503 warn("Failed to get drive list"); 504 return (error); 505 } 506 507 /* Fetch the list of standalone disks for this controller. */ 508 state_len = 0; 509 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) != 0) { 510 nsdisks = 0; 511 sdisks = NULL; 512 } 513 if (nsdisks != 0) 514 state_len = strlen(STANDALONE_STATE); 515 516 /* Walk the drive list to determine width of state column. */ 517 for (i = 0; i < list->ndrives; i++) { 518 len = strlen(mpt_pdstate(list->drives[i])); 519 if (len > state_len) 520 state_len = len; 521 } 522 523 /* List the drives. */ 524 printf("mpt%d Physical Drives:\n", mpt_unit); 525 for (i = 0; i < list->ndrives; i++) { 526 printf("%4u ", list->drives[i]->PhysDiskNum); 527 print_pd(list->drives[i], state_len, 1); 528 printf("\n"); 529 } 530 mpt_free_pd_list(list); 531 for (i = 0; i < nsdisks; i++) { 532 printf("%4s ", sdisks[i].devname); 533 print_standalone(&sdisks[i], state_len, 1); 534 printf("\n"); 535 } 536 free(sdisks); 537 538 close(fd); 539 540 return (0); 541 } 542 MPT_COMMAND(show, drives, show_drives); 543 544 #ifdef DEBUG 545 static int 546 show_physdisks(int ac, char **av) 547 { 548 CONFIG_PAGE_RAID_PHYS_DISK_0 *pinfo; 549 U16 IOCStatus; 550 int error, fd, i; 551 552 if (ac != 1) { 553 warnx("show drives: extra arguments"); 554 return (EINVAL); 555 } 556 557 fd = mpt_open(mpt_unit); 558 if (fd < 0) { 559 error = errno; 560 warn("mpt_open"); 561 return (error); 562 } 563 564 /* Try to find each possible phys disk page. */ 565 for (i = 0; i <= 0xff; i++) { 566 pinfo = mpt_pd_info(fd, i, &IOCStatus); 567 if (pinfo == NULL) { 568 if ((IOCStatus & MPI_IOCSTATUS_MASK) != 569 MPI_IOCSTATUS_CONFIG_INVALID_PAGE) 570 warnx("mpt_pd_info(%d): %s", i, 571 mpt_ioc_status(IOCStatus)); 572 continue; 573 } 574 printf("%3u ", i); 575 print_pd(pinfo, -1, 1); 576 printf("\n"); 577 } 578 579 close(fd); 580 581 return (0); 582 } 583 MPT_COMMAND(show, pd, show_physdisks); 584 #endif 585