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