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