📄 megaraid_sas.c
字号:
if (!outstanding) break; if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { printk(KERN_NOTICE "megasas: [%2d]waiting for %d " "commands to complete\n",i,outstanding); /* * Call cmd completion routine. Cmd to be * be completed directly without depending on isr. */ megasas_complete_cmd_dpc((unsigned long)instance); } msleep(1000); } if (atomic_read(&instance->fw_outstanding)) { /* * Send signal to FW to stop processing any pending cmds. * The controller will be taken offline by the OS now. */ writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); megasas_dump_pending_frames(instance); 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 retries=%x\n", scmd->serial_number, scmd->cmnd[0], scmd->retries); 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;} /** * megasas_reset_timer - quiesce the adapter if required * @scmd: scsi cmnd * * Sets the FW busy flag and reduces the host->can_queue if the * cmd has not been completed within the timeout period. */static enumscsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd){ struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr; struct megasas_instance *instance; unsigned long flags; if (time_after(jiffies, scmd->jiffies_at_alloc + (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) { return EH_NOT_HANDLED; } instance = cmd->instance; if (!(instance->flag & MEGASAS_FW_BUSY)) { /* FW is busy, throttle IO */ spin_lock_irqsave(instance->host->host_lock, flags); instance->host->can_queue = 16; instance->last_time = jiffies; instance->flag |= MEGASAS_FW_BUSY; spin_unlock_irqrestore(instance->host->host_lock, flags); } return EH_RESET_TIMER;}/** * 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; /* * First wait for all commands to complete */ ret = megasas_generic_reset(scmd); return ret;}/** * megasas_bios_param - Returns disk geometry for a disk * @sdev: device handle * @bdev: block device * @capacity: drive capacity * @geom: geometry parameters */static intmegasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]){ int heads; int sectors; sector_t cylinders; unsigned long tmp; /* Default heads (64) & sectors (32) */ heads = 64; sectors = 32; tmp = heads * sectors; cylinders = capacity; sector_div(cylinders, tmp); /* * Handle extended translation size for logical drives > 1Gb */ if (capacity >= 0x200000) { heads = 255; sectors = 63; tmp = heads*sectors; cylinders = capacity; sector_div(cylinders, tmp); } geom[0] = heads; geom[1] = sectors; geom[2] = cylinders; return 0;}/** * 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);}static struct megasas_instance *megasas_lookup_instance(u16 host_no){ int i; for (i = 0; i < megasas_mgmt_info.max_index; i++) { if ((megasas_mgmt_info.instance[i]) && (megasas_mgmt_info.instance[i]->host->host_no == host_no)) return megasas_mgmt_info.instance[i]; } return NULL;}static int megasas_slave_alloc(struct scsi_device *sdev) { struct megasas_instance *instance ; int tmp_fastload = fast_load; instance = megasas_lookup_instance(sdev->host->host_no); if (tmp_fastload && sdev->channel < MEGASAS_MAX_PD_CHANNELS) { if ((sdev->id == MEGASAS_MAX_DEV_PER_CHANNEL -1) && (sdev->channel == MEGASAS_MAX_PD_CHANNELS - 1)) { /* If fast load option was set and scan for last device is * over, reset the fast_load flag so that during a possible * next scan, devices can be made available */ fast_load = 0; } return -ENXIO; } return 0;}/* * 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", .slave_configure = megasas_slave_configure, .slave_alloc = megasas_slave_alloc, .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, .bios_param = megasas_bios_param, .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 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 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 = NULL; 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; } case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: if (alt_status) { cmd->scmd->result = alt_status << 16; exception = 1; } if (exception) { atomic_dec(&instance->fw_outstanding); 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; } atomic_dec(&instance->fw_outstanding); megasas_unmap_sgbuf(instance, cmd); cmd->scmd->scsi_done(cmd->scmd); megasas_return_cmd(instance, cmd); break; case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_DCMD: /* * See if got an event notification */ if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT) megasas_service_aen(instance, cmd); else megasas_complete_int_cmd(instance, cmd); break; case MFI_CMD_ABORT: /* * Cmd issued to abort another cmd returned */ megasas_complete_abort(instance, cmd); break; default: printk("megasas: Unknown command completed! [0x%X]\n", hdr->cmd); break; } /* * Check if we can restore can_queue */ if (instance->flag & MEGASAS_FW_BUSY && time_after(jiffies, instance->last_time + 5 * HZ) && atomic_read(&instance->fw_outstanding) < 17) { spin_lock_irqsave(instance->host->host_lock, flags); instance->flag &= ~MEGASAS_FW_BUSY; instance->host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; spin_unlock_irqrestore(instance->host->host_lock, flags); } }/** * megasas_deplete_reply_queue - Processes all completed commands * @instance: Adapter soft state * @alt_status: Alternate status to be returned to * SCSI mid-layer instead of the status * returned by the FW */static intmegasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status){ /* * Check if it is our interrupt * Clear the interrupt */ if(instance->instancet->clear_intr(instance->reg_set)) return IRQ_NONE; if (instance->hw_crit_error) goto out_done; /* * Schedule the tasklet for cmd completion */ tasklet_schedule(&instance->isr_tasklet);out_done: return IRQ_HANDLED;}/** * megasas_isr - isr entry point */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -