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 <fcntl.h> 38 #include <libutil.h> 39 #include <paths.h> 40 #ifdef DEBUG 41 #include <stdint.h> 42 #endif 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include "mptutil.h" 48 49 #ifdef DEBUG 50 static void dump_config(CONFIG_PAGE_RAID_VOL_0 *vol); 51 #endif 52 53 #define powerof2(x) ((((x)-1)&(x))==0) 54 55 static long 56 dehumanize(const char *value) 57 { 58 char *vtp; 59 long iv; 60 61 if (value == NULL) 62 return (0); 63 iv = strtoq(value, &vtp, 0); 64 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 65 return (0); 66 } 67 switch (vtp[0]) { 68 case 't': case 'T': 69 iv *= 1024; 70 case 'g': case 'G': 71 iv *= 1024; 72 case 'm': case 'M': 73 iv *= 1024; 74 case 'k': case 'K': 75 iv *= 1024; 76 case '\0': 77 break; 78 default: 79 return (0); 80 } 81 return (iv); 82 } 83 84 /* 85 * Lock the volume by opening its /dev device read/write. This will 86 * only work if nothing else has it opened (including mounts). We 87 * leak the fd on purpose since this application is not long-running. 88 */ 89 int 90 mpt_lock_volume(U8 VolumeBus, U8 VolumeID) 91 { 92 char path[MAXPATHLEN]; 93 struct mpt_query_disk qd; 94 int error, vfd; 95 96 error = mpt_query_disk(VolumeBus, VolumeID, &qd); 97 if (error == ENOENT) 98 /* 99 * This means there isn't a CAM device associated with 100 * the volume, and thus it is already implicitly 101 * locked, so just return. 102 */ 103 return (0); 104 if (error) { 105 errno = error; 106 warn("Unable to lookup volume device name"); 107 return (-1); 108 } 109 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, qd.devname); 110 vfd = open(path, O_RDWR); 111 if (vfd < 0) { 112 warn("Unable to lock volume %s", qd.devname); 113 return (-1); 114 } 115 return (0); 116 } 117 118 static int 119 mpt_lock_physdisk(struct mpt_standalone_disk *disk) 120 { 121 char path[MAXPATHLEN]; 122 int dfd; 123 124 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk->devname); 125 dfd = open(path, O_RDWR); 126 if (dfd < 0) { 127 warn("Unable to lock disk %s", disk->devname); 128 return (-1); 129 } 130 return (0); 131 } 132 133 static int 134 mpt_lookup_standalone_disk(const char *name, struct mpt_standalone_disk *disks, 135 int ndisks, int *index) 136 { 137 char *cp; 138 long bus, id; 139 int i; 140 141 /* Check for a raw <bus>:<id> string. */ 142 bus = strtol(name, &cp, 0); 143 if (*cp == ':') { 144 id = strtol(cp + 1, &cp, 0); 145 if (*cp == '\0') { 146 if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) { 147 errno = EINVAL; 148 return (-1); 149 } 150 for (i = 0; i < ndisks; i++) { 151 if (disks[i].bus == (U8)bus && 152 disks[i].target == (U8)id) { 153 *index = i; 154 return (0); 155 } 156 } 157 errno = ENOENT; 158 return (-1); 159 } 160 } 161 162 if (name[0] == 'd' && name[1] == 'a') { 163 for (i = 0; i < ndisks; i++) { 164 if (strcmp(name, disks[i].devname) == 0) { 165 *index = i; 166 return (0); 167 } 168 } 169 errno = ENOENT; 170 return (-1); 171 } 172 173 errno = EINVAL; 174 return (-1); 175 } 176 177 /* 178 * Mark a standalone disk as being a physical disk. 179 */ 180 static int 181 mpt_create_physdisk(int fd, struct mpt_standalone_disk *disk, U8 *PhysDiskNum) 182 { 183 CONFIG_PAGE_HEADER header; 184 CONFIG_PAGE_RAID_PHYS_DISK_0 *config_page; 185 U32 ActionData; 186 187 if (mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK, 188 0, 0, &header, NULL) < 0) 189 return (-1); 190 if (header.PageVersion > MPI_RAIDPHYSDISKPAGE0_PAGEVERSION) { 191 warnx("Unsupported RAID physdisk page 0 version %d", 192 header.PageVersion); 193 errno = EOPNOTSUPP; 194 return (-1); 195 } 196 config_page = calloc(1, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0)); 197 config_page->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK; 198 config_page->Header.PageNumber = 0; 199 config_page->Header.PageLength = sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) / 200 4; 201 config_page->PhysDiskIOC = 0; /* XXX */ 202 config_page->PhysDiskBus = disk->bus; 203 config_page->PhysDiskID = disk->target; 204 205 /* XXX: Enclosure info for PhysDiskSettings? */ 206 if (mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_PHYSDISK, 0, 0, 0, 0, 207 config_page, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0), NULL, 208 &ActionData, sizeof(ActionData), NULL, NULL, 1) < 0) 209 return (-1); 210 *PhysDiskNum = ActionData & 0xff; 211 return (0); 212 } 213 214 static int 215 mpt_delete_physdisk(int fd, U8 PhysDiskNum) 216 { 217 218 return (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_PHYSDISK, 0, 0, 219 PhysDiskNum, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0)); 220 } 221 222 /* 223 * MPT's firmware does not have a clear command. Instead, we 224 * implement it by deleting each array and disk by hand. 225 */ 226 static int 227 clear_config(int ac, char **av) 228 { 229 CONFIG_PAGE_IOC_2 *ioc2; 230 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 231 CONFIG_PAGE_IOC_3 *ioc3; 232 IOC_3_PHYS_DISK *disk; 233 CONFIG_PAGE_IOC_5 *ioc5; 234 IOC_5_HOT_SPARE *spare; 235 int ch, fd, i; 236 237 fd = mpt_open(mpt_unit); 238 if (fd < 0) { 239 warn("mpt_open"); 240 return (errno); 241 } 242 243 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 244 if (ioc2 == NULL) { 245 warn("Failed to fetch volume list"); 246 return (errno); 247 } 248 249 /* Lock all the volumes first. */ 250 vol = ioc2->RaidVolume; 251 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 252 if (mpt_lock_volume(vol->VolumeBus, vol->VolumeID) < 0) { 253 warnx("Volume %s is busy and cannot be deleted", 254 mpt_volume_name(vol->VolumeBus, vol->VolumeID)); 255 return (EBUSY); 256 } 257 } 258 259 printf( 260 "Are you sure you wish to clear the configuration on mpt%u? [y/N] ", 261 mpt_unit); 262 ch = getchar(); 263 if (ch != 'y' && ch != 'Y') { 264 printf("\nAborting\n"); 265 return (0); 266 } 267 268 /* Delete all the volumes. */ 269 vol = ioc2->RaidVolume; 270 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) 271 if (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, 272 vol->VolumeBus, vol->VolumeID, 0, 273 MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS | 274 MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, 275 NULL, NULL, 0) < 0) 276 warn("Failed to delete volume %s", 277 mpt_volume_name(vol->VolumeBus, vol->VolumeID)); 278 free(ioc2); 279 280 /* Delete all the spares. */ 281 ioc5 = mpt_read_ioc_page(fd, 5, NULL); 282 if (ioc5 == NULL) 283 warn("Failed to fetch spare list"); 284 else { 285 spare = ioc5->HotSpare; 286 for (i = 0; i < ioc5->NumHotSpares; spare++, i++) 287 if (mpt_delete_physdisk(fd, spare->PhysDiskNum) < 0) 288 warn("Failed to delete physical disk %d", 289 spare->PhysDiskNum); 290 free(ioc5); 291 } 292 293 /* Delete any RAID physdisks that may be left. */ 294 ioc3 = mpt_read_ioc_page(fd, 3, NULL); 295 if (ioc3 == NULL) 296 warn("Failed to fetch drive list"); 297 else { 298 disk = ioc3->PhysDisk; 299 for (i = 0; i < ioc3->NumPhysDisks; disk++, i++) 300 if (mpt_delete_physdisk(fd, disk->PhysDiskNum) < 0) 301 warn("Failed to delete physical disk %d", 302 disk->PhysDiskNum); 303 free(ioc3); 304 } 305 306 printf("mpt%d: Configuration cleared\n", mpt_unit); 307 mpt_rescan_bus(-1, -1); 308 close(fd); 309 310 return (0); 311 } 312 MPT_COMMAND(top, clear, clear_config); 313 314 #define RT_RAID0 0 315 #define RT_RAID1 1 316 #define RT_RAID1E 2 317 318 static struct raid_type_entry { 319 const char *name; 320 int raid_type; 321 } raid_type_table[] = { 322 { "raid0", RT_RAID0 }, 323 { "raid-0", RT_RAID0 }, 324 { "raid1", RT_RAID1 }, 325 { "raid-1", RT_RAID1 }, 326 { "mirror", RT_RAID1 }, 327 { "raid1e", RT_RAID1E }, 328 { "raid-1e", RT_RAID1E }, 329 { NULL, 0 }, 330 }; 331 332 struct config_id_state { 333 struct mpt_standalone_disk *sdisks; 334 struct mpt_drive_list *list; 335 CONFIG_PAGE_IOC_2 *ioc2; 336 U8 target_id; 337 int nsdisks; 338 }; 339 340 struct drive_info { 341 CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 342 struct mpt_standalone_disk *sdisk; 343 }; 344 345 struct volume_info { 346 int drive_count; 347 struct drive_info *drives; 348 }; 349 350 /* Parse a comma-separated list of drives for a volume. */ 351 static int 352 parse_volume(int fd, int raid_type, struct config_id_state *state, 353 char *volume_str, struct volume_info *info) 354 { 355 struct drive_info *dinfo; 356 U8 PhysDiskNum; 357 char *cp; 358 int count, error, i; 359 360 cp = volume_str; 361 for (count = 0; cp != NULL; count++) { 362 cp = strchr(cp, ','); 363 if (cp != NULL) { 364 cp++; 365 if (*cp == ',') { 366 warnx("Invalid drive list '%s'", volume_str); 367 return (EINVAL); 368 } 369 } 370 } 371 372 /* Validate the number of drives for this volume. */ 373 switch (raid_type) { 374 case RT_RAID0: 375 if (count < 2) { 376 warnx("RAID0 requires at least 2 drives in each " 377 "array"); 378 return (EINVAL); 379 } 380 break; 381 case RT_RAID1: 382 if (count != 2) { 383 warnx("RAID1 requires exactly 2 drives in each " 384 "array"); 385 return (EINVAL); 386 } 387 break; 388 case RT_RAID1E: 389 if (count < 3) { 390 warnx("RAID1E requires at least 3 drives in each " 391 "array"); 392 return (EINVAL); 393 } 394 break; 395 } 396 397 /* Validate each drive. */ 398 info->drives = calloc(count, sizeof(struct drive_info)); 399 info->drive_count = count; 400 for (dinfo = info->drives; (cp = strsep(&volume_str, ",")) != NULL; 401 dinfo++) { 402 /* If this drive is already a RAID phys just fetch the info. */ 403 error = mpt_lookup_drive(state->list, cp, &PhysDiskNum); 404 if (error == 0) { 405 dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL); 406 if (dinfo->info == NULL) 407 return (errno); 408 continue; 409 } 410 411 /* See if it is a standalone disk. */ 412 if (mpt_lookup_standalone_disk(cp, state->sdisks, 413 state->nsdisks, &i) < 0) { 414 warn("Unable to lookup drive %s", cp); 415 return (errno); 416 } 417 dinfo->sdisk = &state->sdisks[i]; 418 419 /* Lock the disk, we will create phys disk pages later. */ 420 if (mpt_lock_physdisk(dinfo->sdisk) < 0) 421 return (errno); 422 } 423 424 return (0); 425 } 426 427 /* 428 * Add RAID physdisk pages for any standalone disks that a volume is 429 * going to use. 430 */ 431 static int 432 add_drives(int fd, struct volume_info *info, int verbose) 433 { 434 struct drive_info *dinfo; 435 U8 PhysDiskNum; 436 int i; 437 438 for (i = 0, dinfo = info->drives; i < info->drive_count; 439 i++, dinfo++) { 440 if (dinfo->info == NULL) { 441 if (mpt_create_physdisk(fd, dinfo->sdisk, 442 &PhysDiskNum) < 0) { 443 warn( 444 "Failed to create physical disk page for %s", 445 dinfo->sdisk->devname); 446 return (errno); 447 } 448 if (verbose) 449 printf("Added drive %s with PhysDiskNum %u\n", 450 dinfo->sdisk->devname, PhysDiskNum); 451 452 dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL); 453 if (dinfo->info == NULL) 454 return (errno); 455 } 456 } 457 return (0); 458 } 459 460 /* 461 * Find the next free target ID assuming that 'target_id' is the last 462 * one used. 'target_id' should be 0xff for the initial test. 463 */ 464 static U8 465 find_next_volume(struct config_id_state *state) 466 { 467 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 468 int i; 469 470 restart: 471 /* Assume the current one is used. */ 472 state->target_id++; 473 474 /* Search drives first. */ 475 for (i = 0; i < state->nsdisks; i++) 476 if (state->sdisks[i].target == state->target_id) 477 goto restart; 478 for (i = 0; i < state->list->ndrives; i++) 479 if (state->list->drives[i]->PhysDiskID == state->target_id) 480 goto restart; 481 482 /* Seach volumes second. */ 483 vol = state->ioc2->RaidVolume; 484 for (i = 0; i < state->ioc2->NumActiveVolumes; vol++, i++) 485 if (vol->VolumeID == state->target_id) 486 goto restart; 487 488 return (state->target_id); 489 } 490 491 /* Create a volume and populate it with drives. */ 492 static CONFIG_PAGE_RAID_VOL_0 * 493 build_volume(int fd, struct volume_info *info, int raid_type, long stripe_size, 494 struct config_id_state *state, int verbose) 495 { 496 CONFIG_PAGE_HEADER header; 497 CONFIG_PAGE_RAID_VOL_0 *vol; 498 RAID_VOL0_PHYS_DISK *rdisk; 499 struct drive_info *dinfo; 500 U32 MinLBA; 501 uint64_t MaxLBA; 502 size_t page_size; 503 int i; 504 505 if (mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME, 506 0, 0, &header, NULL) < 0) 507 return (NULL); 508 if (header.PageVersion > MPI_RAIDVOLPAGE0_PAGEVERSION) { 509 warnx("Unsupported RAID volume page 0 version %d", 510 header.PageVersion); 511 errno = EOPNOTSUPP; 512 return (NULL); 513 } 514 page_size = sizeof(CONFIG_PAGE_RAID_VOL_0) + 515 sizeof(RAID_VOL0_PHYS_DISK) * (info->drive_count - 1); 516 vol = calloc(1, page_size); 517 518 /* Header */ 519 vol->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME; 520 vol->Header.PageNumber = 0; 521 vol->Header.PageLength = page_size / 4; 522 523 /* Properties */ 524 vol->VolumeID = find_next_volume(state); 525 vol->VolumeBus = 0; 526 vol->VolumeIOC = 0; /* XXX */ 527 vol->VolumeStatus.Flags = MPI_RAIDVOL0_STATUS_FLAG_ENABLED; 528 vol->VolumeStatus.State = MPI_RAIDVOL0_STATUS_STATE_OPTIMAL; 529 vol->VolumeSettings.Settings = MPI_RAIDVOL0_SETTING_USE_DEFAULTS; 530 vol->VolumeSettings.HotSparePool = MPI_RAID_HOT_SPARE_POOL_0; 531 vol->NumPhysDisks = info->drive_count; 532 533 /* Find the smallest drive. */ 534 MinLBA = info->drives[0].info->MaxLBA; 535 for (i = 1; i < info->drive_count; i++) 536 if (info->drives[i].info->MaxLBA < MinLBA) 537 MinLBA = info->drives[i].info->MaxLBA; 538 539 /* 540 * Now chop off 512MB at the end to leave room for the 541 * metadata. The controller might only use 64MB, but we just 542 * chop off the max to be simple. 543 */ 544 MinLBA -= (512 * 1024 * 1024) / 512; 545 546 switch (raid_type) { 547 case RT_RAID0: 548 vol->VolumeType = MPI_RAID_VOL_TYPE_IS; 549 vol->StripeSize = stripe_size / 512; 550 MaxLBA = MinLBA * info->drive_count; 551 break; 552 case RT_RAID1: 553 vol->VolumeType = MPI_RAID_VOL_TYPE_IM; 554 MaxLBA = MinLBA * (info->drive_count / 2); 555 break; 556 case RT_RAID1E: 557 vol->VolumeType = MPI_RAID_VOL_TYPE_IME; 558 vol->StripeSize = stripe_size / 512; 559 MaxLBA = MinLBA * info->drive_count / 2; 560 break; 561 default: 562 /* Pacify gcc. */ 563 abort(); 564 } 565 566 /* 567 * If the controller doesn't support 64-bit addressing and the 568 * new volume is larger than 2^32 blocks, warn the user and 569 * truncate the volume. 570 */ 571 if (MaxLBA >> 32 != 0 && 572 !(state->ioc2->CapabilitiesFlags & 573 MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING)) { 574 warnx( 575 "Controller does not support volumes > 2TB, truncating volume."); 576 MaxLBA = 0xffffffff; 577 } 578 vol->MaxLBA = MaxLBA; 579 vol->MaxLBAHigh = MaxLBA >> 32; 580 581 /* Populate drives. */ 582 for (i = 0, dinfo = info->drives, rdisk = vol->PhysDisk; 583 i < info->drive_count; i++, dinfo++, rdisk++) { 584 if (verbose) 585 printf("Adding drive %u (%u:%u) to volume %u:%u\n", 586 dinfo->info->PhysDiskNum, dinfo->info->PhysDiskBus, 587 dinfo->info->PhysDiskID, vol->VolumeBus, 588 vol->VolumeID); 589 if (raid_type == RT_RAID1) { 590 if (i == 0) 591 rdisk->PhysDiskMap = 592 MPI_RAIDVOL0_PHYSDISK_PRIMARY; 593 else 594 rdisk->PhysDiskMap = 595 MPI_RAIDVOL0_PHYSDISK_SECONDARY; 596 } else 597 rdisk->PhysDiskMap = i; 598 rdisk->PhysDiskNum = dinfo->info->PhysDiskNum; 599 } 600 601 return (vol); 602 } 603 604 static int 605 create_volume(int ac, char **av) 606 { 607 CONFIG_PAGE_RAID_VOL_0 *vol; 608 struct config_id_state state; 609 struct volume_info *info; 610 int ch, error, fd, i, raid_type, verbose, quick; 611 long stripe_size; 612 #ifdef DEBUG 613 int dump; 614 #endif 615 616 if (ac < 2) { 617 warnx("create: volume type required"); 618 return (EINVAL); 619 } 620 621 fd = mpt_open(mpt_unit); 622 if (fd < 0) { 623 warn("mpt_open"); 624 return (errno); 625 } 626 627 /* Lookup the RAID type first. */ 628 raid_type = -1; 629 for (i = 0; raid_type_table[i].name != NULL; i++) 630 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 631 raid_type = raid_type_table[i].raid_type; 632 break; 633 } 634 635 if (raid_type == -1) { 636 warnx("Unknown or unsupported volume type %s", av[1]); 637 return (EINVAL); 638 } 639 640 /* Parse any options. */ 641 optind = 2; 642 #ifdef DEBUG 643 dump = 0; 644 #endif 645 quick = 0; 646 verbose = 0; 647 stripe_size = 64 * 1024; 648 649 while ((ch = getopt(ac, av, "dqs:v")) != -1) { 650 switch (ch) { 651 #ifdef DEBUG 652 case 'd': 653 dump = 1; 654 break; 655 #endif 656 case 'q': 657 quick = 1; 658 break; 659 case 's': 660 stripe_size = dehumanize(optarg); 661 if ((stripe_size < 512) || (!powerof2(stripe_size))) { 662 warnx("Invalid stripe size %s", optarg); 663 return (EINVAL); 664 } 665 break; 666 case 'v': 667 verbose = 1; 668 break; 669 case '?': 670 default: 671 return (EINVAL); 672 } 673 } 674 ac -= optind; 675 av += optind; 676 677 /* Fetch existing config data. */ 678 state.ioc2 = mpt_read_ioc_page(fd, 2, NULL); 679 if (state.ioc2 == NULL) { 680 warn("Failed to read volume list"); 681 return (errno); 682 } 683 state.list = mpt_pd_list(fd); 684 if (state.list == NULL) 685 return (errno); 686 error = mpt_fetch_disks(fd, &state.nsdisks, &state.sdisks); 687 if (error) { 688 warn("Failed to fetch standalone disk list"); 689 return (error); 690 } 691 state.target_id = 0xff; 692 693 /* Parse the drive list. */ 694 if (ac != 1) { 695 warnx("Exactly one drive list is required"); 696 return (EINVAL); 697 } 698 info = calloc(1, sizeof(*info)); 699 error = parse_volume(fd, raid_type, &state, av[0], info); 700 if (error) 701 return (error); 702 703 /* Create RAID physdisk pages for standalone disks. */ 704 error = add_drives(fd, info, verbose); 705 if (error) 706 return (error); 707 708 /* Build the volume. */ 709 vol = build_volume(fd, info, raid_type, stripe_size, &state, verbose); 710 711 #ifdef DEBUG 712 if (dump) { 713 dump_config(vol); 714 goto skip; 715 } 716 #endif 717 718 /* Send the new volume to the controller. */ 719 if (mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_VOLUME, vol->VolumeBus, 720 vol->VolumeID, 0, quick ? MPI_RAID_ACTION_ADATA_DO_NOT_SYNC : 0, 721 vol, vol->Header.PageLength * 4, NULL, NULL, 0, NULL, NULL, 1) < 722 0) { 723 warn("Failed to add volume"); 724 return (errno); 725 } 726 727 #ifdef DEBUG 728 skip: 729 #endif 730 mpt_rescan_bus(vol->VolumeBus, vol->VolumeID); 731 732 /* Clean up. */ 733 free(vol); 734 free(info); 735 free(state.sdisks); 736 mpt_free_pd_list(state.list); 737 free(state.ioc2); 738 close(fd); 739 740 return (0); 741 } 742 MPT_COMMAND(top, create, create_volume); 743 744 static int 745 delete_volume(int ac, char **av) 746 { 747 U8 VolumeBus, VolumeID; 748 int fd; 749 750 if (ac != 2) { 751 warnx("delete: volume required"); 752 return (EINVAL); 753 } 754 755 fd = mpt_open(mpt_unit); 756 if (fd < 0) { 757 warn("mpt_open"); 758 return (errno); 759 } 760 761 if (mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID) < 0) { 762 warn("Invalid volume %s", av[1]); 763 return (errno); 764 } 765 766 if (mpt_lock_volume(VolumeBus, VolumeID) < 0) 767 return (errno); 768 769 if (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, VolumeBus, 770 VolumeID, 0, MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS | 771 MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, NULL, 772 NULL, 0) < 0) { 773 warn("Failed to delete volume"); 774 return (errno); 775 } 776 777 mpt_rescan_bus(-1, -1); 778 close(fd); 779 780 return (0); 781 } 782 MPT_COMMAND(top, delete, delete_volume); 783 784 static int 785 find_volume_spare_pool(int fd, const char *name, int *pool) 786 { 787 CONFIG_PAGE_RAID_VOL_0 *info; 788 CONFIG_PAGE_IOC_2 *ioc2; 789 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 790 U8 VolumeBus, VolumeID; 791 int i, j, new_pool, pool_count[7]; 792 793 if (mpt_lookup_volume(fd, name, &VolumeBus, &VolumeID) < 0) { 794 warn("Invalid volume %s", name); 795 return (-1); 796 } 797 798 info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL); 799 if (info == NULL) 800 return (-1); 801 802 /* 803 * Check for an existing pool other than pool 0 (used for 804 * global spares). 805 */ 806 if ((info->VolumeSettings.HotSparePool & ~MPI_RAID_HOT_SPARE_POOL_0) != 807 0) { 808 *pool = 1 << (ffs(info->VolumeSettings.HotSparePool & 809 ~MPI_RAID_HOT_SPARE_POOL_0) - 1); 810 return (0); 811 } 812 free(info); 813 814 /* 815 * Try to find a free pool. First, figure out which pools are 816 * in use. 817 */ 818 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 819 if (ioc2 == NULL) { 820 warn("Failed to fetch volume list"); 821 return (-1); 822 } 823 bzero(pool_count, sizeof(pool_count)); 824 vol = ioc2->RaidVolume; 825 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 826 info = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL); 827 if (info == NULL) 828 return (-1); 829 for (j = 0; j < 7; j++) 830 if (info->VolumeSettings.HotSparePool & (1 << (j + 1))) 831 pool_count[j]++; 832 free(info); 833 } 834 free(ioc2); 835 836 /* Find the pool with the lowest use count. */ 837 new_pool = 0; 838 for (i = 1; i < 7; i++) 839 if (pool_count[i] < pool_count[new_pool]) 840 new_pool = i; 841 new_pool++; 842 843 /* Add this pool to the volume. */ 844 info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL); 845 if (info == NULL) 846 return (-1); 847 info->VolumeSettings.HotSparePool |= (1 << new_pool); 848 if (mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS, 849 VolumeBus, VolumeID, 0, *(U32 *)&info->VolumeSettings, NULL, 0, 850 NULL, NULL, 0, NULL, NULL, 0) < 0) { 851 warnx("Failed to add spare pool %d to %s", new_pool, 852 mpt_volume_name(VolumeBus, VolumeID)); 853 return (-1); 854 } 855 free(info); 856 857 *pool = (1 << new_pool); 858 return (0); 859 } 860 861 static int 862 add_spare(int ac, char **av) 863 { 864 CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 865 struct mpt_standalone_disk *sdisks; 866 struct mpt_drive_list *list; 867 U8 PhysDiskNum; 868 int error, fd, i, nsdisks, pool; 869 870 if (ac < 2) { 871 warnx("add spare: drive required"); 872 return (EINVAL); 873 } 874 if (ac > 3) { 875 warnx("add spare: extra arguments"); 876 return (EINVAL); 877 } 878 879 fd = mpt_open(mpt_unit); 880 if (fd < 0) { 881 warn("mpt_open"); 882 return (errno); 883 } 884 885 if (ac == 3) { 886 if (find_volume_spare_pool(fd, av[2], &pool) < 0) 887 return (errno); 888 } else 889 pool = MPI_RAID_HOT_SPARE_POOL_0; 890 891 list = mpt_pd_list(fd); 892 if (list == NULL) 893 return (errno); 894 895 error = mpt_lookup_drive(list, av[1], &PhysDiskNum); 896 if (error) { 897 error = mpt_fetch_disks(fd, &nsdisks, &sdisks); 898 if (error != 0) { 899 warn("Failed to fetch standalone disk list"); 900 return (error); 901 } 902 903 if (mpt_lookup_standalone_disk(av[1], sdisks, nsdisks, &i) < 904 0) { 905 warn("Unable to lookup drive %s", av[1]); 906 return (errno); 907 } 908 909 if (mpt_lock_physdisk(&sdisks[i]) < 0) 910 return (errno); 911 912 if (mpt_create_physdisk(fd, &sdisks[i], &PhysDiskNum) < 0) { 913 warn("Failed to create physical disk page"); 914 return (errno); 915 } 916 free(sdisks); 917 } 918 mpt_free_pd_list(list); 919 920 info = mpt_pd_info(fd, PhysDiskNum, NULL); 921 if (info == NULL) { 922 warn("Failed to fetch drive info"); 923 return (errno); 924 } 925 926 info->PhysDiskSettings.HotSparePool = pool; 927 error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS, 0, 928 0, PhysDiskNum, *(U32 *)&info->PhysDiskSettings, NULL, 0, NULL, 929 NULL, 0, NULL, NULL, 0); 930 if (error) { 931 warn("Failed to assign spare"); 932 return (errno); 933 } 934 935 free(info); 936 close(fd); 937 938 return (0); 939 } 940 MPT_COMMAND(top, add, add_spare); 941 942 static int 943 remove_spare(int ac, char **av) 944 { 945 CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 946 struct mpt_drive_list *list; 947 U8 PhysDiskNum; 948 int error, fd; 949 950 if (ac != 2) { 951 warnx("remove spare: drive required"); 952 return (EINVAL); 953 } 954 955 fd = mpt_open(mpt_unit); 956 if (fd < 0) { 957 warn("mpt_open"); 958 return (errno); 959 } 960 961 list = mpt_pd_list(fd); 962 if (list == NULL) 963 return (errno); 964 965 error = mpt_lookup_drive(list, av[1], &PhysDiskNum); 966 if (error) { 967 warn("Failed to find drive %s", av[1]); 968 return (error); 969 } 970 mpt_free_pd_list(list); 971 972 973 info = mpt_pd_info(fd, PhysDiskNum, NULL); 974 if (info == NULL) { 975 warn("Failed to fetch drive info"); 976 return (errno); 977 } 978 979 if (info->PhysDiskSettings.HotSparePool == 0) { 980 warnx("Drive %u is not a hot spare", PhysDiskNum); 981 return (EINVAL); 982 } 983 984 if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) { 985 warn("Failed to delete physical disk page"); 986 return (errno); 987 } 988 989 mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID); 990 free(info); 991 close(fd); 992 993 return (0); 994 } 995 MPT_COMMAND(top, remove, remove_spare); 996 997 #ifdef DEBUG 998 MPT_TABLE(top, pd); 999 1000 static int 1001 pd_create(int ac, char **av) 1002 { 1003 struct mpt_standalone_disk *disks; 1004 int error, fd, i, ndisks; 1005 U8 PhysDiskNum; 1006 1007 if (ac != 2) { 1008 warnx("pd create: drive required"); 1009 return (EINVAL); 1010 } 1011 1012 fd = mpt_open(mpt_unit); 1013 if (fd < 0) { 1014 warn("mpt_open"); 1015 return (errno); 1016 } 1017 1018 error = mpt_fetch_disks(fd, &ndisks, &disks); 1019 if (error != 0) { 1020 warn("Failed to fetch standalone disk list"); 1021 return (error); 1022 } 1023 1024 if (mpt_lookup_standalone_disk(av[1], disks, ndisks, &i) < 0) { 1025 warn("Unable to lookup drive"); 1026 return (errno); 1027 } 1028 1029 if (mpt_lock_physdisk(&disks[i]) < 0) 1030 return (errno); 1031 1032 if (mpt_create_physdisk(fd, &disks[i], &PhysDiskNum) < 0) { 1033 warn("Failed to create physical disk page"); 1034 return (errno); 1035 } 1036 free(disks); 1037 1038 printf("Added drive %s with PhysDiskNum %u\n", av[1], PhysDiskNum); 1039 1040 close(fd); 1041 1042 return (0); 1043 } 1044 MPT_COMMAND(pd, create, pd_create); 1045 1046 static int 1047 pd_delete(int ac, char **av) 1048 { 1049 CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 1050 struct mpt_drive_list *list; 1051 int fd; 1052 U8 PhysDiskNum; 1053 1054 if (ac != 2) { 1055 warnx("pd delete: drive required"); 1056 return (EINVAL); 1057 } 1058 1059 fd = mpt_open(mpt_unit); 1060 if (fd < 0) { 1061 warn("mpt_open"); 1062 return (errno); 1063 } 1064 1065 list = mpt_pd_list(fd); 1066 if (list == NULL) 1067 return (errno); 1068 1069 if (mpt_lookup_drive(list, av[1], &PhysDiskNum) < 0) { 1070 warn("Failed to find drive %s", av[1]); 1071 return (errno); 1072 } 1073 mpt_free_pd_list(list); 1074 1075 info = mpt_pd_info(fd, PhysDiskNum, NULL); 1076 if (info == NULL) { 1077 warn("Failed to fetch drive info"); 1078 return (errno); 1079 } 1080 1081 if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) { 1082 warn("Failed to delete physical disk page"); 1083 return (errno); 1084 } 1085 1086 mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID); 1087 free(info); 1088 close(fd); 1089 1090 return (0); 1091 } 1092 MPT_COMMAND(pd, delete, pd_delete); 1093 1094 /* Display raw data about a volume config. */ 1095 static void 1096 dump_config(CONFIG_PAGE_RAID_VOL_0 *vol) 1097 { 1098 int i; 1099 1100 printf("Volume Configuration (Debug):\n"); 1101 printf( 1102 " Page Header: Type 0x%02x Number 0x%02x Length 0x%02x(%u) Version 0x%02x\n", 1103 vol->Header.PageType, vol->Header.PageNumber, 1104 vol->Header.PageLength, vol->Header.PageLength * 4, 1105 vol->Header.PageVersion); 1106 printf(" Address: %d:%d IOC %d\n", vol->VolumeBus, vol->VolumeID, 1107 vol->VolumeIOC); 1108 printf(" Type: %d (%s)\n", vol->VolumeType, 1109 mpt_raid_level(vol->VolumeType)); 1110 printf(" Status: %s (Flags 0x%02x)\n", 1111 mpt_volstate(vol->VolumeStatus.State), vol->VolumeStatus.Flags); 1112 printf(" Settings: 0x%04x (Spare Pools 0x%02x)\n", 1113 vol->VolumeSettings.Settings, vol->VolumeSettings.HotSparePool); 1114 printf(" MaxLBA: %ju\n", (uintmax_t)vol->MaxLBAHigh << 32 | 1115 vol->MaxLBA); 1116 printf(" Stripe Size: %ld\n", (long)vol->StripeSize * 512); 1117 printf(" %d Disks:\n", vol->NumPhysDisks); 1118 1119 for (i = 0; i < vol->NumPhysDisks; i++) 1120 printf(" Disk %d: Num 0x%02x Map 0x%02x\n", i, 1121 vol->PhysDisk[i].PhysDiskNum, vol->PhysDisk[i].PhysDiskMap); 1122 } 1123 1124 static int 1125 debug_config(int ac, char **av) 1126 { 1127 CONFIG_PAGE_RAID_VOL_0 *vol; 1128 U8 VolumeBus, VolumeID; 1129 int fd; 1130 1131 if (ac != 2) { 1132 warnx("debug: volume required"); 1133 return (EINVAL); 1134 } 1135 1136 fd = mpt_open(mpt_unit); 1137 if (fd < 0) { 1138 warn("mpt_open"); 1139 return (errno); 1140 } 1141 1142 if (mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID) < 0) { 1143 warn("Invalid volume: %s", av[1]); 1144 return (errno); 1145 } 1146 1147 vol = mpt_vol_info(fd, VolumeBus, VolumeID, NULL); 1148 if (vol == NULL) { 1149 warn("Failed to get volume info"); 1150 return (errno); 1151 } 1152 1153 dump_config(vol); 1154 free(vol); 1155 close(fd); 1156 1157 return (0); 1158 } 1159 MPT_COMMAND(top, debug, debug_config); 1160 #endif 1161