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