]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mfiutil/mfi_cmd.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[FreeBSD/FreeBSD.git] / usr.sbin / mfiutil / mfi_cmd.c
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  * $FreeBSD$
32  */
33
34 #include <sys/param.h>
35 #include <sys/ioctl.h>
36 #include <sys/sysctl.h>
37 #include <sys/uio.h>
38
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <unistd.h>
47
48 #include "mfiutil.h"
49 #include <dev/mfi/mfi_ioctl.h>
50
51 static const char *mfi_status_codes[] = {
52         "Command completed successfully",
53         "Invalid command",
54         "Invalid DMCD opcode",
55         "Invalid parameter",
56         "Invalid Sequence Number",
57         "Abort isn't possible for the requested command",
58         "Application 'host' code not found",
59         "Application in use",
60         "Application not initialized",
61         "Array index invalid",
62         "Array row not empty",
63         "Configuration resource conflict",
64         "Device not found",
65         "Drive too small",
66         "Flash memory allocation failed",
67         "Flash download already in progress",
68         "Flash operation failed",
69         "Bad flash image",
70         "Incomplete flash image",
71         "Flash not open",
72         "Flash not started",
73         "Flush failed",
74         "Specified application doesn't have host-resident code",
75         "Volume consistency check in progress",
76         "Volume initialization in progress",
77         "Volume LBA out of range",
78         "Maximum number of volumes are already configured",
79         "Volume is not OPTIMAL",
80         "Volume rebuild in progress",
81         "Volume reconstruction in progress",
82         "Volume RAID level is wrong for requested operation",
83         "Too many spares assigned",
84         "Scratch memory not available",
85         "Error writing MFC data to SEEPROM",
86         "Required hardware is missing",
87         "Item not found",
88         "Volume drives are not within an enclosure",
89         "Drive clear in progress",
90         "Drive type mismatch (SATA vs SAS)",
91         "Patrol read disabled",
92         "Invalid row index",
93         "SAS Config - Invalid action",
94         "SAS Config - Invalid data",
95         "SAS Config - Invalid page",
96         "SAS Config - Invalid type",
97         "SCSI command completed with error",
98         "SCSI I/O request failed",
99         "SCSI RESERVATION_CONFLICT",
100         "One or more flush operations during shutdown failed",
101         "Firmware time is not set",
102         "Wrong firmware or drive state",
103         "Volume is offline",
104         "Peer controller rejected request",
105         "Unable to inform peer of communication changes",
106         "Volume reservation already in progress",
107         "I2C errors were detected",
108         "PCI errors occurred during XOR/DMA operation",
109         "Diagnostics failed",
110         "Unable to process command as boot messages are pending",
111         "Foreign configuration is incomplete"
112 };
113
114 const char *
115 mfi_status(u_int status_code)
116 {
117         static char buffer[16];
118
119         if (status_code == MFI_STAT_INVALID_STATUS)
120                 return ("Invalid status");
121         if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
122                 return (mfi_status_codes[status_code]);
123         snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
124         return (buffer);
125 }
126
127 const char *
128 mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
129 {
130         static char buf[16];
131
132         switch (primary_level) {
133         case DDF_RAID0:
134                 return ("RAID-0");
135         case DDF_RAID1:
136                 if (secondary_level != 0)
137                         return ("RAID-10");
138                 else
139                         return ("RAID-1");
140         case DDF_RAID1E:
141                 return ("RAID-1E");
142         case DDF_RAID3:
143                 return ("RAID-3");
144         case DDF_RAID5:
145                 if (secondary_level != 0)
146                         return ("RAID-50");
147                 else
148                         return ("RAID-5");
149         case DDF_RAID5E:
150                 return ("RAID-5E");
151         case DDF_RAID5EE:
152                 return ("RAID-5EE");
153         case DDF_RAID6:
154                 if (secondary_level != 0)
155                         return ("RAID-60");
156                 else
157                         return ("RAID-6");
158         case DDF_JBOD:
159                 return ("JBOD");
160         case DDF_CONCAT:
161                 return ("CONCAT");
162         default:
163                 sprintf(buf, "LVL 0x%02x", primary_level);
164                 return (buf);
165         }
166 }
167
168 static int
169 mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
170 {
171
172         bzero(info, sizeof(*info));
173         info->array_id = target_id;
174         if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
175                 return (-1);
176         if (!info->present) {
177                 errno = ENXIO;
178                 return (-1);
179         }
180         return (0);
181 }
182
183 const char *
184 mfi_volume_name(int fd, uint8_t target_id)
185 {
186         static struct mfi_query_disk info;
187         static char buf[4];
188
189         if (mfi_query_disk(fd, target_id, &info) < 0) {
190                 snprintf(buf, sizeof(buf), "%d", target_id);
191                 return (buf);
192         }
193         return (info.devname);
194 }
195
196 int
197 mfi_volume_busy(int fd, uint8_t target_id)
198 {
199         struct mfi_query_disk info;
200
201         /* Assume it isn't mounted if we can't get information. */
202         if (mfi_query_disk(fd, target_id, &info) < 0)
203                 return (0);
204         return (info.open != 0);
205 }
206
207 /*
208  * Check if the running kernel supports changing the RAID
209  * configuration of the mfi controller.
210  */
211 int
212 mfi_reconfig_supported(void)
213 {
214         char mibname[64];
215         size_t len;
216         int dummy;
217
218         len = sizeof(dummy);
219         snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
220             mfi_unit);
221         return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
222 }
223
224 int
225 mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
226 {
227         struct mfi_query_disk info;
228         struct mfi_ld_list list;
229         char *cp;
230         long val;
231         u_int i;
232
233         /* If it's a valid number, treat it as a raw target ID. */
234         val = strtol(name, &cp, 0);
235         if (*cp == '\0') {
236                 *target_id = val;
237                 return (0);
238         }
239
240         if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
241             NULL, 0, NULL) < 0)
242                 return (-1);    
243
244         for (i = 0; i < list.ld_count; i++) {
245                 if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
246                     &info) < 0)
247                         continue;
248                 if (strcmp(name, info.devname) == 0) {
249                         *target_id = list.ld_list[i].ld.v.target_id;
250                         return (0);
251                 }
252         }
253         errno = EINVAL;
254         return (-1);
255 }
256
257 int
258 mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
259     uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
260 {
261         struct mfi_ioc_passthru ioc;
262         struct mfi_dcmd_frame *dcmd;
263         int r;
264
265         if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
266             (mbox == NULL && mboxlen != 0)) {
267                 errno = EINVAL;
268                 return (-1);
269         }
270
271         bzero(&ioc, sizeof(ioc));
272         dcmd = &ioc.ioc_frame;
273         if (mbox)
274                 bcopy(mbox, dcmd->mbox, mboxlen);
275         dcmd->header.cmd = MFI_CMD_DCMD;
276         dcmd->header.timeout = 0;
277         dcmd->header.flags = 0;
278         dcmd->header.data_len = bufsize;
279         dcmd->opcode = opcode;
280
281         ioc.buf = buf;
282         ioc.buf_size = bufsize;
283         r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
284         if (r < 0)
285                 return (r);
286
287         if (statusp != NULL)
288                 *statusp = dcmd->header.cmd_status;
289         else if (dcmd->header.cmd_status != MFI_STAT_OK) {
290                 warnx("Command failed: %s",
291                     mfi_status(dcmd->header.cmd_status));
292                 errno = EIO;
293                 return (-1);
294         }
295         return (0);
296 }
297
298 int
299 mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
300 {
301
302         return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
303             sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
304 }
305
306 int
307 mfi_open(int unit, int acs)
308 {
309         char path[MAXPATHLEN];
310
311         snprintf(path, sizeof(path), "/dev/mfi%d", unit);
312         return (open(path, acs));
313 }
314
315 static void
316 print_time_humanized(uint seconds)
317 {
318
319         if (seconds > 3600) {
320                 printf("%u:", seconds / 3600);
321         }
322         if (seconds > 60) {
323                 seconds %= 3600;
324                 printf("%02u:%02u", seconds / 60, seconds % 60);
325         } else {
326                 printf("%us", seconds);
327         }
328 }
329
330 void
331 mfi_display_progress(const char *label, struct mfi_progress *prog)
332 {
333         uint seconds;
334
335         printf("%s: %.2f%% complete after ", label,
336             (float)prog->progress * 100 / 0xffff);
337         print_time_humanized(prog->elapsed_seconds);
338         if (prog->progress != 0 && prog->elapsed_seconds > 10) {
339                 printf(" finished in ");
340                 seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
341                     prog->progress - prog->elapsed_seconds;
342                 print_time_humanized(seconds);
343         }
344         printf("\n");
345 }
346
347 int
348 mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
349     int ac, char **av)
350 {
351         struct mfiutil_command **cmd;
352
353         if (ac < 2) {
354                 warnx("The %s command requires a sub-command.", av[0]);
355                 return (EINVAL);
356         }
357         for (cmd = start; cmd < end; cmd++) {
358                 if (strcmp((*cmd)->name, av[1]) == 0)
359                         return ((*cmd)->handler(ac - 1, av + 1));
360         }
361
362         warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
363         return (ENOENT);
364 }