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 31 #include <sys/cdefs.h> 32 __RCSID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/errno.h> 36 #include <err.h> 37 #include <libutil.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include "mptutil.h" 43 44 MPT_TABLE(top, show); 45 46 #define STANDALONE_STATE "ONLINE" 47 48 static void 49 format_stripe(char *buf, size_t buflen, U32 stripe) 50 { 51 52 humanize_number(buf, buflen, stripe * 512, "", HN_AUTOSCALE, 53 HN_B | HN_NOSPACE); 54 } 55 56 static void 57 display_stripe_map(const char *label, U32 StripeMap) 58 { 59 char stripe[5]; 60 int comma, i; 61 62 comma = 0; 63 printf("%s: ", label); 64 for (i = 0; StripeMap != 0; i++, StripeMap >>= 1) 65 if (StripeMap & 1) { 66 format_stripe(stripe, sizeof(stripe), 1 << i); 67 if (comma) 68 printf(", "); 69 printf("%s", stripe); 70 comma = 1; 71 } 72 printf("\n"); 73 } 74 75 static int 76 show_adapter(int ac, char **av) 77 { 78 CONFIG_PAGE_MANUFACTURING_0 *man0; 79 CONFIG_PAGE_IOC_2 *ioc2; 80 CONFIG_PAGE_IOC_6 *ioc6; 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, NULL); 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 } 155 156 ioc6 = mpt_read_ioc_page(fd, 6, NULL); 157 if (ioc6 != NULL) { 158 display_stripe_map(" RAID0 Stripes", 159 ioc6->SupportedStripeSizeMapIS); 160 display_stripe_map(" RAID1E Stripes", 161 ioc6->SupportedStripeSizeMapIME); 162 printf(" RAID0 Drives/Vol: %u", ioc6->MinDrivesIS); 163 if (ioc6->MinDrivesIS != ioc6->MaxDrivesIS) 164 printf("-%u", ioc6->MaxDrivesIS); 165 printf("\n"); 166 printf(" RAID1 Drives/Vol: %u", ioc6->MinDrivesIM); 167 if (ioc6->MinDrivesIM != ioc6->MaxDrivesIM) 168 printf("-%u", ioc6->MaxDrivesIM); 169 printf("\n"); 170 printf("RAID1E Drives/Vol: %u", ioc6->MinDrivesIME); 171 if (ioc6->MinDrivesIME != ioc6->MaxDrivesIME) 172 printf("-%u", ioc6->MaxDrivesIME); 173 printf("\n"); 174 free(ioc6); 175 } 176 177 /* TODO: Add an ioctl to fetch IOC_FACTS and print firmware version. */ 178 179 close(fd); 180 181 return (0); 182 } 183 MPT_COMMAND(show, adapter, show_adapter); 184 185 static void 186 print_vol(CONFIG_PAGE_RAID_VOL_0 *info, int state_len) 187 { 188 uint64_t size; 189 const char *level, *state; 190 char buf[6], stripe[5]; 191 192 size = ((uint64_t)info->MaxLBAHigh << 32) | info->MaxLBA; 193 humanize_number(buf, sizeof(buf), (size + 1) * 512, "", HN_AUTOSCALE, 194 HN_B | HN_NOSPACE | HN_DECIMAL); 195 if (info->VolumeType == MPI_RAID_VOL_TYPE_IM) 196 stripe[0] = '\0'; 197 else 198 format_stripe(stripe, sizeof(stripe), info->StripeSize); 199 level = mpt_raid_level(info->VolumeType); 200 state = mpt_volstate(info->VolumeStatus.State); 201 if (state_len > 0) 202 printf("(%6s) %-8s %6s %-*s", buf, level, stripe, state_len, 203 state); 204 else if (stripe[0] != '\0') 205 printf("(%s) %s %s %s", buf, level, stripe, state); 206 else 207 printf("(%s) %s %s", buf, level, state); 208 } 209 210 static void 211 print_pd(CONFIG_PAGE_RAID_PHYS_DISK_0 *info, int state_len, int location) 212 { 213 const char *inq, *state; 214 char buf[6]; 215 216 humanize_number(buf, sizeof(buf), ((uint64_t)info->MaxLBA + 1) * 512, 217 "", HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 218 state = mpt_pdstate(info); 219 if (state_len > 0) 220 printf("(%6s) %-*s", buf, state_len, state); 221 else 222 printf("(%s) %s", buf, state); 223 inq = mpt_pd_inq_string(info); 224 if (inq != NULL) 225 printf(" %s", inq); 226 if (!location) 227 return; 228 printf(" bus %d id %d", info->PhysDiskBus, info->PhysDiskID); 229 } 230 231 static void 232 print_standalone(struct mpt_standalone_disk *disk, int state_len, int location) 233 { 234 char buf[6]; 235 236 humanize_number(buf, sizeof(buf), (disk->maxlba + 1) * 512, 237 "", HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL); 238 if (state_len > 0) 239 printf("(%6s) %-*s", buf, state_len, STANDALONE_STATE); 240 else 241 printf("(%s) %s", buf, STANDALONE_STATE); 242 if (disk->inqstring[0] != '\0') 243 printf(" %s", disk->inqstring); 244 if (!location) 245 return; 246 printf(" bus %d id %d", disk->bus, disk->target); 247 } 248 249 static void 250 print_spare_pools(U8 HotSparePool) 251 { 252 int i; 253 254 if (HotSparePool == 0) { 255 printf("none"); 256 return; 257 } 258 for (i = 0; HotSparePool != 0; i++) { 259 if (HotSparePool & 1) { 260 printf("%d", i); 261 if (HotSparePool == 1) 262 break; 263 printf(", "); 264 } 265 HotSparePool >>= 1; 266 } 267 } 268 269 static int 270 show_config(int ac, char **av) 271 { 272 CONFIG_PAGE_IOC_2 *ioc2; 273 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 274 CONFIG_PAGE_IOC_5 *ioc5; 275 IOC_5_HOT_SPARE *spare; 276 CONFIG_PAGE_RAID_VOL_0 *vinfo; 277 RAID_VOL0_PHYS_DISK *disk; 278 CONFIG_PAGE_RAID_VOL_1 *vnames; 279 CONFIG_PAGE_RAID_PHYS_DISK_0 *pinfo; 280 struct mpt_standalone_disk *sdisks; 281 int fd, i, j, nsdisks; 282 283 if (ac != 1) { 284 warnx("show config: extra arguments"); 285 return (EINVAL); 286 } 287 288 fd = mpt_open(mpt_unit); 289 if (fd < 0) { 290 warn("mpt_open"); 291 return (errno); 292 } 293 294 /* Get the config from the controller. */ 295 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 296 ioc5 = mpt_read_ioc_page(fd, 5, NULL); 297 if (ioc2 == NULL || ioc5 == NULL) { 298 warn("Failed to get config"); 299 return (errno); 300 } 301 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) < 0) { 302 warn("Failed to get standalone drive list"); 303 return (errno); 304 } 305 306 /* Dump out the configuration. */ 307 printf("mpt%d Configuration: %d volumes, %d drives\n", 308 mpt_unit, ioc2->NumActiveVolumes, ioc2->NumActivePhysDisks + 309 nsdisks); 310 vol = ioc2->RaidVolume; 311 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 312 printf(" volume %s ", mpt_volume_name(vol->VolumeBus, 313 vol->VolumeID)); 314 vinfo = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL); 315 if (vinfo == NULL) { 316 printf("%s UNKNOWN", mpt_raid_level(vol->VolumeType)); 317 } else 318 print_vol(vinfo, -1); 319 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 320 if (vnames != NULL) { 321 if (vnames->Name[0] != '\0') 322 printf(" <%s>", vnames->Name); 323 free(vnames); 324 } 325 if (vinfo == NULL) { 326 printf("\n"); 327 continue; 328 } 329 printf(" spans:\n"); 330 disk = vinfo->PhysDisk; 331 for (j = 0; j < vinfo->NumPhysDisks; disk++, j++) { 332 printf(" drive %u ", disk->PhysDiskNum); 333 pinfo = mpt_pd_info(fd, disk->PhysDiskNum, NULL); 334 if (pinfo != NULL) { 335 print_pd(pinfo, -1, 0); 336 free(pinfo); 337 } 338 printf("\n"); 339 } 340 if (vinfo->VolumeSettings.HotSparePool != 0) { 341 printf(" spare pools: "); 342 print_spare_pools(vinfo->VolumeSettings.HotSparePool); 343 printf("\n"); 344 } 345 free(vinfo); 346 } 347 348 spare = ioc5->HotSpare; 349 for (i = 0; i < ioc5->NumHotSpares; spare++, i++) { 350 printf(" spare %u ", spare->PhysDiskNum); 351 pinfo = mpt_pd_info(fd, spare->PhysDiskNum, NULL); 352 if (pinfo != NULL) { 353 print_pd(pinfo, -1, 0); 354 free(pinfo); 355 } 356 printf(" backs pool %d\n", ffs(spare->HotSparePool) - 1); 357 } 358 for (i = 0; i < nsdisks; i++) { 359 printf(" drive %s ", sdisks[i].devname); 360 print_standalone(&sdisks[i], -1, 0); 361 printf("\n"); 362 } 363 free(ioc2); 364 free(ioc5); 365 free(sdisks); 366 close(fd); 367 368 return (0); 369 } 370 MPT_COMMAND(show, config, show_config); 371 372 static int 373 show_volumes(int ac, char **av) 374 { 375 CONFIG_PAGE_IOC_2 *ioc2; 376 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 377 CONFIG_PAGE_RAID_VOL_0 **volumes; 378 CONFIG_PAGE_RAID_VOL_1 *vnames; 379 int fd, i, len, state_len; 380 381 if (ac != 1) { 382 warnx("show volumes: extra arguments"); 383 return (EINVAL); 384 } 385 386 fd = mpt_open(mpt_unit); 387 if (fd < 0) { 388 warn("mpt_open"); 389 return (errno); 390 } 391 392 /* Get the volume list from the controller. */ 393 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 394 if (ioc2 == NULL) { 395 warn("Failed to get volume list"); 396 return (errno); 397 } 398 399 /* 400 * Go ahead and read the info for all the volumes and figure 401 * out the maximum width of the state field. 402 */ 403 volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 404 state_len = strlen("State"); 405 vol = ioc2->RaidVolume; 406 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 407 volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 408 NULL); 409 if (volumes[i] == NULL) 410 len = strlen("UNKNOWN"); 411 else 412 len = strlen(mpt_volstate( 413 volumes[i]->VolumeStatus.State)); 414 if (len > state_len) 415 state_len = len; 416 } 417 printf("mpt%d Volumes:\n", mpt_unit); 418 printf(" Id Size Level Stripe "); 419 len = state_len - strlen("State"); 420 for (i = 0; i < (len + 1) / 2; i++) 421 printf(" "); 422 printf("State"); 423 for (i = 0; i < len / 2; i++) 424 printf(" "); 425 printf(" Write-Cache Name\n"); 426 vol = ioc2->RaidVolume; 427 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 428 printf("%6s ", mpt_volume_name(vol->VolumeBus, vol->VolumeID)); 429 if (volumes[i] != NULL) 430 print_vol(volumes[i], state_len); 431 else 432 printf(" %-8s %-*s", 433 mpt_raid_level(vol->VolumeType), state_len, 434 "UNKNOWN"); 435 if (volumes[i] != NULL) { 436 if (volumes[i]->VolumeSettings.Settings & 437 MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE) 438 printf(" Enabled "); 439 else 440 printf(" Disabled "); 441 } else 442 printf(" "); 443 free(volumes[i]); 444 vnames = mpt_vol_names(fd, vol->VolumeBus, vol->VolumeID, NULL); 445 if (vnames != NULL) { 446 if (vnames->Name[0] != '\0') 447 printf(" <%s>", vnames->Name); 448 free(vnames); 449 } 450 printf("\n"); 451 } 452 free(ioc2); 453 close(fd); 454 455 return (0); 456 } 457 MPT_COMMAND(show, volumes, show_volumes); 458 459 static int 460 show_drives(int ac, char **av) 461 { 462 struct mpt_drive_list *list; 463 struct mpt_standalone_disk *sdisks; 464 int fd, i, len, nsdisks, state_len; 465 466 if (ac != 1) { 467 warnx("show drives: extra arguments"); 468 return (EINVAL); 469 } 470 471 fd = mpt_open(mpt_unit); 472 if (fd < 0) { 473 warn("mpt_open"); 474 return (errno); 475 } 476 477 /* Get the drive list. */ 478 list = mpt_pd_list(fd); 479 if (list == NULL) { 480 warn("Failed to get drive list"); 481 return (errno); 482 } 483 484 /* Fetch the list of standalone disks for this controller. */ 485 state_len = 0; 486 if (mpt_fetch_disks(fd, &nsdisks, &sdisks) != 0) { 487 nsdisks = 0; 488 sdisks = NULL; 489 } 490 if (nsdisks != 0) 491 state_len = strlen(STANDALONE_STATE); 492 493 /* Walk the drive list to determine width of state column. */ 494 for (i = 0; i < list->ndrives; i++) { 495 len = strlen(mpt_pdstate(list->drives[i])); 496 if (len > state_len) 497 state_len = len; 498 } 499 500 /* List the drives. */ 501 printf("mpt%d Physical Drives:\n", mpt_unit); 502 for (i = 0; i < list->ndrives; i++) { 503 printf("%4u ", list->drives[i]->PhysDiskNum); 504 print_pd(list->drives[i], state_len, 1); 505 printf("\n"); 506 } 507 mpt_free_pd_list(list); 508 for (i = 0; i < nsdisks; i++) { 509 printf("%4s ", sdisks[i].devname); 510 print_standalone(&sdisks[i], state_len, 1); 511 printf("\n"); 512 } 513 free(sdisks); 514 515 close(fd); 516 517 return (0); 518 } 519 MPT_COMMAND(show, drives, show_drives); 520 521 #ifdef DEBUG 522 static int 523 show_physdisks(int ac, char **av) 524 { 525 CONFIG_PAGE_RAID_PHYS_DISK_0 *pinfo; 526 U16 IOCStatus; 527 int fd, i; 528 529 if (ac != 1) { 530 warnx("show drives: extra arguments"); 531 return (EINVAL); 532 } 533 534 fd = mpt_open(mpt_unit); 535 if (fd < 0) { 536 warn("mpt_open"); 537 return (errno); 538 } 539 540 /* Try to find each possible phys disk page. */ 541 for (i = 0; i <= 0xff; i++) { 542 pinfo = mpt_pd_info(fd, i, &IOCStatus); 543 if (pinfo == NULL) { 544 if (IOCStatus != MPI_IOCSTATUS_CONFIG_INVALID_PAGE) 545 warnx("mpt_pd_info(%d): %s", i, 546 mpt_ioc_status(IOCStatus)); 547 continue; 548 } 549 printf("%3u ", i); 550 print_pd(pinfo, -1, 1); 551 printf("\n"); 552 } 553 554 close(fd); 555 556 return (0); 557 } 558 MPT_COMMAND(show, pd, show_physdisks); 559 #endif 560