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); }
/* * Print the name of a drive either by drive number as %2u or by enclosure:slot * as Exx:Sxx (or both). Use default unless command line options override it * and the command allows this (which we usually do unless we already print * both). We prefer pinfo if given, otherwise try to look it up by device_id. */ const char * mfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def) { struct mfi_pd_info info; static char buf[16]; char *p; int error, fd, len; if ((def & MFI_DNAME_HONOR_OPTS) != 0 && (mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) != 0) def = mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID); buf[0] = '\0'; if (pinfo == NULL && def & MFI_DNAME_ES) { /* Fallback in case of error, just ignore flags. */ if (device_id == 0xffff) snprintf(buf, sizeof(buf), "MISSING"); else snprintf(buf, sizeof(buf), "%2u", device_id); fd = mfi_open(mfi_unit); if (fd < 0) { warn("mfi_open"); return (buf); } /* Get the info for this drive. */ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { warn("Failed to fetch info for drive %2u", device_id); close(fd); return (buf); } close(fd); pinfo = &info; } p = buf; len = sizeof(buf); if (def & MFI_DNAME_DEVICE_ID) { if (device_id == 0xffff) error = snprintf(p, len, "MISSING"); else error = snprintf(p, len, "%2u", device_id); if (error >= 0) { p += error; len -= error; } } if ((def & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) == (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID) && len >= 2) { *p++ = ' '; len--; *p = '\0'; len--; } if (def & MFI_DNAME_ES) { if (pinfo->encl_device_id == 0xffff) error = snprintf(p, len, "S%u", pinfo->slot_number); else if (pinfo->encl_device_id == pinfo->ref.v.device_id) error = snprintf(p, len, "E%u", pinfo->encl_index); else error = snprintf(p, len, "E%u:S%u", pinfo->encl_index, pinfo->slot_number); if (error >= 0) { p += error; len -= error; } } return (buf); }
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); }
/* Parse a comma-separated list of drives for an array. */ static int parse_array(int fd, int raid_type, char *array_str, struct array_info *info) { struct mfi_pd_info *pinfo; uint16_t device_id; char *cp; u_int count; int error; cp = array_str; for (count = 0; cp != NULL; count++) { cp = strchr(cp, ','); if (cp != NULL) { cp++; if (*cp == ',') { warnx("Invalid drive list '%s'", array_str); return (EINVAL); } } } /* Validate the number of drives for this array. */ if (count >= MAX_DRIVES_PER_ARRAY) { warnx("Too many drives for a single array: max is %zu", MAX_DRIVES_PER_ARRAY); return (EINVAL); } switch (raid_type) { case RT_RAID1: case RT_RAID10: if (count % 2 != 0) { warnx("RAID1 and RAID10 require an even number of " "drives in each array"); return (EINVAL); } break; case RT_RAID5: case RT_RAID50: if (count < 3) { warnx("RAID5 and RAID50 require at least 3 drives in " "each array"); return (EINVAL); } break; case RT_RAID6: case RT_RAID60: if (count < 4) { warnx("RAID6 and RAID60 require at least 4 drives in " "each array"); return (EINVAL); } break; } /* Validate each drive. */ info->drives = calloc(count, sizeof(struct mfi_pd_info)); if (info->drives == NULL) { warnx("malloc failed"); return (ENOMEM); } info->drive_count = count; for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; pinfo++) { error = mfi_lookup_drive(fd, cp, &device_id); if (error) { free(info->drives); return (error); } if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { error = errno; warn("Failed to fetch drive info for drive %s", cp); free(info->drives); return (error); } if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { warnx("Drive %u is not available", device_id); free(info->drives); return (EINVAL); } } return (0); }
/* Display raw data about a config. */ static void dump_config(int fd, struct mfi_config_data *config) { struct mfi_array *ar; struct mfi_ld_config *ld; struct mfi_spare *sp; struct mfi_pd_info pinfo; uint16_t device_id; char *p; int i, j; printf( "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n", mfi_unit, config->array_count, config->log_drv_count, config->spares_count); printf(" array size: %u\n", config->array_size); printf(" volume size: %u\n", config->log_drv_size); printf(" spare size: %u\n", config->spares_size); p = (char *)config->array; for (i = 0; i < config->array_count; i++) { ar = (struct mfi_array *)p; printf(" array %u of %u drives:\n", ar->array_ref, ar->num_drives); printf(" size = %ju\n", (uintmax_t)ar->size); for (j = 0; j < ar->num_drives; j++) { device_id = ar->pd[j].ref.v.device_id; if (device_id == 0xffff) printf(" drive MISSING\n"); else { printf(" drive %u %s\n", device_id, mfi_pdstate(ar->pd[j].fw_state)); if (mfi_pd_get_info(fd, device_id, &pinfo, NULL) >= 0) { printf(" raw size: %ju\n", (uintmax_t)pinfo.raw_size); printf(" non-coerced size: %ju\n", (uintmax_t)pinfo.non_coerced_size); printf(" coerced size: %ju\n", (uintmax_t)pinfo.coerced_size); } } } p += config->array_size; } for (i = 0; i < config->log_drv_count; i++) { ld = (struct mfi_ld_config *)p; printf(" volume %s ", mfi_volume_name(fd, ld->properties.ld.v.target_id)); printf("%s %s", mfi_raid_level(ld->params.primary_raid_level, ld->params.secondary_raid_level), mfi_ldstate(ld->params.state)); if (ld->properties.name[0] != '\0') printf(" <%s>", ld->properties.name); printf("\n"); printf(" primary raid level: %u\n", ld->params.primary_raid_level); printf(" raid level qualifier: %u\n", ld->params.raid_level_qualifier); printf(" secondary raid level: %u\n", ld->params.secondary_raid_level); printf(" stripe size: %u\n", ld->params.stripe_size); printf(" num drives: %u\n", ld->params.num_drives); printf(" init state: %u\n", ld->params.init_state); printf(" consistent: %u\n", ld->params.is_consistent); printf(" no bgi: %u\n", ld->properties.no_bgi); printf(" spans:\n"); for (j = 0; j < ld->params.span_depth; j++) { printf(" array %u @ ", ld->span[j].array_ref); printf("%ju : %ju\n", (uintmax_t)ld->span[j].start_block, (uintmax_t)ld->span[j].num_blocks); } p += config->log_drv_size; } for (i = 0; i < config->spares_count; i++) { sp = (struct mfi_spare *)p; printf(" %s spare %u ", sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : "global", sp->ref.v.device_id); printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); printf(" backs:\n"); for (j = 0; j < sp->array_count; j++) printf(" array %u\n", sp->array_ref[j]); p += config->spares_size; } }
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); }