📄 megaraid_mbox.c
字号:
memcpy(epthru->cdb, scp->cmnd, scp->cmd_len); if (scp->request_bufflen) { epthru->dataxferlen = scp->request_bufflen; epthru->dataxferaddr = ccb->sgl_dma_h; epthru->numsge = megaraid_mbox_mksgl(adapter, scb); } else { epthru->dataxferaddr = 0; epthru->dataxferlen = 0; epthru->numsge = 0; } return;}/** * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs * @adapter - controller's soft state * * Interrupt ackrowledgement sequence for memory mapped HBAs. Find out the * completed command and put them on the completed list for later processing. * * Returns: 1 if the interrupt is valid, 0 otherwise */static inline intmegaraid_ack_sequence(adapter_t *adapter){ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); mbox_t *mbox; scb_t *scb; uint8_t nstatus; uint8_t completed[MBOX_MAX_FIRMWARE_STATUS]; struct list_head clist; int handled; uint32_t dword; unsigned long flags; int i, j; mbox = raid_dev->mbox; // move the SCBs from the firmware completed array to our local list INIT_LIST_HEAD(&clist); // loop till F/W has more commands for us to complete handled = 0; spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags); do { /* * Check if a valid interrupt is pending. If found, force the * interrupt line low. */ dword = RDOUTDOOR(raid_dev); if (dword != 0x10001234) break; handled = 1; WROUTDOOR(raid_dev, 0x10001234); nstatus = 0; // wait for valid numstatus to post for (i = 0; i < 0xFFFFF; i++) { if (mbox->numstatus != 0xFF) { nstatus = mbox->numstatus; break; } rmb(); } mbox->numstatus = 0xFF; adapter->outstanding_cmds -= nstatus; for (i = 0; i < nstatus; i++) { // wait for valid command index to post for (j = 0; j < 0xFFFFF; j++) { if (mbox->completed[i] != 0xFF) break; rmb(); } completed[i] = mbox->completed[i]; mbox->completed[i] = 0xFF; if (completed[i] == 0xFF) { con_log(CL_ANN, (KERN_CRIT "megaraid: command posting timed out\n")); BUG(); continue; } // Get SCB associated with this command id if (completed[i] >= MBOX_MAX_SCSI_CMDS) { // a cmm command scb = adapter->uscb_list + (completed[i] - MBOX_MAX_SCSI_CMDS); } else { // an os command scb = adapter->kscb_list + completed[i]; } scb->status = mbox->status; list_add_tail(&scb->list, &clist); } // Acknowledge interrupt WRINDOOR(raid_dev, 0x02); } while(1); spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); // put the completed commands in the completed list. DPC would // complete these commands later spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); list_splice(&clist, &adapter->completed_list); spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); // schedule the DPC if there is some work for it if (handled) tasklet_schedule(&adapter->dpc_h); return handled;}/** * megaraid_isr - isr for memory based mailbox based controllers * @irq - irq * @devp - pointer to our soft state * @regs - unused * * Interrupt service routine for memory-mapped mailbox controllers. */static irqreturn_tmegaraid_isr(int irq, void *devp, struct pt_regs *regs){ adapter_t *adapter = devp; int handled; handled = megaraid_ack_sequence(adapter); /* Loop through any pending requests */ if (!adapter->quiescent) { megaraid_mbox_runpendq(adapter, NULL); } return IRQ_RETVAL(handled);}/** * megaraid_mbox_sync_scb - sync kernel buffers * @adapter : controller's soft state * @scb : pointer to the resource packet * * DMA sync if required. */static inline voidmegaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb){ mbox_ccb_t *ccb; ccb = (mbox_ccb_t *)scb->ccb; switch (scb->dma_type) { case MRAID_DMA_WBUF: if (scb->dma_direction == PCI_DMA_FROMDEVICE) { pci_dma_sync_single_for_cpu(adapter->pdev, ccb->buf_dma_h, scb->scp->request_bufflen, PCI_DMA_FROMDEVICE); } pci_unmap_page(adapter->pdev, ccb->buf_dma_h, scb->scp->request_bufflen, scb->dma_direction); break; case MRAID_DMA_WSG: if (scb->dma_direction == PCI_DMA_FROMDEVICE) { pci_dma_sync_sg_for_cpu(adapter->pdev, scb->scp->request_buffer, scb->scp->use_sg, PCI_DMA_FROMDEVICE); } pci_unmap_sg(adapter->pdev, scb->scp->request_buffer, scb->scp->use_sg, scb->dma_direction); break; default: break; } return;}/** * megaraid_mbox_dpc - the tasklet to complete the commands from completed list * @devp : pointer to HBA soft state * * Pick up the commands from the completed list and send back to the owners. * This is a reentrant function and does not assume any locks are held while * it is being called. */static voidmegaraid_mbox_dpc(unsigned long devp){ adapter_t *adapter = (adapter_t *)devp; mraid_device_t *raid_dev; struct list_head clist; struct scatterlist *sgl; scb_t *scb; scb_t *tmp; struct scsi_cmnd *scp; mraid_passthru_t *pthru; mraid_epassthru_t *epthru; mbox_ccb_t *ccb; int islogical; int pdev_index; int pdev_state; mbox_t *mbox; unsigned long flags; uint8_t c; int status; if (!adapter) return; raid_dev = ADAP2RAIDDEV(adapter); // move the SCBs from the completed list to our local list INIT_LIST_HEAD(&clist); spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); list_splice_init(&adapter->completed_list, &clist); spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); list_for_each_entry_safe(scb, tmp, &clist, list) { status = scb->status; scp = scb->scp; ccb = (mbox_ccb_t *)scb->ccb; pthru = ccb->pthru; epthru = ccb->epthru; mbox = ccb->mbox; // Make sure f/w has completed a valid command if (scb->state != SCB_ISSUED) { con_log(CL_ANN, (KERN_CRIT "megaraid critical err: invalid command %d:%d:%p\n", scb->sno, scb->state, scp)); BUG(); continue; // Must never happen! } // check for the management command and complete it right away if (scb->sno >= MBOX_MAX_SCSI_CMDS) { scb->state = SCB_FREE; scb->status = status; // remove from local clist list_del_init(&scb->list); megaraid_mbox_mm_done(adapter, scb); continue; } // Was an abort issued for this command earlier if (scb->state & SCB_ABORT) { con_log(CL_ANN, (KERN_NOTICE "megaraid: aborted cmd %lx[%x] completed\n", scp->serial_number, scb->sno)); } /* * If the inquiry came of a disk drive which is not part of * any RAID array, expose it to the kernel. For this to be * enabled, user must set the "megaraid_expose_unconf_disks" * flag to 1 by specifying it on module parameter list. * This would enable data migration off drives from other * configurations. */ islogical = MRAID_IS_LOGICAL(adapter, scp); if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0 && IS_RAID_CH(raid_dev, scb->dev_channel)) { if (scp->use_sg) { sgl = (struct scatterlist *) scp->request_buffer; if (sgl->page) { c = *(unsigned char *) (page_address((&sgl[0])->page) + (&sgl[0])->offset); } else { con_log(CL_ANN, (KERN_WARNING "megaraid mailbox: invalid sg:%d\n", __LINE__)); c = 0; } } else { c = *(uint8_t *)scp->request_buffer; } if ((c & 0x1F ) == TYPE_DISK) { pdev_index = (scb->dev_channel * 16) + scb->dev_target; pdev_state = raid_dev->pdrv_state[pdev_index] & 0x0F; if (pdev_state == PDRV_ONLINE || pdev_state == PDRV_FAILED || pdev_state == PDRV_RBLD || pdev_state == PDRV_HOTSPARE || megaraid_expose_unconf_disks == 0) { status = 0xF0; } } } // Convert MegaRAID status to Linux error code switch (status) { case 0x00: scp->result = (DID_OK << 16); break; case 0x02: /* set sense_buffer and result fields */ if (mbox->cmd == MBOXCMD_PASSTHRU || mbox->cmd == MBOXCMD_PASSTHRU64) { memcpy(scp->sense_buffer, pthru->reqsensearea, 14); scp->result = DRIVER_SENSE << 24 | DID_OK << 16 | CHECK_CONDITION << 1; } else { if (mbox->cmd == MBOXCMD_EXTPTHRU) { memcpy(scp->sense_buffer, epthru->reqsensearea, 14); scp->result = DRIVER_SENSE << 24 | DID_OK << 16 | CHECK_CONDITION << 1; } else { scp->sense_buffer[0] = 0x70; scp->sense_buffer[2] = ABORTED_COMMAND; scp->result = CHECK_CONDITION << 1; } } break; case 0x08: scp->result = DID_BUS_BUSY << 16 | status; break; default: /* * If TEST_UNIT_READY fails, we know RESERVATION_STATUS * failed */ if (scp->cmnd[0] == TEST_UNIT_READY) { scp->result = DID_ERROR << 16 | RESERVATION_CONFLICT << 1; } else /* * Error code returned is 1 if Reserve or Release * failed or the input parameter is invalid */ if (status == 1 && (scp->cmnd[0] == RESERVE || scp->cmnd[0] == RELEASE)) { scp->result = DID_ERROR << 16 | RESERVATION_CONFLICT << 1; } else { scp->result = DID_BAD_TARGET << 16 | status; } } // print a debug message for all failed commands if (status) { megaraid_mbox_display_scb(adapter, scb); } // Free our internal resources and call the mid-layer callback // routine megaraid_mbox_sync_scb(adapter, scb); // remove from local clist list_del_init(&scb->list); // put back in free list megaraid_dealloc_scb(adapter, scb); // send the scsi packet back to kernel scp->scsi_done(scp); } return;}/** * megaraid_abort_handler - abort the scsi command * @scp : command to be aborted * * Abort a previous SCSI request. Only commands on the pending list can be * aborted. All the commands issued to the F/W must complete. **/static intmegaraid_abort_handler(struct scsi_cmnd *scp){ adapter_t *adapter; mraid_device_t *raid_dev; scb_t *scb; scb_t *tmp; int found; unsigned long flags; int i; adapter = SCP2ADAPTER(scp); raid_dev = ADAP2RAIDDEV(adapter); con_log(CL_ANN, (KERN_WARNING "megaraid: aborting-%ld cmd=%x <c=%d t=%d l=%d>\n", scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp), SCP2TARGET(scp), SCP2LUN(scp))); // If FW has stopped responding, simply return failure if (raid_dev->hw_error) { con_log(CL_ANN, (KERN_NOTICE "megaraid: hw error, not aborting\n")); return FAILED; } // There might a race here, where the command was completed by the // firmware and now it is on the completed list. Before we could // complete the command to the kernel in dpc, the abort came. // Find out if this is the case to avoid the race. scb = NULL; spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) { if (scb->scp == scp) { // Found command list_del_init(&scb->list); // from completed list con_log(CL_ANN, (KERN_WARNING "megaraid: %ld:%d[%d:%d], abort from completed list\n", scp->serial_number, scb->sno, scb->dev_channel, scb->dev_target)); scp->result = (DID_ABORT << 16); scp->scsi_done(scp); megaraid_dealloc_scb(adapter, scb); spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); return SUCCESS; } } spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); // Find out if this command is still on the pending list. If it is and // was never issued, abort and return success. If the command is owned // by the firmware, we must wait for it to complete by the FW. spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { if (scb->scp == scp) { // Found command list_del_init(&scb->list); // from pending list ASSERT(!(scb->state & SCB_ISSUED)); con_log(CL_ANN, (KERN_WARNING "megaraid abort: %ld[%d:%d], driver owner\n", scp->serial_number, scb->dev_channel, scb->dev_target)); scp->result = (DID_ABORT << 16); scp->scsi_done(scp); megaraid_dealloc_scb(adapter, scb); spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); return SUCCESS; } } spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); // Check do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -