xref: /dragonfly/usr.sbin/mfiutil/mfi_cmd.c (revision dd1001661885416a2edf3b3065de1e9942e72af5)
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: src/usr.sbin/mfiutil/mfi_cmd.c,v 1.3 2011/02/25 13:59:59 pluknet Exp $
30  */
31 
32 #define _KERNEL_STRUCTURES
33 #include <sys/errno.h>
34 #include <sys/ioctl.h>
35 #include <sys/param.h>
36 #include <sys/conf.h>
37 #include <sys/sysctl.h>
38 #include <sys/uio.h>
39 
40 #include <err.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "mfiutil.h"
48 #include <dev/raid/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 *
mfi_status(u_int status_code)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 *
mfi_raid_level(uint8_t primary_level,uint8_t secondary_level)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
mfi_query_disk(int fd,uint8_t target_id,struct mfi_query_disk * info)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 *
mfi_volume_name(int fd,uint8_t target_id)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
mfi_volume_busy(int fd,uint8_t target_id)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
mfi_reconfig_supported(void)211 mfi_reconfig_supported(void)
212 {
213           char mibname[64];
214           size_t len;
215           int dummy;
216 
217           len = sizeof(dummy);
218           snprintf(mibname, sizeof(mibname), "hw.mfi%d.delete_busy_volumes",
219               mfi_unit);
220           return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
221 }
222 
223 int
mfi_lookup_volume(int fd,const char * name,uint8_t * target_id)224 mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
225 {
226           struct mfi_query_disk info;
227           struct mfi_ld_list list;
228           char *cp;
229           long val;
230           u_int i;
231 
232           /* If it's a valid number, treat it as a raw target ID. */
233           val = strtol(name, &cp, 0);
234           if (*cp == '\0') {
235                     *target_id = val;
236                     return (0);
237           }
238 
239           if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
240               NULL, 0, NULL) < 0)
241                     return (-1);
242 
243           for (i = 0; i < list.ld_count; i++) {
244                     if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
245                         &info) < 0)
246                               continue;
247                     if (strcmp(name, info.devname) == 0) {
248                               *target_id = list.ld_list[i].ld.v.target_id;
249                               return (0);
250                     }
251           }
252           errno = EINVAL;
253           return (-1);
254 }
255 
256 int
mfi_dcmd_command(int fd,uint32_t opcode,void * buf,size_t bufsize,uint8_t * mbox,size_t mboxlen,uint8_t * statusp)257 mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
258     uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
259 {
260           struct mfi_ioc_passthru ioc;
261           struct mfi_dcmd_frame *dcmd;
262           int r;
263 
264           if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
265               (mbox == NULL && mboxlen != 0)) {
266                     errno = EINVAL;
267                     return (-1);
268           }
269 
270           bzero(&ioc, sizeof(ioc));
271           dcmd = &ioc.ioc_frame;
272           if (mbox)
273                     bcopy(mbox, dcmd->mbox, mboxlen);
274           dcmd->header.cmd = MFI_CMD_DCMD;
275           dcmd->header.timeout = 0;
276           dcmd->header.flags = 0;
277           dcmd->header.data_len = bufsize;
278           dcmd->opcode = opcode;
279 
280           ioc.buf = buf;
281           ioc.buf_size = bufsize;
282           r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
283           if (r < 0)
284                     return (r);
285 
286           if (statusp != NULL)
287                     *statusp = dcmd->header.cmd_status;
288           else if (dcmd->header.cmd_status != MFI_STAT_OK) {
289                     warnx("Command 0x%08x failed: %s", opcode,
290                         mfi_status(dcmd->header.cmd_status));
291                     errno = EIO;
292                     return (-1);
293           }
294           return (0);
295 }
296 
297 int
mfi_ctrl_get_info(int fd,struct mfi_ctrl_info * info,uint8_t * statusp)298 mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
299 {
300 
301           return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
302               sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
303 }
304 
305 int
mfi_open(int unit)306 mfi_open(int unit)
307 {
308           char path[MAXPATHLEN];
309 
310           snprintf(path, sizeof(path), "/dev/mfi%d", unit);
311           return (open(path, O_RDWR));
312 }
313 
314 void
mfi_display_progress(const char * label,struct mfi_progress * prog)315 mfi_display_progress(const char *label, struct mfi_progress *prog)
316 {
317           uint seconds;
318 
319           printf("%s: %.2f%% complete, after %ds", label,
320               (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
321           if (prog->progress != 0 && prog->elapsed_seconds > 10) {
322                     printf(" finished in ");
323                     seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
324                         prog->progress - prog->elapsed_seconds;
325                     if (seconds > 3600)
326                               printf("%u:", seconds / 3600);
327                     if (seconds > 60) {
328                               seconds %= 3600;
329                               printf("%02u:%02u", seconds / 60, seconds % 60);
330                     } else
331                               printf("%us", seconds);
332           }
333           printf("\n");
334 }
335 
336 int
mfi_table_handler(struct mfiutil_command ** start,struct mfiutil_command ** end,int ac,char ** av)337 mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
338     int ac, char **av)
339 {
340           struct mfiutil_command **cmd;
341 
342           if (ac < 2) {
343                     warnx("The %s command requires a sub-command.", av[0]);
344                     return (EINVAL);
345           }
346           for (cmd = start; cmd < end; cmd++) {
347                     if (strcmp((*cmd)->name, av[1]) == 0)
348                               return ((*cmd)->handler(ac - 1, av + 1));
349           }
350 
351           warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
352           return (ENOENT);
353 }
354