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/cdefs.h> 34 __RCSID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/errno.h> 38 #include <err.h> 39 #include <libutil.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include "mptutil.h" 45 46 MPT_TABLE(top, show); 47 48 #define STANDALONE_STATE "ONLINE" 49 50 static void 51 format_stripe(char *buf, size_t buflen, U32 stripe) 52 { 53 54 humanize_number(buf, buflen, stripe * 512, "", HN_AUTOSCALE, 55 HN_B | HN_NOSPACE); 56 } 57 58 static void 59 display_stripe_map(const char *label, U32 StripeMap) 60 { 61 char stripe[5]; 62 int comma, i; 63 64 comma = 0; 65 printf("%s: ", label); 66 for (i = 0; StripeMap != 0; i++, StripeMap >>= 1) 67 if (StripeMap & 1) { 68 format_stripe(stripe, sizeof(stripe), 1 << i); 69 if (comma) 70 printf(", "); 71 printf("%s", stripe); 72 comma = 1; 73 } 74 printf("\n"); 75 } 76 77 static int 78 show_adapter(int ac, char **av) 79 { 80 CONFIG_PAGE_MANUFACTURING_0 *man0; 81 CONFIG_PAGE_IOC_2 *ioc2; 82 CONFIG_PAGE_IOC_6 *ioc6; 83 U16 IOCStatus; 84 int comma, error, fd; 85 86 if (ac != 1) { 87 warnx("show adapter: extra arguments"); 88 return (EINVAL); 89 } 90 91 fd = mpt_open(mpt_unit); 92 if (fd < 0) { 93 error = errno; 94 warn("mpt_open"); 95 return (error); 96 } 97 98 man0 = mpt_read_man_page(fd, 0, NULL); 99 if (man0 == NULL) { 100 error = errno; 101 warn("Failed to get controller info"); 102 return (error); 103 } 104 if (man0->Header.PageLength < sizeof(*man0) / 4) { 105 warnx("Invalid controller info"); 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 return (error); 311 } 312 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) < 0) { 313 error = errno; 314 warn("Failed to get standalone drive list"); 315 return (error); 316 } 317 318 /* Dump out the configuration. */ 319 printf("mpt%d Configuration: %d volumes, %d drives\n", 320 mpt_unit, ioc2->NumActiveVolumes, ioc2->NumActivePhysDisks + 321 nsdisks); 322 vol = ioc2->RaidVolume; 323 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 324 printf(" volume %s ", mpt_volume_name(vol->VolumeBus, 325 vol->VolumeID)); 326 vinfo = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL); 327 if (vinfo == NULL) { 328 printf("%s UNKNOWN", mpt_raid_level(vol->VolumeType)); 329 } else 330 print_vol(vinfo, -1); 331 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 332 if (vnames != NULL) { 333 if (vnames->Name[0] != '\0') 334 printf(" <%s>", vnames->Name); 335 free(vnames); 336 } 337 if (vinfo == NULL) { 338 printf("\n"); 339 continue; 340 } 341 printf(" spans:\n"); 342 disk = vinfo->PhysDisk; 343 for (j = 0; j < vinfo->NumPhysDisks; disk++, j++) { 344 printf(" drive %u ", disk->PhysDiskNum); 345 pinfo = mpt_pd_info(fd, disk->PhysDiskNum, NULL); 346 if (pinfo != NULL) { 347 print_pd(pinfo, -1, 0); 348 free(pinfo); 349 } 350 printf("\n"); 351 } 352 if (vinfo->VolumeSettings.HotSparePool != 0) { 353 printf(" spare pools: "); 354 print_spare_pools(vinfo->VolumeSettings.HotSparePool); 355 printf("\n"); 356 } 357 free(vinfo); 358 } 359 360 spare = ioc5->HotSpare; 361 for (i = 0; i < ioc5->NumHotSpares; spare++, i++) { 362 printf(" spare %u ", spare->PhysDiskNum); 363 pinfo = mpt_pd_info(fd, spare->PhysDiskNum, NULL); 364 if (pinfo != NULL) { 365 print_pd(pinfo, -1, 0); 366 free(pinfo); 367 } 368 printf(" backs pool %d\n", ffs(spare->HotSparePool) - 1); 369 } 370 for (i = 0; i < nsdisks; i++) { 371 printf(" drive %s ", sdisks[i].devname); 372 print_standalone(&sdisks[i], -1, 0); 373 printf("\n"); 374 } 375 free(ioc2); 376 free(ioc5); 377 free(sdisks); 378 close(fd); 379 380 return (0); 381 } 382 MPT_COMMAND(show, config, show_config); 383 384 static int 385 show_volumes(int ac, char **av) 386 { 387 CONFIG_PAGE_IOC_2 *ioc2; 388 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 389 CONFIG_PAGE_RAID_VOL_0 **volumes; 390 CONFIG_PAGE_RAID_VOL_1 *vnames; 391 int error, fd, i, len, state_len; 392 393 if (ac != 1) { 394 warnx("show volumes: extra arguments"); 395 return (EINVAL); 396 } 397 398 fd = mpt_open(mpt_unit); 399 if (fd < 0) { 400 error = errno; 401 warn("mpt_open"); 402 return (error); 403 } 404 405 /* Get the volume list from the controller. */ 406 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 407 if (ioc2 == NULL) { 408 error = errno; 409 warn("Failed to get volume list"); 410 return (error); 411 } 412 413 /* 414 * Go ahead and read the info for all the volumes and figure 415 * out the maximum width of the state field. 416 */ 417 volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 418 state_len = strlen("State"); 419 vol = ioc2->RaidVolume; 420 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 421 volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 422 NULL); 423 if (volumes[i] == NULL) 424 len = strlen("UNKNOWN"); 425 else 426 len = strlen(mpt_volstate( 427 volumes[i]->VolumeStatus.State)); 428 if (len > state_len) 429 state_len = len; 430 } 431 printf("mpt%d Volumes:\n", mpt_unit); 432 printf(" Id Size Level Stripe "); 433 len = state_len - strlen("State"); 434 for (i = 0; i < (len + 1) / 2; i++) 435 printf(" "); 436 printf("State"); 437 for (i = 0; i < len / 2; i++) 438 printf(" "); 439 printf(" Write-Cache Name\n"); 440 vol = ioc2->RaidVolume; 441 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 442 printf("%6s ", mpt_volume_name(vol->VolumeBus, vol->VolumeID)); 443 if (volumes[i] != NULL) 444 print_vol(volumes[i], state_len); 445 else 446 printf(" %-8s %-*s", 447 mpt_raid_level(vol->VolumeType), state_len, 448 "UNKNOWN"); 449 if (volumes[i] != NULL) { 450 if (volumes[i]->VolumeSettings.Settings & 451 MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE) 452 printf(" Enabled "); 453 else 454 printf(" Disabled "); 455 } else 456 printf(" "); 457 free(volumes[i]); 458 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 459 if (vnames != NULL) { 460 if (vnames->Name[0] != '\0') 461 printf(" <%s>", vnames->Name); 462 free(vnames); 463 } 464 printf("\n"); 465 } 466 free(ioc2); 467 close(fd); 468 469 return (0); 470 } 471 MPT_COMMAND(show, volumes, show_volumes); 472 473 static int 474 show_drives(int ac, char **av) 475 { 476 struct mpt_drive_list *list; 477 struct mpt_standalone_disk *sdisks; 478 int error, fd, i, len, nsdisks, state_len; 479 480 if (ac != 1) { 481 warnx("show drives: extra arguments"); 482 return (EINVAL); 483 } 484 485 fd = mpt_open(mpt_unit); 486 if (fd < 0) { 487 error = errno; 488 warn("mpt_open"); 489 return (error); 490 } 491 492 /* Get the drive list. */ 493 list = mpt_pd_list(fd); 494 if (list == NULL) { 495 error = errno; 496 warn("Failed to get drive list"); 497 return (error); 498 } 499 500 /* Fetch the list of standalone disks for this controller. */ 501 state_len = 0; 502 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) != 0) { 503 nsdisks = 0; 504 sdisks = NULL; 505 } 506 if (nsdisks != 0) 507 state_len = strlen(STANDALONE_STATE); 508 509 /* Walk the drive list to determine width of state column. */ 510 for (i = 0; i < list->ndrives; i++) { 511 len = strlen(mpt_pdstate(list->drives[i])); 512 if (len > state_len) 513 state_len = len; 514 } 515 516 /* List the drives. */ 517 printf("mpt%d Physical Drives:\n", mpt_unit); 518 for (i = 0; i < list->ndrives; i++) { 519 printf("%4u ", list->drives[i]->PhysDiskNum); 520 print_pd(list->drives[i], state_len, 1); 521 printf("\n"); 522 } 523 mpt_free_pd_list(list); 524 for (i = 0; i < nsdisks; i++) { 525 printf("%4s ", sdisks[i].devname); 526 print_standalone(&sdisks[i], state_len, 1); 527 printf("\n"); 528 } 529 free(sdisks); 530 531 close(fd); 532 533 return (0); 534 } 535 MPT_COMMAND(show, drives, show_drives); 536 537 #ifdef DEBUG 538 static int 539 show_physdisks(int ac, char **av) 540 { 541 CONFIG_PAGE_RAID_PHYS_DISK_0 *pinfo; 542 U16 IOCStatus; 543 int error, fd, i; 544 545 if (ac != 1) { 546 warnx("show drives: extra arguments"); 547 return (EINVAL); 548 } 549 550 fd = mpt_open(mpt_unit); 551 if (fd < 0) { 552 error = errno; 553 warn("mpt_open"); 554 return (error); 555 } 556 557 /* Try to find each possible phys disk page. */ 558 for (i = 0; i <= 0xff; i++) { 559 pinfo = mpt_pd_info(fd, i, &IOCStatus); 560 if (pinfo == NULL) { 561 if ((IOCStatus & MPI_IOCSTATUS_MASK) != 562 MPI_IOCSTATUS_CONFIG_INVALID_PAGE) 563 warnx("mpt_pd_info(%d): %s", i, 564 mpt_ioc_status(IOCStatus)); 565 continue; 566 } 567 printf("%3u ", i); 568 print_pd(pinfo, -1, 1); 569 printf("\n"); 570 } 571 572 close(fd); 573 574 return (0); 575 } 576 MPT_COMMAND(show, pd, show_physdisks); 577 #endif 578