2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
32 #include <sys/types.h>
33 #include <sys/errno.h>
44 #include <cam/scsi/scsi_all.h>
47 MFI_TABLE(top
, drive
);
50 * Print the name of a drive either by drive number as %2u or by enclosure:slot
51 * as Exx:Sxx (or both). Use default unless command line options override it
52 * and the command allows this (which we usually do unless we already print
53 * both). We prefer pinfo if given, otherwise try to look it up by device_id.
56 mfi_drive_name(struct mfi_pd_info
*pinfo
, uint16_t device_id
, uint32_t def
)
58 struct mfi_pd_info info
;
63 if ((def
& MFI_DNAME_HONOR_OPTS
) != 0 &&
64 (mfi_opts
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
)) != 0)
65 def
= mfi_opts
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
);
68 if (pinfo
== NULL
&& def
& MFI_DNAME_ES
) {
69 /* Fallback in case of error, just ignore flags. */
70 if (device_id
== 0xffff)
71 snprintf(buf
, sizeof(buf
), "MISSING");
73 snprintf(buf
, sizeof(buf
), "%2u", device_id
);
75 fd
= mfi_open(mfi_unit
, O_RDWR
);
81 /* Get the info for this drive. */
82 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
83 warn("Failed to fetch info for drive %2u", device_id
);
94 if (def
& MFI_DNAME_DEVICE_ID
) {
95 if (device_id
== 0xffff)
96 error
= snprintf(p
, len
, "MISSING");
98 error
= snprintf(p
, len
, "%2u", device_id
);
104 if ((def
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
)) ==
105 (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
) && len
>= 2) {
111 if (def
& MFI_DNAME_ES
) {
112 if (pinfo
->encl_device_id
== 0xffff)
113 error
= snprintf(p
, len
, "S%u",
115 else if (pinfo
->encl_device_id
== pinfo
->ref
.v
.device_id
)
116 error
= snprintf(p
, len
, "E%u",
119 error
= snprintf(p
, len
, "E%u:S%u",
120 pinfo
->encl_index
, pinfo
->slot_number
);
131 mfi_pdstate(enum mfi_pd_state state
)
136 case MFI_PD_STATE_UNCONFIGURED_GOOD
:
137 return ("UNCONFIGURED GOOD");
138 case MFI_PD_STATE_UNCONFIGURED_BAD
:
139 return ("UNCONFIGURED BAD");
140 case MFI_PD_STATE_HOT_SPARE
:
141 return ("HOT SPARE");
142 case MFI_PD_STATE_OFFLINE
:
144 case MFI_PD_STATE_FAILED
:
146 case MFI_PD_STATE_REBUILD
:
148 case MFI_PD_STATE_ONLINE
:
150 case MFI_PD_STATE_COPYBACK
:
152 case MFI_PD_STATE_SYSTEM
:
155 sprintf(buf
, "PSTATE 0x%04x", state
);
161 mfi_lookup_drive(int fd
, char *drive
, uint16_t *device_id
)
163 struct mfi_pd_list
*list
;
170 /* Look for a raw device id first. */
171 val
= strtol(drive
, &cp
, 0);
173 if (val
< 0 || val
>= 0xffff)
179 /* Support for MegaCli style [Exx]:Syy notation. */
180 if (toupper(drive
[0]) == 'E' || toupper(drive
[0]) == 'S') {
181 if (drive
[1] == '\0')
184 if (toupper(drive
[0]) == 'E') {
186 val
= strtol(cp
, &cp
, 0);
187 if (val
< 0 || val
> 0xff || *cp
!= ':')
191 if (toupper(*cp
) != 'S')
198 val
= strtol(cp
, &cp
, 0);
199 if (val
< 0 || val
> 0xff || *cp
!= '\0')
203 if (mfi_pd_get_list(fd
, &list
, NULL
) < 0) {
205 warn("Failed to fetch drive list");
209 for (i
= 0; i
< list
->count
; i
++) {
210 if (list
->addr
[i
].scsi_dev_type
!= 0)
213 if (((encl
== 0xff &&
214 list
->addr
[i
].encl_device_id
== 0xffff) ||
215 list
->addr
[i
].encl_index
== encl
) &&
216 list
->addr
[i
].slot_number
== slot
) {
217 *device_id
= list
->addr
[i
].device_id
;
223 warnx("Unknown drive %s", drive
);
228 warnx("Invalid drive number %s", drive
);
233 mbox_store_device_id(uint8_t *mbox
, uint16_t device_id
)
236 mbox
[0] = device_id
& 0xff;
237 mbox
[1] = device_id
>> 8;
241 mbox_store_pdref(uint8_t *mbox
, union mfi_pd_ref
*ref
)
244 mbox
[0] = ref
->v
.device_id
& 0xff;
245 mbox
[1] = ref
->v
.device_id
>> 8;
246 mbox
[2] = ref
->v
.seq_num
& 0xff;
247 mbox
[3] = ref
->v
.seq_num
>> 8;
251 mfi_pd_get_list(int fd
, struct mfi_pd_list
**listp
, uint8_t *statusp
)
253 struct mfi_pd_list
*list
;
257 * Keep fetching the list in a loop until we have a large enough
258 * buffer to hold the entire list.
263 list
= reallocf(list
, list_size
);
266 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_GET_LIST
, list
, list_size
, NULL
,
272 if (list
->size
> list_size
) {
273 list_size
= list
->size
;
282 mfi_pd_get_info(int fd
, uint16_t device_id
, struct mfi_pd_info
*info
,
287 mbox_store_device_id(&mbox
[0], device_id
);
288 return (mfi_dcmd_command(fd
, MFI_DCMD_PD_GET_INFO
, info
,
289 sizeof(struct mfi_pd_info
), mbox
, 2, statusp
));
293 cam_strvis(char *dst
, const char *src
, int srclen
, int dstlen
)
296 /* Trim leading/trailing spaces, nulls. */
297 while (srclen
> 0 && src
[0] == ' ')
300 && (src
[srclen
-1] == ' ' || src
[srclen
-1] == '\0'))
303 while (srclen
> 0 && dstlen
> 1) {
307 /* SCSI-II Specifies that these should never occur. */
308 /* non-printable character */
311 *cur_pos
++ = ((*src
& 0300) >> 6) + '0';
312 *cur_pos
++ = ((*src
& 0070) >> 3) + '0';
313 *cur_pos
++ = ((*src
& 0007) >> 0) + '0';
318 /* normal character */
323 dstlen
-= cur_pos
- dst
;
329 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
331 mfi_pd_inq_string(struct mfi_pd_info
*info
)
333 struct scsi_inquiry_data iqd
, *inq_data
= &iqd
;
334 char vendor
[16], product
[48], revision
[16], rstr
[12], serial
[SID_VENDOR_SPECIFIC_0_SIZE
];
335 static char inq_string
[64];
337 memcpy(inq_data
, info
->inquiry_data
,
338 (sizeof (iqd
) < sizeof (info
->inquiry_data
))?
339 sizeof (iqd
) : sizeof (info
->inquiry_data
));
340 if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data
))
342 if (SID_TYPE(inq_data
) != T_DIRECT
)
344 if (SID_QUAL(inq_data
) != SID_QUAL_LU_CONNECTED
)
347 cam_strvis(vendor
, inq_data
->vendor
, sizeof(inq_data
->vendor
),
349 cam_strvis(product
, inq_data
->product
, sizeof(inq_data
->product
),
351 cam_strvis(revision
, inq_data
->revision
, sizeof(inq_data
->revision
),
353 cam_strvis(serial
, (char *)inq_data
->vendor_specific0
, sizeof(inq_data
->vendor_specific0
),
356 /* Hack for SATA disks, no idea how to tell speed. */
357 if (strcmp(vendor
, "ATA") == 0) {
358 snprintf(inq_string
, sizeof(inq_string
), "<%s %s serial=%s> SATA",
359 product
, revision
, serial
);
363 switch (SID_ANSI_REV(inq_data
)) {
365 strcpy(rstr
, "SCSI-CCS");
371 snprintf(rstr
, sizeof (rstr
), "SCSI-%d",
372 SID_ANSI_REV(inq_data
));
375 snprintf(inq_string
, sizeof(inq_string
), "<%s %s %s serial=%s> %s", vendor
,
376 product
, revision
, serial
, rstr
);
380 /* Helper function to set a drive to a given state. */
382 drive_set_state(char *drive
, uint16_t new_state
)
384 struct mfi_pd_info info
;
389 fd
= mfi_open(mfi_unit
, O_RDWR
);
396 error
= mfi_lookup_drive(fd
, drive
, &device_id
);
402 /* Get the info for this drive. */
403 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
405 warn("Failed to fetch info for drive %u", device_id
);
410 /* Try to change the state. */
411 if (info
.fw_state
== new_state
) {
412 warnx("Drive %u is already in the desired state", device_id
);
417 mbox_store_pdref(&mbox
[0], &info
.ref
);
418 mbox
[4] = new_state
& 0xff;
419 mbox
[5] = new_state
>> 8;
420 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_STATE_SET
, NULL
, 0, mbox
, 6,
423 warn("Failed to set drive %u to %s", device_id
,
424 mfi_pdstate(new_state
));
435 fail_drive(int ac
, char **av
)
439 warnx("fail: %s", ac
> 2 ? "extra arguments" :
444 return (drive_set_state(av
[1], MFI_PD_STATE_FAILED
));
446 MFI_COMMAND(top
, fail
, fail_drive
);
449 good_drive(int ac
, char **av
)
453 warnx("good: %s", ac
> 2 ? "extra arguments" :
458 return (drive_set_state(av
[1], MFI_PD_STATE_UNCONFIGURED_GOOD
));
460 MFI_COMMAND(top
, good
, good_drive
);
463 rebuild_drive(int ac
, char **av
)
467 warnx("rebuild: %s", ac
> 2 ? "extra arguments" :
472 return (drive_set_state(av
[1], MFI_PD_STATE_REBUILD
));
474 MFI_COMMAND(top
, rebuild
, rebuild_drive
);
477 syspd_drive(int ac
, char **av
)
481 warnx("syspd: %s", ac
> 2 ? "extra arguments" :
486 return (drive_set_state(av
[1], MFI_PD_STATE_SYSTEM
));
488 MFI_COMMAND(top
, syspd
, syspd_drive
);
491 start_rebuild(int ac
, char **av
)
493 struct mfi_pd_info info
;
499 warnx("start rebuild: %s", ac
> 2 ? "extra arguments" :
504 fd
= mfi_open(mfi_unit
, O_RDWR
);
511 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
517 /* Get the info for this drive. */
518 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
520 warn("Failed to fetch info for drive %u", device_id
);
525 /* Check the state, must be REBUILD. */
526 if (info
.fw_state
!= MFI_PD_STATE_REBUILD
) {
527 warnx("Drive %d is not in the REBUILD state", device_id
);
532 /* Start the rebuild. */
533 mbox_store_pdref(&mbox
[0], &info
.ref
);
534 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_REBUILD_START
, NULL
, 0, mbox
, 4,
537 warn("Failed to start rebuild on drive %u", device_id
);
545 MFI_COMMAND(start
, rebuild
, start_rebuild
);
548 abort_rebuild(int ac
, char **av
)
550 struct mfi_pd_info info
;
556 warnx("abort rebuild: %s", ac
> 2 ? "extra arguments" :
561 fd
= mfi_open(mfi_unit
, O_RDWR
);
568 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
574 /* Get the info for this drive. */
575 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
577 warn("Failed to fetch info for drive %u", device_id
);
582 /* Check the state, must be REBUILD. */
583 if (info
.fw_state
!= MFI_PD_STATE_REBUILD
) {
584 warn("Drive %d is not in the REBUILD state", device_id
);
589 /* Abort the rebuild. */
590 mbox_store_pdref(&mbox
[0], &info
.ref
);
591 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_REBUILD_ABORT
, NULL
, 0, mbox
, 4,
594 warn("Failed to abort rebuild on drive %u", device_id
);
602 MFI_COMMAND(abort
, rebuild
, abort_rebuild
);
605 drive_progress(int ac
, char **av
)
607 struct mfi_pd_info info
;
612 warnx("drive progress: %s", ac
> 2 ? "extra arguments" :
617 fd
= mfi_open(mfi_unit
, O_RDWR
);
624 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
630 /* Get the info for this drive. */
631 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
633 warn("Failed to fetch info for drive %u", device_id
);
639 /* Display any of the active events. */
640 if (info
.prog_info
.active
& MFI_PD_PROGRESS_REBUILD
)
641 mfi_display_progress("Rebuild", &info
.prog_info
.rbld
);
642 if (info
.prog_info
.active
& MFI_PD_PROGRESS_PATROL
)
643 mfi_display_progress("Patrol Read", &info
.prog_info
.patrol
);
644 if (info
.prog_info
.active
& MFI_PD_PROGRESS_CLEAR
)
645 mfi_display_progress("Clear", &info
.prog_info
.clear
);
646 if ((info
.prog_info
.active
& (MFI_PD_PROGRESS_REBUILD
|
647 MFI_PD_PROGRESS_PATROL
| MFI_PD_PROGRESS_CLEAR
)) == 0)
648 printf("No activity in progress for drive %s.\n",
649 mfi_drive_name(NULL
, device_id
,
650 MFI_DNAME_DEVICE_ID
|MFI_DNAME_HONOR_OPTS
));
654 MFI_COMMAND(drive
, progress
, drive_progress
);
657 drive_clear(int ac
, char **av
)
659 struct mfi_pd_info info
;
667 warnx("drive clear: %s", ac
> 3 ? "extra arguments" :
668 "drive and action requires");
672 for (s1
= av
[2]; *s1
!= '\0'; s1
++)
674 if (strcmp(av
[2], "start") == 0)
675 opcode
= MFI_DCMD_PD_CLEAR_START
;
676 else if ((strcmp(av
[2], "stop") == 0) || (strcmp(av
[2], "abort") == 0))
677 opcode
= MFI_DCMD_PD_CLEAR_ABORT
;
679 warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
683 fd
= mfi_open(mfi_unit
, O_RDWR
);
690 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
696 /* Get the info for this drive. */
697 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
699 warn("Failed to fetch info for drive %u", device_id
);
704 mbox_store_pdref(&mbox
[0], &info
.ref
);
705 if (mfi_dcmd_command(fd
, opcode
, NULL
, 0, mbox
, 4, NULL
) < 0) {
707 warn("Failed to %s clear on drive %u",
708 opcode
== MFI_DCMD_PD_CLEAR_START
? "start" : "stop",
717 MFI_COMMAND(drive
, clear
, drive_clear
);
720 drive_locate(int ac
, char **av
)
728 warnx("locate: %s", ac
> 3 ? "extra arguments" :
729 "drive and state required");
733 if (strcasecmp(av
[2], "on") == 0 || strcasecmp(av
[2], "start") == 0)
734 opcode
= MFI_DCMD_PD_LOCATE_START
;
735 else if (strcasecmp(av
[2], "off") == 0 ||
736 strcasecmp(av
[2], "stop") == 0)
737 opcode
= MFI_DCMD_PD_LOCATE_STOP
;
739 warnx("locate: invalid state %s", av
[2]);
743 fd
= mfi_open(mfi_unit
, O_RDWR
);
750 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
757 mbox_store_device_id(&mbox
[0], device_id
);
760 if (mfi_dcmd_command(fd
, opcode
, NULL
, 0, mbox
, 4, NULL
) < 0) {
762 warn("Failed to %s locate on drive %u",
763 opcode
== MFI_DCMD_PD_LOCATE_START
? "start" : "stop",
772 MFI_COMMAND(top
, locate
, drive_locate
);