/* * mpt_watchdog - routine that acts as a watchdog. */ int mpt_watchdog(mpt_adap_t *adap) { #if 0 // move code to mpt_interrupt, so we don't poll during DMA U32 doorbell; int state; doorbell = rl(DOORBELL); state = doorbell & MPI_IOC_STATE_MASK; if (state != MPI_IOC_STATE_OPERATIONAL) { if (state == MPI_IOC_STATE_FAULT) { printf("%s: MPT Firmware Fault, code %04x\n", adap->name, doorbell & MPI_DOORBELL_DATA_MASK); if (wFlag) fprintf(logFile, "%s: MPT Firmware Fault, code %04x\n", adap->name, doorbell & MPI_DOORBELL_DATA_MASK); } adap->restart_needed = TRUE; } #endif if (adap->restart_needed == TRUE) { mpt_restart(adap); return 1; } else if (adap->bus_reset_needed == TRUE) { adap->bus_reset_needed = FALSE; mpt_bus_reset(adap); } return 0; }
static void mpt_done(mpt_softc_t *mpt, uint32_t reply) { struct scsipi_xfer *xs = NULL; struct scsipi_periph *periph; int index; request_t *req; MSG_REQUEST_HEADER *mpt_req; MSG_SCSI_IO_REPLY *mpt_reply; int restart = 0; /* nonzero if we need to restart the IOC*/ if (__predict_true((reply & MPT_CONTEXT_REPLY) == 0)) { /* context reply (ok) */ mpt_reply = NULL; index = reply & MPT_CONTEXT_MASK; } else { /* address reply (error) */ /* XXX BUS_DMASYNC_POSTREAD XXX */ mpt_reply = MPT_REPLY_PTOV(mpt, reply); if (mpt_reply != NULL) { if (mpt->verbose > 1) { uint32_t *pReply = (uint32_t *) mpt_reply; mpt_prt(mpt, "Address Reply (index %u):", le32toh(mpt_reply->MsgContext) & 0xffff); mpt_prt(mpt, "%08x %08x %08x %08x", pReply[0], pReply[1], pReply[2], pReply[3]); mpt_prt(mpt, "%08x %08x %08x %08x", pReply[4], pReply[5], pReply[6], pReply[7]); mpt_prt(mpt, "%08x %08x %08x %08x", pReply[8], pReply[9], pReply[10], pReply[11]); } index = le32toh(mpt_reply->MsgContext); } else index = reply & MPT_CONTEXT_MASK; } /* * Address reply with MessageContext high bit set. * This is most likely a notify message, so we try * to process it, then free it. */ if (__predict_false((index & 0x80000000) != 0)) { if (mpt_reply != NULL) mpt_ctlop(mpt, mpt_reply, reply); else mpt_prt(mpt, "%s: index 0x%x, NULL reply", __func__, index); return; } /* Did we end up with a valid index into the table? */ if (__predict_false(index < 0 || index >= MPT_MAX_REQUESTS(mpt))) { mpt_prt(mpt, "%s: invalid index (0x%x) in reply", __func__, index); return; } req = &mpt->request_pool[index]; /* Make sure memory hasn't been trashed. */ if (__predict_false(req->index != index)) { mpt_prt(mpt, "%s: corrupted request_t (0x%x)", __func__, index); return; } MPT_SYNC_REQ(mpt, req, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); mpt_req = req->req_vbuf; /* Short cut for task management replies; nothing more for us to do. */ if (__predict_false(mpt_req->Function == MPI_FUNCTION_SCSI_TASK_MGMT)) { if (mpt->verbose > 1) mpt_prt(mpt, "%s: TASK MGMT", __func__); KASSERT(req == mpt->mngt_req); mpt->mngt_req = NULL; goto done; } if (__predict_false(mpt_req->Function == MPI_FUNCTION_PORT_ENABLE)) goto done; /* * At this point, it had better be a SCSI I/O command, but don't * crash if it isn't. */ if (__predict_false(mpt_req->Function != MPI_FUNCTION_SCSI_IO_REQUEST)) { if (mpt->verbose > 1) mpt_prt(mpt, "%s: unknown Function 0x%x (0x%x)", __func__, mpt_req->Function, index); goto done; } /* Recover scsipi_xfer from the request structure. */ xs = req->xfer; /* Can't have a SCSI command without a scsipi_xfer. */ if (__predict_false(xs == NULL)) { mpt_prt(mpt, "%s: no scsipi_xfer, index = 0x%x, seq = 0x%08x", __func__, req->index, req->sequence); mpt_prt(mpt, "request state: %s", mpt_req_state(req->debug)); mpt_prt(mpt, "mpt_request:"); mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf); if (mpt_reply != NULL) { mpt_prt(mpt, "mpt_reply:"); mpt_print_reply(mpt_reply); } else { mpt_prt(mpt, "context reply: 0x%08x", reply); } goto done; } callout_stop(&xs->xs_callout); periph = xs->xs_periph; /* * If we were a data transfer, unload the map that described * the data buffer. */ if (__predict_true(xs->datalen != 0)) { bus_dmamap_sync(mpt->sc_dmat, req->dmap, 0, req->dmap->dm_mapsize, (xs->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(mpt->sc_dmat, req->dmap); } if (__predict_true(mpt_reply == NULL)) { /* * Context reply; report that the command was * successful! * * Also report the xfer mode, if necessary. */ if (__predict_false(mpt->mpt_report_xfer_mode != 0)) { if ((mpt->mpt_report_xfer_mode & (1 << periph->periph_target)) != 0) mpt_get_xfer_mode(mpt, periph); } xs->error = XS_NOERROR; xs->status = SCSI_OK; xs->resid = 0; mpt_free_request(mpt, req); scsipi_done(xs); return; } xs->status = mpt_reply->SCSIStatus; switch (le16toh(mpt_reply->IOCStatus) & MPI_IOCSTATUS_MASK) { case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC overrun!", __func__); break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* * Yikes! Tagged queue full comes through this path! * * So we'll change it to a status error and anything * that returns status should probably be a status * error as well. */ xs->resid = xs->datalen - le32toh(mpt_reply->TransferCount); if (mpt_reply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS) { xs->error = XS_DRIVER_STUFFUP; break; } /* FALLTHROUGH */ case MPI_IOCSTATUS_SUCCESS: case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: switch (xs->status) { case SCSI_OK: /* Report the xfer mode, if necessary. */ if ((mpt->mpt_report_xfer_mode & (1 << periph->periph_target)) != 0) mpt_get_xfer_mode(mpt, periph); xs->resid = 0; break; case SCSI_CHECK: xs->error = XS_SENSE; break; case SCSI_BUSY: case SCSI_QUEUE_FULL: xs->error = XS_BUSY; break; default: scsipi_printaddr(periph); printf("invalid status code %d\n", xs->status); xs->error = XS_DRIVER_STUFFUP; break; } break; case MPI_IOCSTATUS_BUSY: case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: xs->error = XS_RESOURCE_SHORTAGE; break; case MPI_IOCSTATUS_SCSI_INVALID_BUS: case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: xs->error = XS_SELTIMEOUT; break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC SCSI residual mismatch!", __func__); restart = 1; break; case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* XXX What should we do here? */ mpt_prt(mpt, "%s: IOC SCSI task terminated!", __func__); restart = 1; break; case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* XXX */ xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC SCSI task failed!", __func__); restart = 1; break; case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* XXX */ xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC task terminated!", __func__); restart = 1; break; case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* XXX This is a bus-reset */ xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC SCSI bus reset!", __func__); restart = 1; break; case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* * FreeBSD and Linux indicate this is a phase error between * the IOC and the drive itself. When this happens, the IOC * becomes unhappy and stops processing all transactions. * Call mpt_timeout which knows how to get the IOC back * on its feet. */ mpt_prt(mpt, "%s: IOC indicates protocol error -- " "recovering...", __func__); xs->error = XS_TIMEOUT; restart = 1; break; default: /* XXX unrecognized HBA error */ xs->error = XS_DRIVER_STUFFUP; mpt_prt(mpt, "%s: IOC returned unknown code: 0x%x", __func__, le16toh(mpt_reply->IOCStatus)); restart = 1; break; } if (mpt_reply != NULL) { if (mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { memcpy(&xs->sense.scsi_sense, req->sense_vbuf, sizeof(xs->sense.scsi_sense)); } else if (mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) { /* * This will cause the scsipi layer to issue * a REQUEST SENSE. */ if (xs->status == SCSI_CHECK) xs->error = XS_BUSY; } } done: if (mpt_reply != NULL && le16toh(mpt_reply->IOCStatus) & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { mpt_prt(mpt, "%s: IOC has error - logging...\n", __func__); mpt_ctlop(mpt, mpt_reply, reply); } /* If IOC done with this request, free it up. */ if (mpt_reply == NULL || (mpt_reply->MsgFlags & 0x80) == 0) mpt_free_request(mpt, req); /* If address reply, give the buffer back to the IOC. */ if (mpt_reply != NULL) mpt_free_reply(mpt, (reply << 1)); if (xs != NULL) scsipi_done(xs); if (restart) { mpt_prt(mpt, "%s: IOC fatal error: restarting...", __func__); mpt_restart(mpt, NULL); } }
static void mpt_timeout(void *arg) { request_t *req = arg; struct scsipi_xfer *xs; struct scsipi_periph *periph; mpt_softc_t *mpt; uint32_t oseq; int s, nrepl = 0; if (req->xfer == NULL) { printf("mpt_timeout: NULL xfer for request index 0x%x, sequenc 0x%x\n", req->index, req->sequence); return; } xs = req->xfer; periph = xs->xs_periph; mpt = (void *) periph->periph_channel->chan_adapter->adapt_dev; scsipi_printaddr(periph); printf("command timeout\n"); s = splbio(); oseq = req->sequence; mpt->timeouts++; if (mpt_intr(mpt)) { if (req->sequence != oseq) { mpt->success++; mpt_prt(mpt, "recovered from command timeout"); splx(s); return; } } /* * Ensure the IOC is really done giving us data since it appears it can * sometimes fail to give us interrupts under heavy load. */ nrepl = mpt_drain_queue(mpt); if (nrepl ) { mpt_prt(mpt, "mpt_timeout: recovered %d commands",nrepl); } if (req->sequence != oseq) { mpt->success++; splx(s); return; } mpt_prt(mpt, "timeout on request index = 0x%x, seq = 0x%08x", req->index, req->sequence); mpt_check_doorbell(mpt); mpt_prt(mpt, "Status 0x%08x, Mask 0x%08x, Doorbell 0x%08x", mpt_read(mpt, MPT_OFFSET_INTR_STATUS), mpt_read(mpt, MPT_OFFSET_INTR_MASK), mpt_read(mpt, MPT_OFFSET_DOORBELL)); mpt_prt(mpt, "request state: %s", mpt_req_state(req->debug)); if (mpt->verbose > 1) mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf); xs->error = XS_TIMEOUT; splx(s); mpt_restart(mpt, req); }