megaraid_sas.c

来自「linux2.6.16版本」· C语言 代码 · 共 2,625 行 · 第 1/5 页

C
2,625
字号
	if (scp->sc_data_direction == PCI_DMA_TODEVICE)		flags = MFI_FRAME_DIR_WRITE;	else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)		flags = MFI_FRAME_DIR_READ;	else if (scp->sc_data_direction == PCI_DMA_NONE)		flags = MFI_FRAME_DIR_NONE;	/*	 * Prepare the DCDB frame	 */	pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO : MFI_CMD_PD_SCSI_IO;	pthru->cmd_status = 0x0;	pthru->scsi_status = 0x0;	pthru->target_id = device_id;	pthru->lun = scp->device->lun;	pthru->cdb_len = scp->cmd_len;	pthru->timeout = 0;	pthru->flags = flags;	pthru->data_xfer_len = scp->request_bufflen;	memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);	/*	 * Construct SGL	 */	sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :	    sizeof(struct megasas_sge32);	if (IS_DMA64) {		pthru->flags |= MFI_FRAME_SGL64;		pthru->sge_count = megasas_make_sgl64(instance, scp,						      &pthru->sgl);	} else		pthru->sge_count = megasas_make_sgl32(instance, scp,						      &pthru->sgl);	/*	 * Sense info specific	 */	pthru->sense_len = SCSI_SENSE_BUFFERSIZE;	pthru->sense_buf_phys_addr_hi = 0;	pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr;	sge_bytes = sge_sz * pthru->sge_count;	/*	 * Compute the total number of frames this command consumes. FW uses	 * this number to pull sufficient number of frames from host memory.	 */	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_ldio -	Prepares IOs to logical devices * @instance:		Adapter soft state * @scp:		SCSI command * @cmd:		Command to to be prepared * * Frames (and accompanying SGLs) for regular SCSI IOs use this function. */static intmegasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,		   struct megasas_cmd *cmd){	u32 sge_sz;	int sge_bytes;	u32 device_id;	u8 sc = scp->cmnd[0];	u16 flags = 0;	struct megasas_io_frame *ldio;	device_id = MEGASAS_DEV_INDEX(instance, scp);	ldio = (struct megasas_io_frame *)cmd->frame;	if (scp->sc_data_direction == PCI_DMA_TODEVICE)		flags = MFI_FRAME_DIR_WRITE;	else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)		flags = MFI_FRAME_DIR_READ;	/*	 * Preare the Logical IO frame: 2nd bit is zero for all read cmds	 */	ldio->cmd = (sc & 0x02) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ;	ldio->cmd_status = 0x0;	ldio->scsi_status = 0x0;	ldio->target_id = device_id;	ldio->timeout = 0;	ldio->reserved_0 = 0;	ldio->pad_0 = 0;	ldio->flags = flags;	ldio->start_lba_hi = 0;	ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0;	/*	 * 6-byte READ(0x08) or WRITE(0x0A) cdb	 */	if (scp->cmd_len == 6) {		ldio->lba_count = (u32) scp->cmnd[4];		ldio->start_lba_lo = ((u32) scp->cmnd[1] << 16) |		    ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3];		ldio->start_lba_lo &= 0x1FFFFF;	}	/*	 * 10-byte READ(0x28) or WRITE(0x2A) cdb	 */	else if (scp->cmd_len == 10) {		ldio->lba_count = (u32) scp->cmnd[8] |		    ((u32) scp->cmnd[7] << 8);		ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) |		    ((u32) scp->cmnd[3] << 16) |		    ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];	}	/*	 * 12-byte READ(0xA8) or WRITE(0xAA) cdb	 */	else if (scp->cmd_len == 12) {		ldio->lba_count = ((u32) scp->cmnd[6] << 24) |		    ((u32) scp->cmnd[7] << 16) |		    ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];		ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) |		    ((u32) scp->cmnd[3] << 16) |		    ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];	}	/*	 * 16-byte READ(0x88) or WRITE(0x8A) cdb	 */	else if (scp->cmd_len == 16) {		ldio->lba_count = ((u32) scp->cmnd[10] << 24) |		    ((u32) scp->cmnd[11] << 16) |		    ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13];		ldio->start_lba_lo = ((u32) scp->cmnd[6] << 24) |		    ((u32) scp->cmnd[7] << 16) |		    ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];		ldio->start_lba_hi = ((u32) scp->cmnd[2] << 24) |		    ((u32) scp->cmnd[3] << 16) |		    ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];	}	/*	 * Construct SGL	 */	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_is_ldio -		Checks if the cmd is for logical drive * @scmd:			SCSI command *	 * Called by megasas_queue_command to find out if the command to be queued * is a logical drive command	 */static inline int megasas_is_ldio(struct scsi_cmnd *cmd){	if (!MEGASAS_IS_LOGICAL(cmd))		return 0;	switch (cmd->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:		return 1;	default:		return 0;	}}/** * 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;	if (MEGASAS_IS_LOGICAL(scmd) &&	    (scmd->device->id >= MEGASAS_MAX_LD || scmd->device->lun)) {		scmd->result = DID_BAD_TARGET << 16;		goto out_done;	}	cmd = megasas_get_cmd(instance);	if (!cmd)		return SCSI_MLQUEUE_HOST_BUSY;	/*	 * Logical drive command	 */	if (megasas_is_ldio(scmd))		frame_count = megasas_build_ldio(instance, scmd, cmd);	else		frame_count = megasas_build_dcdb(instance, scmd, cmd);	if (!frame_count)		goto out_return_cmd;	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);	instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);	return 0; out_return_cmd:	megasas_return_cmd(instance, cmd); out_done:	done(scmd);	return 0;}static int megasas_slave_configure(struct scsi_device *sdev){	/*	 * Don't export physical disk devices to the disk driver.	 *	 * FIXME: Currently we don't export them to the midlayer at all.	 * 	  That will be fixed once LSI engineers have audited the	 * 	  firmware for possible issues.	 */	if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK)		return -ENXIO;	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",	.slave_configure = megasas_slave_configure,	.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 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;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?