freebsd-nq/usr.sbin/mptutil/mpt_cmd.c
Scott Long fc58801ccc Add mptutil, a basic utility for managing MPT SCSI/SATA/SAS controllers.
Drive and controller status can be reported, basic attributes changed,
and arrays and spares can be created and deleted.

Approved by:	re
Obtained from:	Yahoo! Inc.
2009-08-14 13:13:12 +00:00

640 lines
14 KiB
C

/*-
* Copyright (c) 2008 Yahoo!, Inc.
* All rights reserved.
* Written by: John Baldwin <jhb@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$FreeBSD$");
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mpt_ioctl.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mptutil.h"
static const char *mpt_ioc_status_codes[] = {
"Success", /* 0x0000 */
"Invalid function",
"Busy",
"Invalid scatter-gather list",
"Internal error",
"Reserved",
"Insufficient resources",
"Invalid field",
"Invalid state", /* 0x0008 */
"Operation state not supported",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0010 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0018 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Invalid configuration action", /* 0x0020 */
"Invalid configuration type",
"Invalid configuration page",
"Invalid configuration data",
"No configuration defaults",
"Unable to commit configuration change",
NULL,
NULL,
NULL, /* 0x0028 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0030 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0038 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Recovered SCSI error", /* 0x0040 */
"Invalid SCSI bus",
"Invalid SCSI target ID",
"SCSI device not there",
"SCSI data overrun",
"SCSI data underrun",
"SCSI I/O error",
"SCSI protocol error",
"SCSI task terminated", /* 0x0048 */
"SCSI residual mismatch",
"SCSI task management failed",
"SCSI I/O controller terminated",
"SCSI external controller terminated",
"EEDP guard error",
"EEDP reference tag error",
"EEDP application tag error",
NULL, /* 0x0050 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0058 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"SCSI target priority I/O", /* 0x0060 */
"Invalid SCSI target port",
"Invalid SCSI target I/O index",
"SCSI target aborted",
"No connection retryable",
"No connection",
"FC aborted",
"Invalid FC receive ID",
"FC did invalid", /* 0x0068 */
"FC node logged out",
"Transfer count mismatch",
"STS data not set",
"FC exchange canceled",
"Data offset error",
"Too much write data",
"IU too short",
"ACK NAK timeout", /* 0x0070 */
"NAK received",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, /* 0x0078 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"LAN device not found", /* 0x0080 */
"LAN device failure",
"LAN transmit error",
"LAN transmit aborted",
"LAN receive error",
"LAN receive aborted",
"LAN partial packet",
"LAN canceled",
NULL, /* 0x0088 */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"SAS SMP request failed", /* 0x0090 */
"SAS SMP data overrun",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Inband aborted", /* 0x0098 */
"No inband connection",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Diagnostic released", /* 0x00A0 */
};
static const char *mpt_raid_action_status_codes[] = {
"Success",
"Invalid action",
"Failure",
"Operation in progress",
};
const char *
mpt_ioc_status(U16 IOCStatus)
{
static char buffer[16];
IOCStatus &= MPI_IOCSTATUS_MASK;
if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) &&
mpt_ioc_status_codes[IOCStatus] != NULL)
return (mpt_ioc_status_codes[IOCStatus]);
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
return (buffer);
}
const char *
mpt_raid_status(U16 ActionStatus)
{
static char buffer[16];
if (ActionStatus < sizeof(mpt_raid_action_status_codes) /
sizeof(char *))
return (mpt_raid_action_status_codes[ActionStatus]);
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus);
return (buffer);
}
const char *
mpt_raid_level(U8 VolumeType)
{
static char buf[16];
switch (VolumeType) {
case MPI_RAID_VOL_TYPE_IS:
return ("RAID-0");
case MPI_RAID_VOL_TYPE_IM:
return ("RAID-1");
case MPI_RAID_VOL_TYPE_IME:
return ("RAID-1E");
case MPI_RAID_VOL_TYPE_RAID_5:
return ("RAID-5");
case MPI_RAID_VOL_TYPE_RAID_6:
return ("RAID-6");
case MPI_RAID_VOL_TYPE_RAID_10:
return ("RAID-10");
case MPI_RAID_VOL_TYPE_RAID_50:
return ("RAID-50");
default:
sprintf(buf, "LVL 0x%02x", VolumeType);
return (buf);
}
}
const char *
mpt_volume_name(U8 VolumeBus, U8 VolumeID)
{
static struct mpt_query_disk info;
static char buf[16];
if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) {
/*
* We only print out the bus number if it is non-zero
* since mpt(4) only supports devices on bus zero
* anyway.
*/
if (VolumeBus == 0)
snprintf(buf, sizeof(buf), "%d", VolumeID);
else
snprintf(buf, sizeof(buf), "%d:%d", VolumeBus,
VolumeID);
return (buf);
}
return (info.devname);
}
int
mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID)
{
CONFIG_PAGE_IOC_2 *ioc2;
CONFIG_PAGE_IOC_2_RAID_VOL *vol;
struct mpt_query_disk info;
char *cp;
long bus, id;
int i;
/*
* Check for a raw [<bus>:]<id> string. If the bus is not
* specified, assume bus 0.
*/
bus = strtol(name, &cp, 0);
if (*cp == ':') {
id = strtol(cp + 1, &cp, 0);
if (*cp == '\0') {
if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
errno = EINVAL;
return (-1);
}
*VolumeBus = bus;
*VolumeID = id;
return (0);
}
} else if (*cp == '\0') {
if (bus < 0 || bus > 0xff) {
errno = EINVAL;
return (-1);
}
*VolumeBus = 0;
*VolumeID = bus;
return (0);
}
ioc2 = mpt_read_ioc_page(fd, 2, NULL);
if (ioc2 == NULL)
return (-1);
vol = ioc2->RaidVolume;
for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
continue;
if (strcmp(name, info.devname) == 0) {
*VolumeBus = vol->VolumeBus;
*VolumeID = vol->VolumeID;
free(ioc2);
return (0);
}
}
free(ioc2);
errno = EINVAL;
return (-1);
}
int
mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
{
struct mpt_cfg_page_req req;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageType = PageType;
req.header.PageNumber = PageNumber;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
return (-1);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page header failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (-1);
}
*header = req.header;
return (0);
}
void *
mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
U16 *IOCStatus)
{
struct mpt_cfg_page_req req;
void *buf;
int save_errno;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageType = PageType;
req.header.PageNumber = PageNumber;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
return (NULL);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page header failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (NULL);
}
req.len = req.header.PageLength * 4;
buf = malloc(req.len);
req.buf = buf;
bcopy(&req.header, buf, sizeof(req.header));
if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
save_errno = errno;
free(buf);
errno = save_errno;
return (NULL);
}
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page failed: %s",
mpt_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
void *
mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
{
struct mpt_ext_cfg_page_req req;
void *buf;
int save_errno;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageVersion = PageVersion;
req.header.PageNumber = PageNumber;
req.header.ExtPageType = ExtPageType;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
return (NULL);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page header failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (NULL);
}
req.len = req.header.ExtPageLength * 4;
buf = malloc(req.len);
req.buf = buf;
bcopy(&req.header, buf, sizeof(req.header));
if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
save_errno = errno;
free(buf);
errno = save_errno;
return (NULL);
}
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page failed: %s",
mpt_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
int
mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
{
CONFIG_PAGE_HEADER *hdr;
struct mpt_cfg_page_req req;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.buf = buf;
hdr = buf;
req.len = hdr->PageLength * 4;
if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
return (-1);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL) {
*IOCStatus = req.ioc_status;
return (0);
}
warnx("Writing config page failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (-1);
}
return (0);
}
int
mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write)
{
struct mpt_raid_action raid_act;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data)) {
errno = EINVAL;
return (-1);
}
bzero(&raid_act, sizeof(raid_act));
raid_act.action = Action;
raid_act.volume_bus = VolumeBus;
raid_act.volume_id = VolumeID;
raid_act.phys_disk_num = PhysDiskNum;
raid_act.action_data_word = ActionDataWord;
if (buf != NULL && len != 0) {
raid_act.buf = buf;
raid_act.len = len;
raid_act.write = write;
}
if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
return (-1);
if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
if (IOCStatus != NULL) {
*IOCStatus = raid_act.ioc_status;
return (0);
}
warnx("RAID action failed: %s",
mpt_ioc_status(raid_act.ioc_status));
errno = EIO;
return (-1);
}
if (ActionStatus != NULL)
*ActionStatus = raid_act.action_status;
if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
if (ActionStatus != NULL)
return (0);
warnx("RAID action failed: %s",
mpt_raid_status(raid_act.action_status));
errno = EIO;
return (-1);
}
if (VolumeStatus != NULL)
*((U32 *)VolumeStatus) = raid_act.volume_status;
if (ActionData != NULL)
bcopy(raid_act.action_data, ActionData, datalen);
return (0);
}
int
mpt_open(int unit)
{
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "/dev/mpt%d", unit);
return (open(path, O_RDWR));
}
int
mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
int ac, char **av)
{
struct mptutil_command **cmd;
if (ac < 2) {
warnx("The %s command requires a sub-command.", av[0]);
return (EINVAL);
}
for (cmd = start; cmd < end; cmd++) {
if (strcmp((*cmd)->name, av[1]) == 0)
return ((*cmd)->handler(ac - 1, av + 1));
}
warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
return (ENOENT);
}
#ifdef DEBUG
void
hexdump(const void *ptr, int length, const char *hdr, int flags)
{
int i, j, k;
int cols;
const unsigned char *cp;
char delim;
if ((flags & HD_DELIM_MASK) != 0)
delim = (flags & HD_DELIM_MASK) >> 8;
else
delim = ' ';
if ((flags & HD_COLUMN_MASK) != 0)
cols = flags & HD_COLUMN_MASK;
else
cols = 16;
cp = ptr;
for (i = 0; i < length; i+= cols) {
if (hdr != NULL)
printf("%s", hdr);
if ((flags & HD_OMIT_COUNT) == 0)
printf("%04x ", i);
if ((flags & HD_OMIT_HEX) == 0) {
for (j = 0; j < cols; j++) {
k = i + j;
if (k < length)
printf("%c%02x", delim, cp[k]);
else
printf(" ");
}
}
if ((flags & HD_OMIT_CHARS) == 0) {
printf(" |");
for (j = 0; j < cols; j++) {
k = i + j;
if (k >= length)
printf(" ");
else if (cp[k] >= ' ' && cp[k] <= '~')
printf("%c", cp[k]);
else
printf(".");
}
printf("|");
}
printf("\n");
}
}
#endif