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