aic7xxx_osm.c

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

C
2,346
字号
		 */		dev->openings = 1;		ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);		ahc_platform_set_tags(ahc, sdev, &devinfo,			     (dev->flags & AHC_DEV_Q_BASIC)			   ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);		break;	}	}}static voidahc_linux_queue_cmd_complete(struct ahc_softc *ahc, struct scsi_cmnd *cmd){	/*	 * Map CAM error codes into Linux Error codes.  We	 * avoid the conversion so that the DV code has the	 * full error information available when making	 * state change decisions.	 */	{		u_int new_status;		switch (ahc_cmd_get_transaction_status(cmd)) {		case CAM_REQ_INPROG:		case CAM_REQ_CMP:		case CAM_SCSI_STATUS_ERROR:			new_status = DID_OK;			break;		case CAM_REQ_ABORTED:			new_status = DID_ABORT;			break;		case CAM_BUSY:			new_status = DID_BUS_BUSY;			break;		case CAM_REQ_INVALID:		case CAM_PATH_INVALID:			new_status = DID_BAD_TARGET;			break;		case CAM_SEL_TIMEOUT:			new_status = DID_NO_CONNECT;			break;		case CAM_SCSI_BUS_RESET:		case CAM_BDR_SENT:			new_status = DID_RESET;			break;		case CAM_UNCOR_PARITY:			new_status = DID_PARITY;			break;		case CAM_CMD_TIMEOUT:			new_status = DID_TIME_OUT;			break;		case CAM_UA_ABORT:		case CAM_REQ_CMP_ERR:		case CAM_AUTOSENSE_FAIL:		case CAM_NO_HBA:		case CAM_DATA_RUN_ERR:		case CAM_UNEXP_BUSFREE:		case CAM_SEQUENCE_FAIL:		case CAM_CCB_LEN_ERR:		case CAM_PROVIDE_FAIL:		case CAM_REQ_TERMIO:		case CAM_UNREC_HBA_ERROR:		case CAM_REQ_TOO_BIG:			new_status = DID_ERROR;			break;		case CAM_REQUEUE_REQ:			new_status = DID_REQUEUE;			break;		default:			/* We should never get here */			new_status = DID_ERROR;			break;		}		ahc_cmd_set_transaction_status(cmd, new_status);	}	cmd->scsi_done(cmd);}static voidahc_linux_freeze_simq(struct ahc_softc *ahc){	unsigned long s;	ahc_lock(ahc, &s);	ahc->platform_data->qfrozen++;	if (ahc->platform_data->qfrozen == 1) {		scsi_block_requests(ahc->platform_data->host);		/* XXX What about Twin channels? */		ahc_platform_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,					CAM_LUN_WILDCARD, SCB_LIST_NULL,					ROLE_INITIATOR, CAM_REQUEUE_REQ);	}	ahc_unlock(ahc, &s);}static voidahc_linux_release_simq(struct ahc_softc *ahc){	u_long s;	int    unblock_reqs;	unblock_reqs = 0;	ahc_lock(ahc, &s);	if (ahc->platform_data->qfrozen > 0)		ahc->platform_data->qfrozen--;	if (ahc->platform_data->qfrozen == 0)		unblock_reqs = 1;	ahc_unlock(ahc, &s);	/*	 * There is still a race here.  The mid-layer	 * should keep its own freeze count and use	 * a bottom half handler to run the queues	 * so we can unblock with our own lock held.	 */	if (unblock_reqs)		scsi_unblock_requests(ahc->platform_data->host);}static intahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag){	struct ahc_softc *ahc;	struct ahc_linux_device *dev;	struct scb *pending_scb;	u_int  saved_scbptr;	u_int  active_scb_index;	u_int  last_phase;	u_int  saved_scsiid;	u_int  cdb_byte;	int    retval;	int    was_paused;	int    paused;	int    wait;	int    disconnected;	unsigned long flags;	pending_scb = NULL;	paused = FALSE;	wait = FALSE;	ahc = *(struct ahc_softc **)cmd->device->host->hostdata;	scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message\n",	       flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");	printf("CDB:");	for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)		printf(" 0x%x", cmd->cmnd[cdb_byte]);	printf("\n");	ahc_lock(ahc, &flags);	/*	 * First determine if we currently own this command.	 * Start by searching the device queue.  If not found	 * there, check the pending_scb list.  If not found	 * at all, and the system wanted us to just abort the	 * command, return success.	 */	dev = scsi_transport_device_data(cmd->device);	if (dev == NULL) {		/*		 * No target device for this command exists,		 * so we must not still own the command.		 */		printf("%s:%d:%d:%d: Is not an active device\n",		       ahc_name(ahc), cmd->device->channel, cmd->device->id,		       cmd->device->lun);		retval = SUCCESS;		goto no_cmd;	}	if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0	 && ahc_search_untagged_queues(ahc, cmd, cmd->device->id,				       cmd->device->channel + 'A',				       cmd->device->lun,				       CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {		printf("%s:%d:%d:%d: Command found on untagged queue\n",		       ahc_name(ahc), cmd->device->channel, cmd->device->id,		       cmd->device->lun);		retval = SUCCESS;		goto done;	}	/*	 * See if we can find a matching cmd in the pending list.	 */	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {		if (pending_scb->io_ctx == cmd)			break;	}	if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {		/* Any SCB for this device will do for a target reset */		LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {		  	if (ahc_match_scb(ahc, pending_scb, scmd_id(cmd),					  scmd_channel(cmd) + 'A',					  CAM_LUN_WILDCARD,					  SCB_LIST_NULL, ROLE_INITIATOR))				break;		}	}	if (pending_scb == NULL) {		scmd_printk(KERN_INFO, cmd, "Command not found\n");		goto no_cmd;	}	if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {		/*		 * We can't queue two recovery actions using the same SCB		 */		retval = FAILED;		goto  done;	}	/*	 * Ensure that the card doesn't do anything	 * behind our back and that we didn't "just" miss	 * an interrupt that would affect this cmd.	 */	was_paused = ahc_is_paused(ahc);	ahc_pause_and_flushwork(ahc);	paused = TRUE;	if ((pending_scb->flags & SCB_ACTIVE) == 0) {		scmd_printk(KERN_INFO, cmd, "Command already completed\n");		goto no_cmd;	}	printf("%s: At time of recovery, card was %spaused\n",	       ahc_name(ahc), was_paused ? "" : "not ");	ahc_dump_card_state(ahc);	disconnected = TRUE;	if (flag == SCB_ABORT) {		if (ahc_search_qinfifo(ahc, cmd->device->id,				       cmd->device->channel + 'A',				       cmd->device->lun,				       pending_scb->hscb->tag,				       ROLE_INITIATOR, CAM_REQ_ABORTED,				       SEARCH_COMPLETE) > 0) {			printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",			       ahc_name(ahc), cmd->device->channel,					cmd->device->id, cmd->device->lun);			retval = SUCCESS;			goto done;		}	} else if (ahc_search_qinfifo(ahc, cmd->device->id,				      cmd->device->channel + 'A',				      cmd->device->lun, pending_scb->hscb->tag,				      ROLE_INITIATOR, /*status*/0,				      SEARCH_COUNT) > 0) {		disconnected = FALSE;	}	if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {		struct scb *bus_scb;		bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));		if (bus_scb == pending_scb)			disconnected = FALSE;		else if (flag != SCB_ABORT		      && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid		      && ahc_inb(ahc, SAVED_LUN) == SCB_GET_LUN(pending_scb))			disconnected = FALSE;	}	/*	 * At this point, pending_scb is the scb associated with the	 * passed in command.  That command is currently active on the	 * bus, is in the disconnected state, or we're hoping to find	 * a command for the same target active on the bus to abuse to	 * send a BDR.  Queue the appropriate message based on which of	 * these states we are in.	 */	last_phase = ahc_inb(ahc, LASTPHASE);	saved_scbptr = ahc_inb(ahc, SCBPTR);	active_scb_index = ahc_inb(ahc, SCB_TAG);	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);	if (last_phase != P_BUSFREE	 && (pending_scb->hscb->tag == active_scb_index	  || (flag == SCB_DEVICE_RESET	   && SCSIID_TARGET(ahc, saved_scsiid) == scmd_id(cmd)))) {		/*		 * We're active on the bus, so assert ATN		 * and hope that the target responds.		 */		pending_scb = ahc_lookup_scb(ahc, active_scb_index);		pending_scb->flags |= SCB_RECOVERY_SCB|flag;		ahc_outb(ahc, MSG_OUT, HOST_MSG);		ahc_outb(ahc, SCSISIGO, last_phase|ATNO);		scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");		wait = TRUE;	} else if (disconnected) {		/*		 * Actually re-queue this SCB in an attempt		 * to select the device before it reconnects.		 * In either case (selection or reselection),		 * we will now issue the approprate message		 * to the timed-out device.		 *		 * Set the MK_MESSAGE control bit indicating		 * that we desire to send a message.  We		 * also set the disconnected flag since		 * in the paging case there is no guarantee		 * that our SCB control byte matches the		 * version on the card.  We don't want the		 * sequencer to abort the command thinking		 * an unsolicited reselection occurred.		 */		pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;		pending_scb->flags |= SCB_RECOVERY_SCB|flag;		/*		 * Remove any cached copy of this SCB in the		 * disconnected list in preparation for the		 * queuing of our abort SCB.  We use the		 * same element in the SCB, SCB_NEXT, for		 * both the qinfifo and the disconnected list.		 */		ahc_search_disc_list(ahc, cmd->device->id,				     cmd->device->channel + 'A',				     cmd->device->lun, pending_scb->hscb->tag,				     /*stop_on_first*/TRUE,				     /*remove*/TRUE,				     /*save_state*/FALSE);		/*		 * In the non-paging case, the sequencer will		 * never re-reference the in-core SCB.		 * To make sure we are notified during		 * reslection, set the MK_MESSAGE flag in		 * the card's copy of the SCB.		 */		if ((ahc->flags & AHC_PAGESCBS) == 0) {			ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);			ahc_outb(ahc, SCB_CONTROL,				 ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);		}		/*		 * Clear out any entries in the QINFIFO first		 * so we are the next SCB for this target		 * to run.		 */		ahc_search_qinfifo(ahc, cmd->device->id,				   cmd->device->channel + 'A',				   cmd->device->lun, SCB_LIST_NULL,				   ROLE_INITIATOR, CAM_REQUEUE_REQ,				   SEARCH_COMPLETE);		ahc_qinfifo_requeue_tail(ahc, pending_scb);		ahc_outb(ahc, SCBPTR, saved_scbptr);		ahc_print_path(ahc, pending_scb);		printf("Device is disconnected, re-queuing SCB\n");		wait = TRUE;	} else {		scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");		retval = FAILED;		goto done;	}no_cmd:	/*	 * Our assumption is that if we don't have the command, no	 * recovery action was required, so we return success.  Again,	 * the semantics of the mid-layer recovery engine are not	 * well defined, so this may change in time.	 */	retval = SUCCESS;done:	if (paused)		ahc_unpause(ahc);	if (wait) {		DECLARE_COMPLETION_ONSTACK(done);		ahc->platform_data->eh_done = &done;		ahc_unlock(ahc, &flags);		printf("Recovery code sleeping\n");		if (!wait_for_completion_timeout(&done, 5 * HZ)) {			ahc_lock(ahc, &flags);			ahc->platform_data->eh_done = NULL;			ahc_unlock(ahc, &flags);			printf("Timer Expired\n");			retval = FAILED;		}		printf("Recovery code awake\n");	} else		ahc_unlock(ahc, &flags);	return (retval);}voidahc_platform_dump_card_state(struct ahc_softc *ahc){}static void ahc_linux_set_width(struct scsi_target *starget, int width){	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);	struct ahc_devinfo devinfo;	unsigned long flags;	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,			    starget->channel + 'A', ROLE_INITIATOR);	ahc_lock(ahc, &flags);	ahc_set_width(ahc, &devinfo, width, AHC_TRANS_GOAL, FALSE);	ahc_unlock(ahc, &flags);}static void ahc_linux_set_period(struct scsi_target *starget, int period){	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);	struct ahc_tmode_tstate *tstate;	struct ahc_initiator_tinfo *tinfo 		= ahc_fetch_transinfo(ahc,				      starget->channel + 'A',				      shost->this_id, starget->id, &tstate);	struct ahc_devinfo devinfo;	unsigned int ppr_options = tinfo->goal.ppr_options;	unsigned long flags;	unsigned long offset = tinfo->goal.offset;	struct ahc_syncrate *syncrate;	if (offset == 0)		offset = MAX_OFFSET;	if (period < 9)		period = 9;	/* 12.5ns is our minimum */	if (period == 9) {		if (spi_max_width(starget))			ppr_options |= MSG_EXT_PPR_DT_REQ;		else			/* need wide for DT and need DT for 12.5 ns */			period = 10;	}	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,			    starget->channel + 'A', ROLE_INITIATOR);	/* all PPR requests apart from QAS require wide transfers */	if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) {		if (spi_width(starget) == 0)			ppr_options &= MSG_EXT_PPR_QAS_REQ;	}	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);	ahc_lock(ahc, &flags);	ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,			 ppr_options, AHC_TRANS_GOAL, FALSE);	ahc_unlock(ahc, &flags);}static void ahc_linux_set_offset(struct scsi_target *starget, int offset)

⌨️ 快捷键说明

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