📄 megaraid_sas.c
字号:
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); if (IS_DMA64) { ldio->flags |= MFI_FRAME_SGL64; ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); } else ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); /* * Sense info specific */ ldio->sense_len = SCSI_SENSE_BUFFERSIZE; ldio->sense_buf_phys_addr_hi = 0; ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr; sge_bytes = sge_sz * ldio->sge_count; cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1; if (cmd->frame_count > 7) cmd->frame_count = 8; return cmd->frame_count;}/** * megasas_build_cmd - Prepares a command packet * @instance: Adapter soft state * @scp: SCSI command * @frame_count: [OUT] Number of frames used to prepare this command */static inline struct megasas_cmd *megasas_build_cmd(struct megasas_instance *instance, struct scsi_cmnd *scp, int *frame_count){ u32 logical_cmd; struct megasas_cmd *cmd; /* * Find out if this is logical or physical drive command. */ logical_cmd = MEGASAS_IS_LOGICAL(scp); /* * Logical drive command */ if (logical_cmd) { if (scp->device->id >= MEGASAS_MAX_LD) { scp->result = DID_BAD_TARGET << 16; return NULL; } switch (scp->cmnd[0]) { case READ_10: case WRITE_10: case READ_12: case WRITE_12: case READ_6: case WRITE_6: case READ_16: case WRITE_16: /* * Fail for LUN > 0 */ if (scp->device->lun) { scp->result = DID_BAD_TARGET << 16; return NULL; } cmd = megasas_get_cmd(instance); if (!cmd) { scp->result = DID_IMM_RETRY << 16; return NULL; } *frame_count = megasas_build_ldio(instance, scp, cmd); if (!(*frame_count)) { megasas_return_cmd(instance, cmd); return NULL; } return cmd; default: /* * Fail for LUN > 0 */ if (scp->device->lun) { scp->result = DID_BAD_TARGET << 16; return NULL; } cmd = megasas_get_cmd(instance); if (!cmd) { scp->result = DID_IMM_RETRY << 16; return NULL; } *frame_count = megasas_build_dcdb(instance, scp, cmd); if (!(*frame_count)) { megasas_return_cmd(instance, cmd); return NULL; } return cmd; } } else { cmd = megasas_get_cmd(instance); if (!cmd) { scp->result = DID_IMM_RETRY << 16; return NULL; } *frame_count = megasas_build_dcdb(instance, scp, cmd); if (!(*frame_count)) { megasas_return_cmd(instance, cmd); return NULL; } return cmd; } return NULL;}/** * megasas_queue_command - Queue entry point * @scmd: SCSI command to be queued * @done: Callback entry point */static intmegasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)){ u32 frame_count; unsigned long flags; struct megasas_cmd *cmd; struct megasas_instance *instance; instance = (struct megasas_instance *) scmd->device->host->hostdata; scmd->scsi_done = done; scmd->result = 0; cmd = megasas_build_cmd(instance, scmd, &frame_count); if (!cmd) { done(scmd); return 0; } cmd->scmd = scmd; scmd->SCp.ptr = (char *)cmd; scmd->SCp.sent_command = jiffies; /* * Issue the command to the FW */ spin_lock_irqsave(&instance->instance_lock, flags); instance->fw_outstanding++; spin_unlock_irqrestore(&instance->instance_lock, flags); writel(((cmd->frame_phys_addr >> 3) | (cmd->frame_count - 1)), &instance->reg_set->inbound_queue_port); return 0;}/** * megasas_wait_for_outstanding - Wait for all outstanding cmds * @instance: Adapter soft state * * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to * complete all its outstanding commands. Returns error if one or more IOs * are pending after this time period. It also marks the controller dead. */static int megasas_wait_for_outstanding(struct megasas_instance *instance){ int i; u32 wait_time = MEGASAS_RESET_WAIT_TIME; for (i = 0; i < wait_time; i++) { if (!instance->fw_outstanding) break; if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { printk(KERN_NOTICE "megasas: [%2d]waiting for %d " "commands to complete\n", i, instance->fw_outstanding); } msleep(1000); } if (instance->fw_outstanding) { instance->hw_crit_error = 1; return FAILED; } return SUCCESS;}/** * megasas_generic_reset - Generic reset routine * @scmd: Mid-layer SCSI command * * This routine implements a generic reset handler for device, bus and host * reset requests. Device, bus and host specific reset handlers can use this * function after they do their specific tasks. */static int megasas_generic_reset(struct scsi_cmnd *scmd){ int ret_val; struct megasas_instance *instance; instance = (struct megasas_instance *)scmd->device->host->hostdata; scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x\n", scmd->serial_number, scmd->cmnd[0]); if (instance->hw_crit_error) { printk(KERN_ERR "megasas: cannot recover from previous reset " "failures\n"); return FAILED; } ret_val = megasas_wait_for_outstanding(instance); if (ret_val == SUCCESS) printk(KERN_NOTICE "megasas: reset successful \n"); else printk(KERN_ERR "megasas: failed to do reset\n"); return ret_val;}static enum scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd){ unsigned long seconds; if (scmd->SCp.ptr) { seconds = (jiffies - scmd->SCp.sent_command) / HZ; if (seconds < 90) { return EH_RESET_TIMER; } else { return EH_NOT_HANDLED; } } return EH_HANDLED;}/** * megasas_reset_device - Device reset handler entry point */static int megasas_reset_device(struct scsi_cmnd *scmd){ int ret; /* * First wait for all commands to complete */ ret = megasas_generic_reset(scmd); return ret;}/** * megasas_reset_bus_host - Bus & host reset handler entry point */static int megasas_reset_bus_host(struct scsi_cmnd *scmd){ int ret; /* * Frist wait for all commands to complete */ ret = megasas_generic_reset(scmd); return ret;}/** * megasas_service_aen - Processes an event notification * @instance: Adapter soft state * @cmd: AEN command completed by the ISR * * For AEN, driver sends a command down to FW that is held by the FW till an * event occurs. When an event of interest occurs, FW completes the command * that it was previously holding. * * This routines sends SIGIO signal to processes that have registered with the * driver for AEN. */static voidmegasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd){ /* * Don't signal app if it is just an aborted previously registered aen */ if (!cmd->abort_aen) kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); else cmd->abort_aen = 0; instance->aen_cmd = NULL; megasas_return_cmd(instance, cmd);}/* * Scsi host template for megaraid_sas driver */static struct scsi_host_template megasas_template = { .module = THIS_MODULE, .name = "LSI Logic SAS based MegaRAID driver", .proc_name = "megaraid_sas", .queuecommand = megasas_queue_command, .eh_device_reset_handler = megasas_reset_device, .eh_bus_reset_handler = megasas_reset_bus_host, .eh_host_reset_handler = megasas_reset_bus_host, .eh_timed_out = megasas_reset_timer, .use_clustering = ENABLE_CLUSTERING,};/** * megasas_complete_int_cmd - Completes an internal command * @instance: Adapter soft state * @cmd: Command to be completed * * The megasas_issue_blocked_cmd() function waits for a command to complete * after it issues a command. This function wakes up that waiting routine by * calling wake_up() on the wait queue. */static voidmegasas_complete_int_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd){ cmd->cmd_status = cmd->frame->io.cmd_status; if (cmd->cmd_status == ENODATA) { cmd->cmd_status = 0; } wake_up(&instance->int_cmd_wait_q);}/** * megasas_complete_abort - Completes aborting a command * @instance: Adapter soft state * @cmd: Cmd that was issued to abort another cmd * * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q * after it issues an abort on a previously issued command. This function * wakes up all functions waiting on the same wait queue. */static voidmegasas_complete_abort(struct megasas_instance *instance, struct megasas_cmd *cmd){ if (cmd->sync_cmd) { cmd->sync_cmd = 0; cmd->cmd_status = 0; wake_up(&instance->abort_cmd_wait_q); } return;}/** * megasas_unmap_sgbuf - Unmap SG buffers * @instance: Adapter soft state * @cmd: Completed command */static inline voidmegasas_unmap_sgbuf(struct megasas_instance *instance, struct megasas_cmd *cmd){ dma_addr_t buf_h; u8 opcode; if (cmd->scmd->use_sg) { pci_unmap_sg(instance->pdev, cmd->scmd->request_buffer, cmd->scmd->use_sg, cmd->scmd->sc_data_direction); return; } if (!cmd->scmd->request_bufflen) return; opcode = cmd->frame->hdr.cmd; if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) { if (IS_DMA64) buf_h = cmd->frame->io.sgl.sge64[0].phys_addr; else buf_h = cmd->frame->io.sgl.sge32[0].phys_addr; } else { if (IS_DMA64) buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr; else buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr; } pci_unmap_single(instance->pdev, buf_h, cmd->scmd->request_bufflen, cmd->scmd->sc_data_direction); return;}/** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state * @cmd: Command to be completed * @alt_status: If non-zero, use this value as status to * SCSI mid-layer instead of the value returned * by the FW. This should be used if caller wants * an alternate status (as in the case of aborted * commands) */static inline voidmegasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status){ int exception = 0; struct megasas_header *hdr = &cmd->frame->hdr; unsigned long flags; if (cmd->scmd) { cmd->scmd->SCp.ptr = (char *)0; } switch (hdr->cmd) { case MFI_CMD_PD_SCSI_IO: case MFI_CMD_LD_SCSI_IO: /* * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been * issued either through an IO path or an IOCTL path. If it * was via IOCTL, we will send it to internal completion. */ if (cmd->sync_cmd) { cmd->sync_cmd = 0; megasas_complete_int_cmd(instance, cmd); break; } /* * Don't export physical disk devices to mid-layer. */ if (!MEGASAS_IS_LOGICAL(cmd->scmd) && (hdr->cmd_status == MFI_STAT_OK) && (cmd->scmd->cmnd[0] == INQUIRY)) { if (((*(u8 *) cmd->scmd->request_buffer) & 0x1F) == TYPE_DISK) { cmd->scmd->result = DID_BAD_TARGET << 16; exception = 1; } } case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: if (alt_status) { cmd->scmd->result = alt_status << 16; exception = 1; } if (exception) { spin_lock_irqsave(&instance->instance_lock, flags); instance->fw_outstanding--; spin_unlock_irqrestore(&instance->instance_lock, flags); megasas_unmap_sgbuf(instance, cmd); cmd->scmd->scsi_done(cmd->scmd); megasas_return_cmd(instance, cmd); break; } switch (hdr->cmd_status) { case MFI_STAT_OK: cmd->scmd->result = DID_OK << 16; break; case MFI_STAT_SCSI_IO_FAILED: case MFI_STAT_LD_INIT_IN_PROGRESS: cmd->scmd->result = (DID_ERROR << 16) | hdr->scsi_status; break; case MFI_STAT_SCSI_DONE_WITH_ERROR: cmd->scmd->result = (DID_OK << 16) | hdr->scsi_status; if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) { memset(cmd->scmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); memcpy(cmd->scmd->sense_buffer, cmd->sense, hdr->sense_len); cmd->scmd->result |= DRIVER_SENSE << 24; } break; case MFI_STAT_LD_OFFLINE: case MFI_STAT_DEVICE_NOT_FOUND: cmd->scmd->result = DID_BAD_TARGET << 16; break; default: printk(KERN_DEBUG "megasas: MFI FW status %#x\n", hdr->cmd_status); cmd->scmd->result = DID_ERROR << 16; break; } spin_lock_irqsave(&instance->instance_lock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -