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
29 * $FreeBSD: src/usr.sbin/mfiutil/mfi_drive.c,v 1.6 2011/06/20 21:28:50 bz Exp $
32 #include <sys/types.h>
33 #include <sys/errno.h>
43 #include <cam/scsi/scsi_all.h>
46 MFI_TABLE(top
, drive
);
49 * Print the name of a drive either by drive number as %2u or by enclosure:slot
50 * as Exx:Sxx (or both). Use default unless command line options override it
51 * and the command allows this (which we usually do unless we already print
52 * both). We prefer pinfo if given, otherwise try to look it up by device_id.
55 mfi_drive_name(struct mfi_pd_info
*pinfo
, uint16_t device_id
, uint32_t def
)
57 struct mfi_pd_info info
;
62 if ((def
& MFI_DNAME_HONOR_OPTS
) != 0 &&
63 (mfi_opts
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
)) != 0)
64 def
= mfi_opts
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
);
67 if (pinfo
== NULL
&& def
& MFI_DNAME_ES
) {
68 /* Fallback in case of error, just ignore flags. */
69 if (device_id
== 0xffff)
70 snprintf(buf
, sizeof(buf
), "MISSING");
72 snprintf(buf
, sizeof(buf
), "%2u", device_id
);
74 fd
= mfi_open(mfi_unit
);
80 /* Get the info for this drive. */
81 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
82 warn("Failed to fetch info for drive %2u", device_id
);
93 if (def
& MFI_DNAME_DEVICE_ID
) {
94 if (device_id
== 0xffff)
95 error
= snprintf(p
, len
, "MISSING");
97 error
= snprintf(p
, len
, "%2u", device_id
);
103 if ((def
& (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
)) ==
104 (MFI_DNAME_ES
|MFI_DNAME_DEVICE_ID
) && len
>= 2) {
110 if (def
& MFI_DNAME_ES
) {
111 if (pinfo
->encl_device_id
== 0xffff)
112 error
= snprintf(p
, len
, "S%u",
114 else if (pinfo
->encl_device_id
== pinfo
->ref
.v
.device_id
)
115 error
= snprintf(p
, len
, "E%u",
118 error
= snprintf(p
, len
, "E%u:S%u",
119 pinfo
->encl_index
, pinfo
->slot_number
);
130 mfi_pdstate(enum mfi_pd_state state
)
135 case MFI_PD_STATE_UNCONFIGURED_GOOD
:
136 return ("UNCONFIGURED GOOD");
137 case MFI_PD_STATE_UNCONFIGURED_BAD
:
138 return ("UNCONFIGURED BAD");
139 case MFI_PD_STATE_HOT_SPARE
:
140 return ("HOT SPARE");
141 case MFI_PD_STATE_OFFLINE
:
143 case MFI_PD_STATE_FAILED
:
145 case MFI_PD_STATE_REBUILD
:
147 case MFI_PD_STATE_ONLINE
:
149 case MFI_PD_STATE_COPYBACK
:
151 case MFI_PD_STATE_SYSTEM
:
154 sprintf(buf
, "PSTATE 0x%04x", state
);
160 mfi_lookup_drive(int fd
, char *drive
, uint16_t *device_id
)
162 struct mfi_pd_list
*list
;
169 /* Look for a raw device id first. */
170 val
= strtol(drive
, &cp
, 0);
172 if (val
< 0 || val
>= 0xffff)
178 /* Support for MegaCli style [Exx]:Syy notation. */
179 if (toupper(drive
[0]) == 'E' || toupper(drive
[0]) == 'S') {
180 if (drive
[1] == '\0')
183 if (toupper(drive
[0]) == 'E') {
185 val
= strtol(cp
, &cp
, 0);
186 if (val
< 0 || val
> 0xff || *cp
!= ':')
190 if (toupper(*cp
) != 'S')
197 val
= strtol(cp
, &cp
, 0);
198 if (val
< 0 || val
> 0xff || *cp
!= '\0')
202 if (mfi_pd_get_list(fd
, &list
, NULL
) < 0) {
204 warn("Failed to fetch drive list");
208 for (i
= 0; i
< list
->count
; i
++) {
209 if (list
->addr
[i
].scsi_dev_type
!= 0)
212 if (((encl
== 0xff &&
213 list
->addr
[i
].encl_device_id
== 0xffff) ||
214 list
->addr
[i
].encl_index
== encl
) &&
215 list
->addr
[i
].slot_number
== slot
) {
216 *device_id
= list
->addr
[i
].device_id
;
222 warnx("Unknown drive %s", drive
);
227 warnx("Invalid drive number %s", drive
);
232 mbox_store_device_id(uint8_t *mbox
, uint16_t device_id
)
235 mbox
[0] = device_id
& 0xff;
236 mbox
[1] = device_id
>> 8;
240 mbox_store_pdref(uint8_t *mbox
, union mfi_pd_ref
*ref
)
243 mbox
[0] = ref
->v
.device_id
& 0xff;
244 mbox
[1] = ref
->v
.device_id
>> 8;
245 mbox
[2] = ref
->v
.seq_num
& 0xff;
246 mbox
[3] = ref
->v
.seq_num
>> 8;
250 mfi_pd_get_list(int fd
, struct mfi_pd_list
**listp
, uint8_t *statusp
)
252 struct mfi_pd_list
*list
;
256 * Keep fetching the list in a loop until we have a large enough
257 * buffer to hold the entire list.
262 list
= reallocf(list
, list_size
);
265 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_GET_LIST
, list
, list_size
, NULL
,
271 if (list
->size
> list_size
) {
272 list_size
= list
->size
;
281 mfi_pd_get_info(int fd
, uint16_t device_id
, struct mfi_pd_info
*info
,
286 mbox_store_device_id(&mbox
[0], device_id
);
287 return (mfi_dcmd_command(fd
, MFI_DCMD_PD_GET_INFO
, info
,
288 sizeof(struct mfi_pd_info
), mbox
, 2, statusp
));
292 cam_strvis(char *dst
, const char *src
, int srclen
, int dstlen
)
295 /* Trim leading/trailing spaces, nulls. */
296 while (srclen
> 0 && src
[0] == ' ')
299 && (src
[srclen
-1] == ' ' || src
[srclen
-1] == '\0'))
302 while (srclen
> 0 && dstlen
> 1) {
306 /* SCSI-II Specifies that these should never occur. */
307 /* non-printable character */
310 *cur_pos
++ = ((*src
& 0300) >> 6) + '0';
311 *cur_pos
++ = ((*src
& 0070) >> 3) + '0';
312 *cur_pos
++ = ((*src
& 0007) >> 0) + '0';
317 /* normal character */
322 dstlen
-= cur_pos
- dst
;
328 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
330 mfi_pd_inq_string(struct mfi_pd_info
*info
)
332 struct scsi_inquiry_data
*inq_data
;
333 char vendor
[16], product
[48], revision
[16], rstr
[12], serial
[SID_VENDOR_SPECIFIC_0_SIZE
];
334 static char inq_string
[64];
336 inq_data
= (struct scsi_inquiry_data
*)info
->inquiry_data
;
337 if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data
))
339 if (SID_TYPE(inq_data
) != T_DIRECT
)
341 if (SID_QUAL(inq_data
) != SID_QUAL_LU_CONNECTED
)
344 cam_strvis(vendor
, inq_data
->vendor
, sizeof(inq_data
->vendor
),
346 cam_strvis(product
, inq_data
->product
, sizeof(inq_data
->product
),
348 cam_strvis(revision
, inq_data
->revision
, sizeof(inq_data
->revision
),
350 cam_strvis(serial
, (char *)inq_data
->vendor_specific0
, sizeof(inq_data
->vendor_specific0
),
353 /* Hack for SATA disks, no idea how to tell speed. */
354 if (strcmp(vendor
, "ATA") == 0) {
355 snprintf(inq_string
, sizeof(inq_string
), "<%s %s serial=%s> SATA",
356 product
, revision
, serial
);
360 switch (SID_ANSI_REV(inq_data
)) {
362 strcpy(rstr
, "SCSI-CCS");
368 snprintf(rstr
, sizeof (rstr
), "SCSI-%d",
369 SID_ANSI_REV(inq_data
));
372 snprintf(inq_string
, sizeof(inq_string
), "<%s %s %s serial=%s> %s", vendor
,
373 product
, revision
, serial
, rstr
);
377 /* Helper function to set a drive to a given state. */
379 drive_set_state(char *drive
, uint16_t new_state
)
381 struct mfi_pd_info info
;
386 fd
= mfi_open(mfi_unit
);
393 error
= mfi_lookup_drive(fd
, drive
, &device_id
);
399 /* Get the info for this drive. */
400 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
402 warn("Failed to fetch info for drive %u", device_id
);
407 /* Try to change the state. */
408 if (info
.fw_state
== new_state
) {
409 warnx("Drive %u is already in the desired state", device_id
);
414 mbox_store_pdref(&mbox
[0], &info
.ref
);
415 mbox
[4] = new_state
& 0xff;
416 mbox
[5] = new_state
>> 8;
417 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_STATE_SET
, NULL
, 0, mbox
, 6,
420 warn("Failed to set drive %u to %s", device_id
,
421 mfi_pdstate(new_state
));
432 fail_drive(int ac
, char **av
)
436 warnx("fail: %s", ac
> 2 ? "extra arguments" :
441 return (drive_set_state(av
[1], MFI_PD_STATE_FAILED
));
443 MFI_COMMAND(top
, fail
, fail_drive
);
446 good_drive(int ac
, char **av
)
450 warnx("good: %s", ac
> 2 ? "extra arguments" :
455 return (drive_set_state(av
[1], MFI_PD_STATE_UNCONFIGURED_GOOD
));
457 MFI_COMMAND(top
, good
, good_drive
);
460 rebuild_drive(int ac
, char **av
)
464 warnx("rebuild: %s", ac
> 2 ? "extra arguments" :
469 return (drive_set_state(av
[1], MFI_PD_STATE_REBUILD
));
471 MFI_COMMAND(top
, rebuild
, rebuild_drive
);
474 start_rebuild(int ac
, char **av
)
476 struct mfi_pd_info info
;
482 warnx("start rebuild: %s", ac
> 2 ? "extra arguments" :
487 fd
= mfi_open(mfi_unit
);
494 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
500 /* Get the info for this drive. */
501 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
503 warn("Failed to fetch info for drive %u", device_id
);
508 /* Check the state, must be REBUILD. */
509 if (info
.fw_state
!= MFI_PD_STATE_REBUILD
) {
510 warnx("Drive %d is not in the REBUILD state", device_id
);
515 /* Start the rebuild. */
516 mbox_store_pdref(&mbox
[0], &info
.ref
);
517 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_REBUILD_START
, NULL
, 0, mbox
, 4,
520 warn("Failed to start rebuild on drive %u", device_id
);
528 MFI_COMMAND(start
, rebuild
, start_rebuild
);
531 abort_rebuild(int ac
, char **av
)
533 struct mfi_pd_info info
;
539 warnx("abort rebuild: %s", ac
> 2 ? "extra arguments" :
544 fd
= mfi_open(mfi_unit
);
551 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
557 /* Get the info for this drive. */
558 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
560 warn("Failed to fetch info for drive %u", device_id
);
565 /* Check the state, must be REBUILD. */
566 if (info
.fw_state
!= MFI_PD_STATE_REBUILD
) {
567 warn("Drive %d is not in the REBUILD state", device_id
);
572 /* Abort the rebuild. */
573 mbox_store_pdref(&mbox
[0], &info
.ref
);
574 if (mfi_dcmd_command(fd
, MFI_DCMD_PD_REBUILD_ABORT
, NULL
, 0, mbox
, 4,
577 warn("Failed to abort rebuild on drive %u", device_id
);
585 MFI_COMMAND(abort
, rebuild
, abort_rebuild
);
588 drive_progress(int ac
, char **av
)
590 struct mfi_pd_info info
;
595 warnx("drive progress: %s", ac
> 2 ? "extra arguments" :
600 fd
= mfi_open(mfi_unit
);
607 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
613 /* Get the info for this drive. */
614 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
616 warn("Failed to fetch info for drive %u", device_id
);
622 /* Display any of the active events. */
623 if (info
.prog_info
.active
& MFI_PD_PROGRESS_REBUILD
)
624 mfi_display_progress("Rebuild", &info
.prog_info
.rbld
);
625 if (info
.prog_info
.active
& MFI_PD_PROGRESS_PATROL
)
626 mfi_display_progress("Patrol Read", &info
.prog_info
.patrol
);
627 if (info
.prog_info
.active
& MFI_PD_PROGRESS_CLEAR
)
628 mfi_display_progress("Clear", &info
.prog_info
.clear
);
629 if ((info
.prog_info
.active
& (MFI_PD_PROGRESS_REBUILD
|
630 MFI_PD_PROGRESS_PATROL
| MFI_PD_PROGRESS_CLEAR
)) == 0)
631 printf("No activity in progress for drive %s.\n",
632 mfi_drive_name(NULL
, device_id
,
633 MFI_DNAME_DEVICE_ID
|MFI_DNAME_HONOR_OPTS
));
637 MFI_COMMAND(drive
, progress
, drive_progress
);
640 drive_clear(int ac
, char **av
)
642 struct mfi_pd_info info
;
650 warnx("drive clear: %s", ac
> 3 ? "extra arguments" :
651 "drive and action required");
655 for (s1
= av
[2]; *s1
!= '\0'; s1
++)
657 if (strcmp(av
[2], "start") == 0)
658 opcode
= MFI_DCMD_PD_CLEAR_START
;
659 else if ((strcmp(av
[2], "stop") == 0) || (strcmp(av
[2], "abort") == 0))
660 opcode
= MFI_DCMD_PD_CLEAR_ABORT
;
662 warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
666 fd
= mfi_open(mfi_unit
);
673 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
679 /* Get the info for this drive. */
680 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
682 warn("Failed to fetch info for drive %u", device_id
);
687 mbox_store_pdref(&mbox
[0], &info
.ref
);
688 if (mfi_dcmd_command(fd
, opcode
, NULL
, 0, mbox
, 4, NULL
) < 0) {
690 warn("Failed to %s clear on drive %u",
691 opcode
== MFI_DCMD_PD_CLEAR_START
? "start" : "stop",
700 MFI_COMMAND(drive
, clear
, drive_clear
);
703 drive_locate(int ac
, char **av
)
711 warnx("locate: %s", ac
> 3 ? "extra arguments" :
712 "drive and state required");
716 if (strcasecmp(av
[2], "on") == 0 || strcasecmp(av
[2], "start") == 0)
717 opcode
= MFI_DCMD_PD_LOCATE_START
;
718 else if (strcasecmp(av
[2], "off") == 0 ||
719 strcasecmp(av
[2], "stop") == 0)
720 opcode
= MFI_DCMD_PD_LOCATE_STOP
;
722 warnx("locate: invalid state %s", av
[2]);
726 fd
= mfi_open(mfi_unit
);
733 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
740 mbox_store_device_id(&mbox
[0], device_id
);
743 if (mfi_dcmd_command(fd
, opcode
, NULL
, 0, mbox
, 4, NULL
) < 0) {
745 warn("Failed to %s locate on drive %u",
746 opcode
== MFI_DCMD_PD_LOCATE_START
? "start" : "stop",
755 MFI_COMMAND(top
, locate
, drive_locate
);