sym_glue.c

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

C
2,086
字号
	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 Scsi_Host *shost = dev_id;	struct sym_data *sym_data = shost_priv(shost);	irqreturn_t result;	/* Avoid spinloop trying to handle interrupts on frozen device */	if (pci_channel_offline(sym_data->pdev))		return IRQ_NONE;	if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");	spin_lock(shost->host_lock);	result = sym_interrupt(shost);	spin_unlock(shost->host_lock);	if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");	return result;}/* *  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/* *  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_ucmd *ucmd = SYM_UCMD_PTR(cmd);	struct Scsi_Host *shost = cmd->device->host;	struct sym_data *sym_data = shost_priv(shost);	struct pci_dev *pdev = sym_data->pdev;	struct sym_hcb *np = sym_data->ncb;	SYM_QUEHEAD *qp;	int cmd_queued = 0;	int sts = -1;	struct completion eh_done;	scmd_printk(KERN_WARNING, cmd, "%s operation started\n", opname);	/* We may be in an error condition because the PCI bus	 * went down. In this case, we need to wait until the	 * PCI bus is reset, the card is reset, and only then	 * proceed with the scsi error recovery.  There's no	 * point in hurrying; take a leisurely wait.	 */#define WAIT_FOR_PCI_RECOVERY	35	if (pci_channel_offline(pdev)) {		struct completion *io_reset;		int finished_reset = 0;		init_completion(&eh_done);		spin_lock_irq(shost->host_lock);		/* Make sure we didn't race */		if (pci_channel_offline(pdev)) {			if (!sym_data->io_reset)				sym_data->io_reset = &eh_done;			io_reset = sym_data->io_reset;		} else {			finished_reset = 1;		}		spin_unlock_irq(shost->host_lock);		if (!finished_reset)			finished_reset = wait_for_completion_timeout(io_reset,						WAIT_FOR_PCI_RECOVERY*HZ);		if (!finished_reset)			return SCSI_FAILED;	}	spin_lock_irq(shost->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) {			cmd_queued = 1;			break;		}	}	/* 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(shost, 1);		sts = 0;		break;	default:		break;	}	/* On error, restore everything and cross fingers :) */	if (sts)		cmd_queued = 0;	if (cmd_queued) {		init_completion(&eh_done);		ucmd->eh_done = &eh_done;		spin_unlock_irq(shost->host_lock);		if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {			ucmd->eh_done = NULL;			sts = -2;		}	} else {		spin_unlock_irq(shost->host_lock);	}	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);	}}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 = sym_driver_setup.max_tag;	if (reqtags > tp->usrtags)		reqtags = tp->usrtags;	if (!sdev->tagged_supported)		reqtags = 0;	if (reqtags > SYM_CONF_MAX_TAG)		reqtags = SYM_CONF_MAX_TAG;	depth_to_use = reqtags ? reqtags : 2;	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;				} else {					if (uc->data < np->minsync)						uc->data = np->minsync;					tp->tgoal.iu = tp->tgoal.dt =						tp->tgoal.qas = 0;					tp->tgoal.period = uc->data;					tp->tgoal.offset = np->maxoffs;				}				tp->tgoal.check_nego = 1;				break;			case UC_SETWIDE:				tp->tgoal.width = uc->data ? 1 : 0;				tp->tgoal.check_nego = 1;				break;			case UC_SETTAGS:				for (l = 0; l < SYM_CONF_MAX_LUN; l++)					sym_tune_dev_queuing(tp, l, uc->data);				break;			case UC_RESETDEV:				tp->to_reset = 1;				np->istat_sem = SEM;				OUTB(np, nc_istat, SIGP|SEM);				break;			case UC_CLEARDEV:				for (l = 0; l < SYM_CONF_MAX_LUN; l++) {					struct sym_lcb *lp = sym_lp(tp, l);					if (lp) lp->to_clear = 1;				}				np->istat_sem = SEM;				OUTB(np, nc_istat, SIGP|SEM);				break;			case UC_SETFLAG:				tp->usrflags = uc->data;				break;			}		}		break;	}}static int skip_spaces(char *ptr, int len){	int cnt, c;	for (cnt = len; cnt > 0 && (c = *ptr++) && isspace(c); cnt--);	return (len - cnt);}static int get_int_arg(char *ptr, int len, u_long *pv){	char *end;	*pv = simple_strtoul(ptr, &end, 10);	return (end - ptr);}static int is_keyword(char *ptr, int len, char *verb){	int verb_len = strlen(verb);	if (len >= verb_len && !memcmp(verb, ptr, verb_len))		return verb_len;	else		return 0;}#define SKIP_SPACES(ptr, len)						\	if ((arg_len = skip_spaces(ptr, len)) < 1)			\		return -EINVAL;						\	ptr += arg_len; len -= arg_len;#define GET_INT_ARG(ptr, len, v)					\	if (!(arg_len = get_int_arg(ptr, len, &(v))))			\		return -EINVAL;						\	ptr += arg_len; len -= arg_len;/* * Parse a control command */static int sym_user_command(struct Scsi_Host *shost, char *buffer, int length){	struct sym_hcb *np = sym_get_hcb(shost);	char *ptr	= buffer;	int len		= length;	struct sym_usrcmd cmd, *uc = &cmd;	int		arg_len;	u_long 		target;	memset(uc, 0, sizeof(*uc));	if (len > 0 && ptr[len-1] == '\n')		--len;	if	((arg_len = is_keyword(ptr, len, "setsync")) != 0)		uc->cmd = UC_SETSYNC;	else if	((arg_len = is_keyword(ptr, len, "settags")) != 0)		uc->cmd = UC_SETTAGS;	else if	((arg_len = is_keyword(ptr, len, "setverbose")) != 0)		uc->cmd = UC_SETVERBOSE;	else if	((arg_len = is_keyword(ptr, len, "setwide")) != 0)		uc->cmd = UC_SETWIDE;#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT	else if	((arg_len = is_keyword(ptr, len, "setdebug")) != 0)		uc->cmd = UC_SETDEBUG;#endif	else if	((arg_len = is_keyword(ptr, len, "setflag")) != 0)		uc->cmd = UC_SETFLAG;	else if	((arg_len = is_keyword(ptr, len, "resetdev")) != 0)		uc->cmd = UC_RESETDEV;	else if	((arg_len = is_keyword(ptr, len, "cleardev")) != 0)		uc->cmd = UC_CLEARDEV;	else		arg_len = 0;#ifdef DEBUG_PROC_INFOprintk("sym_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);#endif	if (!arg_len)		return -EINVAL;	ptr += arg_len; len -= arg_len;	switch(uc->cmd) {	case UC_SETSYNC:	case UC_SETTAGS:	case UC_SETWIDE:	case UC_SETFLAG:	case UC_RESETDEV:	case UC_CLEARDEV:		SKIP_SPACES(ptr, len);		if ((arg_len = is_keyword(ptr, len, "all")) != 0) {			ptr += arg_len; len -= arg_len;			uc->target = ~0;		} else {

⌨️ 快捷键说明

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