1 /*- 2 * Copyright (c) 2008, 2009 Yahoo!, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/errno.h> 33 #include <sys/ioctl.h> 34 #include <sys/param.h> 35 #include <sys/sysctl.h> 36 #include <sys/uio.h> 37 38 #include <err.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include "mfiutil.h" 46 #include <dev/mfi/mfi_ioctl.h> 47 48 static const char *mfi_status_codes[] = { 49 "Command completed successfully", 50 "Invalid command", 51 "Invalid DMCD opcode", 52 "Invalid parameter", 53 "Invalid Sequence Number", 54 "Abort isn't possible for the requested command", 55 "Application 'host' code not found", 56 "Application in use", 57 "Application not initialized", 58 "Array index invalid", 59 "Array row not empty", 60 "Configuration resource conflict", 61 "Device not found", 62 "Drive too small", 63 "Flash memory allocation failed", 64 "Flash download already in progress", 65 "Flash operation failed", 66 "Bad flash image", 67 "Incomplete flash image", 68 "Flash not open", 69 "Flash not started", 70 "Flush failed", 71 "Specified application doesn't have host-resident code", 72 "Volume consistency check in progress", 73 "Volume initialization in progress", 74 "Volume LBA out of range", 75 "Maximum number of volumes are already configured", 76 "Volume is not OPTIMAL", 77 "Volume rebuild in progress", 78 "Volume reconstruction in progress", 79 "Volume RAID level is wrong for requested operation", 80 "Too many spares assigned", 81 "Scratch memory not available", 82 "Error writing MFC data to SEEPROM", 83 "Required hardware is missing", 84 "Item not found", 85 "Volume drives are not within an enclosure", 86 "Drive clear in progress", 87 "Drive type mismatch (SATA vs SAS)", 88 "Patrol read disabled", 89 "Invalid row index", 90 "SAS Config - Invalid action", 91 "SAS Config - Invalid data", 92 "SAS Config - Invalid page", 93 "SAS Config - Invalid type", 94 "SCSI command completed with error", 95 "SCSI I/O request failed", 96 "SCSI RESERVATION_CONFLICT", 97 "One or more flush operations during shutdown failed", 98 "Firmware time is not set", 99 "Wrong firmware or drive state", 100 "Volume is offline", 101 "Peer controller rejected request", 102 "Unable to inform peer of communication changes", 103 "Volume reservation already in progress", 104 "I2C errors were detected", 105 "PCI errors occurred during XOR/DMA operation", 106 "Diagnostics failed", 107 "Unable to process command as boot messages are pending", 108 "Foreign configuration is incomplete" 109 }; 110 111 const char * 112 mfi_status(u_int status_code) 113 { 114 static char buffer[16]; 115 116 if (status_code == MFI_STAT_INVALID_STATUS) 117 return ("Invalid status"); 118 if (status_code < sizeof(mfi_status_codes) / sizeof(char *)) 119 return (mfi_status_codes[status_code]); 120 snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code); 121 return (buffer); 122 } 123 124 const char * 125 mfi_raid_level(uint8_t primary_level, uint8_t secondary_level) 126 { 127 static char buf[16]; 128 129 switch (primary_level) { 130 case DDF_RAID0: 131 return ("RAID-0"); 132 case DDF_RAID1: 133 if (secondary_level != 0) 134 return ("RAID-10"); 135 else 136 return ("RAID-1"); 137 case DDF_RAID1E: 138 return ("RAID-1E"); 139 case DDF_RAID3: 140 return ("RAID-3"); 141 case DDF_RAID5: 142 if (secondary_level != 0) 143 return ("RAID-50"); 144 else 145 return ("RAID-5"); 146 case DDF_RAID5E: 147 return ("RAID-5E"); 148 case DDF_RAID5EE: 149 return ("RAID-5EE"); 150 case DDF_RAID6: 151 if (secondary_level != 0) 152 return ("RAID-60"); 153 else 154 return ("RAID-6"); 155 case DDF_JBOD: 156 return ("JBOD"); 157 case DDF_CONCAT: 158 return ("CONCAT"); 159 default: 160 sprintf(buf, "LVL 0x%02x", primary_level); 161 return (buf); 162 } 163 } 164 165 static int 166 mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info) 167 { 168 169 bzero(info, sizeof(*info)); 170 info->array_id = target_id; 171 if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) 172 return (-1); 173 if (!info->present) { 174 errno = ENXIO; 175 return (-1); 176 } 177 return (0); 178 } 179 180 const char * 181 mfi_volume_name(int fd, uint8_t target_id) 182 { 183 static struct mfi_query_disk info; 184 static char buf[4]; 185 186 if (mfi_query_disk(fd, target_id, &info) < 0) { 187 snprintf(buf, sizeof(buf), "%d", target_id); 188 return (buf); 189 } 190 return (info.devname); 191 } 192 193 int 194 mfi_volume_busy(int fd, uint8_t target_id) 195 { 196 struct mfi_query_disk info; 197 198 /* Assume it isn't mounted if we can't get information. */ 199 if (mfi_query_disk(fd, target_id, &info) < 0) 200 return (0); 201 return (info.open != 0); 202 } 203 204 /* 205 * Check if the running kernel supports changing the RAID 206 * configuration of the mfi controller. 207 */ 208 int 209 mfi_reconfig_supported(void) 210 { 211 char mibname[64]; 212 size_t len; 213 int dummy; 214 215 len = sizeof(dummy); 216 snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes", 217 mfi_unit); 218 return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0); 219 } 220 221 int 222 mfi_lookup_volume(int fd, const char *name, uint8_t *target_id) 223 { 224 struct mfi_query_disk info; 225 struct mfi_ld_list list; 226 char *cp; 227 long val; 228 u_int i; 229 230 /* If it's a valid number, treat it as a raw target ID. */ 231 val = strtol(name, &cp, 0); 232 if (*cp == '\0') { 233 *target_id = val; 234 return (0); 235 } 236 237 if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), 238 NULL, 0, NULL) < 0) 239 return (-1); 240 241 for (i = 0; i < list.ld_count; i++) { 242 if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, 243 &info) < 0) 244 continue; 245 if (strcmp(name, info.devname) == 0) { 246 *target_id = list.ld_list[i].ld.v.target_id; 247 return (0); 248 } 249 } 250 errno = EINVAL; 251 return (-1); 252 } 253 254 int 255 mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize, 256 uint8_t *mbox, size_t mboxlen, uint8_t *statusp) 257 { 258 struct mfi_ioc_passthru ioc; 259 struct mfi_dcmd_frame *dcmd; 260 int r; 261 262 if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || 263 (mbox == NULL && mboxlen != 0)) { 264 errno = EINVAL; 265 return (-1); 266 } 267 268 bzero(&ioc, sizeof(ioc)); 269 dcmd = &ioc.ioc_frame; 270 if (mbox) 271 bcopy(mbox, dcmd->mbox, mboxlen); 272 dcmd->header.cmd = MFI_CMD_DCMD; 273 dcmd->header.timeout = 0; 274 dcmd->header.flags = 0; 275 dcmd->header.data_len = bufsize; 276 dcmd->opcode = opcode; 277 278 ioc.buf = buf; 279 ioc.buf_size = bufsize; 280 r = ioctl(fd, MFIIO_PASSTHRU, &ioc); 281 if (r < 0) 282 return (r); 283 284 if (statusp != NULL) 285 *statusp = dcmd->header.cmd_status; 286 else if (dcmd->header.cmd_status != MFI_STAT_OK) { 287 warnx("Command failed: %s", 288 mfi_status(dcmd->header.cmd_status)); 289 errno = EIO; 290 return (-1); 291 } 292 return (0); 293 } 294 295 int 296 mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) 297 { 298 299 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, 300 sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); 301 } 302 303 int 304 mfi_open(int unit, int acs) 305 { 306 char path[MAXPATHLEN]; 307 308 snprintf(path, sizeof(path), "/dev/mfi%d", unit); 309 return (open(path, acs)); 310 } 311 312 void 313 mfi_display_progress(const char *label, struct mfi_progress *prog) 314 { 315 uint seconds; 316 317 printf("%s: %.2f%% complete, after %ds", label, 318 (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds); 319 if (prog->progress != 0 && prog->elapsed_seconds > 10) { 320 printf(" finished in "); 321 seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) / 322 prog->progress - prog->elapsed_seconds; 323 if (seconds > 3600) 324 printf("%u:", seconds / 3600); 325 if (seconds > 60) { 326 seconds %= 3600; 327 printf("%02u:%02u", seconds / 60, seconds % 60); 328 } else 329 printf("%us", seconds); 330 } 331 printf("\n"); 332 } 333 334 int 335 mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end, 336 int ac, char **av) 337 { 338 struct mfiutil_command **cmd; 339 340 if (ac < 2) { 341 warnx("The %s command requires a sub-command.", av[0]); 342 return (EINVAL); 343 } 344 for (cmd = start; cmd < end; cmd++) { 345 if (strcmp((*cmd)->name, av[1]) == 0) 346 return ((*cmd)->handler(ac - 1, av + 1)); 347 } 348 349 warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 350 return (ENOENT); 351 } 352