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