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