]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mfiutil/mfi_foreign.c
ident(1): Normalizing date format
[FreeBSD/FreeBSD.git] / usr.sbin / mfiutil / mfi_foreign.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 smh@freebsd.org
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *
29  * $FreeBSD$
30  */
31
32 #include <sys/param.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <libutil.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include "mfiutil.h"
43
44 MFI_TABLE(top, foreign);
45
46 static int
47 foreign_clear(__unused int ac, __unused char **av)
48 {
49         int ch, error, fd;
50
51         fd = mfi_open(mfi_unit, O_RDWR);
52         if (fd < 0) {
53                 error = errno;
54                 warn("mfi_open");
55                 return (error);
56         }
57
58         printf(
59             "Are you sure you wish to clear ALL foreign configurations"
60             " on mfi%u? [y/N] ", mfi_unit);
61
62         ch = getchar();
63         if (ch != 'y' && ch != 'Y') {
64                 printf("\nAborting\n");
65                 close(fd);
66                 return (0);
67         }
68
69         if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL,
70             0, NULL) < 0) {
71                 error = errno;
72                 warn("Failed to clear foreign configuration");
73                 close(fd);
74                 return (error);
75         }
76
77         printf("mfi%d: Foreign configuration cleared\n", mfi_unit);
78         close(fd);
79         return (0);
80 }
81 MFI_COMMAND(foreign, clear, foreign_clear);
82
83 static int
84 foreign_scan(__unused int ac, __unused char **av)
85 {
86         struct mfi_foreign_scan_info info;
87         int error, fd;
88
89         fd = mfi_open(mfi_unit, O_RDONLY);
90         if (fd < 0) {
91                 error = errno;
92                 warn("mfi_open");
93                 return (error);
94         }
95
96         if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
97             sizeof(info), NULL, 0, NULL) < 0) {
98                 error = errno;
99                 warn("Failed to scan foreign configuration");
100                 close(fd);
101                 return (error);
102         }
103
104         printf("mfi%d: Found %d foreign configurations\n", mfi_unit,
105                info.count);
106         close(fd);
107         return (0);
108 }
109 MFI_COMMAND(foreign, scan, foreign_scan);
110
111 static int
112 foreign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic)
113 {
114         struct mfi_config_data *config;
115         char prefix[64];
116         int error;
117         uint8_t mbox[4];
118
119         bzero(mbox, sizeof(mbox));
120         mbox[0] = cfgidx;
121         if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) {
122                 error = errno;
123                 warn("Failed to get foreign config %d", error);
124                 close(fd);
125                 return (error);
126         }
127
128         if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW)
129                 sprintf(prefix, "Foreign configuration preview %d", cfgidx);
130         else
131                 sprintf(prefix, "Foreign configuration %d", cfgidx);
132         /*
133          * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by
134          * 0x1a721880 which returns what looks to be drive / volume info
135          * but we have no real information on what these are or what they do
136          * so we're currently relying solely on the config returned above
137          */
138         if (diagnostic)
139                 dump_config(fd, config, prefix);
140         else {
141                 char *ld_list;
142                 int i;
143
144                 ld_list = (char *)(config->array);
145
146                 printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 
147                        config->array_count, config->log_drv_count,
148                        config->spares_count);
149
150
151                 for (i = 0; i < config->array_count; i++)
152                          ld_list += config->array_size;
153
154                 for (i = 0; i < config->log_drv_count; i++) {
155                         const char *level;
156                         char size[6], stripe[5];
157                         struct mfi_ld_config *ld;
158
159                         ld = (struct mfi_ld_config *)ld_list;
160
161                         format_stripe(stripe, sizeof(stripe),
162                                 ld->params.stripe_size);
163                         /*
164                          * foreign configs don't seem to have a secondary raid level
165                          * but, we can use span depth here as if a LD spans multiple
166                          * arrays of disks (2 raid 1 sets for example), we will have an
167                          * indication based on the spam depth. swb
168                          */ 
169                         level = mfi_raid_level(ld->params.primary_raid_level,
170                                                 (ld->params.span_depth - 1));
171
172                         humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512,
173                                 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
174
175                         printf(" ID%d ", i);
176                         printf("(%6s) %-8s |",
177                                 size, level);
178                         printf("volume spans %d %s\n",  ld->params.span_depth,
179                                                         (ld->params.span_depth > 1) ? "arrays" : "array");
180                         for (int j = 0; j < ld->params.span_depth; j++) {
181                                 char *ar_list;
182                                 struct mfi_array *ar;
183                                 uint16_t device_id;
184
185                                 printf("      array %u @ ", ld->span[j].array_ref);
186                                 humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512,
187                                         "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
188                                 
189                                 printf("(%6s)\n",size);
190                                 ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size);
191
192                                 ar = (struct mfi_array *)ar_list;
193                                 for (int k = 0; k < ar->num_drives; k++) {
194                                         device_id = ar->pd[k].ref.v.device_id;
195                                         if (device_id == 0xffff)
196                                                 printf("        drive MISSING\n");
197                                         else {
198                                                 printf("        drive %u %s\n", device_id,
199                                                         mfi_pdstate(ar->pd[k].fw_state));
200                                         }
201                                 }
202
203                         }
204                         ld_list += config->log_drv_size;
205                 }
206         }
207
208         free(config);
209
210         return (0);
211 }
212
213 int
214 display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd)
215 {
216         struct mfi_foreign_scan_info info;
217         uint8_t i;
218         int error, fd;
219
220         if (ac > 2) {
221                 warnx("foreign display: extra arguments");
222                 return (EINVAL);
223         }
224
225         fd = mfi_open(mfi_unit, O_RDONLY);
226         if (fd < 0) {
227                 error = errno;
228                 warn("mfi_open");
229                 return (error);
230         }
231
232         if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
233             sizeof(info), NULL, 0, NULL) < 0) {
234                 error = errno;
235                 warn("Failed to scan foreign configuration");
236                 close(fd);
237                 return (error);
238         }
239
240         if (info.count == 0) {
241                 warnx("foreign display: no foreign configs found");
242                 close(fd);
243                 return (EINVAL);
244         }
245
246         if (ac == 1) {
247                 for (i = 0; i < info.count; i++) {
248                         error = foreign_show_cfg(fd,
249                                 display_cmd, i, diagnostic);
250                         if(error != 0) {
251                                 close(fd);
252                                 return (error);
253                         }
254                         if (i < info.count - 1)
255                                 printf("\n");
256                 }
257         } else if (ac == 2) {
258                 error = foreign_show_cfg(fd,
259                         display_cmd, atoi(av[1]), diagnostic);
260                 if (error != 0) {
261                         close(fd);
262                         return (error);
263                 }
264         }
265         
266         close(fd);
267         return (0);
268 }
269
270 static int
271 foreign_display(int ac, char **av)
272 {
273         return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY));
274 }
275 MFI_COMMAND(foreign, diag, foreign_display);
276
277 static int
278 foreign_preview(int ac, char **av)
279 {
280         return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW));
281 }
282 MFI_COMMAND(foreign, preview, foreign_preview);
283
284 static int
285 foreign_import(int ac, char **av)
286 {
287         struct mfi_foreign_scan_info info;
288         int ch, error, fd;
289         uint8_t cfgidx;
290         uint8_t mbox[4];
291
292         if (ac > 2) {
293                 warnx("foreign preview: extra arguments");
294                 return (EINVAL);
295         }
296
297         fd = mfi_open(mfi_unit, O_RDWR);
298         if (fd < 0) {
299                 error = errno;
300                 warn("mfi_open");
301                 return (error);
302         }
303
304         if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
305             sizeof(info), NULL, 0, NULL) < 0) {
306                 error = errno;
307                 warn("Failed to scan foreign configuration");
308                 close(fd);
309                 return (error);
310         }
311
312         if (info.count == 0) {
313                 warnx("foreign import: no foreign configs found");
314                 close(fd);
315                 return (EINVAL);
316         }
317
318         if (ac == 1) {
319                 cfgidx = 0xff;
320                 printf("Are you sure you wish to import ALL foreign "
321                        "configurations on mfi%u? [y/N] ", mfi_unit);
322         } else {
323                 /*
324                  * While this is docmmented for MegaCli this failed with
325                  * exit code 0x03 on the test controller which was a Supermicro
326                  * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based
327                  * controller.
328                  */
329                 cfgidx = atoi(av[1]);
330                 if (cfgidx >= info.count) {
331                         warnx("Invalid foreign config %d specified max is %d",
332                               cfgidx, info.count - 1);
333                         close(fd);
334                         return (EINVAL);
335                 }
336                 printf("Are you sure you wish to import the foreign "
337                        "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit);
338         }
339
340         ch = getchar();
341         if (ch != 'y' && ch != 'Y') {
342                 printf("\nAborting\n");
343                 close(fd);
344                 return (0);
345         }
346
347         bzero(mbox, sizeof(mbox));
348         mbox[0] = cfgidx;
349         if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox,
350             sizeof(mbox), NULL) < 0) {
351                 error = errno;
352                 warn("Failed to import foreign configuration");
353                 close(fd);
354                 return (error);
355         }
356
357         if (ac == 1)
358                 printf("mfi%d: All foreign configurations imported\n",
359                        mfi_unit);
360         else
361                 printf("mfi%d: Foreign configuration %d imported\n", mfi_unit,
362                        cfgidx);
363         close(fd);
364         return (0);
365 }
366 MFI_COMMAND(foreign, import, foreign_import);