xref: /freebsd/usr.sbin/mfiutil/mfi_cmd.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
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 *
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 *
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
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 *
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
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
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
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
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
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
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
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
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
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