sym_glue.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,149 行 · 第 1/4 页

C
2,149
字号
		break;	default:		break;	}#endif	/*	 *	activate this job.	 */	sym_put_start_queue(np, cp);	return 0;out_abort:	sym_free_ccb(np, cp);	sym_xpt_done(np, cmd);	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);	}}/* * 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;	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)		return SCSI_MLQUEUE_HOST_BUSY;	sts = sym_queue_command(np, cmd);	if (sts)		return SCSI_MLQUEUE_HOST_BUSY;	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);	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);	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_WAIT		2/* *  scsi_done() alias when error recovery is in progress. */static void sym_eh_done(struct scsi_cmnd *cmd){	struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);	BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));	cmd->scsi_done = ucmd->old_done;	if (ucmd->to_do == SYM_EH_DO_WAIT)		complete(ucmd->eh_done);}/* *  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);	struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);	struct Scsi_Host *host = cmd->device->host;	SYM_QUEHEAD *qp;	int to_do = SYM_EH_DO_IGNORE;	int sts = -1;	struct completion eh_done;	dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname);	spin_lock_irq(host->host_lock);	/* 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->cmd == cmd) {			to_do = SYM_EH_DO_WAIT;			break;		}	}	if (to_do == SYM_EH_DO_WAIT) {		init_completion(&eh_done);		ucmd->old_done = cmd->scsi_done;		ucmd->eh_done = &eh_done;		wmb();		cmd->scsi_done = sym_eh_done;	}	/* 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) {		cmd->scsi_done = ucmd->old_done;		to_do = SYM_EH_DO_IGNORE;	}	ucmd->to_do = to_do;	spin_unlock_irq(host->host_lock);	if (to_do == SYM_EH_DO_WAIT) {		if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {			ucmd->to_do = SYM_EH_DO_IGNORE;			wmb();			sts = -2;		}	}	dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", 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. */static void sym_tune_dev_queuing(struct sym_tcb *tp, int lun, u_short reqtags){	struct sym_lcb *lp = sym_lp(tp, lun);	u_short	oldtags;	if (!lp)		return;	oldtags = lp->s.reqtags;	if (reqtags > lp->s.scdev_depth)		reqtags = lp->s.scdev_depth;	lp->s.reqtags     = reqtags;	if (reqtags != oldtags) {		dev_info(&tp->starget->dev,		         "tagged command queuing %s, command queue depth %d.\n",		          lp->s.reqtags ? "enabled" : "disabled", reqtags);	}}/* *  Linux select queue depths function */#define DEF_DEPTH	(sym_driver_setup.max_tag)#define ALL_TARGETS	-2#define NO_TARGET	-1#define ALL_LUNS	-2#define NO_LUN		-1static int device_queue_depth(struct sym_hcb *np, int target, int lun){	int c, h, t, u, v;	char *p = sym_driver_setup.tag_ctrl;	char *ep;	h = -1;	t = NO_TARGET;	u = NO_LUN;	while ((c = *p++) != 0) {		v = simple_strtoul(p, &ep, 0);		switch(c) {		case '/':			++h;			t = ALL_TARGETS;			u = ALL_LUNS;			break;		case 't':			if (t != target)				t = (target == v) ? v : NO_TARGET;			u = ALL_LUNS;			break;		case 'u':			if (u != lun)				u = (lun == v) ? v : NO_LUN;			break;		case 'q':			if (h == np->s.unit &&				(t == ALL_TARGETS || t == target) &&				(u == ALL_LUNS    || u == lun))				return v;			break;		case '-':			t = ALL_TARGETS;			u = ALL_LUNS;			break;		default:			break;		}		p = ep;	}	return DEF_DEPTH;}static int sym53c8xx_slave_alloc(struct scsi_device *sdev){	struct sym_hcb *np = sym_get_hcb(sdev->host);	struct sym_tcb *tp = &np->target[sdev->id];	struct sym_lcb *lp;	if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN)		return -ENXIO;	tp->starget = sdev->sdev_target;	/*	 * Fail the device init if the device is flagged NOSCAN at BOOT in	 * the NVRAM.  This may speed up boot and maintain coherency with	 * BIOS device numbering.  Clearing the flag allows the user to	 * rescan skipped devices later.  We also return an error for	 * devices not flagged for SCAN LUNS in the NVRAM since some single	 * lun devices behave badly when asked for a non zero LUN.	 */	if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) {		tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;		starget_printk(KERN_INFO, tp->starget,				"Scan at boot disabled in NVRAM\n");		return -ENXIO;	}	if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) {		if (sdev->lun != 0)			return -ENXIO;		starget_printk(KERN_INFO, tp->starget,				"Multiple LUNs disabled in NVRAM\n");	}	lp = sym_alloc_lcb(np, sdev->id, sdev->lun);	if (!lp)		return -ENOMEM;	spi_min_period(tp->starget) = tp->usr_period;	spi_max_width(tp->starget) = tp->usr_width;	return 0;}/* * Linux entry point for device queue sizing. */static int sym53c8xx_slave_configure(struct scsi_device *sdev){	struct sym_hcb *np = sym_get_hcb(sdev->host);	struct sym_tcb *tp = &np->target[sdev->id];	struct sym_lcb *lp = sym_lp(tp, sdev->lun);	int reqtags, depth_to_use;	/*	 *  Get user flags.	 */	lp->curr_flags = lp->user_flags;	/*	 *  Select queue depth from driver setup.	 *  Donnot use more than configured by user.	 *  Use at least 2.	 *  Donnot use more than our maximum.	 */	reqtags = device_queue_depth(np, sdev->id, sdev->lun);	if (reqtags > tp->usrtags)		reqtags = tp->usrtags;	if (!sdev->tagged_supported)		reqtags = 0;#if 1 /* Avoid to locally queue commands for no good reasons */	if (reqtags > SYM_CONF_MAX_TAG)		reqtags = SYM_CONF_MAX_TAG;	depth_to_use = (reqtags ? reqtags : 2);#else	depth_to_use = (reqtags ? SYM_CONF_MAX_TAG : 2);#endif	scsi_adjust_queue_depth(sdev,				(sdev->tagged_supported ?				 MSG_SIMPLE_TAG : 0),				depth_to_use);	lp->s.scdev_depth = depth_to_use;	sym_tune_dev_queuing(tp, sdev->lun, reqtags);	if (!spi_initial_dv(sdev->sdev_target))		spi_dv_device(sdev);	return 0;}static void sym53c8xx_slave_destroy(struct scsi_device *sdev){	struct sym_hcb *np = sym_get_hcb(sdev->host);	struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun);	if (lp->itlq_tbl)		sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL");	kfree(lp->cb_tags);	sym_mfree_dma(lp, sizeof(*lp), "LCB");}/* *  Linux entry point for info() function */static const char *sym53c8xx_info (struct Scsi_Host *host){	return SYM_DRIVER_NAME;}#ifdef SYM_LINUX_PROC_INFO_SUPPORT/* *  Proc file system stuff * *  A read operation returns adapter information. *  A write operation is a control command. *  The string is parsed in the driver code and the command is passed  *  to the sym_usercmd() function. */#ifdef SYM_LINUX_USER_COMMAND_SUPPORTstruct	sym_usrcmd {	u_long	target;	u_long	lun;	u_long	data;	u_long	cmd;};#define UC_SETSYNC      10#define UC_SETTAGS	11#define UC_SETDEBUG	12#define UC_SETWIDE	14#define UC_SETFLAG	15#define UC_SETVERBOSE	17#define UC_RESETDEV	18#define UC_CLEARDEV	19static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc){	struct sym_tcb *tp;	int t, l;	switch (uc->cmd) {	case 0: return;#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT	case UC_SETDEBUG:		sym_debug_flags = uc->data;		break;#endif	case UC_SETVERBOSE:		np->verbose = uc->data;		break;	default:		/*		 * We assume that other commands apply to targets.		 * This should always be the case and avoid the below 		 * 4 lines to be repeated 6 times.		 */		for (t = 0; t < SYM_CONF_MAX_TARGET; t++) {			if (!((uc->target >> t) & 1))				continue;			tp = &np->target[t];			switch (uc->cmd) {			case UC_SETSYNC:				if (!uc->data || uc->data >= 255) {					tp->tgoal.iu = tp->tgoal.dt =						tp->tgoal.qas = 0;					tp->tgoal.offset = 0;				} else if (uc->data <= 9 && np->minsync_dt) {					if (uc->data < np->minsync_dt)						uc->data = np->minsync_dt;					tp->tgoal.iu = tp->tgoal.dt =						tp->tgoal.qas = 1;					tp->tgoal.width = 1;					tp->tgoal.period = uc->data;					tp->tgoal.offset = np->maxoffs_dt;

⌨️ 快捷键说明

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