]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.sbin/mfiutil/mfi_drive.c
MFC: Update DNSBL information (MAPS has been acquired, used a generic example)
[FreeBSD/stable/8.git] / usr.sbin / mfiutil / mfi_drive.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/types.h>
33 #include <sys/errno.h>
34 #include <ctype.h>
35 #include <err.h>
36 #include <libutil.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <unistd.h>
43 #include <cam/scsi/scsi_all.h>
44 #include "mfiutil.h"
45
46 MFI_TABLE(top, drive);
47
48 const char *
49 mfi_pdstate(enum mfi_pd_state state)
50 {
51         static char buf[16];
52
53         switch (state) {
54         case MFI_PD_STATE_UNCONFIGURED_GOOD:
55                 return ("UNCONFIGURED GOOD");
56         case MFI_PD_STATE_UNCONFIGURED_BAD:
57                 return ("UNCONFIGURED BAD");
58         case MFI_PD_STATE_HOT_SPARE:
59                 return ("HOT SPARE");
60         case MFI_PD_STATE_OFFLINE:
61                 return ("OFFLINE");
62         case MFI_PD_STATE_FAILED:
63                 return ("FAILED");
64         case MFI_PD_STATE_REBUILD:
65                 return ("REBUILD");
66         case MFI_PD_STATE_ONLINE:
67                 return ("ONLINE");
68         case MFI_PD_STATE_COPYBACK:
69                 return ("COPYBACK");
70         case MFI_PD_STATE_SYSTEM:
71                 return ("SYSTEM");
72         default:
73                 sprintf(buf, "PSTATE 0x%04x", state);
74                 return (buf);
75         }
76 }
77
78 int
79 mfi_lookup_drive(int fd, char *drive, uint16_t *device_id)
80 {
81         struct mfi_pd_list *list;
82         long val;
83         int error;
84         u_int i;
85         char *cp;
86         uint8_t encl, slot;
87
88         /* Look for a raw device id first. */
89         val = strtol(drive, &cp, 0);
90         if (*cp == '\0') {
91                 if (val < 0 || val >= 0xffff)
92                         goto bad;
93                 *device_id = val;
94                 return (0);
95         }
96
97         /* Support for MegaCli style [Exx]:Syy notation. */
98         if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') {
99                 if (drive[1] == '\0')
100                         goto bad;
101                 cp = drive;
102                 if (toupper(drive[0]) == 'E') {
103                         cp++;                   /* Eat 'E' */
104                         val = strtol(cp, &cp, 0);
105                         if (val < 0 || val > 0xff || *cp != ':')
106                                 goto bad;
107                         encl = val;
108                         cp++;                   /* Eat ':' */
109                         if (toupper(*cp) != 'S')
110                                 goto bad;
111                 } else
112                         encl = 0xff;
113                 cp++;                           /* Eat 'S' */
114                 if (*cp == '\0')
115                         goto bad;
116                 val = strtol(cp, &cp, 0);
117                 if (val < 0 || val > 0xff || *cp != '\0')
118                         goto bad;
119                 slot = val;
120
121                 if (mfi_pd_get_list(fd, &list, NULL) < 0) {
122                         error = errno;
123                         warn("Failed to fetch drive list");
124                         return (error);
125                 }
126
127                 for (i = 0; i < list->count; i++) {
128                         if (list->addr[i].scsi_dev_type != 0)
129                                 continue;
130
131                         if (((encl == 0xff &&
132                             list->addr[i].encl_device_id == 0xffff) ||
133                             list->addr[i].encl_index == encl) &&
134                             list->addr[i].slot_number == slot) {
135                                 *device_id = list->addr[i].device_id;
136                                 free(list);
137                                 return (0);
138                         }
139                 }
140                 free(list);
141                 warnx("Unknown drive %s", drive);
142                 return (EINVAL);
143         }
144
145 bad:
146         warnx("Invalid drive number %s", drive);
147         return (EINVAL);
148 }
149
150 static void
151 mbox_store_device_id(uint8_t *mbox, uint16_t device_id)
152 {
153
154         mbox[0] = device_id & 0xff;
155         mbox[1] = device_id >> 8;
156 }
157
158 void
159 mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref)
160 {
161
162         mbox[0] = ref->v.device_id & 0xff;
163         mbox[1] = ref->v.device_id >> 8;
164         mbox[2] = ref->v.seq_num & 0xff;
165         mbox[3] = ref->v.seq_num >> 8;
166 }
167
168 int
169 mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp)
170 {
171         struct mfi_pd_list *list;
172         uint32_t list_size;
173
174         /*
175          * Keep fetching the list in a loop until we have a large enough
176          * buffer to hold the entire list.
177          */
178         list = NULL;
179         list_size = 1024;
180 fetch:
181         list = reallocf(list, list_size);
182         if (list == NULL)
183                 return (-1);
184         if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL,
185             0, statusp) < 0) {
186                 free(list);
187                 return (-1);
188         }
189
190         if (list->size > list_size) {
191                 list_size = list->size;
192                 goto fetch;
193         }
194
195         *listp = list;
196         return (0);
197 }
198
199 int
200 mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
201     uint8_t *statusp)
202 {
203         uint8_t mbox[2];
204
205         mbox_store_device_id(&mbox[0], device_id);
206         return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info,
207             sizeof(struct mfi_pd_info), mbox, 2, statusp));
208 }
209
210 static void
211 cam_strvis(char *dst, const char *src, int srclen, int dstlen)
212 {
213
214         /* Trim leading/trailing spaces, nulls. */
215         while (srclen > 0 && src[0] == ' ')
216                 src++, srclen--;
217         while (srclen > 0
218             && (src[srclen-1] == ' ' || src[srclen-1] == '\0'))
219                 srclen--;
220
221         while (srclen > 0 && dstlen > 1) {
222                 char *cur_pos = dst;
223
224                 if (*src < 0x20) {
225                         /* SCSI-II Specifies that these should never occur. */
226                         /* non-printable character */
227                         if (dstlen > 4) {
228                                 *cur_pos++ = '\\';
229                                 *cur_pos++ = ((*src & 0300) >> 6) + '0';
230                                 *cur_pos++ = ((*src & 0070) >> 3) + '0';
231                                 *cur_pos++ = ((*src & 0007) >> 0) + '0';
232                         } else {
233                                 *cur_pos++ = '?';
234                         }
235                 } else {
236                         /* normal character */
237                         *cur_pos++ = *src;
238                 }
239                 src++;
240                 srclen--;
241                 dstlen -= cur_pos - dst;
242                 dst = cur_pos;
243         }
244         *dst = '\0';
245 }
246
247 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
248 const char *
249 mfi_pd_inq_string(struct mfi_pd_info *info)
250 {
251         struct scsi_inquiry_data *inq_data;
252         char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE];
253         static char inq_string[64];
254
255         inq_data = (struct scsi_inquiry_data *)info->inquiry_data;
256         if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
257                 return (NULL);
258         if (SID_TYPE(inq_data) != T_DIRECT)
259                 return (NULL);
260         if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
261                 return (NULL);
262
263         cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
264             sizeof(vendor));
265         cam_strvis(product, inq_data->product, sizeof(inq_data->product),
266             sizeof(product));
267         cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
268             sizeof(revision));
269         cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
270             sizeof(serial));
271
272         /* Hack for SATA disks, no idea how to tell speed. */
273         if (strcmp(vendor, "ATA") == 0) {
274                 snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA",
275                     product, revision, serial);
276                 return (inq_string);
277         }
278
279         switch (SID_ANSI_REV(inq_data)) {
280         case SCSI_REV_CCS:
281                 strcpy(rstr, "SCSI-CCS");
282                 break;
283         case 5:
284                 strcpy(rstr, "SAS");
285                 break;
286         default:
287                 snprintf(rstr, sizeof (rstr), "SCSI-%d",
288                     SID_ANSI_REV(inq_data));
289                 break;
290         }
291         snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor,
292             product, revision, serial, rstr);
293         return (inq_string);
294 }
295
296 /* Helper function to set a drive to a given state. */
297 static int
298 drive_set_state(char *drive, uint16_t new_state)
299 {
300         struct mfi_pd_info info;
301         uint16_t device_id;
302         uint8_t mbox[6];
303         int error, fd;
304
305         fd = mfi_open(mfi_unit);
306         if (fd < 0) {
307                 error = errno;
308                 warn("mfi_open");
309                 return (error);
310         }
311
312         error = mfi_lookup_drive(fd, drive, &device_id);
313         if (error) {
314                 close(fd);
315                 return (error);
316         }
317
318         /* Get the info for this drive. */
319         if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
320                 error = errno;
321                 warn("Failed to fetch info for drive %u", device_id);
322                 close(fd);
323                 return (error);
324         }
325
326         /* Try to change the state. */
327         if (info.fw_state == new_state) {
328                 warnx("Drive %u is already in the desired state", device_id);
329                 close(fd);
330                 return (EINVAL);
331         }
332
333         mbox_store_pdref(&mbox[0], &info.ref);
334         mbox[4] = new_state & 0xff;
335         mbox[5] = new_state >> 8;
336         if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6,
337             NULL) < 0) {
338                 error = errno;
339                 warn("Failed to set drive %u to %s", device_id,
340                     mfi_pdstate(new_state));
341                 close(fd);
342                 return (error);
343         }
344
345         close(fd);
346
347         return (0);
348 }
349
350 static int
351 fail_drive(int ac, char **av)
352 {
353
354         if (ac != 2) {
355                 warnx("fail: %s", ac > 2 ? "extra arguments" :
356                     "drive required");
357                 return (EINVAL);
358         }
359
360         return (drive_set_state(av[1], MFI_PD_STATE_FAILED));
361 }
362 MFI_COMMAND(top, fail, fail_drive);
363
364 static int
365 good_drive(int ac, char **av)
366 {
367
368         if (ac != 2) {
369                 warnx("good: %s", ac > 2 ? "extra arguments" :
370                     "drive required");
371                 return (EINVAL);
372         }
373
374         return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD));
375 }
376 MFI_COMMAND(top, good, good_drive);
377
378 static int
379 rebuild_drive(int ac, char **av)
380 {
381
382         if (ac != 2) {
383                 warnx("rebuild: %s", ac > 2 ? "extra arguments" :
384                     "drive required");
385                 return (EINVAL);
386         }
387
388         return (drive_set_state(av[1], MFI_PD_STATE_REBUILD));
389 }
390 MFI_COMMAND(top, rebuild, rebuild_drive);
391
392 static int
393 start_rebuild(int ac, char **av)
394 {
395         struct mfi_pd_info info;
396         uint16_t device_id;
397         uint8_t mbox[4];
398         int error, fd;
399
400         if (ac != 2) {
401                 warnx("start rebuild: %s", ac > 2 ? "extra arguments" :
402                     "drive required");
403                 return (EINVAL);
404         }
405
406         fd = mfi_open(mfi_unit);
407         if (fd < 0) {
408                 error = errno;
409                 warn("mfi_open");
410                 return (error);
411         }
412
413         error = mfi_lookup_drive(fd, av[1], &device_id);
414         if (error) {
415                 close(fd);
416                 return (error);
417         }
418
419         /* Get the info for this drive. */
420         if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
421                 error = errno;
422                 warn("Failed to fetch info for drive %u", device_id);
423                 close(fd);
424                 return (error);
425         }
426
427         /* Check the state, must be REBUILD. */
428         if (info.fw_state != MFI_PD_STATE_REBUILD) {
429                 warnx("Drive %d is not in the REBUILD state", device_id);
430                 close(fd);
431                 return (EINVAL);
432         }
433
434         /* Start the rebuild. */
435         mbox_store_pdref(&mbox[0], &info.ref);
436         if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4,
437             NULL) < 0) {
438                 error = errno;
439                 warn("Failed to start rebuild on drive %u", device_id);
440                 close(fd);
441                 return (error);
442         }
443         close(fd);
444
445         return (0);
446 }
447 MFI_COMMAND(start, rebuild, start_rebuild);
448
449 static int
450 abort_rebuild(int ac, char **av)
451 {
452         struct mfi_pd_info info;
453         uint16_t device_id;
454         uint8_t mbox[4];
455         int error, fd;
456
457         if (ac != 2) {
458                 warnx("abort rebuild: %s", ac > 2 ? "extra arguments" :
459                     "drive required");
460                 return (EINVAL);
461         }
462
463         fd = mfi_open(mfi_unit);
464         if (fd < 0) {
465                 error = errno;
466                 warn("mfi_open");
467                 return (error);
468         }
469
470         error = mfi_lookup_drive(fd, av[1], &device_id);
471         if (error) {
472                 close(fd);
473                 return (error);
474         }
475
476         /* Get the info for this drive. */
477         if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
478                 error = errno;
479                 warn("Failed to fetch info for drive %u", device_id);
480                 close(fd);
481                 return (error);
482         }
483
484         /* Check the state, must be REBUILD. */
485         if (info.fw_state != MFI_PD_STATE_REBUILD) {
486                 warn("Drive %d is not in the REBUILD state", device_id);
487                 close(fd);
488                 return (EINVAL);
489         }
490
491         /* Abort the rebuild. */
492         mbox_store_pdref(&mbox[0], &info.ref);
493         if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4,
494             NULL) < 0) {
495                 error = errno;
496                 warn("Failed to abort rebuild on drive %u", device_id);
497                 close(fd);
498                 return (error);
499         }
500         close(fd);
501
502         return (0);
503 }
504 MFI_COMMAND(abort, rebuild, abort_rebuild);
505
506 static int
507 drive_progress(int ac, char **av)
508 {
509         struct mfi_pd_info info;
510         uint16_t device_id;
511         int error, fd;
512
513         if (ac != 2) {
514                 warnx("drive progress: %s", ac > 2 ? "extra arguments" :
515                     "drive required");
516                 return (EINVAL);
517         }
518
519         fd = mfi_open(mfi_unit);
520         if (fd < 0) {
521                 error = errno;
522                 warn("mfi_open");
523                 return (error);
524         }
525
526         error = mfi_lookup_drive(fd, av[1], &device_id);
527         if (error) {
528                 close(fd);
529                 return (error);
530         }
531
532         /* Get the info for this drive. */
533         if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
534                 error = errno;
535                 warn("Failed to fetch info for drive %u", device_id);
536                 close(fd);
537                 return (error);
538         }
539         close(fd);
540
541         /* Display any of the active events. */
542         if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD)
543                 mfi_display_progress("Rebuild", &info.prog_info.rbld);
544         if (info.prog_info.active & MFI_PD_PROGRESS_PATROL)
545                 mfi_display_progress("Patrol Read", &info.prog_info.patrol);
546         if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR)
547                 mfi_display_progress("Clear", &info.prog_info.clear);
548         if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD |
549             MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0)
550                 printf("No activity in progress for drive %u.\n", device_id);
551
552         return (0);
553 }
554 MFI_COMMAND(drive, progress, drive_progress);
555
556 static int
557 drive_clear(int ac, char **av)
558 {
559         struct mfi_pd_info info;
560         uint32_t opcode;
561         uint16_t device_id;
562         uint8_t mbox[4];
563         char *s1;
564         int error, fd;
565
566         if (ac != 3) {
567                 warnx("drive clear: %s", ac > 3 ? "extra arguments" :
568                     "drive and action requires");
569                 return (EINVAL);
570         }
571
572         for (s1 = av[2]; *s1 != '\0'; s1++)
573                 *s1 = tolower(*s1);
574         if (strcmp(av[2], "start") == 0)
575                 opcode = MFI_DCMD_PD_CLEAR_START;
576         else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0))
577                 opcode = MFI_DCMD_PD_CLEAR_ABORT;
578         else {
579                 warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
580                 return (EINVAL);
581         }
582
583         fd = mfi_open(mfi_unit);
584         if (fd < 0) {
585                 error = errno;
586                 warn("mfi_open");
587                 return (error);
588         }
589
590         error = mfi_lookup_drive(fd, av[1], &device_id);
591         if (error) {
592                 close(fd);
593                 return (error);
594         }
595
596         /* Get the info for this drive. */
597         if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
598                 error = errno;
599                 warn("Failed to fetch info for drive %u", device_id);
600                 close(fd);
601                 return (error);
602         }
603
604         mbox_store_pdref(&mbox[0], &info.ref);
605         if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
606                 error = errno;
607                 warn("Failed to %s clear on drive %u",
608                     opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop",
609                     device_id);
610                 close(fd);
611                 return (error);
612         }
613
614         close(fd);
615         return (0);
616 }
617 MFI_COMMAND(drive, clear, drive_clear);
618
619 static int
620 drive_locate(int ac, char **av)
621 {
622         uint16_t device_id;
623         uint32_t opcode;
624         int error, fd;
625         uint8_t mbox[4];
626
627         if (ac != 3) {
628                 warnx("locate: %s", ac > 3 ? "extra arguments" :
629                     "drive and state required");
630                 return (EINVAL);
631         }
632
633         if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0)
634                 opcode = MFI_DCMD_PD_LOCATE_START;
635         else if (strcasecmp(av[2], "off") == 0 ||
636             strcasecmp(av[2], "stop") == 0)
637                 opcode = MFI_DCMD_PD_LOCATE_STOP;
638         else {
639                 warnx("locate: invalid state %s", av[2]);
640                 return (EINVAL);
641         }
642
643         fd = mfi_open(mfi_unit);
644         if (fd < 0) {
645                 error = errno;
646                 warn("mfi_open");
647                 return (error);
648         }
649
650         error = mfi_lookup_drive(fd, av[1], &device_id);
651         if (error) {
652                 close(fd);
653                 return (error);
654         }
655
656
657         mbox_store_device_id(&mbox[0], device_id);
658         mbox[2] = 0;
659         mbox[3] = 0;
660         if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
661                 error = errno;
662                 warn("Failed to %s locate on drive %u",
663                     opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop",
664                     device_id);
665                 close(fd);
666                 return (error);
667         }
668         close(fd);
669
670         return (0);
671 }
672 MFI_COMMAND(top, locate, drive_locate);