#include "cpqary3.h"
uint8_t cpqary3_disable_NOE_command(cpqary3_t *);
#define MAX_KNOWN_FAILURE_REASON 31
char *ascii_failure_reason[] = {
"NONE",
"TOO_SMALL_IN_LOAD_CONFIG",
"ERROR_ERASING_RIS",
"ERROR_SAVING_RIS",
"FAIL_DRIVE_COMMAND",
"MARK_BAD_FAILED",
"MARK_BAD_FAILED_IN_FINISH_REMAP",
"TIMEOUT",
"AUTOSENSE_FAILED",
"MEDIUM_ERROR_1",
"MEDIUM_ERROR_2",
"NOT_READY_BAD_SENSE",
"NOT_READY",
"HARDWARE_ERROR",
"ABORTED_COMMAND",
"WRITE_PROTECTED",
"SPIN_UP_FAILURE_IN_RECOVER",
"REBUILD_WRITE_ERROR",
"TOO_SMALL_IN_HOT_PLUG",
"RESET_RECOVERY_ABORT",
"REMOVED_IN_HOT_PLUG",
"INIT_REQUEST_SENSE_FAILED",
"INIT_START_UNIT_FAILED",
"GDP_INQUIRY_FAILED",
"GDP_NON_DISK_DEVICE",
"GDP_READ_CAPACITY_FAILED",
"GDP_INVALID_BLOCK_SIZE",
"HOTP_REQUEST_SENSE_FAILED",
"HOTP_START_UNIT_FAILED",
"WRITE_ERROR_AFTER_REMAP",
"INIT_RESET_RECOVERY_ABORTED"
};
char *log_vol_status[] = {
"OK",
"Failed",
"Not Configured",
"Regenerating",
"Needs Rebuild Permission",
"Rebuilding",
"Wrong Drive Replaced",
"Bad Drive Connection",
"Box Overheating",
"Box Overheated",
"Volume Expanding",
"Not Yet Available",
"Volume Needs to Expand",
"Unknown"
};
uint8_t
cpqary3_send_NOE_command(cpqary3_t *ctlr, cpqary3_cmdpvt_t *memp, uint8_t flag)
{
uint32_t phys_addr = 0;
NoeBuffer *databuf;
CommandList_t *cmdlist;
cpqary3_phyctg_t *phys_handle;
int rv;
RETURN_FAILURE_IF_NULL(ctlr);
if (CPQARY3_NOE_RESUBMIT == flag) {
if ((NULL == memp) || (NULL == memp->cmdlist_memaddr)) {
cmn_err(CE_WARN, " CPQary3 : _send_NOE_command : "
"Re-Use Not possible; CommandList NULL");
return (CPQARY3_FAILURE);
}
bzero(MEM2DRVPVT(memp)->sg, sizeof (NoeBuffer));
memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
CPQARY3_NOECMD_SUCCESS;
} else if (CPQARY3_NOE_INIT == flag) {
phys_handle =
(cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (cpqary3_phyctg_t));
if (!phys_handle)
return (CPQARY3_FAILURE);
databuf = (NoeBuffer *)cpqary3_alloc_phyctgs_mem(ctlr,
sizeof (NoeBuffer), &phys_addr, phys_handle);
if (!databuf) {
return (CPQARY3_FAILURE);
}
bzero(databuf, sizeof (NoeBuffer));
if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
cpqary3_free_phyctgs_mem(phys_handle,
CPQARY3_FREE_PHYCTG_MEM);
return (CPQARY3_FAILURE);
}
memp->driverdata = (cpqary3_private_t *)
MEM_ZALLOC(sizeof (cpqary3_private_t));
if (NULL == memp->driverdata) {
cpqary3_free_phyctgs_mem(phys_handle,
CPQARY3_FREE_PHYCTG_MEM);
cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
return (CPQARY3_FAILURE);
}
memp->driverdata->sg = databuf;
memp->driverdata->phyctgp = phys_handle;
cmdlist = memp->cmdlist_memaddr;
cmdlist->Header.SGTotal = 1;
cmdlist->Header.SGList = 1;
cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
cmdlist->Request.CDBLen = CISS_NOE_CDB_LEN;
cmdlist->Request.Timeout = 0;
cmdlist->Request.Type.Type = CISS_TYPE_CMD;
cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
cmdlist->Request.Type.Direction = CISS_XFER_READ;
cmdlist->Request.CDB[0] = CISS_NEW_READ;
cmdlist->Request.CDB[1] = BMIC_NOTIFY_ON_EVENT;
cmdlist->Request.CDB[10] = (NOE_BUFFER_LENGTH >> 8) & 0xff;
cmdlist->Request.CDB[11] = NOE_BUFFER_LENGTH & 0xff;
cmdlist->SG[0].Addr = phys_addr;
cmdlist->SG[0].Len = NOE_BUFFER_LENGTH;
}
memp->complete = cpqary3_noe_complete;
mutex_enter(&ctlr->hw_mutex);
rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
mutex_exit(&ctlr->hw_mutex);
if (rv != 0)
return (CPQARY3_FAILURE);
return (CPQARY3_SUCCESS);
}
uint8_t
cpqary3_disable_NOE_command(cpqary3_t *ctlr)
{
CommandList_t *cmdlist;
cpqary3_cmdpvt_t *memp;
int rv;
RETURN_FAILURE_IF_NULL(ctlr);
if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
cmn_err(CE_WARN, "CPQary3 : _disable_NOE_command : Failed");
return (CPQARY3_FAILURE);
}
cmdlist = memp->cmdlist_memaddr;
cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
cmdlist->Request.CDBLen = CISS_CANCEL_NOE_CDB_LEN;
cmdlist->Request.Timeout = 0;
cmdlist->Request.Type.Type = CISS_TYPE_CMD;
cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
cmdlist->Request.Type.Direction = CISS_XFER_NONE;
cmdlist->Request.CDB[0] = ARRAY_WRITE;
cmdlist->Request.CDB[6] = BMIC_CANCEL_NOTIFY_ON_EVENT;
memp->complete = cpqary3_noe_complete;
mutex_enter(&ctlr->hw_mutex);
rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
mutex_exit(&ctlr->hw_mutex);
if (rv != 0)
return (CPQARY3_FAILURE);
return (CPQARY3_SUCCESS);
}
void
cpqary3_NOE_handler(cpqary3_cmdpvt_t *memp)
{
uint16_t drive = 0;
NoeBuffer *evt;
cpqary3_t *ctlr;
cpqary3_phyctg_t *phys_handle;
uint8_t driveId = 0;
VERIFY(memp != NULL);
evt = (NoeBuffer *)MEM2DRVPVT(memp)->sg;
ctlr = (cpqary3_t *)memp->ctlr;
phys_handle = (cpqary3_phyctg_t *)MEM2DRVPVT(memp)->phyctgp;
evt->ascii_message[79] = 0;
switch (evt->event_class_code) {
case CLASS_PROTOCOL:
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
if ((evt->event_subclass_code == SUB_CLASS_NON_EVENT) &&
(evt->event_detail_code == DETAIL_DISABLED)) {
cmn_err(CE_CONT, " %s", ctlr->hba_name);
cmn_err(CE_CONT,
"CPQary3 : Event Notifier Disabled \n");
MEM_SFREE(memp->driverdata, sizeof (cpqary3_private_t));
cpqary3_free_phyctgs_mem(phys_handle,
CPQARY3_FREE_PHYCTG_MEM);
cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
return;
} else if ((evt->event_subclass_code ==
SUB_CLASS_PROTOCOL_ERR) &&
(evt->event_detail_code == DETAIL_EVENT_Q_OVERFLOW)) {
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
}
cmn_err(CE_CONT, "\n");
break;
case CLASS_HOT_PLUG:
if (evt->event_subclass_code == SUB_CLASS_HP_CHANGE) {
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
driveId =
*(uint16_t *)(&evt->event_specific_data[0]);
if (driveId & 0x80) {
driveId -= 0x80;
cmn_err(CE_CONT, " Physical Drive Num "
"....... SCSI Port %u, "
"Drive Id %u\n",
(driveId / 16) + 1,
(driveId % 16));
} else {
cmn_err(CE_CONT, " Physical Drive Num "
"....... SCSI Port %u, "
"Drive Id %u\n",
(driveId / 16) + 1, (driveId % 16));
}
}
cmn_err(CE_CONT, " Configured Drive ? ....... %s\n",
evt->event_specific_data[2] ? "YES" : "NO");
if (evt->event_specific_data[3]) {
cmn_err(CE_CONT, " Spare Drive? "
"............. %s\n",
evt->event_specific_data[3] ? "YES" : "NO");
}
} else if (evt->event_subclass_code == SUB_CLASS_SB_HP_CHANGE) {
if (evt->event_detail_code == DETAIL_PATH_REMOVED) {
cmn_err(CE_WARN, " %s", ctlr->hba_name);
cmn_err(CE_CONT,
" Storage Enclosure cable or %s\n",
evt->ascii_message);
} else if (evt->event_detail_code ==
DETAIL_PATH_REPAIRED) {
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
cmn_err(CE_CONT,
" Storage Enclosure Cable or %s\n",
evt->ascii_message);
} else {
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
}
} else {
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
}
cmn_err(CE_CONT, "\n");
break;
case CLASS_HARDWARE:
case CLASS_ENVIRONMENT:
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
cmn_err(CE_CONT, "\n");
break;
case CLASS_PHYSICAL_DRIVE:
cmn_err(CE_WARN, " %s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
driveId = *(uint16_t *)(&evt->event_specific_data[0]);
if (driveId & 0x80) {
driveId -= 0x80;
cmn_err(CE_CONT, " Physical Drive Num ....... "
"SCSI Port %u, Drive Id %u\n",
(driveId / 16) + 1, (driveId % 16));
} else {
cmn_err(CE_CONT, " Physical Drive Num ....... "
"SCSI Port %u, Drive Id %u\n",
(driveId / 16) + 1, (driveId % 16));
}
}
if (evt->event_specific_data[2] < MAX_KNOWN_FAILURE_REASON) {
cmn_err(CE_CONT, " Failure Reason............ %s\n",
ascii_failure_reason[evt->event_specific_data[2]]);
} else {
cmn_err(CE_CONT,
" Failure Reason............ UNKNOWN \n");
}
cmn_err(CE_CONT, "\n");
break;
case CLASS_LOGICAL_DRIVE:
cmn_err(CE_NOTE, " %s", ctlr->hba_name);
if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
(evt->event_detail_code == DETAIL_CHANGE)) {
cmn_err(CE_CONT, " State change, logical drive %u\n",
(*(uint16_t *)(&evt->event_specific_data[0]) + 1));
cmn_err(CE_CONT, " New Logical Drive State... %s\n",
log_vol_status[evt->event_specific_data[3]]);
if ((evt->event_specific_data[3] == 1) ||
(evt->event_specific_data[3] == 2)) {
drive = *(uint16_t *)
(&evt->event_specific_data[0]);
drive = ((drive < CTLR_SCSI_ID)
? drive : drive + CPQARY3_TGT_ALIGNMENT);
if (ctlr && ctlr->cpqary3_tgtp[drive]) {
ctlr->cpqary3_tgtp[drive]->type =
CPQARY3_TARGET_NONE;
}
}
if (evt->event_specific_data[4] & SPARE_REBUILDING) {
cmn_err(CE_CONT, " Logical Drive %d: "
"Data is rebuilding on spare drive\n",
(*(uint16_t *)
(&evt->event_specific_data[0]) + 1));
}
if (evt->event_specific_data[4] & SPARE_REBUILT) {
cmn_err(CE_CONT,
" Logical Drive %d: Rebuild complete. "
"Spare is now active\n",
(*(uint16_t *)
(&evt->event_specific_data[0]) + 1));
}
} else if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
(evt->event_detail_code == MEDIA_EXCHANGE)) {
cmn_err(CE_CONT, " Media exchange detected, "
"logical drive %u\n",
(*(uint16_t *)
(&evt->event_specific_data[0]) + 1));
} else {
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
}
cmn_err(CE_CONT, "\n");
break;
default:
cmn_err(CE_NOTE, "%s", ctlr->hba_name);
cmn_err(CE_CONT, " %s\n", evt->ascii_message);
cmn_err(CE_CONT, "\n");
break;
}
if (CPQARY3_FAILURE ==
cpqary3_send_NOE_command(ctlr, memp, CPQARY3_NOE_RESUBMIT)) {
cmn_err(CE_WARN, "CPQary3: Failed to ReInitialize "
"NOTIFY OF EVENT");
cpqary3_free_phyctgs_mem(MEM2DRVPVT(memp)->phyctgp,
CPQARY3_FREE_PHYCTG_MEM);
cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
}
}
void
cpqary3_noe_complete(cpqary3_cmdpvt_t *cpqary3_cmdpvtp)
{
ASSERT(cpqary3_cmdpvtp != NULL);
if (CPQARY3_TIMEOUT == cpqary3_cmdpvtp->cmdpvt_flag) {
cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
return;
}
if (cpqary3_cmdpvtp->cmdlist_memaddr->Request.CDB[6] ==
BMIC_CANCEL_NOTIFY_ON_EVENT) {
cv_signal(&cpqary3_cmdpvtp->ctlr->cv_noe_wait);
cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
} else {
cpqary3_NOE_handler(cpqary3_cmdpvtp);
}
}