megaraid_sas.c

来自「linux 内核源代码」· C语言 代码 · 共 2,561 行 · 第 1/5 页

C
2,561
字号
	return frame_count;}/** * megasas_build_dcdb -	Prepares a direct cdb (DCDB) command * @instance:		Adapter soft state * @scp:		SCSI command * @cmd:		Command to be prepared in * * This function prepares CDB commands. These are typcially pass-through * commands to the devices. */static intmegasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,		   struct megasas_cmd *cmd){	u32 is_logical;	u32 device_id;	u16 flags = 0;	struct megasas_pthru_frame *pthru;	is_logical = MEGASAS_IS_LOGICAL(scp);	device_id = MEGASAS_DEV_INDEX(instance, scp);	pthru = (struct megasas_pthru_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;	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 = scsi_bufflen(scp);	memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);	/*	 * Construct SGL	 */	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;	/*	 * 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 = megasas_get_frame_count(pthru->sge_count);	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 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;	/*	 * Prepare 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	 */	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;	/*	 * 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 = megasas_get_frame_count(ldio->sge_count);	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_dump_pending_frames -	Dumps the frame address of all pending cmds *                              	in FW * @instance:				Adapter soft state */static inline voidmegasas_dump_pending_frames(struct megasas_instance *instance){	struct megasas_cmd *cmd;	int i,n;	union megasas_sgl *mfi_sgl;	struct megasas_io_frame *ldio;	struct megasas_pthru_frame *pthru;	u32 sgcount;	u32 max_cmd = instance->max_fw_cmds;	printk(KERN_ERR "\nmegasas[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no);	printk(KERN_ERR "megasas[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding));	if (IS_DMA64)		printk(KERN_ERR "\nmegasas[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no);	else		printk(KERN_ERR "\nmegasas[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no);	printk(KERN_ERR "megasas[%d]: Pending OS cmds in FW : \n",instance->host->host_no);	for (i = 0; i < max_cmd; i++) {		cmd = instance->cmd_list[i];		if(!cmd->scmd)			continue;		printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr);		if (megasas_is_ldio(cmd->scmd)){			ldio = (struct megasas_io_frame *)cmd->frame;			mfi_sgl = &ldio->sgl;			sgcount = ldio->sge_count;			printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no, cmd->frame_count,ldio->cmd,ldio->target_id, ldio->start_lba_lo,ldio->start_lba_hi,ldio->sense_buf_phys_addr_lo,sgcount);		}		else {			pthru = (struct megasas_pthru_frame *) cmd->frame;			mfi_sgl = &pthru->sgl;			sgcount = pthru->sge_count;			printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no,cmd->frame_count,pthru->cmd,pthru->target_id,pthru->lun,pthru->cdb_len , pthru->data_xfer_len,pthru->sense_buf_phys_addr_lo,sgcount);		}	if(megasas_dbg_lvl & MEGASAS_DBG_LVL){		for (n = 0; n < sgcount; n++){			if (IS_DMA64)				printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%08lx ",mfi_sgl->sge64[n].length , (unsigned long)mfi_sgl->sge64[n].phys_addr) ;			else				printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%x ",mfi_sgl->sge32[n].length , mfi_sgl->sge32[n].phys_addr) ;			}		}		printk(KERN_ERR "\n");	} /*for max_cmd*/	printk(KERN_ERR "\nmegasas[%d]: Pending Internal cmds in FW : \n",instance->host->host_no);	for (i = 0; i < max_cmd; i++) {		cmd = instance->cmd_list[i];		if(cmd->sync_cmd == 1){			printk(KERN_ERR "0x%08lx : ", (unsigned long)cmd->frame_phys_addr);		}	}	printk(KERN_ERR "megasas[%d]: Dumping Done.\n\n",instance->host->host_no);}/** * 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;	struct megasas_cmd *cmd;	struct megasas_instance *instance;	instance = (struct megasas_instance *)	    scmd->device->host->hostdata;	/* Don't process if we have already declared adapter dead */	if (instance->hw_crit_error)		return SCSI_MLQUEUE_HOST_BUSY;	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;	}	switch (scmd->cmnd[0]) {	case SYNCHRONIZE_CACHE:		/*		 * FW takes care of flush cache on its own		 * No need to send it down		 */		scmd->result = DID_OK << 16;		goto out_done;	default:		break;	}	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;	/*	 * Issue the command to the FW	 */	atomic_inc(&instance->fw_outstanding);	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;	/*	 * The RAID firmware may require extended timeouts.	 */	if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS)		sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ;	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++) {		int outstanding = atomic_read(&instance->fw_outstanding);		if (!outstanding)			break;		if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {			printk(KERN_NOTICE "megasas: [%2d]waiting for %d "			       "commands to complete\n",i,outstanding);		}		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;}

⌨️ 快捷键说明

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