sym_glue.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 2,552 行 · 第 1/5 页

C
2,552
字号
	 *  Minimal checkings, so that we will not 	 *  go outside our tables.	 */	if (ccb->device->id == np->myaddr ||	    ccb->device->id >= SYM_CONF_MAX_TARGET ||	    ccb->device->lun >= SYM_CONF_MAX_LUN) {		sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE);		return 0;	}	/*	 *  Retreive the target descriptor.	 */	tp = &np->target[ccb->device->id];	/*	 *  Complete the 1st INQUIRY command with error 	 *  condition if the device is flagged NOSCAN 	 *  at BOOT in the NVRAM. This may speed up 	 *  the boot and maintain coherency with BIOS 	 *  device numbering. Clearing the flag allows 	 *  user to rescan skipped devices later.	 *  We also return error for devices not flagged 	 *  for SCAN LUNS in the NVRAM since some mono-lun 	 *  devices behave badly when asked for some non 	 *  zero LUN. Btw, this is an absolute hack.:-)	 */	if (ccb->cmnd[0] == 0x12 || ccb->cmnd[0] == 0x0) {		if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) ||		    ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) && 		     ccb->device->lun != 0)) {			tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;			sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE);			return 0;		}	}	/*	 *  Select tagged/untagged.	 */	lp = sym_lp(np, tp, ccb->device->lun);	order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0;	/*	 *  Queue the SCSI IO.	 */	cp = sym_get_ccb(np, ccb->device->id, ccb->device->lun, order);	if (!cp)		return 1;	/* Means resource shortage */	sym_queue_scsiio(np, ccb, cp);	return 0;}/* *  Setup buffers and pointers that address the CDB. */static inline int sym_setup_cdb(struct sym_hcb *np, struct scsi_cmnd *ccb, struct sym_ccb *cp){	u32	cmd_ba;	int	cmd_len;	/*	 *  CDB is 16 bytes max.	 */	if (ccb->cmd_len > sizeof(cp->cdb_buf)) {		sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID);		return -1;	}	memcpy(cp->cdb_buf, ccb->cmnd, ccb->cmd_len);	cmd_ba  = CCB_BA (cp, cdb_buf[0]);	cmd_len = ccb->cmd_len;	cp->phys.cmd.addr	= cpu_to_scr(cmd_ba);	cp->phys.cmd.size	= cpu_to_scr(cmd_len);	return 0;}/* *  Setup pointers that address the data and start the I/O. */int sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp){	int dir;	struct sym_tcb *tp = &np->target[cp->target];	struct sym_lcb *lp = sym_lp(np, tp, cp->lun);	/*	 *  Build the CDB.	 */	if (sym_setup_cdb(np, csio, cp))		goto out_abort;	/*	 *  No direction means no data.	 */	dir = csio->sc_data_direction;	if (dir != DMA_NONE) {		cp->segments = sym_scatter(np, cp, csio);		if (cp->segments < 0) {			if (cp->segments == -2)				sym_set_cam_status(csio, CAM_RESRC_UNAVAIL);			else				sym_set_cam_status(csio, CAM_REQ_TOO_BIG);			goto out_abort;		}	} else {		cp->data_len = 0;		cp->segments = 0;	}	/*	 *  Set data pointers.	 */	sym_setup_data_pointers(np, cp, dir);	/*	 *  When `#ifed 1', the code below makes the driver 	 *  panic on the first attempt to write to a SCSI device.	 *  It is the first test we want to do after a driver 	 *  change that does not seem obviously safe. :)	 */#if 0	switch (cp->cdb_buf[0]) {	case 0x0A: case 0x2A: case 0xAA:		panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n");		MDELAY(10000);		break;	default:		break;	}#endif	/*	 *	activate this job.	 */	if (lp)		sym_start_next_ccbs(np, lp, 2);	else		sym_put_start_queue(np, cp);	return 0;out_abort:	sym_free_ccb(np, cp);	sym_xpt_done(np, csio);	return 0;}/* *  timer daemon. * *  Misused to keep the driver running when *  interrupts are not configured correctly. */static void sym_timer(struct sym_hcb *np){	unsigned long thistime = jiffies;	/*	 *  Restart the timer.	 */	np->s.timer.expires = thistime + SYM_CONF_TIMER_INTERVAL;	add_timer(&np->s.timer);	/*	 *  If we are resetting the ncr, wait for settle_time before 	 *  clearing it. Then command processing will be resumed.	 */	if (np->s.settle_time_valid) {		if (time_before_eq(np->s.settle_time, thistime)) {			if (sym_verbose >= 2 )				printk("%s: command processing resumed\n",				       sym_name(np));			np->s.settle_time_valid = 0;		}		return;	}	/*	 *	Nothing to do for now, but that may come.	 */	if (np->s.lasttime + 4*HZ < thistime) {		np->s.lasttime = thistime;	}#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS	/*	 *  Some way-broken PCI bridges may lead to 	 *  completions being lost when the clearing 	 *  of the INTFLY flag by the CPU occurs 	 *  concurrently with the chip raising this flag.	 *  If this ever happen, lost completions will 	 * be reaped here.	 */	sym_wakeup_done(np);#endif}/* *  PCI BUS error handler. */void sym_log_bus_error(struct sym_hcb *np){	u_short pci_sts;	pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts);	if (pci_sts & 0xf900) {		pci_write_config_word(np->s.device, PCI_STATUS, pci_sts);		printf("%s: PCI STATUS = 0x%04x\n",			sym_name(np), pci_sts & 0xf900);	}}/* *  Requeue awaiting commands. */static void sym_requeue_awaiting_cmds(struct sym_hcb *np){	struct scsi_cmnd *cmd;	struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd);	SYM_QUEHEAD tmp_cmdq;	int sts;	sym_que_move(&np->s.wait_cmdq, &tmp_cmdq);	while ((ucp = (struct sym_ucmd *) sym_remque_head(&tmp_cmdq)) != 0) {		sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq);		cmd = SYM_SCMD_PTR(ucp);		sts = sym_queue_command(np, cmd);		if (sts) {			sym_remque(&ucp->link_cmdq);			sym_insque_head(&ucp->link_cmdq, &np->s.wait_cmdq);		}	}}/* * queuecommand method.  Entered with the host adapter lock held and * interrupts disabled. */static int sym53c8xx_queue_command(struct scsi_cmnd *cmd,					void (*done)(struct scsi_cmnd *)){	struct sym_hcb *np = SYM_SOFTC_PTR(cmd);	struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd);	int sts = 0;	cmd->scsi_done     = done;	cmd->host_scribble = NULL;	memset(ucp, 0, sizeof(*ucp));	/*	 *  Shorten our settle_time if needed for 	 *  this command not to time out.	 */	if (np->s.settle_time_valid && cmd->timeout_per_command) {		unsigned long tlimit = jiffies + cmd->timeout_per_command;		tlimit -= SYM_CONF_TIMER_INTERVAL*2;		if (time_after(np->s.settle_time, tlimit)) {			np->s.settle_time = tlimit;		}	}	if (np->s.settle_time_valid || !sym_que_empty(&np->s.wait_cmdq)) {		sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq);		goto out;	}	sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq);	sts = sym_queue_command(np, cmd);	if (sts) {		sym_remque(&ucp->link_cmdq);		sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq);	}out:	return 0;}/* *  Linux entry point of the interrupt handler. */static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs){	unsigned long flags;	struct sym_hcb *np = (struct sym_hcb *)dev_id;	if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");	spin_lock_irqsave(np->s.host->host_lock, flags);	sym_interrupt(np);	/*	 * push queue walk-through to tasklet	 */	if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid)		sym_requeue_awaiting_cmds(np);	spin_unlock_irqrestore(np->s.host->host_lock, flags);	if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");	return IRQ_HANDLED;}/* *  Linux entry point of the timer handler */static void sym53c8xx_timer(unsigned long npref){	struct sym_hcb *np = (struct sym_hcb *)npref;	unsigned long flags;	spin_lock_irqsave(np->s.host->host_lock, flags);	sym_timer(np);	if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid)		sym_requeue_awaiting_cmds(np);	spin_unlock_irqrestore(np->s.host->host_lock, flags);}/* *  What the eh thread wants us to perform. */#define SYM_EH_ABORT		0#define SYM_EH_DEVICE_RESET	1#define SYM_EH_BUS_RESET	2#define SYM_EH_HOST_RESET	3/* *  What we will do regarding the involved SCSI command. */#define SYM_EH_DO_IGNORE	0#define SYM_EH_DO_COMPLETE	1#define SYM_EH_DO_WAIT		2/* *  Our general completion handler. */static void __sym_eh_done(struct scsi_cmnd *cmd, int timed_out){	struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait;	if (!ep)		return;	/* Try to avoid a race here (not 100% safe) */	if (!timed_out) {		ep->timed_out = 0;		if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer))			return;	}	/* Revert everything */	SYM_UCMD_PTR(cmd)->eh_wait = NULL;	cmd->scsi_done = ep->old_done;	/* Wake up the eh thread if it wants to sleep */	if (ep->to_do == SYM_EH_DO_WAIT)		up(&ep->sem);}/* *  scsi_done() alias when error recovery is in progress.  */static void sym_eh_done(struct scsi_cmnd *cmd) { __sym_eh_done(cmd, 0); }/* *  Some timeout handler to avoid waiting too long. */static void sym_eh_timeout(u_long p) { __sym_eh_done((struct scsi_cmnd *)p, 1); }/* *  Generic method for our eh processing. *  The 'op' argument tells what we have to do. */static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd){	struct sym_hcb *np = SYM_SOFTC_PTR(cmd);	SYM_QUEHEAD *qp;	int to_do = SYM_EH_DO_IGNORE;	int sts = -1;	struct sym_eh_wait eh, *ep = &eh;	char devname[20];	sprintf(devname, "%s:%d:%d", sym_name(np), cmd->device->id, cmd->device->lun);	printf_warning("%s: %s operation started.\n", devname, opname);#if 0	/* This one should be the result of some race, thus to ignore */	if (cmd->serial_number != cmd->serial_number_at_timeout)		goto prepare;#endif	/* This one is not queued to the core driver -> to complete here */ 	FOR_EACH_QUEUED_ELEMENT(&np->s.wait_cmdq, qp) {		if (SYM_SCMD_PTR(qp) == cmd) {			to_do = SYM_EH_DO_COMPLETE;			goto prepare;		}	}	/* This one is queued in some place -> to wait for completion */	FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {		struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);		if (cp->cam_ccb == cmd) {			to_do = SYM_EH_DO_WAIT;			goto prepare;		}	}prepare:	/* Prepare stuff to either ignore, complete or wait for completion */	switch(to_do) {	default:	case SYM_EH_DO_IGNORE:		break;	case SYM_EH_DO_WAIT:		init_MUTEX_LOCKED(&ep->sem);		/* fall through */	case SYM_EH_DO_COMPLETE:		ep->old_done = cmd->scsi_done;		cmd->scsi_done = sym_eh_done;		SYM_UCMD_PTR(cmd)->eh_wait = ep;	}	/* Try to proceed the operation we have been asked for */	sts = -1;	switch(op) {	case SYM_EH_ABORT:		sts = sym_abort_scsiio(np, cmd, 1);		break;	case SYM_EH_DEVICE_RESET:		sts = sym_reset_scsi_target(np, cmd->device->id);		break;	case SYM_EH_BUS_RESET:		sym_reset_scsi_bus(np, 1);		sts = 0;		break;	case SYM_EH_HOST_RESET:		sym_reset_scsi_bus(np, 0);		sym_start_up (np, 1);		sts = 0;		break;	default:		break;	}	/* On error, restore everything and cross fingers :) */	if (sts) {		SYM_UCMD_PTR(cmd)->eh_wait = NULL;		cmd->scsi_done = ep->old_done;		to_do = SYM_EH_DO_IGNORE;	}	ep->to_do = to_do;	/* Complete the command with locks held as required by the driver */	if (to_do == SYM_EH_DO_COMPLETE)		sym_xpt_done2(np, cmd, CAM_REQ_ABORTED);	/* Wait for completion with locks released, as required by kernel */	if (to_do == SYM_EH_DO_WAIT) {		init_timer(&ep->timer);		ep->timer.expires = jiffies + (5*HZ);		ep->timer.function = sym_eh_timeout;		ep->timer.data = (u_long)cmd;		ep->timed_out = 1;	/* Be pessimistic for once :) */		add_timer(&ep->timer);		spin_unlock_irq(np->s.host->host_lock);		down(&ep->sem);		spin_lock_irq(np->s.host->host_lock);		if (ep->timed_out)			sts = -2;	}	printf_warning("%s: %s operation %s.\n", devname, opname,			sts==0?"complete":sts==-2?"timed-out":"failed");	return sts? SCSI_FAILED : SCSI_SUCCESS;}/* * Error handlers called from the eh thread (one thread per HBA). */static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd){	return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);}static int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd){	return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);}static int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd){	return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);}static int sym53c8xx_eh_host_reset_handler(struct scsi_cmnd *cmd){	return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);}/* *  Tune device queuing depth, according to various limits.

⌨️ 快捷键说明

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