⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 megaraid_mbox.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
	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 + -