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