int mfi_lookup_volume(int fd, const char *name, uint8_t *target_id) { struct mfi_query_disk info; struct mfi_ld_list list; char *cp; long val; u_int i; /* If it's a valid number, treat it as a raw target ID. */ val = strtol(name, &cp, 0); if (*cp == '\0') { *target_id = val; return (0); } if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), NULL, 0, NULL) < 0) return (-1); for (i = 0; i < list.ld_count; i++) { if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, &info) < 0) continue; if (strcmp(name, info.devname) == 0) { *target_id = list.ld_list[i].ld.v.target_id; return (0); } } errno = EINVAL; return (-1); }
int mfi_config_read(int fd, struct mfi_config_data **configp) { struct mfi_config_data *config; uint32_t config_size; int error; /* * Keep fetching the config in a loop until we have a large enough * buffer to hold the entire configuration. */ config = NULL; config_size = 1024; fetch: config = reallocf(config, config_size); if (config == NULL) return (-1); if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config, config_size, NULL, 0, NULL) < 0) { error = errno; free(config); errno = error; return (-1); } if (config->size > config_size) { config_size = config->size; goto fetch; } *configp = config; return (0); }
int mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp) { struct mfi_pd_list *list; uint32_t list_size; /* * Keep fetching the list in a loop until we have a large enough * buffer to hold the entire list. */ list = NULL; list_size = 1024; fetch: list = reallocf(list, list_size); if (list == NULL) return (-1); if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 0, statusp) < 0) { free(list); return (-1); } if (list->size > list_size) { list_size = list->size; goto fetch; } *listp = list; return (0); }
static int mfi_ctrl_set_properties(int fd, struct mfi_ctrl_props *info) { return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_SET_PROPS, info, sizeof(struct mfi_ctrl_props), NULL, 0, NULL)); }
int mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) { return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); }
static int clear_config(int ac, char **av) { struct mfi_ld_list list; int ch, error, fd; u_int i; fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (!mfi_reconfig_supported()) { warnx("The current mfi(4) driver does not support " "configuration changes."); close(fd); return (EOPNOTSUPP); } if (mfi_ld_get_list(fd, &list, NULL) < 0) { error = errno; warn("Failed to get volume list"); close(fd); return (error); } for (i = 0; i < list.ld_count; i++) { if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { warnx("Volume %s is busy and cannot be deleted", mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); close(fd); return (EBUSY); } } printf( "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", mfi_unit); ch = getchar(); if (ch != 'y' && ch != 'Y') { printf("\nAborting\n"); close(fd); return (0); } if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { error = errno; warn("Failed to clear configuration"); close(fd); return (error); } printf("mfi%d: Configuration cleared\n", mfi_unit); close(fd); return (0); }
static int mfi_ld_get_props(int fd, uint8_t target_id, struct mfi_ld_props *props) { uint8_t mbox[1]; mbox[0] = target_id; return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_PROP, props, sizeof(struct mfi_ld_props), mbox, 1, NULL)); }
static int mfi_ld_set_props(int fd, struct mfi_ld_props *props) { uint8_t mbox[4]; mbox_store_ldref(mbox, &props->ld); return (mfi_dcmd_command(fd, MFI_DCMD_LD_SET_PROP, props, sizeof(struct mfi_ld_props), mbox, 4, NULL)); }
int display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) { struct mfi_foreign_scan_info info; uint8_t i; int error, fd; if (ac > 2) { warnx("foreign display: extra arguments"); return (EINVAL); } fd = mfi_open(mfi_unit, O_RDONLY); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, sizeof(info), NULL, 0, NULL) < 0) { error = errno; warn("Failed to scan foreign configuration"); close(fd); return (error); } if (info.count == 0) { warnx("foreign display: no foreign configs found"); close(fd); return (EINVAL); } if (ac == 1) { for (i = 0; i < info.count; i++) { error = foreign_show_cfg(fd, display_cmd, i, diagnostic); if(error != 0) { close(fd); return (error); } if (i < info.count - 1) printf("\n"); } } else if (ac == 2) { error = foreign_show_cfg(fd, display_cmd, atoi(av[1]), diagnostic); if (error != 0) { close(fd); return (error); } } close(fd); return (0); }
int mfi_ld_get_info(int fd, uint8_t target_id, struct mfi_ld_info *info, uint8_t *statusp) { uint8_t mbox[1]; mbox[0] = target_id; return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_INFO, info, sizeof(struct mfi_ld_info), mbox, 1, statusp)); }
static void mfi_get_time(int fd, uint32_t *at) { if (mfi_dcmd_command(fd, MFI_DCMD_TIME_SECS_GET, at, sizeof(*at), NULL, 0, NULL) < 0) { warn("Couldn't fetch adapter time"); at = 0; } }
int mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info, uint8_t *statusp) { uint8_t mbox[2]; mbox_store_device_id(&mbox[0], device_id); return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info, sizeof(struct mfi_pd_info), mbox, 2, statusp)); }
static int remove_spare(int ac, char **av) { struct mfi_pd_info info; int error, fd; uint16_t device_id; uint8_t mbox[4]; if (ac != 2) { warnx("remove spare: drive required"); return (EINVAL); } fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } error = mfi_lookup_drive(fd, av[1], &device_id); if (error) { close(fd); return (error); } /* Get the info for this drive. */ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { error = errno; warn("Failed to fetch info for drive %u", device_id); close(fd); return (error); } if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { warnx("Drive %u is not a hot spare", device_id); close(fd); return (EINVAL); } mbox_store_pdref(mbox, &info.ref); if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, sizeof(mbox), NULL) < 0) { error = errno; warn("Failed to delete spare"); close(fd); return (error); } close(fd); return (0); }
static int patrol_get_props(int fd, struct mfi_pr_properties *prop) { int error; if (mfi_dcmd_command(fd, MFI_DCMD_PR_GET_PROPERTIES, prop, sizeof(*prop), NULL, 0, NULL) < 0) { error = errno; warn("Failed to get patrol read properties"); return (error); } return (0); }
static int mfi_get_events(int fd, struct mfi_evt_list *list, int num_events, union mfi_evt filter, uint32_t start_seq, uint8_t *statusp) { uint32_t mbox[2]; size_t size; mbox[0] = start_seq; mbox[1] = filter.word; size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * (num_events - 1); return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size, (uint8_t *)&mbox, sizeof(mbox), statusp)); }
static int stop_patrol(int ac, char **av) { int error, fd; fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (mfi_dcmd_command(fd, MFI_DCMD_PR_STOP, NULL, 0, NULL, 0, NULL) < 0) { error = errno; warn("Failed to stop patrol read"); return (error); } close(fd); return (0); }
static int flash_adapter(int ac, char **av) { struct mfi_progress dummy; off_t offset; size_t nread; char *buf; struct stat sb; int error, fd, flash; uint8_t mbox[4], status; if (ac != 2) { warnx("flash: Firmware file required"); return (EINVAL); } flash = open(av[1], O_RDONLY); if (flash < 0) { error = errno; warn("flash: Failed to open %s", av[1]); return (error); } buf = NULL; fd = -1; if (fstat(flash, &sb) < 0) { error = errno; warn("fstat(%s)", av[1]); goto error; } if (sb.st_size % 1024 != 0 || sb.st_size > 0x7fffffff) { warnx("Invalid flash file size"); error = EINVAL; goto error; } fd = mfi_open(mfi_unit, O_RDWR); if (fd < 0) { error = errno; warn("mfi_open"); goto error; } /* First, ask the firmware to allocate space for the flash file. */ mbox_store_word(mbox, sb.st_size); mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_OPEN, NULL, 0, mbox, 4, &status); if (status != MFI_STAT_OK) { warnx("Failed to alloc flash memory: %s", mfi_status(status)); error = EIO; goto error; } /* Upload the file 64k at a time. */ buf = malloc(FLASH_BUF_SIZE); if (buf == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } offset = 0; while (sb.st_size > 0) { nread = read(flash, buf, FLASH_BUF_SIZE); if (nread <= 0 || nread % 1024 != 0) { warnx("Bad read from flash file"); mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_CLOSE, NULL, 0, NULL, 0, NULL); error = ENXIO; goto error; } mbox_store_word(mbox, offset); mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_DOWNLOAD, buf, nread, mbox, 4, &status); if (status != MFI_STAT_OK) { warnx("Flash download failed: %s", mfi_status(status)); mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_CLOSE, NULL, 0, NULL, 0, NULL); error = ENXIO; goto error; } sb.st_size -= nread; offset += nread; } /* Kick off the flash. */ printf("WARNING: Firmware flash in progress, do not reboot machine... "); fflush(stdout); mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_FLASH, &dummy, sizeof(dummy), NULL, 0, &status); if (status != MFI_STAT_OK) { printf("failed:\n\t%s\n", mfi_status(status)); error = ENXIO; goto error; } printf("finished\n"); error = display_pending_firmware(fd); error: free(buf); if (fd >= 0) close(fd); close(flash); return (error); }
static int foreign_import(int ac, char **av) { struct mfi_foreign_scan_info info; int ch, error, fd; uint8_t cfgidx; uint8_t mbox[4]; if (ac > 2) { warnx("foreign preview: extra arguments"); return (EINVAL); } fd = mfi_open(mfi_unit, O_RDWR); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, sizeof(info), NULL, 0, NULL) < 0) { error = errno; warn("Failed to scan foreign configuration"); close(fd); return (error); } if (info.count == 0) { warnx("foreign import: no foreign configs found"); close(fd); return (EINVAL); } if (ac == 1) { cfgidx = 0xff; printf("Are you sure you wish to import ALL foreign " "configurations on mfi%u? [y/N] ", mfi_unit); } else { /* * While this is docmmented for MegaCli this failed with * exit code 0x03 on the test controller which was a Supermicro * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based * controller. */ cfgidx = atoi(av[1]); if (cfgidx >= info.count) { warnx("Invalid foreign config %d specified max is %d", cfgidx, info.count - 1); close(fd); return (EINVAL); } printf("Are you sure you wish to import the foreign " "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit); } ch = getchar(); if (ch != 'y' && ch != 'Y') { printf("\nAborting\n"); close(fd); return (0); } bzero(mbox, sizeof(mbox)); mbox[0] = cfgidx; if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox, sizeof(mbox), NULL) < 0) { error = errno; warn("Failed to import foreign configuration"); close(fd); return (error); } if (ac == 1) printf("mfi%d: All foreign configurations imported\n", mfi_unit); else printf("mfi%d: Foreign configuration %d imported\n", mfi_unit, cfgidx); close(fd); return (0); }
static int patrol_config(int ac, char **av) { struct mfi_pr_properties prop; long val; time_t now; int error, fd; uint32_t at, next_exec, exec_freq; char *cp; uint8_t op_mode; exec_freq = 0; /* GCC too stupid */ next_exec = 0; if (ac < 2) { warnx("patrol: command required"); return (EINVAL); } if (strcasecmp(av[1], "auto") == 0) { op_mode = MFI_PR_OPMODE_AUTO; if (ac > 2) { if (strcasecmp(av[2], "continuously") == 0) exec_freq = 0xffffffff; else { val = strtol(av[2], &cp, 0); if (*cp != '\0') { warnx("patrol: Invalid interval %s", av[2]); return (EINVAL); } exec_freq = val; } } if (ac > 3) { val = strtol(av[3], &cp, 0); if (*cp != '\0' || val < 0) { warnx("patrol: Invalid start time %s", av[3]); return (EINVAL); } next_exec = val; } } else if (strcasecmp(av[1], "manual") == 0) op_mode = MFI_PR_OPMODE_MANUAL; else if (strcasecmp(av[1], "disable") == 0) op_mode = MFI_PR_OPMODE_DISABLED; else { warnx("patrol: Invalid command %s", av[1]); return (EINVAL); } fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } error = patrol_get_props(fd, &prop); if (error) { close(fd); return (error); } prop.op_mode = op_mode; if (op_mode == MFI_PR_OPMODE_AUTO) { if (ac > 2) prop.exec_freq = exec_freq; if (ac > 3) { time(&now); mfi_get_time(fd, &at); if (at == 0) { close(fd); return (ENXIO); } prop.next_exec = at + next_exec; printf("Starting next patrol read at %s", adapter_time(now, at, prop.next_exec)); } } if (mfi_dcmd_command(fd, MFI_DCMD_PR_SET_PROPERTIES, &prop, sizeof(prop), NULL, 0, NULL) < 0) { error = errno; warn("Failed to set patrol read properties"); close(fd); return (error); } close(fd); return (0); }
static int add_spare(int ac, char **av) { struct mfi_pd_info info; struct mfi_config_data *config; struct mfi_array *ar; struct mfi_ld_config *ld; struct mfi_spare *spare; uint16_t device_id; uint8_t target_id; char *p; int error, fd, i; if (ac < 2) { warnx("add spare: drive required"); return (EINVAL); } fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } config = NULL; spare = NULL; error = mfi_lookup_drive(fd, av[1], &device_id); if (error) goto error; if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { error = errno; warn("Failed to fetch drive info"); goto error; } if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { warnx("Drive %u is not available", device_id); error = EINVAL; goto error; } if (ac > 2) { if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { error = errno; warn("Invalid volume %s", av[2]); goto error; } } if (mfi_config_read(fd, &config) < 0) { error = errno; warn("Failed to read configuration"); goto error; } spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * config->array_count); if (spare == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } bzero(spare, sizeof(struct mfi_spare)); spare->ref = info.ref; if (ac == 2) { /* Global spare backs all arrays. */ p = (char *)config->array; for (i = 0; i < config->array_count; i++) { ar = (struct mfi_array *)p; if (ar->size > info.coerced_size) { warnx("Spare isn't large enough for array %u", ar->array_ref); error = EINVAL; goto error; } p += config->array_size; } spare->array_count = 0; } else { /* * Dedicated spares only back the arrays for a * specific volume. */ ld = mfi_config_lookup_volume(config, target_id); if (ld == NULL) { warnx("Did not find volume %d", target_id); error = EINVAL; goto error; } spare->spare_type |= MFI_SPARE_DEDICATED; spare->array_count = ld->params.span_depth; for (i = 0; i < ld->params.span_depth; i++) { ar = mfi_config_lookup_array(config, ld->span[i].array_ref); if (ar == NULL) { warnx("Missing array; inconsistent config?"); error = ENXIO; goto error; } if (ar->size > info.coerced_size) { warnx("Spare isn't large enough for array %u", ar->array_ref); error = EINVAL; goto error; } spare->array_ref[i] = ar->array_ref; } } if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, NULL, 0, NULL) < 0) { error = errno; warn("Failed to assign spare"); /* FALLTHROUGH. */ } error: free(spare); free(config); close(fd); return (error); }
static int delete_volume(int ac, char **av) { struct mfi_ld_info info; int error, fd; uint8_t target_id, mbox[4]; /* * Backwards compat. Map 'delete volume' to 'delete' and * 'delete spare' to 'remove'. */ if (ac > 1) { if (strcmp(av[1], "volume") == 0) { av++; ac--; } else if (strcmp(av[1], "spare") == 0) { av++; ac--; return (remove_spare(ac, av)); } } if (ac != 2) { warnx("delete volume: volume required"); return (EINVAL); } fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (!mfi_reconfig_supported()) { warnx("The current mfi(4) driver does not support " "configuration changes."); close(fd); return (EOPNOTSUPP); } if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { error = errno; warn("Invalid volume %s", av[1]); close(fd); return (error); } if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { error = errno; warn("Failed to get info for volume %d", target_id); close(fd); return (error); } if (mfi_volume_busy(fd, target_id)) { warnx("Volume %s is busy and cannot be deleted", mfi_volume_name(fd, target_id)); close(fd); return (EBUSY); } mbox_store_ldref(mbox, &info.ld_config.properties.ld); if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, sizeof(mbox), NULL) < 0) { error = errno; warn("Failed to delete volume"); close(fd); return (error); } close(fd); return (0); }
static int create_volume(int ac, char **av) { struct mfi_config_data *config; struct mfi_array *ar; struct mfi_ld_config *ld; struct config_id_state state; size_t config_size; char *p, *cfg_arrays, *cfg_volumes; int error, fd, i, raid_type; int narrays, nvolumes, arrays_per_volume; struct array_info *arrays; long stripe_size; #ifdef DEBUG int dump; #endif int ch, verbose; /* * Backwards compat. Map 'create volume' to 'create' and * 'create spare' to 'add'. */ if (ac > 1) { if (strcmp(av[1], "volume") == 0) { av++; ac--; } else if (strcmp(av[1], "spare") == 0) { av++; ac--; return (add_spare(ac, av)); } } if (ac < 2) { warnx("create volume: volume type required"); return (EINVAL); } bzero(&state, sizeof(state)); config = NULL; arrays = NULL; narrays = 0; error = 0; fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (!mfi_reconfig_supported()) { warnx("The current mfi(4) driver does not support " "configuration changes."); error = EOPNOTSUPP; goto error; } /* Lookup the RAID type first. */ raid_type = -1; for (i = 0; raid_type_table[i].name != NULL; i++) if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { raid_type = raid_type_table[i].raid_type; break; } if (raid_type == -1) { warnx("Unknown or unsupported volume type %s", av[1]); error = EINVAL; goto error; } /* Parse any options. */ optind = 2; #ifdef DEBUG dump = 0; #endif verbose = 0; stripe_size = 64 * 1024; while ((ch = getopt(ac, av, "ds:v")) != -1) { switch (ch) { #ifdef DEBUG case 'd': dump = 1; break; #endif case 's': stripe_size = dehumanize(optarg); if ((stripe_size < 512) || (!powerof2(stripe_size))) stripe_size = 64 * 1024; break; case 'v': verbose = 1; break; case '?': default: error = EINVAL; goto error; } } ac -= optind; av += optind; /* Parse all the arrays. */ narrays = ac; if (narrays == 0) { warnx("At least one drive list is required"); error = EINVAL; goto error; } switch (raid_type) { case RT_RAID0: case RT_RAID1: case RT_RAID5: case RT_RAID6: case RT_CONCAT: if (narrays != 1) { warnx("Only one drive list can be specified"); error = EINVAL; goto error; } break; case RT_RAID10: case RT_RAID50: case RT_RAID60: if (narrays < 1) { warnx("RAID10, RAID50, and RAID60 require at least " "two drive lists"); error = EINVAL; goto error; } if (narrays > MFI_MAX_SPAN_DEPTH) { warnx("Volume spans more than %d arrays", MFI_MAX_SPAN_DEPTH); error = EINVAL; goto error; } break; } arrays = calloc(narrays, sizeof(*arrays)); if (arrays == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } for (i = 0; i < narrays; i++) { error = parse_array(fd, raid_type, av[i], &arrays[i]); if (error) goto error; } switch (raid_type) { case RT_RAID10: case RT_RAID50: case RT_RAID60: for (i = 1; i < narrays; i++) { if (arrays[i].drive_count != arrays[0].drive_count) { warnx("All arrays must contain the same " "number of drives"); error = EINVAL; goto error; } } break; } /* * Fetch the current config and build sorted lists of existing * array and volume identifiers. */ if (mfi_config_read(fd, &config) < 0) { error = errno; warn("Failed to read configuration"); goto error; } p = (char *)config->array; state.array_ref = 0xffff; state.target_id = 0xff; state.array_count = config->array_count; if (config->array_count > 0) { state.arrays = calloc(config->array_count, sizeof(int)); if (state.arrays == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } for (i = 0; i < config->array_count; i++) { ar = (struct mfi_array *)p; state.arrays[i] = ar->array_ref; p += config->array_size; } qsort(state.arrays, config->array_count, sizeof(int), compare_int); } else state.arrays = NULL; state.log_drv_count = config->log_drv_count; if (config->log_drv_count) { state.volumes = calloc(config->log_drv_count, sizeof(int)); if (state.volumes == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } for (i = 0; i < config->log_drv_count; i++) { ld = (struct mfi_ld_config *)p; state.volumes[i] = ld->properties.ld.v.target_id; p += config->log_drv_size; } qsort(state.volumes, config->log_drv_count, sizeof(int), compare_int); } else state.volumes = NULL; free(config); /* Determine the size of the configuration we will build. */ switch (raid_type) { case RT_RAID0: case RT_RAID1: case RT_RAID5: case RT_RAID6: case RT_CONCAT: case RT_JBOD: /* Each volume spans a single array. */ nvolumes = narrays; break; case RT_RAID10: case RT_RAID50: case RT_RAID60: /* A single volume spans multiple arrays. */ nvolumes = 1; break; default: /* Pacify gcc. */ abort(); } config_size = sizeof(struct mfi_config_data) + sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; config = calloc(1, config_size); if (config == NULL) { warnx("malloc failed"); error = ENOMEM; goto error; } config->size = config_size; config->array_count = narrays; config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ config->log_drv_count = nvolumes; config->log_drv_size = sizeof(struct mfi_ld_config); config->spares_count = 0; config->spares_size = 40; /* XXX: Firmware hardcode */ cfg_arrays = (char *)config->array; cfg_volumes = cfg_arrays + config->array_size * narrays; /* Build the arrays. */ for (i = 0; i < narrays; i++) { build_array(fd, cfg_arrays, &arrays[i], &state, verbose); cfg_arrays += config->array_size; } /* Now build the volume(s). */ arrays_per_volume = narrays / nvolumes; for (i = 0; i < nvolumes; i++) { build_volume(cfg_volumes, arrays_per_volume, &arrays[i * arrays_per_volume], raid_type, stripe_size, &state, verbose); cfg_volumes += config->log_drv_size; } #ifdef DEBUG if (dump) dump_config(fd, config); #endif /* Send the new config to the controller. */ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, NULL, 0, NULL) < 0) { error = errno; warn("Failed to add volume"); /* FALLTHROUGH */ } error: /* Clean up. */ free(config); free(state.volumes); free(state.arrays); for (i = 0; i < narrays; i++) free(arrays[i].drives); free(arrays); close(fd); return (error); }
static int show_patrol(int ac, char **av) { struct mfi_pr_properties prop; struct mfi_pr_status status; struct mfi_pd_list *list; struct mfi_pd_info info; char label[24]; time_t now; uint32_t at; int error, fd; u_int i; fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } time(&now); mfi_get_time(fd, &at); error = patrol_get_props(fd, &prop); if (error) { close(fd); return (error); } printf("Operation Mode: "); switch (prop.op_mode) { case MFI_PR_OPMODE_AUTO: printf("auto\n"); break; case MFI_PR_OPMODE_MANUAL: printf("manual\n"); break; case MFI_PR_OPMODE_DISABLED: printf("disabled\n"); break; default: printf("??? (%02x)\n", prop.op_mode); break; } if (prop.op_mode == MFI_PR_OPMODE_AUTO) { if (at != 0 && prop.next_exec) printf(" Next Run Starts: %s", adapter_time(now, at, prop.next_exec)); if (prop.exec_freq == 0xffffffff) printf(" Runs Execute Continuously\n"); else if (prop.exec_freq != 0) printf(" Runs Start Every %u seconds\n", prop.exec_freq); } if (mfi_dcmd_command(fd, MFI_DCMD_PR_GET_STATUS, &status, sizeof(status), NULL, 0, NULL) < 0) { error = errno; warn("Failed to get patrol read properties"); close(fd); return (error); } printf("Runs Completed: %u\n", status.num_iteration); printf("Current State: "); switch (status.state) { case MFI_PR_STATE_STOPPED: printf("stopped\n"); break; case MFI_PR_STATE_READY: printf("ready\n"); break; case MFI_PR_STATE_ACTIVE: printf("active\n"); break; case MFI_PR_STATE_ABORTED: printf("aborted\n"); break; default: printf("??? (%02x)\n", status.state); break; } if (status.state == MFI_PR_STATE_ACTIVE) { if (mfi_pd_get_list(fd, &list, NULL) < 0) { error = errno; warn("Failed to get drive list"); close(fd); return (error); } for (i = 0; i < list->count; i++) { if (list->addr[i].scsi_dev_type != 0) continue; if (mfi_pd_get_info(fd, list->addr[i].device_id, &info, NULL) < 0) { error = errno; warn("Failed to fetch info for drive %u", list->addr[i].device_id); free(list); close(fd); return (error); } if (info.prog_info.active & MFI_PD_PROGRESS_PATROL) { snprintf(label, sizeof(label), " Drive %s", mfi_drive_name(NULL, list->addr[i].device_id, MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS)); mfi_display_progress(label, &info.prog_info.patrol); } } free(list); } close(fd); return (0); }