2 * Copyright (c) 2008 Yahoo!, Inc.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of any co-contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $FreeBSD: src/usr.sbin/mptutil/mpt_drive.c,v 1.2 2010/11/09 19:28:06 jhb Exp $
33 #include <sys/param.h>
34 #include <sys/errno.h>
46 #include <cam/scsi/scsi_all.h>
51 mpt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0
*info
)
55 switch (info
->PhysDiskStatus
.State
) {
56 case MPI_PHYSDISK0_STATUS_ONLINE
:
57 if ((info
->PhysDiskStatus
.Flags
&
58 MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC
) &&
59 info
->PhysDiskSettings
.HotSparePool
== 0)
63 case MPI_PHYSDISK0_STATUS_MISSING
:
65 case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE
:
66 return ("NOT COMPATIBLE");
67 case MPI_PHYSDISK0_STATUS_FAILED
:
69 case MPI_PHYSDISK0_STATUS_INITIALIZING
:
70 return ("INITIALIZING");
71 case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED
:
72 return ("OFFLINE REQUESTED");
73 case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED
:
74 return ("FAILED REQUESTED");
75 case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE
:
76 return ("OTHER OFFLINE");
78 sprintf(buf
, "PSTATE 0x%02x", info
->PhysDiskStatus
.State
);
84 * There are several ways to enumerate physical disks. Unfortunately,
85 * none of them are truly complete, so we have to build a union of all of
88 * - IOC2 : This gives us a list of volumes, and by walking the volumes we
89 * can enumerate all of the drives attached to volumes including
90 * online drives and failed drives.
91 * - IOC3 : This gives us a list of all online physical drives including
92 * drives that are not part of a volume nor a spare drive. It
93 * does not include any failed drives.
94 * - IOC5 : This gives us a list of all spare drives including failed
97 * The specific edge cases are that 1) a failed volume member can only be
98 * found via IOC2, 2) a drive that is neither a volume member nor a spare
99 * can only be found via IOC3, and 3) a failed spare can only be found via
102 * To handle this, walk all of the three lists and use the following
103 * routine to add each drive encountered. It quietly succeeds if the
104 * drive is already present in the list. It also sorts the list as it
105 * inserts new drives.
108 mpt_pd_insert(int fd
, struct mpt_drive_list
*list
, U8 PhysDiskNum
)
113 * First, do a simple linear search to see if we have already
116 for (i
= 0; i
< list
->ndrives
; i
++) {
117 if (list
->drives
[i
]->PhysDiskNum
== PhysDiskNum
)
119 if (list
->drives
[i
]->PhysDiskNum
> PhysDiskNum
)
124 * 'i' is our slot for the 'new' drive. Make room and then
125 * read the drive info.
127 for (j
= list
->ndrives
- 1; j
>= i
; j
--)
128 list
->drives
[j
+ 1] = list
->drives
[j
];
129 list
->drives
[i
] = mpt_pd_info(fd
, PhysDiskNum
, NULL
);
130 if (list
->drives
[i
] == NULL
)
136 struct mpt_drive_list
*
139 CONFIG_PAGE_IOC_2
*ioc2
;
140 CONFIG_PAGE_IOC_2_RAID_VOL
*vol
;
141 CONFIG_PAGE_RAID_VOL_0
**volumes
;
142 RAID_VOL0_PHYS_DISK
*rdisk
;
143 CONFIG_PAGE_IOC_3
*ioc3
;
144 IOC_3_PHYS_DISK
*disk
;
145 CONFIG_PAGE_IOC_5
*ioc5
;
146 IOC_5_HOT_SPARE
*spare
;
147 struct mpt_drive_list
*list
;
148 int count
, error
, i
, j
;
150 ioc2
= mpt_read_ioc_page(fd
, 2, NULL
);
153 warn("Failed to fetch volume list");
158 ioc3
= mpt_read_ioc_page(fd
, 3, NULL
);
161 warn("Failed to fetch drive list");
167 ioc5
= mpt_read_ioc_page(fd
, 5, NULL
);
170 warn("Failed to fetch spare list");
178 * Go ahead and read the info for all the volumes. For this
179 * pass we figure out how many physical drives there are.
181 volumes
= malloc(sizeof(*volumes
) * ioc2
->NumActiveVolumes
);
183 vol
= ioc2
->RaidVolume
;
184 for (i
= 0; i
< ioc2
->NumActiveVolumes
; vol
++, i
++) {
185 volumes
[i
] = mpt_vol_info(fd
, vol
->VolumeBus
, vol
->VolumeID
,
187 if (volumes
[i
] == NULL
) {
189 warn("Failed to read volume info");
193 count
+= volumes
[i
]->NumPhysDisks
;
195 count
+= ioc3
->NumPhysDisks
;
196 count
+= ioc5
->NumHotSpares
;
198 /* Walk the various lists enumerating drives. */
199 list
= malloc(sizeof(*list
) + sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0
) *
203 for (i
= 0; i
< ioc2
->NumActiveVolumes
; i
++) {
204 rdisk
= volumes
[i
]->PhysDisk
;
205 for (j
= 0; j
< volumes
[i
]->NumPhysDisks
; rdisk
++, j
++)
206 if (mpt_pd_insert(fd
, list
, rdisk
->PhysDiskNum
) < 0)
213 spare
= ioc5
->HotSpare
;
214 for (i
= 0; i
< ioc5
->NumHotSpares
; spare
++, i
++)
215 if (mpt_pd_insert(fd
, list
, spare
->PhysDiskNum
) < 0)
219 disk
= ioc3
->PhysDisk
;
220 for (i
= 0; i
< ioc3
->NumPhysDisks
; disk
++, i
++)
221 if (mpt_pd_insert(fd
, list
, disk
->PhysDiskNum
) < 0)
229 mpt_free_pd_list(struct mpt_drive_list
*list
)
233 for (i
= 0; i
< list
->ndrives
; i
++)
234 free(list
->drives
[i
]);
239 mpt_lookup_drive(struct mpt_drive_list
*list
, const char *drive
,
246 /* Look for a raw device id first. */
247 val
= strtol(drive
, &cp
, 0);
249 if (val
< 0 || val
> 0xff)
255 /* Look for a <bus>:<id> string. */
257 if (val
< 0 || val
> 0xff)
260 val
= strtol(cp
+ 1, &cp
, 0);
263 if (val
< 0 || val
> 0xff)
267 for (val
= 0; val
< list
->ndrives
; val
++) {
268 if (list
->drives
[val
]->PhysDiskBus
== bus
&&
269 list
->drives
[val
]->PhysDiskID
== id
) {
270 *PhysDiskNum
= list
->drives
[val
]->PhysDiskNum
;
281 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
283 mpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0
*pd_info
)
285 RAID_PHYS_DISK0_INQUIRY_DATA
*inq_data
;
286 u_char vendor
[9], product
[17], revision
[5];
287 static char inq_string
[64];
289 inq_data
= &pd_info
->InquiryData
;
290 cam_strvis(vendor
, inq_data
->VendorID
, sizeof(inq_data
->VendorID
),
292 cam_strvis(product
, inq_data
->ProductID
, sizeof(inq_data
->ProductID
),
294 cam_strvis(revision
, inq_data
->ProductRevLevel
,
295 sizeof(inq_data
->ProductRevLevel
), sizeof(revision
));
298 if (strcmp(vendor
, "ATA") == 0)
299 snprintf(inq_string
, sizeof(inq_string
), "<%s %s> SATA",
302 snprintf(inq_string
, sizeof(inq_string
), "<%s %s %s> SAS",
303 vendor
, product
, revision
);
307 /* Helper function to set a drive to a given state. */
309 drive_set_state(char *drive
, U8 Action
, U8 State
, const char *name
)
311 CONFIG_PAGE_RAID_PHYS_DISK_0
*info
;
312 struct mpt_drive_list
*list
;
316 fd
= mpt_open(mpt_unit
);
323 list
= mpt_pd_list(fd
);
327 if (mpt_lookup_drive(list
, drive
, &PhysDiskNum
) < 0) {
329 warn("Failed to find drive %s", drive
);
332 mpt_free_pd_list(list
);
334 /* Get the info for this drive. */
335 info
= mpt_pd_info(fd
, PhysDiskNum
, NULL
);
338 warn("Failed to fetch info for drive %u", PhysDiskNum
);
342 /* Try to change the state. */
343 if (info
->PhysDiskStatus
.State
== State
) {
344 warnx("Drive %u is already in the desired state", PhysDiskNum
);
348 error
= mpt_raid_action(fd
, Action
, 0, 0, PhysDiskNum
, 0, NULL
, 0, NULL
,
349 NULL
, 0, NULL
, NULL
, 0);
351 warnc(error
, "Failed to set drive %u to %s", PhysDiskNum
, name
);
362 fail_drive(int ac
, char **av
)
366 warnx("fail: %s", ac
> 2 ? "extra arguments" :
371 return (drive_set_state(av
[1], MPI_RAID_ACTION_FAIL_PHYSDISK
,
372 MPI_PHYSDISK0_STATUS_FAILED_REQUESTED
, "FAILED"));
374 MPT_COMMAND(top
, fail
, fail_drive
);
377 online_drive(int ac
, char **av
)
381 warnx("online: %s", ac
> 2 ? "extra arguments" :
386 return (drive_set_state(av
[1], MPI_RAID_ACTION_PHYSDISK_ONLINE
,
387 MPI_PHYSDISK0_STATUS_ONLINE
, "ONLINE"));
389 MPT_COMMAND(top
, online
, online_drive
);
392 offline_drive(int ac
, char **av
)
396 warnx("offline: %s", ac
> 2 ? "extra arguments" :
401 return (drive_set_state(av
[1], MPI_RAID_ACTION_PHYSDISK_OFFLINE
,
402 MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED
, "OFFLINE"));
404 MPT_COMMAND(top
, offline
, offline_drive
);