7eedb7c68d
+ Add boatloads of KASSERTs and *really* check out more locking issues (to catch recursions when we actually go to real locking in CAM soon). The KASSERTs also caught lots of other issues like using commands that were put back on free lists, etc. + Target mode: role setting is derived directly from port capabilities. There is no need to set a role any more. Some target mode resources are allocated early on (ELS), but target command buffer allocation is deferred until the first lun enable. + Fix some breakages I introduced with target mode in that some commands are *repeating* commands. That is, the reply shows up but the command isn't really done (we don't free it). We still need to take it off the pending list because when we resubmit it, bad things then happen. + Fix more of the way that timed out commands and bus reset is done. The actual TMF response code was being ignored. + For SPI, honor BIOS settings. This doesn't quite fix the problems we've seen where we can't seem to (re)negotiate U320 on all drives but avoids it instead by letting us honor the BIOS settings. I'm sure this is not quite right and will have to change again soon.
1700 lines
44 KiB
C
1700 lines
44 KiB
C
/*-
|
|
* Routines for handling the integrated RAID features LSI MPT Fusion adapters.
|
|
*
|
|
* Copyright (c) 2005, WHEEL Sp. z o.o.
|
|
* Copyright (c) 2005 Justin T. Gibbs.
|
|
* All rights reserved.
|
|
*
|
|
* 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 at minimum a disclaimer
|
|
* substantially similar to the "NO WARRANTY" disclaimer below
|
|
* ("Disclaimer") and any redistribution must be conditioned upon including
|
|
* a substantially similar Disclaimer requirement for further binary
|
|
* redistribution.
|
|
* 3. Neither the names of the above listed copyright holders nor the names
|
|
* of any contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <dev/mpt/mpt.h>
|
|
#include <dev/mpt/mpt_raid.h>
|
|
|
|
#include "dev/mpt/mpilib/mpi_ioc.h" /* XXX Fix Event Handling!!! */
|
|
#include "dev/mpt/mpilib/mpi_raid.h"
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_sim.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
|
|
#if __FreeBSD_version < 500000
|
|
#include <sys/devicestat.h>
|
|
#define GIANT_REQUIRED
|
|
#endif
|
|
#include <cam/cam_periph.h>
|
|
|
|
#include <sys/callout.h>
|
|
#include <sys/kthread.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
struct mpt_raid_action_result
|
|
{
|
|
union {
|
|
MPI_RAID_VOL_INDICATOR indicator_struct;
|
|
uint32_t new_settings;
|
|
uint8_t phys_disk_num;
|
|
} action_data;
|
|
uint16_t action_status;
|
|
};
|
|
|
|
#define REQ_TO_RAID_ACTION_RESULT(req) ((struct mpt_raid_action_result *) \
|
|
(((MSG_RAID_ACTION_REQUEST *)(req->req_vbuf)) + 1))
|
|
|
|
#define REQ_IOCSTATUS(req) ((req)->IOCStatus & MPI_IOCSTATUS_MASK)
|
|
|
|
|
|
static mpt_probe_handler_t mpt_raid_probe;
|
|
static mpt_attach_handler_t mpt_raid_attach;
|
|
static mpt_event_handler_t mpt_raid_event;
|
|
static mpt_shutdown_handler_t mpt_raid_shutdown;
|
|
static mpt_reset_handler_t mpt_raid_ioc_reset;
|
|
static mpt_detach_handler_t mpt_raid_detach;
|
|
|
|
static struct mpt_personality mpt_raid_personality =
|
|
{
|
|
.name = "mpt_raid",
|
|
.probe = mpt_raid_probe,
|
|
.attach = mpt_raid_attach,
|
|
.event = mpt_raid_event,
|
|
.reset = mpt_raid_ioc_reset,
|
|
.shutdown = mpt_raid_shutdown,
|
|
.detach = mpt_raid_detach,
|
|
};
|
|
|
|
DECLARE_MPT_PERSONALITY(mpt_raid, SI_ORDER_THIRD);
|
|
MPT_PERSONALITY_DEPEND(mpt_raid, mpt_cam, 1, 1, 1);
|
|
|
|
static mpt_reply_handler_t mpt_raid_reply_handler;
|
|
static int mpt_raid_reply_frame_handler(struct mpt_softc *mpt, request_t *req,
|
|
MSG_DEFAULT_REPLY *reply_frame);
|
|
static int mpt_spawn_raid_thread(struct mpt_softc *mpt);
|
|
static void mpt_terminate_raid_thread(struct mpt_softc *mpt);
|
|
static void mpt_raid_thread(void *arg);
|
|
static timeout_t mpt_raid_timer;
|
|
static timeout_t mpt_raid_quiesce_timeout;
|
|
#if 0
|
|
static void mpt_enable_vol(struct mpt_softc *mpt,
|
|
struct mpt_raid_volume *mpt_vol, int enable);
|
|
#endif
|
|
static void mpt_verify_mwce(struct mpt_softc *mpt,
|
|
struct mpt_raid_volume *mpt_vol);
|
|
static void mpt_adjust_queue_depth(struct mpt_softc *mpt,
|
|
struct mpt_raid_volume *mpt_vol,
|
|
struct cam_path *path);
|
|
static void mpt_raid_sysctl_attach(struct mpt_softc *mpt);
|
|
|
|
static uint32_t raid_handler_id = MPT_HANDLER_ID_NONE;
|
|
|
|
const char *
|
|
mpt_vol_type(struct mpt_raid_volume *vol)
|
|
{
|
|
switch (vol->config_page->VolumeType) {
|
|
case MPI_RAID_VOL_TYPE_IS:
|
|
return ("RAID-0");
|
|
case MPI_RAID_VOL_TYPE_IME:
|
|
return ("RAID-1E");
|
|
case MPI_RAID_VOL_TYPE_IM:
|
|
return ("RAID-1");
|
|
default:
|
|
return ("Unknown");
|
|
}
|
|
}
|
|
|
|
const char *
|
|
mpt_vol_state(struct mpt_raid_volume *vol)
|
|
{
|
|
switch (vol->config_page->VolumeStatus.State) {
|
|
case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL:
|
|
return ("Optimal");
|
|
case MPI_RAIDVOL0_STATUS_STATE_DEGRADED:
|
|
return ("Degraded");
|
|
case MPI_RAIDVOL0_STATUS_STATE_FAILED:
|
|
return ("Failed");
|
|
default:
|
|
return ("Unknown");
|
|
}
|
|
}
|
|
|
|
const char *
|
|
mpt_disk_state(struct mpt_raid_disk *disk)
|
|
{
|
|
switch (disk->config_page.PhysDiskStatus.State) {
|
|
case MPI_PHYSDISK0_STATUS_ONLINE:
|
|
return ("Online");
|
|
case MPI_PHYSDISK0_STATUS_MISSING:
|
|
return ("Missing");
|
|
case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE:
|
|
return ("Incompatible");
|
|
case MPI_PHYSDISK0_STATUS_FAILED:
|
|
return ("Failed");
|
|
case MPI_PHYSDISK0_STATUS_INITIALIZING:
|
|
return ("Initializing");
|
|
case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED:
|
|
return ("Offline Requested");
|
|
case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED:
|
|
return ("Failed per Host Request");
|
|
case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE:
|
|
return ("Offline");
|
|
default:
|
|
return ("Unknown");
|
|
}
|
|
}
|
|
|
|
void
|
|
mpt_vol_prt(struct mpt_softc *mpt, struct mpt_raid_volume *vol,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
printf("%s:vol%d(%s:%d:%d): ", device_get_nameunit(mpt->dev),
|
|
(u_int)(vol - mpt->raid_volumes), device_get_nameunit(mpt->dev),
|
|
vol->config_page->VolumeBus, vol->config_page->VolumeID);
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
mpt_disk_prt(struct mpt_softc *mpt, struct mpt_raid_disk *disk,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (disk->volume != NULL) {
|
|
printf("(%s:vol%d:%d): ",
|
|
device_get_nameunit(mpt->dev),
|
|
disk->volume->config_page->VolumeID,
|
|
disk->member_number);
|
|
} else {
|
|
printf("(%s:%d:%d): ", device_get_nameunit(mpt->dev),
|
|
disk->config_page.PhysDiskBus,
|
|
disk->config_page.PhysDiskID);
|
|
}
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void
|
|
mpt_raid_async(void *callback_arg, u_int32_t code,
|
|
struct cam_path *path, void *arg)
|
|
{
|
|
struct mpt_softc *mpt;
|
|
|
|
mpt = (struct mpt_softc*)callback_arg;
|
|
switch (code) {
|
|
case AC_FOUND_DEVICE:
|
|
{
|
|
struct ccb_getdev *cgd;
|
|
struct mpt_raid_volume *mpt_vol;
|
|
|
|
cgd = (struct ccb_getdev *)arg;
|
|
if (cgd == NULL)
|
|
break;
|
|
|
|
mpt_lprt(mpt, MPT_PRT_DEBUG, " Callback for %d\n",
|
|
cgd->ccb_h.target_id);
|
|
|
|
RAID_VOL_FOREACH(mpt, mpt_vol) {
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
if (mpt_vol->config_page->VolumeID
|
|
== cgd->ccb_h.target_id) {
|
|
mpt_adjust_queue_depth(mpt, mpt_vol, path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
mpt_raid_probe(struct mpt_softc *mpt)
|
|
{
|
|
if (mpt->ioc_page2 == NULL || mpt->ioc_page2->MaxPhysDisks == 0) {
|
|
return (ENODEV);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mpt_raid_attach(struct mpt_softc *mpt)
|
|
{
|
|
struct ccb_setasync csa;
|
|
mpt_handler_t handler;
|
|
int error;
|
|
|
|
mpt_callout_init(&mpt->raid_timer);
|
|
|
|
handler.reply_handler = mpt_raid_reply_handler;
|
|
error = mpt_register_handler(mpt, MPT_HANDLER_REPLY, handler,
|
|
&raid_handler_id);
|
|
if (error != 0)
|
|
goto cleanup;
|
|
|
|
error = mpt_spawn_raid_thread(mpt);
|
|
if (error != 0) {
|
|
mpt_prt(mpt, "Unable to spawn RAID thread!\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
xpt_setup_ccb(&csa.ccb_h, mpt->path, /*priority*/5);
|
|
csa.ccb_h.func_code = XPT_SASYNC_CB;
|
|
csa.event_enable = AC_FOUND_DEVICE;
|
|
csa.callback = mpt_raid_async;
|
|
csa.callback_arg = mpt;
|
|
MPTLOCK_2_CAMLOCK(mpt);
|
|
xpt_action((union ccb *)&csa);
|
|
CAMLOCK_2_MPTLOCK(mpt);
|
|
if (csa.ccb_h.status != CAM_REQ_CMP) {
|
|
mpt_prt(mpt, "mpt_raid_attach: Unable to register "
|
|
"CAM async handler.\n");
|
|
}
|
|
|
|
mpt_raid_sysctl_attach(mpt);
|
|
return (0);
|
|
cleanup:
|
|
mpt_raid_detach(mpt);
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
mpt_raid_detach(struct mpt_softc *mpt)
|
|
{
|
|
struct ccb_setasync csa;
|
|
mpt_handler_t handler;
|
|
|
|
callout_stop(&mpt->raid_timer);
|
|
mpt_terminate_raid_thread(mpt);
|
|
|
|
handler.reply_handler = mpt_raid_reply_handler;
|
|
mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler,
|
|
raid_handler_id);
|
|
xpt_setup_ccb(&csa.ccb_h, mpt->path, /*priority*/5);
|
|
csa.ccb_h.func_code = XPT_SASYNC_CB;
|
|
csa.event_enable = 0;
|
|
csa.callback = mpt_raid_async;
|
|
csa.callback_arg = mpt;
|
|
MPTLOCK_2_CAMLOCK(mpt);
|
|
xpt_action((union ccb *)&csa);
|
|
CAMLOCK_2_MPTLOCK(mpt);
|
|
}
|
|
|
|
static void
|
|
mpt_raid_ioc_reset(struct mpt_softc *mpt, int type)
|
|
{
|
|
/* Nothing to do yet. */
|
|
}
|
|
|
|
static const char *raid_event_txt[] =
|
|
{
|
|
"Volume Created",
|
|
"Volume Deleted",
|
|
"Volume Settings Changed",
|
|
"Volume Status Changed",
|
|
"Volume Physical Disk Membership Changed",
|
|
"Physical Disk Created",
|
|
"Physical Disk Deleted",
|
|
"Physical Disk Settings Changed",
|
|
"Physical Disk Status Changed",
|
|
"Domain Validation Required",
|
|
"SMART Data Received",
|
|
"Replace Action Started",
|
|
};
|
|
|
|
static int
|
|
mpt_raid_event(struct mpt_softc *mpt, request_t *req,
|
|
MSG_EVENT_NOTIFY_REPLY *msg)
|
|
{
|
|
EVENT_DATA_RAID *raid_event;
|
|
struct mpt_raid_volume *mpt_vol;
|
|
struct mpt_raid_disk *mpt_disk;
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
int i;
|
|
int print_event;
|
|
|
|
if (msg->Event != MPI_EVENT_INTEGRATED_RAID)
|
|
return (/*handled*/0);
|
|
|
|
raid_event = (EVENT_DATA_RAID *)&msg->Data;
|
|
|
|
mpt_vol = NULL;
|
|
vol_pg = NULL;
|
|
if (mpt->raid_volumes != NULL && mpt->ioc_page2 != NULL) {
|
|
for (i = 0; i < mpt->ioc_page2->MaxVolumes; i++) {
|
|
mpt_vol = &mpt->raid_volumes[i];
|
|
vol_pg = mpt_vol->config_page;
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
if (vol_pg->VolumeID == raid_event->VolumeID
|
|
&& vol_pg->VolumeBus == raid_event->VolumeBus)
|
|
break;
|
|
}
|
|
if (i >= mpt->ioc_page2->MaxVolumes) {
|
|
mpt_vol = NULL;
|
|
vol_pg = NULL;
|
|
}
|
|
}
|
|
|
|
mpt_disk = NULL;
|
|
if (raid_event->PhysDiskNum != 0xFF
|
|
&& mpt->raid_disks != NULL) {
|
|
mpt_disk = mpt->raid_disks
|
|
+ raid_event->PhysDiskNum;
|
|
if ((mpt_disk->flags & MPT_RDF_ACTIVE) == 0)
|
|
mpt_disk = NULL;
|
|
}
|
|
|
|
print_event = 1;
|
|
switch(raid_event->ReasonCode) {
|
|
case MPI_EVENT_RAID_RC_VOLUME_CREATED:
|
|
case MPI_EVENT_RAID_RC_VOLUME_DELETED:
|
|
break;
|
|
case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED:
|
|
if (mpt_vol != NULL) {
|
|
if ((mpt_vol->flags & MPT_RVF_UP2DATE) != 0) {
|
|
mpt_vol->flags &= ~MPT_RVF_UP2DATE;
|
|
} else {
|
|
/*
|
|
* Coalesce status messages into one
|
|
* per background run of our RAID thread.
|
|
* This removes "spurious" status messages
|
|
* from our output.
|
|
*/
|
|
print_event = 0;
|
|
}
|
|
}
|
|
break;
|
|
case MPI_EVENT_RAID_RC_VOLUME_SETTINGS_CHANGED:
|
|
case MPI_EVENT_RAID_RC_VOLUME_PHYSDISK_CHANGED:
|
|
mpt->raid_rescan++;
|
|
if (mpt_vol != NULL)
|
|
mpt_vol->flags &= ~(MPT_RVF_UP2DATE|MPT_RVF_ANNOUNCED);
|
|
break;
|
|
case MPI_EVENT_RAID_RC_PHYSDISK_CREATED:
|
|
case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
|
|
mpt->raid_rescan++;
|
|
break;
|
|
case MPI_EVENT_RAID_RC_PHYSDISK_SETTINGS_CHANGED:
|
|
case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED:
|
|
mpt->raid_rescan++;
|
|
if (mpt_disk != NULL)
|
|
mpt_disk->flags &= ~MPT_RDF_UP2DATE;
|
|
break;
|
|
case MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED:
|
|
mpt->raid_rescan++;
|
|
break;
|
|
case MPI_EVENT_RAID_RC_SMART_DATA:
|
|
case MPI_EVENT_RAID_RC_REPLACE_ACTION_STARTED:
|
|
break;
|
|
}
|
|
|
|
if (print_event) {
|
|
if (mpt_disk != NULL) {
|
|
mpt_disk_prt(mpt, mpt_disk, "");
|
|
} else if (mpt_vol != NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol, "");
|
|
} else {
|
|
mpt_prt(mpt, "Volume(%d:%d", raid_event->VolumeBus,
|
|
raid_event->VolumeID);
|
|
|
|
if (raid_event->PhysDiskNum != 0xFF)
|
|
mpt_prtc(mpt, ":%d): ",
|
|
raid_event->PhysDiskNum);
|
|
else
|
|
mpt_prtc(mpt, "): ");
|
|
}
|
|
|
|
if (raid_event->ReasonCode >= NUM_ELEMENTS(raid_event_txt))
|
|
mpt_prtc(mpt, "Unhandled RaidEvent %#x\n",
|
|
raid_event->ReasonCode);
|
|
else
|
|
mpt_prtc(mpt, "%s\n",
|
|
raid_event_txt[raid_event->ReasonCode]);
|
|
}
|
|
|
|
if (raid_event->ReasonCode == MPI_EVENT_RAID_RC_SMART_DATA) {
|
|
/* XXX Use CAM's print sense for this... */
|
|
if (mpt_disk != NULL)
|
|
mpt_disk_prt(mpt, mpt_disk, "");
|
|
else
|
|
mpt_prt(mpt, "Volume(%d:%d:%d: ",
|
|
raid_event->VolumeBus, raid_event->VolumeID,
|
|
raid_event->PhysDiskNum);
|
|
mpt_prtc(mpt, "ASC 0x%x, ASCQ 0x%x)\n",
|
|
raid_event->ASC, raid_event->ASCQ);
|
|
}
|
|
|
|
mpt_raid_wakeup(mpt);
|
|
return (/*handled*/1);
|
|
}
|
|
|
|
static void
|
|
mpt_raid_shutdown(struct mpt_softc *mpt)
|
|
{
|
|
struct mpt_raid_volume *mpt_vol;
|
|
|
|
if (mpt->raid_mwce_setting != MPT_RAID_MWCE_REBUILD_ONLY) {
|
|
return;
|
|
}
|
|
|
|
mpt->raid_mwce_setting = MPT_RAID_MWCE_OFF;
|
|
RAID_VOL_FOREACH(mpt, mpt_vol) {
|
|
mpt_verify_mwce(mpt, mpt_vol);
|
|
}
|
|
}
|
|
|
|
static int
|
|
mpt_raid_reply_handler(struct mpt_softc *mpt, request_t *req,
|
|
uint32_t reply_desc, MSG_DEFAULT_REPLY *reply_frame)
|
|
{
|
|
int free_req;
|
|
|
|
if (req == NULL)
|
|
return (/*free_reply*/TRUE);
|
|
|
|
free_req = TRUE;
|
|
if (reply_frame != NULL)
|
|
free_req = mpt_raid_reply_frame_handler(mpt, req, reply_frame);
|
|
#ifdef NOTYET
|
|
else if (req->ccb != NULL) {
|
|
/* Complete Quiesce CCB with error... */
|
|
}
|
|
#endif
|
|
|
|
req->state &= ~REQ_STATE_QUEUED;
|
|
req->state |= REQ_STATE_DONE;
|
|
TAILQ_REMOVE(&mpt->request_pending_list, req, links);
|
|
|
|
if ((req->state & REQ_STATE_NEED_WAKEUP) != 0) {
|
|
wakeup(req);
|
|
} else if (free_req) {
|
|
mpt_free_request(mpt, req);
|
|
}
|
|
|
|
return (/*free_reply*/TRUE);
|
|
}
|
|
|
|
/*
|
|
* Parse additional completion information in the reply
|
|
* frame for RAID I/O requests.
|
|
*/
|
|
static int
|
|
mpt_raid_reply_frame_handler(struct mpt_softc *mpt, request_t *req,
|
|
MSG_DEFAULT_REPLY *reply_frame)
|
|
{
|
|
MSG_RAID_ACTION_REPLY *reply;
|
|
struct mpt_raid_action_result *action_result;
|
|
MSG_RAID_ACTION_REQUEST *rap;
|
|
|
|
reply = (MSG_RAID_ACTION_REPLY *)reply_frame;
|
|
req->IOCStatus = le16toh(reply->IOCStatus);
|
|
rap = (MSG_RAID_ACTION_REQUEST *)req->req_vbuf;
|
|
|
|
switch (rap->Action) {
|
|
case MPI_RAID_ACTION_QUIESCE_PHYS_IO:
|
|
/*
|
|
* Parse result, call mpt_start with ccb,
|
|
* release device queue.
|
|
* COWWWWW
|
|
*/
|
|
break;
|
|
case MPI_RAID_ACTION_ENABLE_PHYS_IO:
|
|
/*
|
|
* Need additional state for transition to enabled to
|
|
* protect against attempts to disable??
|
|
*/
|
|
break;
|
|
default:
|
|
action_result = REQ_TO_RAID_ACTION_RESULT(req);
|
|
memcpy(&action_result->action_data, &reply->ActionData,
|
|
sizeof(action_result->action_data));
|
|
action_result->action_status = reply->ActionStatus;
|
|
break;
|
|
}
|
|
|
|
return (/*Free Request*/TRUE);
|
|
}
|
|
|
|
/*
|
|
* Utiltity routine to perform a RAID action command;
|
|
*/
|
|
int
|
|
mpt_issue_raid_req(struct mpt_softc *mpt, struct mpt_raid_volume *vol,
|
|
struct mpt_raid_disk *disk, request_t *req, u_int Action,
|
|
uint32_t ActionDataWord, bus_addr_t addr, bus_size_t len,
|
|
int write, int wait)
|
|
{
|
|
MSG_RAID_ACTION_REQUEST *rap;
|
|
SGE_SIMPLE32 *se;
|
|
|
|
rap = req->req_vbuf;
|
|
memset(rap, 0, sizeof *rap);
|
|
rap->Action = Action;
|
|
rap->ActionDataWord = ActionDataWord;
|
|
rap->Function = MPI_FUNCTION_RAID_ACTION;
|
|
rap->VolumeID = vol->config_page->VolumeID;
|
|
rap->VolumeBus = vol->config_page->VolumeBus;
|
|
if (disk != 0)
|
|
rap->PhysDiskNum = disk->config_page.PhysDiskNum;
|
|
else
|
|
rap->PhysDiskNum = 0xFF;
|
|
se = (SGE_SIMPLE32 *)&rap->ActionDataSGE;
|
|
se->Address = addr;
|
|
MPI_pSGE_SET_LENGTH(se, len);
|
|
MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
|
|
MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER |
|
|
MPI_SGE_FLAGS_END_OF_LIST |
|
|
write ? MPI_SGE_FLAGS_HOST_TO_IOC : MPI_SGE_FLAGS_IOC_TO_HOST));
|
|
rap->MsgContext = htole32(req->index | raid_handler_id);
|
|
|
|
mpt_check_doorbell(mpt);
|
|
mpt_send_cmd(mpt, req);
|
|
|
|
if (wait) {
|
|
return (mpt_wait_req(mpt, req, REQ_STATE_DONE, REQ_STATE_DONE,
|
|
/*sleep_ok*/FALSE, /*time_ms*/2000));
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*************************** RAID Status Monitoring ***************************/
|
|
static int
|
|
mpt_spawn_raid_thread(struct mpt_softc *mpt)
|
|
{
|
|
int error;
|
|
|
|
/*
|
|
* Freeze out any CAM transactions until our thread
|
|
* is able to run at least once. We need to update
|
|
* our RAID pages before acception I/O or we may
|
|
* reject I/O to an ID we later determine is for a
|
|
* hidden physdisk.
|
|
*/
|
|
xpt_freeze_simq(mpt->phydisk_sim, 1);
|
|
error = mpt_kthread_create(mpt_raid_thread, mpt,
|
|
&mpt->raid_thread, /*flags*/0, /*altstack*/0,
|
|
"mpt_raid%d", mpt->unit);
|
|
if (error != 0)
|
|
xpt_release_simq(mpt->phydisk_sim, /*run_queue*/FALSE);
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
mpt_terminate_raid_thread(struct mpt_softc *mpt)
|
|
{
|
|
|
|
if (mpt->raid_thread == NULL) {
|
|
return;
|
|
}
|
|
mpt->shutdwn_raid = 1;
|
|
wakeup(mpt->raid_volumes);
|
|
/*
|
|
* Sleep on a slightly different location
|
|
* for this interlock just for added safety.
|
|
*/
|
|
mpt_sleep(mpt, &mpt->raid_thread, PUSER, "thtrm", 0);
|
|
}
|
|
|
|
static void
|
|
mpt_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
|
|
{
|
|
xpt_free_path(ccb->ccb_h.path);
|
|
free(ccb, M_DEVBUF);
|
|
}
|
|
|
|
static void
|
|
mpt_raid_thread(void *arg)
|
|
{
|
|
struct mpt_softc *mpt;
|
|
int firstrun;
|
|
|
|
#if __FreeBSD_version >= 500000
|
|
mtx_lock(&Giant);
|
|
#endif
|
|
mpt = (struct mpt_softc *)arg;
|
|
firstrun = 1;
|
|
MPT_LOCK(mpt);
|
|
while (mpt->shutdwn_raid == 0) {
|
|
|
|
if (mpt->raid_wakeup == 0) {
|
|
mpt_sleep(mpt, &mpt->raid_volumes, PUSER, "idle", 0);
|
|
continue;
|
|
}
|
|
|
|
mpt->raid_wakeup = 0;
|
|
|
|
mpt_refresh_raid_data(mpt);
|
|
|
|
/*
|
|
* Now that we have our first snapshot of RAID data,
|
|
* allow CAM to access our physical disk bus.
|
|
*/
|
|
if (firstrun) {
|
|
firstrun = 0;
|
|
xpt_release_simq(mpt->phydisk_sim, /*run_queue*/TRUE);
|
|
}
|
|
|
|
if (mpt->raid_rescan != 0) {
|
|
union ccb *ccb;
|
|
struct cam_path *path;
|
|
int error;
|
|
|
|
mpt->raid_rescan = 0;
|
|
|
|
ccb = malloc(sizeof(*ccb), M_DEVBUF, M_WAITOK);
|
|
error = xpt_create_path(&path, xpt_periph,
|
|
cam_sim_path(mpt->phydisk_sim),
|
|
CAM_TARGET_WILDCARD,
|
|
CAM_LUN_WILDCARD);
|
|
if (error != CAM_REQ_CMP) {
|
|
free(ccb, M_DEVBUF);
|
|
mpt_prt(mpt, "Unable to rescan RAID Bus!\n");
|
|
} else {
|
|
xpt_setup_ccb(&ccb->ccb_h, path, /*priority*/5);
|
|
ccb->ccb_h.func_code = XPT_SCAN_BUS;
|
|
ccb->ccb_h.cbfcnp = mpt_cam_rescan_callback;
|
|
ccb->crcn.flags = CAM_FLAG_NONE;
|
|
xpt_action(ccb);
|
|
}
|
|
}
|
|
}
|
|
mpt->raid_thread = NULL;
|
|
wakeup(&mpt->raid_thread);
|
|
MPT_UNLOCK(mpt);
|
|
#if __FreeBSD_version >= 500000
|
|
mtx_unlock(&Giant);
|
|
#endif
|
|
kthread_exit(0);
|
|
}
|
|
|
|
cam_status
|
|
mpt_raid_quiesce_disk(struct mpt_softc *mpt, struct mpt_raid_disk *mpt_disk,
|
|
request_t *req)
|
|
{
|
|
union ccb *ccb;
|
|
|
|
ccb = req->ccb;
|
|
if ((mpt_disk->flags & MPT_RDF_QUIESCED) != 0)
|
|
return (CAM_REQ_CMP);
|
|
|
|
if ((mpt_disk->flags & MPT_RDF_QUIESCING) == 0) {
|
|
int rv;
|
|
|
|
mpt_disk->flags |= MPT_RDF_QUIESCING;
|
|
xpt_freeze_devq(ccb->ccb_h.path, 1);
|
|
|
|
rv = mpt_issue_raid_req(mpt, mpt_disk->volume, mpt_disk, req,
|
|
MPI_RAID_ACTION_QUIESCE_PHYS_IO,
|
|
/*ActionData*/0, /*addr*/0,
|
|
/*len*/0, /*write*/FALSE,
|
|
/*wait*/FALSE);
|
|
if (rv != 0)
|
|
return (CAM_REQ_CMP_ERR);
|
|
|
|
ccb->ccb_h.timeout_ch =
|
|
timeout(mpt_raid_quiesce_timeout, (caddr_t)ccb, 5 * hz);
|
|
#if 0
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_disk_prt(mpt, mpt_disk, "mpt_raid_quiesce_disk: "
|
|
"Quiece Timed-out\n");
|
|
xpt_release_devq(ccb->ccb_h.path, 1, /*run*/0);
|
|
return (CAM_REQ_CMP_ERR);
|
|
}
|
|
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv != 0
|
|
|| REQ_IOCSTATUS(req) != MPI_IOCSTATUS_SUCCESS
|
|
|| (ar->action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS)) {
|
|
mpt_disk_prt(mpt, mpt_disk, "Quiece Failed"
|
|
"%d:%x:%x\n", rv, req->IOCStatus,
|
|
ar->action_status);
|
|
xpt_release_devq(ccb->ccb_h.path, 1, /*run*/0);
|
|
return (CAM_REQ_CMP_ERR);
|
|
}
|
|
#endif
|
|
return (CAM_REQ_INPROG);
|
|
}
|
|
return (CAM_REQUEUE_REQ);
|
|
}
|
|
|
|
/* XXX Ignores that there may be multiple busses/IOCs involved. */
|
|
cam_status
|
|
mpt_map_physdisk(struct mpt_softc *mpt, union ccb *ccb, u_int *tgt)
|
|
{
|
|
struct mpt_raid_disk *mpt_disk;
|
|
|
|
mpt_disk = mpt->raid_disks + ccb->ccb_h.target_id;
|
|
if (ccb->ccb_h.target_id < mpt->raid_max_disks
|
|
&& (mpt_disk->flags & MPT_RDF_ACTIVE) != 0) {
|
|
|
|
*tgt = mpt_disk->config_page.PhysDiskID;
|
|
return (0);
|
|
}
|
|
mpt_lprt(mpt, MPT_PRT_DEBUG, "mpt_map_physdisk(%d) - Not Active\n",
|
|
ccb->ccb_h.target_id);
|
|
return (-1);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
mpt_enable_vol(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol,
|
|
int enable)
|
|
{
|
|
request_t *req;
|
|
struct mpt_raid_action_result *ar;
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
int enabled;
|
|
int rv;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
enabled = vol_pg->VolumeStatus.Flags & MPI_RAIDVOL0_STATUS_FLAG_ENABLED;
|
|
|
|
/*
|
|
* If the setting matches the configuration,
|
|
* there is nothing to do.
|
|
*/
|
|
if ((enabled && enable)
|
|
|| (!enabled && !enable))
|
|
return;
|
|
|
|
req = mpt_get_request(mpt, /*sleep_ok*/TRUE);
|
|
if (req == NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol,
|
|
"mpt_enable_vol: Get request failed!\n");
|
|
return;
|
|
}
|
|
|
|
rv = mpt_issue_raid_req(mpt, mpt_vol, /*disk*/NULL, req,
|
|
enable ? MPI_RAID_ACTION_ENABLE_VOLUME
|
|
: MPI_RAID_ACTION_DISABLE_VOLUME,
|
|
/*data*/0, /*addr*/0, /*len*/0,
|
|
/*write*/FALSE, /*wait*/TRUE);
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_enable_vol: "
|
|
"%s Volume Timed-out\n",
|
|
enable ? "Enable" : "Disable");
|
|
return;
|
|
}
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv != 0
|
|
|| REQ_IOCSTATUS(req) != MPI_IOCSTATUS_SUCCESS
|
|
|| (ar->action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS)) {
|
|
mpt_vol_prt(mpt, mpt_vol, "%s Volume Failed: %d:%x:%x\n",
|
|
enable ? "Enable" : "Disable",
|
|
rv, req->IOCStatus, ar->action_status);
|
|
}
|
|
|
|
mpt_free_request(mpt, req);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
mpt_verify_mwce(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol)
|
|
{
|
|
request_t *req;
|
|
struct mpt_raid_action_result *ar;
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
uint32_t data;
|
|
int rv;
|
|
int resyncing;
|
|
int mwce;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
resyncing = vol_pg->VolumeStatus.Flags
|
|
& MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS;
|
|
mwce = vol_pg->VolumeSettings.Settings
|
|
& MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE;
|
|
|
|
/*
|
|
* If the setting matches the configuration,
|
|
* there is nothing to do.
|
|
*/
|
|
switch (mpt->raid_mwce_setting) {
|
|
case MPT_RAID_MWCE_REBUILD_ONLY:
|
|
if ((resyncing && mwce) || (!resyncing && !mwce)) {
|
|
return;
|
|
}
|
|
mpt_vol->flags ^= MPT_RVF_WCE_CHANGED;
|
|
if ((mpt_vol->flags & MPT_RVF_WCE_CHANGED) == 0) {
|
|
/*
|
|
* Wait one more status update to see if
|
|
* resyncing gets enabled. It gets disabled
|
|
* temporarilly when WCE is changed.
|
|
*/
|
|
return;
|
|
}
|
|
break;
|
|
case MPT_RAID_MWCE_ON:
|
|
if (mwce)
|
|
return;
|
|
break;
|
|
case MPT_RAID_MWCE_OFF:
|
|
if (!mwce)
|
|
return;
|
|
break;
|
|
case MPT_RAID_MWCE_NC:
|
|
return;
|
|
}
|
|
|
|
req = mpt_get_request(mpt, /*sleep_ok*/TRUE);
|
|
if (req == NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol,
|
|
"mpt_verify_mwce: Get request failed!\n");
|
|
return;
|
|
}
|
|
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE;
|
|
memcpy(&data, &vol_pg->VolumeSettings, sizeof(data));
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE;
|
|
rv = mpt_issue_raid_req(mpt, mpt_vol, /*disk*/NULL, req,
|
|
MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
|
|
data, /*addr*/0, /*len*/0,
|
|
/*write*/FALSE, /*wait*/TRUE);
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_verify_mwce: "
|
|
"Write Cache Enable Timed-out\n");
|
|
return;
|
|
}
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv != 0
|
|
|| REQ_IOCSTATUS(req) != MPI_IOCSTATUS_SUCCESS
|
|
|| (ar->action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS)) {
|
|
mpt_vol_prt(mpt, mpt_vol, "Write Cache Enable Failed: "
|
|
"%d:%x:%x\n", rv, req->IOCStatus,
|
|
ar->action_status);
|
|
} else {
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE;
|
|
}
|
|
mpt_free_request(mpt, req);
|
|
}
|
|
|
|
static void
|
|
mpt_verify_resync_rate(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol)
|
|
{
|
|
request_t *req;
|
|
struct mpt_raid_action_result *ar;
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
u_int prio;
|
|
int rv;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
|
|
if (mpt->raid_resync_rate == MPT_RAID_RESYNC_RATE_NC)
|
|
return;
|
|
|
|
/*
|
|
* If the current RAID resync rate does not
|
|
* match our configured rate, update it.
|
|
*/
|
|
prio = vol_pg->VolumeSettings.Settings
|
|
& MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC;
|
|
if (vol_pg->ResyncRate != 0
|
|
&& vol_pg->ResyncRate != mpt->raid_resync_rate) {
|
|
|
|
req = mpt_get_request(mpt, /*sleep_ok*/TRUE);
|
|
if (req == NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_verify_resync_rate: "
|
|
"Get request failed!\n");
|
|
return;
|
|
}
|
|
|
|
rv = mpt_issue_raid_req(mpt, mpt_vol, /*disk*/NULL, req,
|
|
MPI_RAID_ACTION_SET_RESYNC_RATE,
|
|
mpt->raid_resync_rate, /*addr*/0,
|
|
/*len*/0, /*write*/FALSE, /*wait*/TRUE);
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_data: "
|
|
"Resync Rate Setting Timed-out\n");
|
|
return;
|
|
}
|
|
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv != 0
|
|
|| REQ_IOCSTATUS(req) != MPI_IOCSTATUS_SUCCESS
|
|
|| (ar->action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS)) {
|
|
mpt_vol_prt(mpt, mpt_vol, "Resync Rate Setting Failed: "
|
|
"%d:%x:%x\n", rv, req->IOCStatus,
|
|
ar->action_status);
|
|
} else
|
|
vol_pg->ResyncRate = mpt->raid_resync_rate;
|
|
mpt_free_request(mpt, req);
|
|
} else if ((prio && mpt->raid_resync_rate < 128)
|
|
|| (!prio && mpt->raid_resync_rate >= 128)) {
|
|
uint32_t data;
|
|
|
|
req = mpt_get_request(mpt, /*sleep_ok*/TRUE);
|
|
if (req == NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_verify_resync_rate: "
|
|
"Get request failed!\n");
|
|
return;
|
|
}
|
|
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC;
|
|
memcpy(&data, &vol_pg->VolumeSettings, sizeof(data));
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC;
|
|
rv = mpt_issue_raid_req(mpt, mpt_vol, /*disk*/NULL, req,
|
|
MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
|
|
data, /*addr*/0, /*len*/0,
|
|
/*write*/FALSE, /*wait*/TRUE);
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_data: "
|
|
"Resync Rate Setting Timed-out\n");
|
|
return;
|
|
}
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv != 0
|
|
|| REQ_IOCSTATUS(req) != MPI_IOCSTATUS_SUCCESS
|
|
|| (ar->action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS)) {
|
|
mpt_vol_prt(mpt, mpt_vol, "Resync Rate Setting Failed: "
|
|
"%d:%x:%x\n", rv, req->IOCStatus,
|
|
ar->action_status);
|
|
} else {
|
|
vol_pg->VolumeSettings.Settings ^=
|
|
MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC;
|
|
}
|
|
|
|
mpt_free_request(mpt, req);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpt_adjust_queue_depth(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol,
|
|
struct cam_path *path)
|
|
{
|
|
struct ccb_relsim crs;
|
|
|
|
xpt_setup_ccb(&crs.ccb_h, path, /*priority*/5);
|
|
crs.ccb_h.func_code = XPT_REL_SIMQ;
|
|
crs.release_flags = RELSIM_ADJUST_OPENINGS;
|
|
crs.openings = mpt->raid_queue_depth;
|
|
xpt_action((union ccb *)&crs);
|
|
if (crs.ccb_h.status != CAM_REQ_CMP)
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_adjust_queue_depth failed "
|
|
"with CAM status %#x\n", crs.ccb_h.status);
|
|
}
|
|
|
|
static void
|
|
mpt_announce_vol(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol)
|
|
{
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
u_int i;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
mpt_vol_prt(mpt, mpt_vol, "Settings (");
|
|
for (i = 1; i <= 0x8000; i <<= 1) {
|
|
switch (vol_pg->VolumeSettings.Settings & i) {
|
|
case MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE:
|
|
mpt_prtc(mpt, " Member-WCE");
|
|
break;
|
|
case MPI_RAIDVOL0_SETTING_OFFLINE_ON_SMART:
|
|
mpt_prtc(mpt, " Offline-On-SMART-Err");
|
|
break;
|
|
case MPI_RAIDVOL0_SETTING_AUTO_CONFIGURE:
|
|
mpt_prtc(mpt, " Hot-Plug-Spares");
|
|
break;
|
|
case MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC:
|
|
mpt_prtc(mpt, " High-Priority-ReSync");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
mpt_prtc(mpt, " )\n");
|
|
if (vol_pg->VolumeSettings.HotSparePool != 0) {
|
|
mpt_vol_prt(mpt, mpt_vol, "Using Spare Pool%s",
|
|
powerof2(vol_pg->VolumeSettings.HotSparePool)
|
|
? ":" : "s:");
|
|
for (i = 0; i < 8; i++) {
|
|
u_int mask;
|
|
|
|
mask = 0x1 << i;
|
|
if ((vol_pg->VolumeSettings.HotSparePool & mask) == 0)
|
|
continue;
|
|
mpt_prtc(mpt, " %d", i);
|
|
}
|
|
mpt_prtc(mpt, "\n");
|
|
}
|
|
mpt_vol_prt(mpt, mpt_vol, "%d Members:\n", vol_pg->NumPhysDisks);
|
|
for (i = 0; i < vol_pg->NumPhysDisks; i++){
|
|
struct mpt_raid_disk *mpt_disk;
|
|
CONFIG_PAGE_RAID_PHYS_DISK_0 *disk_pg;
|
|
|
|
mpt_disk = mpt->raid_disks
|
|
+ vol_pg->PhysDisk[i].PhysDiskNum;
|
|
disk_pg = &mpt_disk->config_page;
|
|
mpt_prtc(mpt, " ");
|
|
mpt_prtc(mpt, "(%s:%d:%d): ", device_get_nameunit(mpt->dev),
|
|
disk_pg->PhysDiskBus, disk_pg->PhysDiskID);
|
|
if (vol_pg->VolumeType == MPI_RAID_VOL_TYPE_IM)
|
|
mpt_prtc(mpt, "%s\n",
|
|
mpt_disk->member_number == 0
|
|
? "Primary" : "Secondary");
|
|
else
|
|
mpt_prtc(mpt, "Stripe Position %d\n",
|
|
mpt_disk->member_number);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpt_announce_disk(struct mpt_softc *mpt, struct mpt_raid_disk *mpt_disk)
|
|
{
|
|
CONFIG_PAGE_RAID_PHYS_DISK_0 *disk_pg;
|
|
u_int i;
|
|
|
|
disk_pg = &mpt_disk->config_page;
|
|
mpt_disk_prt(mpt, mpt_disk,
|
|
"Physical (%s:%d:%d), Pass-thru (%s:%d:%d)\n",
|
|
device_get_nameunit(mpt->dev), disk_pg->PhysDiskBus,
|
|
disk_pg->PhysDiskID, device_get_nameunit(mpt->dev),
|
|
/*bus*/1, mpt_disk - mpt->raid_disks);
|
|
|
|
if (disk_pg->PhysDiskSettings.HotSparePool == 0)
|
|
return;
|
|
mpt_disk_prt(mpt, mpt_disk, "Member of Hot Spare Pool%s",
|
|
powerof2(disk_pg->PhysDiskSettings.HotSparePool)
|
|
? ":" : "s:");
|
|
for (i = 0; i < 8; i++) {
|
|
u_int mask;
|
|
|
|
mask = 0x1 << i;
|
|
if ((disk_pg->PhysDiskSettings.HotSparePool & mask) == 0)
|
|
continue;
|
|
mpt_prtc(mpt, " %d", i);
|
|
}
|
|
mpt_prtc(mpt, "\n");
|
|
}
|
|
|
|
static void
|
|
mpt_refresh_raid_disk(struct mpt_softc *mpt, struct mpt_raid_disk *mpt_disk,
|
|
IOC_3_PHYS_DISK *ioc_disk)
|
|
{
|
|
int rv;
|
|
|
|
rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK,
|
|
/*PageNumber*/0, ioc_disk->PhysDiskNum,
|
|
&mpt_disk->config_page.Header,
|
|
/*sleep_ok*/TRUE, /*timeout_ms*/5000);
|
|
if (rv != 0) {
|
|
mpt_prt(mpt, "mpt_refresh_raid_disk: "
|
|
"Failed to read RAID Disk Hdr(%d)\n",
|
|
ioc_disk->PhysDiskNum);
|
|
return;
|
|
}
|
|
rv = mpt_read_cur_cfg_page(mpt, ioc_disk->PhysDiskNum,
|
|
&mpt_disk->config_page.Header,
|
|
sizeof(mpt_disk->config_page),
|
|
/*sleep_ok*/TRUE, /*timeout_ms*/5000);
|
|
if (rv != 0)
|
|
mpt_prt(mpt, "mpt_refresh_raid_disk: "
|
|
"Failed to read RAID Disk Page(%d)\n",
|
|
ioc_disk->PhysDiskNum);
|
|
}
|
|
|
|
static void
|
|
mpt_refresh_raid_vol(struct mpt_softc *mpt, struct mpt_raid_volume *mpt_vol,
|
|
CONFIG_PAGE_IOC_2_RAID_VOL *ioc_vol)
|
|
{
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
struct mpt_raid_action_result *ar;
|
|
request_t *req;
|
|
int rv;
|
|
int i;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
mpt_vol->flags &= ~MPT_RVF_UP2DATE;
|
|
rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_RAID_VOLUME,
|
|
/*PageNumber*/0, ioc_vol->VolumePageNumber,
|
|
&vol_pg->Header, /*sleep_ok*/TRUE,
|
|
/*timeout_ms*/5000);
|
|
if (rv != 0) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_vol: "
|
|
"Failed to read RAID Vol Hdr(%d)\n",
|
|
ioc_vol->VolumePageNumber);
|
|
return;
|
|
}
|
|
rv = mpt_read_cur_cfg_page(mpt, ioc_vol->VolumePageNumber,
|
|
&vol_pg->Header, mpt->raid_page0_len,
|
|
/*sleep_ok*/TRUE, /*timeout_ms*/5000);
|
|
if (rv != 0) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_vol: "
|
|
"Failed to read RAID Vol Page(%d)\n",
|
|
ioc_vol->VolumePageNumber);
|
|
return;
|
|
}
|
|
mpt_vol->flags |= MPT_RVF_ACTIVE;
|
|
|
|
/* Update disk entry array data. */
|
|
for (i = 0; i < vol_pg->NumPhysDisks; i++) {
|
|
struct mpt_raid_disk *mpt_disk;
|
|
|
|
mpt_disk = mpt->raid_disks + vol_pg->PhysDisk[i].PhysDiskNum;
|
|
mpt_disk->volume = mpt_vol;
|
|
mpt_disk->member_number = vol_pg->PhysDisk[i].PhysDiskMap;
|
|
if (vol_pg->VolumeType == MPI_RAID_VOL_TYPE_IM)
|
|
mpt_disk->member_number--;
|
|
}
|
|
|
|
if ((vol_pg->VolumeStatus.Flags
|
|
& MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) == 0)
|
|
return;
|
|
|
|
req = mpt_get_request(mpt, /*sleep_ok*/TRUE);
|
|
if (req == NULL) {
|
|
mpt_vol_prt(mpt, mpt_vol,
|
|
"mpt_refresh_raid_vol: Get request failed!\n");
|
|
return;
|
|
}
|
|
rv = mpt_issue_raid_req(mpt, mpt_vol, /*disk*/NULL, req,
|
|
MPI_RAID_ACTION_INDICATOR_STRUCT,
|
|
/*ActionWord*/0, /*addr*/0, /*len*/0,
|
|
/*write*/FALSE, /*wait*/TRUE);
|
|
if (rv == ETIMEDOUT) {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_vol: "
|
|
"Progress indicator fetch timedout!\n");
|
|
return;
|
|
}
|
|
|
|
ar = REQ_TO_RAID_ACTION_RESULT(req);
|
|
if (rv == 0
|
|
&& ar->action_status == MPI_RAID_ACTION_ASTATUS_SUCCESS
|
|
&& REQ_IOCSTATUS(req) == MPI_IOCSTATUS_SUCCESS) {
|
|
memcpy(&mpt_vol->sync_progress,
|
|
&ar->action_data.indicator_struct,
|
|
sizeof(mpt_vol->sync_progress));
|
|
} else {
|
|
mpt_vol_prt(mpt, mpt_vol, "mpt_refresh_raid_vol: "
|
|
"Progress indicator fetch failed!\n");
|
|
}
|
|
mpt_free_request(mpt, req);
|
|
}
|
|
|
|
/*
|
|
* Update in-core information about RAID support. We update any entries
|
|
* that didn't previously exists or have been marked as needing to
|
|
* be updated by our event handler. Interesting changes are displayed
|
|
* to the console.
|
|
*/
|
|
void
|
|
mpt_refresh_raid_data(struct mpt_softc *mpt)
|
|
{
|
|
CONFIG_PAGE_IOC_2_RAID_VOL *ioc_vol;
|
|
CONFIG_PAGE_IOC_2_RAID_VOL *ioc_last_vol;
|
|
IOC_3_PHYS_DISK *ioc_disk;
|
|
IOC_3_PHYS_DISK *ioc_last_disk;
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
size_t len;
|
|
int rv;
|
|
int i;
|
|
u_int nonopt_volumes;
|
|
|
|
if (mpt->ioc_page2 == NULL || mpt->ioc_page3 == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Mark all items as unreferenced by the configuration.
|
|
* This allows us to find, report, and discard stale
|
|
* entries.
|
|
*/
|
|
for (i = 0; i < mpt->ioc_page2->MaxPhysDisks; i++) {
|
|
mpt->raid_disks[i].flags &= ~MPT_RDF_REFERENCED;
|
|
}
|
|
for (i = 0; i < mpt->ioc_page2->MaxVolumes; i++) {
|
|
mpt->raid_volumes[i].flags &= ~MPT_RVF_REFERENCED;
|
|
}
|
|
|
|
/*
|
|
* Get Physical Disk information.
|
|
*/
|
|
len = mpt->ioc_page3->Header.PageLength * sizeof(uint32_t);
|
|
rv = mpt_read_cur_cfg_page(mpt, /*PageAddress*/0,
|
|
&mpt->ioc_page3->Header, len,
|
|
/*sleep_ok*/TRUE, /*timeout_ms*/5000);
|
|
if (rv) {
|
|
mpt_prt(mpt, "mpt_refresh_raid_data: "
|
|
"Failed to read IOC Page 3\n");
|
|
return;
|
|
}
|
|
|
|
ioc_disk = mpt->ioc_page3->PhysDisk;
|
|
ioc_last_disk = ioc_disk + mpt->ioc_page3->NumPhysDisks;
|
|
for (; ioc_disk != ioc_last_disk; ioc_disk++) {
|
|
struct mpt_raid_disk *mpt_disk;
|
|
|
|
mpt_disk = mpt->raid_disks + ioc_disk->PhysDiskNum;
|
|
mpt_disk->flags |= MPT_RDF_REFERENCED;
|
|
if ((mpt_disk->flags & (MPT_RDF_ACTIVE|MPT_RDF_UP2DATE))
|
|
!= (MPT_RDF_ACTIVE|MPT_RDF_UP2DATE)) {
|
|
|
|
mpt_refresh_raid_disk(mpt, mpt_disk, ioc_disk);
|
|
|
|
}
|
|
mpt_disk->flags |= MPT_RDF_ACTIVE;
|
|
mpt->raid_rescan++;
|
|
}
|
|
|
|
/*
|
|
* Refresh volume data.
|
|
*/
|
|
len = mpt->ioc_page2->Header.PageLength * sizeof(uint32_t);
|
|
rv = mpt_read_cur_cfg_page(mpt, /*PageAddress*/0,
|
|
&mpt->ioc_page2->Header, len,
|
|
/*sleep_ok*/TRUE, /*timeout_ms*/5000);
|
|
if (rv) {
|
|
mpt_prt(mpt, "mpt_refresh_raid_data: "
|
|
"Failed to read IOC Page 2\n");
|
|
return;
|
|
}
|
|
|
|
ioc_vol = mpt->ioc_page2->RaidVolume;
|
|
ioc_last_vol = ioc_vol + mpt->ioc_page2->NumActiveVolumes;
|
|
for (;ioc_vol != ioc_last_vol; ioc_vol++) {
|
|
struct mpt_raid_volume *mpt_vol;
|
|
|
|
mpt_vol = mpt->raid_volumes + ioc_vol->VolumePageNumber;
|
|
mpt_vol->flags |= MPT_RVF_REFERENCED;
|
|
vol_pg = mpt_vol->config_page;
|
|
if (vol_pg == NULL)
|
|
continue;
|
|
if (((mpt_vol->flags & (MPT_RVF_ACTIVE|MPT_RVF_UP2DATE))
|
|
!= (MPT_RVF_ACTIVE|MPT_RVF_UP2DATE))
|
|
|| (vol_pg->VolumeStatus.Flags
|
|
& MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) != 0) {
|
|
|
|
mpt_refresh_raid_vol(mpt, mpt_vol, ioc_vol);
|
|
}
|
|
mpt_vol->flags |= MPT_RVF_ACTIVE;
|
|
}
|
|
|
|
nonopt_volumes = 0;
|
|
for (i = 0; i < mpt->ioc_page2->MaxVolumes; i++) {
|
|
struct mpt_raid_volume *mpt_vol;
|
|
uint64_t total;
|
|
uint64_t left;
|
|
int m;
|
|
u_int prio;
|
|
|
|
mpt_vol = &mpt->raid_volumes[i];
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
if ((mpt_vol->flags & (MPT_RVF_REFERENCED|MPT_RVF_ANNOUNCED))
|
|
== MPT_RVF_ANNOUNCED) {
|
|
mpt_vol_prt(mpt, mpt_vol, "No longer configured\n");
|
|
mpt_vol->flags = 0;
|
|
continue;
|
|
}
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_ANNOUNCED) == 0) {
|
|
|
|
mpt_announce_vol(mpt, mpt_vol);
|
|
mpt_vol->flags |= MPT_RVF_ANNOUNCED;
|
|
}
|
|
|
|
if (vol_pg->VolumeStatus.State !=
|
|
MPI_RAIDVOL0_STATUS_STATE_OPTIMAL)
|
|
nonopt_volumes++;
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_UP2DATE) != 0)
|
|
continue;
|
|
|
|
mpt_vol->flags |= MPT_RVF_UP2DATE;
|
|
mpt_vol_prt(mpt, mpt_vol, "%s - %s\n",
|
|
mpt_vol_type(mpt_vol), mpt_vol_state(mpt_vol));
|
|
mpt_verify_mwce(mpt, mpt_vol);
|
|
|
|
if (vol_pg->VolumeStatus.Flags == 0)
|
|
continue;
|
|
|
|
mpt_vol_prt(mpt, mpt_vol, "Status (");
|
|
for (m = 1; m <= 0x80; m <<= 1) {
|
|
switch (vol_pg->VolumeStatus.Flags & m) {
|
|
case MPI_RAIDVOL0_STATUS_FLAG_ENABLED:
|
|
mpt_prtc(mpt, " Enabled");
|
|
break;
|
|
case MPI_RAIDVOL0_STATUS_FLAG_QUIESCED:
|
|
mpt_prtc(mpt, " Quiesced");
|
|
break;
|
|
case MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS:
|
|
mpt_prtc(mpt, " Re-Syncing");
|
|
break;
|
|
case MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE:
|
|
mpt_prtc(mpt, " Inactive");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
mpt_prtc(mpt, " )\n");
|
|
|
|
if ((vol_pg->VolumeStatus.Flags
|
|
& MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) == 0)
|
|
continue;
|
|
|
|
mpt_verify_resync_rate(mpt, mpt_vol);
|
|
|
|
left = u64toh(mpt_vol->sync_progress.BlocksRemaining);
|
|
total = u64toh(mpt_vol->sync_progress.TotalBlocks);
|
|
if (vol_pg->ResyncRate != 0) {
|
|
|
|
prio = ((u_int)vol_pg->ResyncRate * 100000) / 0xFF;
|
|
mpt_vol_prt(mpt, mpt_vol, "Rate %d.%d%%\n",
|
|
prio / 1000, prio % 1000);
|
|
} else {
|
|
prio = vol_pg->VolumeSettings.Settings
|
|
& MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC;
|
|
mpt_vol_prt(mpt, mpt_vol, "%s Priority Re-Sync\n",
|
|
prio ? "High" : "Low");
|
|
}
|
|
#if __FreeBSD_version >= 500000
|
|
mpt_vol_prt(mpt, mpt_vol, "%ju of %ju "
|
|
"blocks remaining\n", (uintmax_t)left,
|
|
(uintmax_t)total);
|
|
#else
|
|
mpt_vol_prt(mpt, mpt_vol, "%llu of %llu "
|
|
"blocks remaining\n", (uint64_t)left,
|
|
(uint64_t)total);
|
|
#endif
|
|
|
|
/* Periodically report on sync progress. */
|
|
mpt_schedule_raid_refresh(mpt);
|
|
}
|
|
|
|
for (i = 0; i < mpt->ioc_page2->MaxPhysDisks; i++) {
|
|
struct mpt_raid_disk *mpt_disk;
|
|
CONFIG_PAGE_RAID_PHYS_DISK_0 *disk_pg;
|
|
int m;
|
|
|
|
mpt_disk = &mpt->raid_disks[i];
|
|
disk_pg = &mpt_disk->config_page;
|
|
|
|
if ((mpt_disk->flags & MPT_RDF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
if ((mpt_disk->flags & (MPT_RDF_REFERENCED|MPT_RDF_ANNOUNCED))
|
|
== MPT_RDF_ANNOUNCED) {
|
|
mpt_disk_prt(mpt, mpt_disk, "No longer configured\n");
|
|
mpt_disk->flags = 0;
|
|
mpt->raid_rescan++;
|
|
continue;
|
|
}
|
|
|
|
if ((mpt_disk->flags & MPT_RDF_ANNOUNCED) == 0) {
|
|
|
|
mpt_announce_disk(mpt, mpt_disk);
|
|
mpt_disk->flags |= MPT_RVF_ANNOUNCED;
|
|
}
|
|
|
|
if ((mpt_disk->flags & MPT_RDF_UP2DATE) != 0)
|
|
continue;
|
|
|
|
mpt_disk->flags |= MPT_RDF_UP2DATE;
|
|
mpt_disk_prt(mpt, mpt_disk, "%s\n", mpt_disk_state(mpt_disk));
|
|
if (disk_pg->PhysDiskStatus.Flags == 0)
|
|
continue;
|
|
|
|
mpt_disk_prt(mpt, mpt_disk, "Status (");
|
|
for (m = 1; m <= 0x80; m <<= 1) {
|
|
switch (disk_pg->PhysDiskStatus.Flags & m) {
|
|
case MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC:
|
|
mpt_prtc(mpt, " Out-Of-Sync");
|
|
break;
|
|
case MPI_PHYSDISK0_STATUS_FLAG_QUIESCED:
|
|
mpt_prtc(mpt, " Quiesced");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
mpt_prtc(mpt, " )\n");
|
|
}
|
|
|
|
mpt->raid_nonopt_volumes = nonopt_volumes;
|
|
}
|
|
|
|
static void
|
|
mpt_raid_timer(void *arg)
|
|
{
|
|
struct mpt_softc *mpt;
|
|
|
|
mpt = (struct mpt_softc *)arg;
|
|
MPT_LOCK(mpt);
|
|
mpt_raid_wakeup(mpt);
|
|
MPT_UNLOCK(mpt);
|
|
}
|
|
|
|
static void
|
|
mpt_raid_quiesce_timeout(void *arg)
|
|
{
|
|
/* Complete the CCB with error */
|
|
/* COWWWW */
|
|
}
|
|
|
|
void
|
|
mpt_schedule_raid_refresh(struct mpt_softc *mpt)
|
|
{
|
|
callout_reset(&mpt->raid_timer, MPT_RAID_SYNC_REPORT_INTERVAL,
|
|
mpt_raid_timer, mpt);
|
|
}
|
|
|
|
static int
|
|
mpt_raid_set_vol_resync_rate(struct mpt_softc *mpt, u_int rate)
|
|
{
|
|
struct mpt_raid_volume *mpt_vol;
|
|
|
|
if ((rate > MPT_RAID_RESYNC_RATE_MAX
|
|
|| rate < MPT_RAID_RESYNC_RATE_MIN)
|
|
&& rate != MPT_RAID_RESYNC_RATE_NC)
|
|
return (EINVAL);
|
|
|
|
MPT_LOCK(mpt);
|
|
mpt->raid_resync_rate = rate;
|
|
RAID_VOL_FOREACH(mpt, mpt_vol) {
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0) {
|
|
continue;
|
|
}
|
|
mpt_verify_resync_rate(mpt, mpt_vol);
|
|
}
|
|
MPT_UNLOCK(mpt);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpt_raid_set_vol_queue_depth(struct mpt_softc *mpt, u_int vol_queue_depth)
|
|
{
|
|
struct mpt_raid_volume *mpt_vol;
|
|
|
|
if (vol_queue_depth > 255 || vol_queue_depth < 1)
|
|
return (EINVAL);
|
|
|
|
MPT_LOCK(mpt);
|
|
mpt->raid_queue_depth = vol_queue_depth;
|
|
RAID_VOL_FOREACH(mpt, mpt_vol) {
|
|
struct cam_path *path;
|
|
int error;
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
mpt->raid_rescan = 0;
|
|
|
|
error = xpt_create_path(&path, xpt_periph,
|
|
cam_sim_path(mpt->sim),
|
|
mpt_vol->config_page->VolumeID,
|
|
/*lun*/0);
|
|
if (error != CAM_REQ_CMP) {
|
|
mpt_vol_prt(mpt, mpt_vol, "Unable to allocate path!\n");
|
|
continue;
|
|
}
|
|
mpt_adjust_queue_depth(mpt, mpt_vol, path);
|
|
xpt_free_path(path);
|
|
}
|
|
MPT_UNLOCK(mpt);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpt_raid_set_vol_mwce(struct mpt_softc *mpt, mpt_raid_mwce_t mwce)
|
|
{
|
|
struct mpt_raid_volume *mpt_vol;
|
|
int force_full_resync;
|
|
|
|
MPT_LOCK(mpt);
|
|
if (mwce == mpt->raid_mwce_setting) {
|
|
MPT_UNLOCK(mpt);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Catch MWCE being left on due to a failed shutdown. Since
|
|
* sysctls cannot be set by the loader, we treat the first
|
|
* setting of this varible specially and force a full volume
|
|
* resync if MWCE is enabled and a resync is in progress.
|
|
*/
|
|
force_full_resync = 0;
|
|
if (mpt->raid_mwce_set == 0
|
|
&& mpt->raid_mwce_setting == MPT_RAID_MWCE_NC
|
|
&& mwce == MPT_RAID_MWCE_REBUILD_ONLY)
|
|
force_full_resync = 1;
|
|
|
|
mpt->raid_mwce_setting = mwce;
|
|
RAID_VOL_FOREACH(mpt, mpt_vol) {
|
|
CONFIG_PAGE_RAID_VOL_0 *vol_pg;
|
|
int resyncing;
|
|
int mwce;
|
|
|
|
if ((mpt_vol->flags & MPT_RVF_ACTIVE) == 0)
|
|
continue;
|
|
|
|
vol_pg = mpt_vol->config_page;
|
|
resyncing = vol_pg->VolumeStatus.Flags
|
|
& MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS;
|
|
mwce = vol_pg->VolumeSettings.Settings
|
|
& MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE;
|
|
if (force_full_resync && resyncing && mwce) {
|
|
|
|
/*
|
|
* XXX disable/enable volume should force a resync,
|
|
* but we'll need to queice, drain, and restart
|
|
* I/O to do that.
|
|
*/
|
|
mpt_vol_prt(mpt, mpt_vol, "WARNING - Unsafe shutdown "
|
|
"detected. Suggest full resync.\n");
|
|
}
|
|
mpt_verify_mwce(mpt, mpt_vol);
|
|
}
|
|
mpt->raid_mwce_set = 1;
|
|
MPT_UNLOCK(mpt);
|
|
return (0);
|
|
}
|
|
|
|
const char *mpt_vol_mwce_strs[] =
|
|
{
|
|
"On",
|
|
"Off",
|
|
"On-During-Rebuild",
|
|
"NC"
|
|
};
|
|
|
|
static int
|
|
mpt_raid_sysctl_vol_member_wce(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
char inbuf[20];
|
|
struct mpt_softc *mpt;
|
|
const char *str;
|
|
int error;
|
|
u_int size;
|
|
u_int i;
|
|
|
|
GIANT_REQUIRED;
|
|
mpt = (struct mpt_softc *)arg1;
|
|
str = mpt_vol_mwce_strs[mpt->raid_mwce_setting];
|
|
error = SYSCTL_OUT(req, str, strlen(str) + 1);
|
|
if (error || !req->newptr)
|
|
return (error);
|
|
|
|
size = req->newlen - req->newidx;
|
|
if (size >= sizeof(inbuf))
|
|
return (EINVAL);
|
|
|
|
error = SYSCTL_IN(req, inbuf, size);
|
|
if (error)
|
|
return (error);
|
|
inbuf[size] = '\0';
|
|
for (i = 0; i < NUM_ELEMENTS(mpt_vol_mwce_strs); i++) {
|
|
|
|
if (strcmp(mpt_vol_mwce_strs[i], inbuf) == 0)
|
|
return (mpt_raid_set_vol_mwce(mpt, i));
|
|
}
|
|
return (EINVAL);
|
|
}
|
|
|
|
static int
|
|
mpt_raid_sysctl_vol_resync_rate(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct mpt_softc *mpt;
|
|
u_int raid_resync_rate;
|
|
int error;
|
|
|
|
GIANT_REQUIRED;
|
|
mpt = (struct mpt_softc *)arg1;
|
|
raid_resync_rate = mpt->raid_resync_rate;
|
|
|
|
error = sysctl_handle_int(oidp, &raid_resync_rate, 0, req);
|
|
if (error || !req->newptr)
|
|
return error;
|
|
|
|
return (mpt_raid_set_vol_resync_rate(mpt, raid_resync_rate));
|
|
}
|
|
|
|
static int
|
|
mpt_raid_sysctl_vol_queue_depth(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct mpt_softc *mpt;
|
|
u_int raid_queue_depth;
|
|
int error;
|
|
|
|
GIANT_REQUIRED;
|
|
mpt = (struct mpt_softc *)arg1;
|
|
raid_queue_depth = mpt->raid_queue_depth;
|
|
|
|
error = sysctl_handle_int(oidp, &raid_queue_depth, 0, req);
|
|
if (error || !req->newptr)
|
|
return error;
|
|
|
|
return (mpt_raid_set_vol_queue_depth(mpt, raid_queue_depth));
|
|
}
|
|
|
|
static void
|
|
mpt_raid_sysctl_attach(struct mpt_softc *mpt)
|
|
{
|
|
#if __FreeBSD_version >= 500000
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(mpt->dev);
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(mpt->dev);
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
"vol_member_wce", CTLTYPE_STRING | CTLFLAG_RW, mpt, 0,
|
|
mpt_raid_sysctl_vol_member_wce, "A",
|
|
"volume member WCE(On,Off,On-During-Rebuild,NC)");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
"vol_queue_depth", CTLTYPE_INT | CTLFLAG_RW, mpt, 0,
|
|
mpt_raid_sysctl_vol_queue_depth, "I",
|
|
"default volume queue depth");
|
|
|
|
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
"vol_resync_rate", CTLTYPE_INT | CTLFLAG_RW, mpt, 0,
|
|
mpt_raid_sysctl_vol_resync_rate, "I",
|
|
"volume resync priority (0 == NC, 1 - 255)");
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
"nonoptimal_volumes", CTLFLAG_RD,
|
|
&mpt->raid_nonopt_volumes, 0,
|
|
"number of nonoptimal volumes");
|
|
#endif
|
|
}
|