mfi_cmd.c revision 237259
155714Skris/*-
255714Skris * Copyright (c) 2008, 2009 Yahoo!, Inc.
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8280304Sjkim * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris * 3. The names of the authors may not be used to endorse or promote
1455714Skris *    products derived from this software without specific prior written
15280304Sjkim *    permission.
1655714Skris *
1755714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1855714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1955714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2055714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2155714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22280304Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2355714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2455714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2555714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2655714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2755714Skris * SUCH DAMAGE.
2855714Skris *
2955714Skris * $FreeBSD: head/usr.sbin/mfiutil/mfi_cmd.c 237259 2012-06-19 06:18:37Z eadler $
3055714Skris */
3155714Skris
3255714Skris#include <sys/errno.h>
3355714Skris#include <sys/ioctl.h>
3455714Skris#include <sys/param.h>
3555714Skris#include <sys/sysctl.h>
3655714Skris#include <sys/uio.h>
37280304Sjkim
3855714Skris#include <err.h>
3955714Skris#include <fcntl.h>
40280304Sjkim#include <stdio.h>
4155714Skris#include <stdlib.h>
4255714Skris#include <string.h>
4355714Skris#include <unistd.h>
4455714Skris
4555714Skris#include "mfiutil.h"
4655714Skris#include <dev/mfi/mfi_ioctl.h>
4755714Skris
4855714Skrisstatic const char *mfi_status_codes[] = {
4955714Skris	"Command completed successfully",
5055714Skris	"Invalid command",
5155714Skris	"Invalid DMCD opcode",
52280304Sjkim	"Invalid parameter",
5355714Skris	"Invalid Sequence Number",
5455714Skris	"Abort isn't possible for the requested command",
5555714Skris	"Application 'host' code not found",
5655714Skris	"Application in use",
5755714Skris	"Application not initialized",
58160814Ssimon	"Array index invalid",
59238405Sjkim	"Array row not empty",
60238405Sjkim	"Configuration resource conflict",
61238405Sjkim	"Device not found",
62238405Sjkim	"Drive too small",
63238405Sjkim	"Flash memory allocation failed",
64238405Sjkim	"Flash download already in progress",
65238405Sjkim	"Flash operation failed",
66280304Sjkim	"Bad flash image",
67238405Sjkim	"Incomplete flash image",
68238405Sjkim	"Flash not open",
69238405Sjkim	"Flash not started",
70238405Sjkim	"Flush failed",
71238405Sjkim	"Specified application doesn't have host-resident code",
72238405Sjkim	"Volume consistency check in progress",
73238405Sjkim	"Volume initialization in progress",
74238405Sjkim	"Volume LBA out of range",
75238405Sjkim	"Maximum number of volumes are already configured",
76238405Sjkim	"Volume is not OPTIMAL",
77238405Sjkim	"Volume rebuild in progress",
78238405Sjkim	"Volume reconstruction in progress",
79238405Sjkim	"Volume RAID level is wrong for requested operation",
80238405Sjkim	"Too many spares assigned",
81238405Sjkim	"Scratch memory not available",
82238405Sjkim	"Error writing MFC data to SEEPROM",
83238405Sjkim	"Required hardware is missing",
84238405Sjkim	"Item not found",
85238405Sjkim	"Volume drives are not within an enclosure",
86238405Sjkim	"Drive clear in progress",
87238405Sjkim	"Drive type mismatch (SATA vs SAS)",
88238405Sjkim	"Patrol read disabled",
89238405Sjkim	"Invalid row index",
90238405Sjkim	"SAS Config - Invalid action",
91238405Sjkim	"SAS Config - Invalid data",
92238405Sjkim	"SAS Config - Invalid page",
93238405Sjkim	"SAS Config - Invalid type",
94238405Sjkim	"SCSI command completed with error",
95238405Sjkim	"SCSI I/O request failed",
96238405Sjkim	"SCSI RESERVATION_CONFLICT",
97238405Sjkim	"One or more flush operations during shutdown failed",
98238405Sjkim	"Firmware time is not set",
99238405Sjkim	"Wrong firmware or drive state",
100238405Sjkim	"Volume is offline",
101238405Sjkim	"Peer controller rejected request",
102238405Sjkim	"Unable to inform peer of communication changes",
103238405Sjkim	"Volume reservation already in progress",
104238405Sjkim	"I2C errors were detected",
105238405Sjkim	"PCI errors occurred during XOR/DMA operation",
106238405Sjkim	"Diagnostics failed",
107238405Sjkim	"Unable to process command as boot messages are pending",
108238405Sjkim	"Foreign configuration is incomplete"
109238405Sjkim};
110238405Sjkim
111238405Sjkimconst char *
112160814Ssimonmfi_status(u_int status_code)
113160814Ssimon{
114280304Sjkim	static char buffer[16];
115160814Ssimon
116160814Ssimon	if (status_code == MFI_STAT_INVALID_STATUS)
117160814Ssimon		return ("Invalid status");
118160814Ssimon	if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
119160814Ssimon		return (mfi_status_codes[status_code]);
120280304Sjkim	snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
121160814Ssimon	return (buffer);
122160814Ssimon}
123160814Ssimon
12455714Skrisconst char *
12555714Skrismfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
126280304Sjkim{
12755714Skris	static char buf[16];
128296317Sdelphij
129280304Sjkim	switch (primary_level) {
130280304Sjkim	case DDF_RAID0:
131280304Sjkim		return ("RAID-0");
132280304Sjkim	case DDF_RAID1:
133280304Sjkim		if (secondary_level != 0)
134280304Sjkim			return ("RAID-10");
13555714Skris		else
13655714Skris			return ("RAID-1");
13755714Skris	case DDF_RAID1E:
13855714Skris		return ("RAID-1E");
13955714Skris	case DDF_RAID3:
140280304Sjkim		return ("RAID-3");
141280304Sjkim	case DDF_RAID5:
142280304Sjkim		if (secondary_level != 0)
143280304Sjkim			return ("RAID-50");
144280304Sjkim		else
145280304Sjkim			return ("RAID-5");
146280304Sjkim	case DDF_RAID5E:
147160814Ssimon		return ("RAID-5E");
148160814Ssimon	case DDF_RAID5EE:
149160814Ssimon		return ("RAID-5EE");
150160814Ssimon	case DDF_RAID6:
15155714Skris		if (secondary_level != 0)
152280304Sjkim			return ("RAID-60");
153280304Sjkim		else
154280304Sjkim			return ("RAID-6");
155280304Sjkim	case DDF_JBOD:
156280304Sjkim		return ("JBOD");
15755714Skris	case DDF_CONCAT:
158280304Sjkim		return ("CONCAT");
159280304Sjkim	default:
160280304Sjkim		sprintf(buf, "LVL 0x%02x", primary_level);
161280304Sjkim		return (buf);
162280304Sjkim	}
163280304Sjkim}
164280304Sjkim
165280304Sjkimstatic int
166280304Sjkimmfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
167280304Sjkim{
168280304Sjkim
169280304Sjkim	bzero(info, sizeof(*info));
170280304Sjkim	info->array_id = target_id;
171109998Smarkm	if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
172280304Sjkim		return (-1);
173280304Sjkim	if (!info->present) {
174280304Sjkim		errno = ENXIO;
17589837Skris		return (-1);
17655714Skris	}
177280304Sjkim	return (0);
178280304Sjkim}
179280304Sjkim
180280304Sjkimconst char *
181280304Sjkimmfi_volume_name(int fd, uint8_t target_id)
182280304Sjkim{
183280304Sjkim	static struct mfi_query_disk info;
184280304Sjkim	static char buf[4];
185280304Sjkim
186280304Sjkim	if (mfi_query_disk(fd, target_id, &info) < 0) {
187280304Sjkim		snprintf(buf, sizeof(buf), "%d", target_id);
188280304Sjkim		return (buf);
189280304Sjkim	}
190280304Sjkim	return (info.devname);
191280304Sjkim}
192280304Sjkim
193280304Sjkimint
194280304Sjkimmfi_volume_busy(int fd, uint8_t target_id)
195280304Sjkim{
196280304Sjkim	struct mfi_query_disk info;
197280304Sjkim
198280304Sjkim	/* Assume it isn't mounted if we can't get information. */
199280304Sjkim	if (mfi_query_disk(fd, target_id, &info) < 0)
200280304Sjkim		return (0);
201280304Sjkim	return (info.open != 0);
20255714Skris}
203280304Sjkim
204280304Sjkim/*
205280304Sjkim * Check if the running kernel supports changing the RAID
206280304Sjkim * configuration of the mfi controller.
207280304Sjkim */
20855714Skrisint
209280304Sjkimmfi_reconfig_supported(void)
210280304Sjkim{
211280304Sjkim	char mibname[64];
212280304Sjkim	size_t len;
213280304Sjkim	int dummy;
214280304Sjkim
215280304Sjkim	len = sizeof(dummy);
216280304Sjkim	snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
217280304Sjkim	    mfi_unit);
218280304Sjkim	return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
219280304Sjkim}
220280304Sjkim
221280304Sjkimint
222280304Sjkimmfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
223280304Sjkim{
224280304Sjkim	struct mfi_query_disk info;
225280304Sjkim	struct mfi_ld_list list;
226280304Sjkim	char *cp;
227280304Sjkim	long val;
228280304Sjkim	u_int i;
229280304Sjkim
23055714Skris	/* If it's a valid number, treat it as a raw target ID. */
231280304Sjkim	val = strtol(name, &cp, 0);
232280304Sjkim	if (*cp == '\0') {
233280304Sjkim		*target_id = val;
234280304Sjkim		return (0);
235280304Sjkim	}
236280304Sjkim
237280304Sjkim	if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
238280304Sjkim	    NULL, 0, NULL) < 0)
239280304Sjkim		return (-1);
240280304Sjkim
241280304Sjkim	for (i = 0; i < list.ld_count; i++) {
242280304Sjkim		if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
243280304Sjkim		    &info) < 0)
244280304Sjkim			continue;
245280304Sjkim		if (strcmp(name, info.devname) == 0) {
246280304Sjkim			*target_id = list.ld_list[i].ld.v.target_id;
247280304Sjkim			return (0);
248280304Sjkim		}
249280304Sjkim	}
250280304Sjkim	errno = EINVAL;
251280304Sjkim	return (-1);
252280304Sjkim}
253280304Sjkim
254280304Sjkimint
255280304Sjkimmfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
256280304Sjkim    uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
257280304Sjkim{
258160814Ssimon	struct mfi_ioc_passthru ioc;
25955714Skris	struct mfi_dcmd_frame *dcmd;
260280304Sjkim	int r;
261280304Sjkim
262280304Sjkim	if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
263280304Sjkim	    (mbox == NULL && mboxlen != 0)) {
264280304Sjkim		errno = EINVAL;
265280304Sjkim		return (-1);
266280304Sjkim	}
267238405Sjkim
268280304Sjkim	bzero(&ioc, sizeof(ioc));
269280304Sjkim	dcmd = &ioc.ioc_frame;
270280304Sjkim	if (mbox)
271280304Sjkim		bcopy(mbox, dcmd->mbox, mboxlen);
272280304Sjkim	dcmd->header.cmd = MFI_CMD_DCMD;
273280304Sjkim	dcmd->header.timeout = 0;
274280304Sjkim	dcmd->header.flags = 0;
275280304Sjkim	dcmd->header.data_len = bufsize;
276280304Sjkim	dcmd->opcode = opcode;
27755714Skris
278280304Sjkim	ioc.buf = buf;
27955714Skris	ioc.buf_size = bufsize;
280280304Sjkim	r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
281280304Sjkim	if (r < 0)
282194206Ssimon		return (r);
283280304Sjkim
284280304Sjkim	if (statusp != NULL)
285280304Sjkim		*statusp = dcmd->header.cmd_status;
286280304Sjkim	else if (dcmd->header.cmd_status != MFI_STAT_OK) {
287280304Sjkim		warnx("Command failed: %s",
288280304Sjkim		    mfi_status(dcmd->header.cmd_status));
289280304Sjkim		errno = EIO;
290194206Ssimon		return (-1);
291280304Sjkim	}
292280304Sjkim	return (0);
293280304Sjkim}
294280304Sjkim
295280304Sjkimint
296280304Sjkimmfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
297280304Sjkim{
298280304Sjkim
29955714Skris	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
300280304Sjkim	    sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
301280304Sjkim}
302280304Sjkim
303280304Sjkimint
304280304Sjkimmfi_open(int unit, int acs)
305280304Sjkim{
306280304Sjkim	char path[MAXPATHLEN];
307280304Sjkim
308280304Sjkim	snprintf(path, sizeof(path), "/dev/mfi%d", unit);
309280304Sjkim	return (open(path, acs));
310280304Sjkim}
311280304Sjkim
312160814Ssimonvoid
313160814Ssimonmfi_display_progress(const char *label, struct mfi_progress *prog)
314160814Ssimon{
315160814Ssimon	uint seconds;
316160814Ssimon
317160814Ssimon	printf("%s: %.2f%% complete, after %ds", label,
318160814Ssimon	    (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
319160814Ssimon	if (prog->progress != 0 && prog->elapsed_seconds > 10) {
320160814Ssimon		printf(" finished in ");
321280304Sjkim		seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
322160814Ssimon		    prog->progress - prog->elapsed_seconds;
323160814Ssimon		if (seconds > 3600)
324160814Ssimon			printf("%u:", seconds / 3600);
325160814Ssimon		if (seconds > 60) {
326160814Ssimon			seconds %= 3600;
327160814Ssimon			printf("%02u:%02u", seconds / 60, seconds % 60);
328160814Ssimon		} else
329280304Sjkim			printf("%us", seconds);
330160814Ssimon	}
331280304Sjkim	printf("\n");
332280304Sjkim}
333280304Sjkim
334280304Sjkimint
335280304Sjkimmfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
336280304Sjkim    int ac, char **av)
337280304Sjkim{
338280304Sjkim	struct mfiutil_command **cmd;
339280304Sjkim
34055714Skris	if (ac < 2) {
34155714Skris		warnx("The %s command requires a sub-command.", av[0]);
342280304Sjkim		return (EINVAL);
343280304Sjkim	}
344280304Sjkim	for (cmd = start; cmd < end; cmd++) {
345280304Sjkim		if (strcmp((*cmd)->name, av[1]) == 0)
346280304Sjkim			return ((*cmd)->handler(ac - 1, av + 1));
347280304Sjkim	}
348280304Sjkim
349280304Sjkim	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
350280304Sjkim	return (ENOENT);
351280304Sjkim}
352280304Sjkim