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