]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/mfiutil/mfi_cmd.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / mfiutil / mfi_cmd.c
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 }