ipath_intr.c

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

C
855
字号
	if (errs & ~dd->ipath_lasterror) {		errs &= ~dd->ipath_lasterror;		/* never suppress duplicate hwerrors or ibstatuschange */		dd->ipath_lasterror |= errs &			~(INFINIPATH_E_HARDWARE |			  INFINIPATH_E_IBSTATUSCHANGED);	}	if (!errs)		return;	if (!noprint)		/*		 * the ones we mask off are handled specially below or above		 */		ipath_decode_err(msg, sizeof msg,				 errs & ~(INFINIPATH_E_IBSTATUSCHANGED |					  INFINIPATH_E_RRCVEGRFULL |					  INFINIPATH_E_RRCVHDRFULL |					  INFINIPATH_E_HARDWARE));	else		/* so we don't need if (!noprint) at strlcat's below */		*msg = 0;	if (errs & E_SUM_PKTERRS) {		ipath_stats.sps_pkterrs++;		chkerrpkts = 1;	}	if (errs & E_SUM_ERRS)		ipath_stats.sps_errs++;	if (errs & (INFINIPATH_E_RICRC | INFINIPATH_E_RVCRC)) {		ipath_stats.sps_crcerrs++;		chkerrpkts = 1;	}	/*	 * We don't want to print these two as they happen, or we can make	 * the situation even worse, because it takes so long to print	 * messages to serial consoles.  Kernel ports get printed from	 * fast_stats, no more than every 5 seconds, user ports get printed	 * on close	 */	if (errs & INFINIPATH_E_RRCVHDRFULL) {		int any;		u32 hd, tl;		ipath_stats.sps_hdrqfull++;		for (any = i = 0; i < dd->ipath_cfgports; i++) {			struct ipath_portdata *pd = dd->ipath_pd[i];			if (i == 0) {				hd = dd->ipath_port0head;				tl = (u32) le64_to_cpu(					*dd->ipath_hdrqtailptr);			} else if (pd && pd->port_cnt &&				   pd->port_rcvhdrtail_kvaddr) {				/*				 * don't report same point multiple times,				 * except kernel				 */				tl = (u32) * pd->port_rcvhdrtail_kvaddr;				if (tl == dd->ipath_lastrcvhdrqtails[i])					continue;				hd = ipath_read_ureg32(dd, ur_rcvhdrhead,						       i);			} else				continue;			if (hd == (tl + 1) ||			    (!hd && tl == dd->ipath_hdrqlast)) {				dd->ipath_lastrcvhdrqtails[i] = tl;				pd->port_hdrqfull++;				if (i == 0)					chkerrpkts = 1;			}		}	}	if (errs & INFINIPATH_E_RRCVEGRFULL) {		/*		 * since this is of less importance and not likely to		 * happen without also getting hdrfull, only count		 * occurrences; don't check each port (or even the kernel		 * vs user)		 */		ipath_stats.sps_etidfull++;		if (dd->ipath_port0head !=		    (u32) le64_to_cpu(*dd->ipath_hdrqtailptr))			chkerrpkts = 1;	}	/*	 * do this before IBSTATUSCHANGED, in case both bits set in a single	 * interrupt; we want the STATUSCHANGE to "win", so we do our	 * internal copy of state machine correctly	 */	if (errs & INFINIPATH_E_RIBLOSTLINK) {		/*		 * force through block below		 */		errs |= INFINIPATH_E_IBSTATUSCHANGED;		ipath_stats.sps_iblink++;		dd->ipath_flags |= IPATH_LINKDOWN;		dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT				     | IPATH_LINKARMED | IPATH_LINKACTIVE);		*dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;		if (!noprint) {			u64 st = ipath_read_kreg64(				dd, dd->ipath_kregs->kr_ibcstatus);			ipath_dbg("Lost link, link now down (%s)\n",				  ipath_ibcstatus_str[st & 0xf]);		}	}	if (errs & INFINIPATH_E_IBSTATUSCHANGED)		handle_e_ibstatuschanged(dd, errs, noprint);	if (errs & INFINIPATH_E_RESET) {		if (!noprint)			ipath_dev_err(dd, "Got reset, requires re-init "				      "(unload and reload driver)\n");		dd->ipath_flags &= ~IPATH_INITTED;	/* needs re-init */		/* mark as having had error */		*dd->ipath_statusp |= IPATH_STATUS_HWERROR;		*dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF;	}	if (!noprint && *msg)		ipath_dev_err(dd, "%s error\n", msg);	if (dd->ipath_sma_state_wanted & dd->ipath_flags) {		ipath_cdbg(VERBOSE, "sma wanted state %x, iflags now %x, "			   "waking\n", dd->ipath_sma_state_wanted,			   dd->ipath_flags);		wake_up_interruptible(&ipath_sma_state_wait);	}	if (chkerrpkts)		/* process possible error packets in hdrq */		ipath_kreceive(dd);}/* this is separate to allow for better optimization of ipath_intr() */static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp){	/*	 * sometimes happen during driver init and unload, don't want	 * to process any interrupts at that point	 */	/* this is just a bandaid, not a fix, if something goes badly	 * wrong */	if (++*unexpectp > 100) {		if (++*unexpectp > 105) {			/*			 * ok, we must be taking somebody else's interrupts,			 * due to a messed up mptable and/or PIRQ table, so			 * unregister the interrupt.  We've seen this during			 * linuxbios development work, and it may happen in			 * the future again.			 */			if (dd->pcidev && dd->pcidev->irq) {				ipath_dev_err(dd, "Now %u unexpected "					      "interrupts, unregistering "					      "interrupt handler\n",					      *unexpectp);				ipath_dbg("free_irq of irq %x\n",					  dd->pcidev->irq);				free_irq(dd->pcidev->irq, dd);			}		}		if (ipath_read_kreg32(dd, dd->ipath_kregs->kr_intmask)) {			ipath_dev_err(dd, "%u unexpected interrupts, "				      "disabling interrupts completely\n",				      *unexpectp);			/*			 * disable all interrupts, something is very wrong			 */			ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask,					 0ULL);		}	} else if (*unexpectp > 1)		ipath_dbg("Interrupt when not ready, should not happen, "			  "ignoring\n");}static void ipath_bad_regread(struct ipath_devdata *dd){	static int allbits;	/* separate routine, for better optimization of ipath_intr() */	/*	 * We print the message and disable interrupts, in hope of	 * having a better chance of debugging the problem.	 */	ipath_dev_err(dd,		      "Read of interrupt status failed (all bits set)\n");	if (allbits++) {		/* disable all interrupts, something is very wrong */		ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL);		if (allbits == 2) {			ipath_dev_err(dd, "Still bad interrupt status, "				      "unregistering interrupt\n");			free_irq(dd->pcidev->irq, dd);		} else if (allbits > 2) {			if ((allbits % 10000) == 0)				printk(".");		} else			ipath_dev_err(dd, "Disabling interrupts, "				      "multiple errors\n");	}}static void handle_port_pioavail(struct ipath_devdata *dd){	u32 i;	/*	 * start from port 1, since for now port 0  is never using	 * wait_event for PIO	 */	for (i = 1; dd->ipath_portpiowait && i < dd->ipath_cfgports; i++) {		struct ipath_portdata *pd = dd->ipath_pd[i];		if (pd && pd->port_cnt &&		    dd->ipath_portpiowait & (1U << i)) {			clear_bit(i, &dd->ipath_portpiowait);			if (test_bit(IPATH_PORT_WAITING_PIO,				     &pd->port_flag)) {				clear_bit(IPATH_PORT_WAITING_PIO,					  &pd->port_flag);				wake_up_interruptible(&pd->port_wait);			}		}	}}static void handle_layer_pioavail(struct ipath_devdata *dd){	int ret;	ret = __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE);	if (ret > 0)		goto set;	ret = __ipath_verbs_piobufavail(dd);	if (ret > 0)		goto set;	return;set:	set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);	ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,			 dd->ipath_sendctrl);}static void handle_rcv(struct ipath_devdata *dd, u32 istat){	u64 portr;	int i;	int rcvdint = 0;	portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &		 infinipath_i_rcvavail_mask)		| ((istat >> INFINIPATH_I_RCVURG_SHIFT) &		   infinipath_i_rcvurg_mask);	for (i = 0; i < dd->ipath_cfgports; i++) {		struct ipath_portdata *pd = dd->ipath_pd[i];		if (portr & (1 << i) && pd &&		    pd->port_cnt) {			if (i == 0)				ipath_kreceive(dd);			else if (test_bit(IPATH_PORT_WAITING_RCV,					  &pd->port_flag)) {				int rcbit;				clear_bit(IPATH_PORT_WAITING_RCV,					  &pd->port_flag);				rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT;				clear_bit(1UL << rcbit, &dd->ipath_rcvctrl);				wake_up_interruptible(&pd->port_wait);				rcvdint = 1;			}		}	}	if (rcvdint) {		/* only want to take one interrupt, so turn off the rcv		 * interrupt for all the ports that we did the wakeup on		 * (but never for kernel port)		 */		ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,				 dd->ipath_rcvctrl);	}}irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs){	struct ipath_devdata *dd = data;	u32 istat;	ipath_err_t estat = 0;	static unsigned unexpected = 0;	irqreturn_t ret;	if(!(dd->ipath_flags & IPATH_PRESENT)) {		/* this is mostly so we don't try to touch the chip while		 * it is being reset */		/*		 * This return value is perhaps odd, but we do not want the		 * interrupt core code to remove our interrupt handler		 * because we don't appear to be handling an interrupt		 * during a chip reset.		 */		return IRQ_HANDLED;	}	istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);	if (unlikely(!istat)) {		ipath_stats.sps_nullintr++;		ret = IRQ_NONE; /* not our interrupt, or already handled */		goto bail;	}	if (unlikely(istat == -1)) {		ipath_bad_regread(dd);		/* don't know if it was our interrupt or not */		ret = IRQ_NONE;		goto bail;	}	ipath_stats.sps_ints++;	/*	 * this needs to be flags&initted, not statusp, so we keep	 * taking interrupts even after link goes down, etc.	 * Also, we *must* clear the interrupt at some point, or we won't	 * take it again, which can be real bad for errors, etc...	 */	if (!(dd->ipath_flags & IPATH_INITTED)) {		ipath_bad_intr(dd, &unexpected);		ret = IRQ_NONE;		goto bail;	}	if (unexpected)		unexpected = 0;	ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat);	if (istat & ~infinipath_i_bitsextant)		ipath_dev_err(dd,			      "interrupt with unknown interrupts %x set\n",			      istat & (u32) ~ infinipath_i_bitsextant);	if (istat & INFINIPATH_I_ERROR) {		ipath_stats.sps_errints++;		estat = ipath_read_kreg64(dd,					  dd->ipath_kregs->kr_errorstatus);		if (!estat)			dev_info(&dd->pcidev->dev, "error interrupt (%x), "				 "but no error bits set!\n", istat);		else if (estat == -1LL)			/*			 * should we try clearing all, or hope next read			 * works?			 */			ipath_dev_err(dd, "Read of error status failed "				      "(all bits set); ignoring\n");		else			handle_errors(dd, estat);	}	if (istat & INFINIPATH_I_GPIO) {		if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) {			u32 gpiostatus;			gpiostatus = ipath_read_kreg32(				dd, dd->ipath_kregs->kr_gpio_status);			ipath_dbg("Unexpected GPIO interrupt bits %x\n",				  gpiostatus);			ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,					 gpiostatus);		}		else {			/* Clear GPIO status bit 2 */			ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,					 (u64) (1 << 2));			/*			 * Packets are available in the port 0 rcv queue.			 * Eventually this needs to be generalized to check			 * IPATH_GPIO_INTR, and the specific GPIO bit, if			 * GPIO interrupts are used for anything else.			 */			ipath_kreceive(dd);		}	}	/*	 * clear the ones we will deal with on this round	 * We clear it early, mostly for receive interrupts, so we	 * know the chip will have seen this by the time we process	 * the queue, and will re-interrupt if necessary.  The processor	 * itself won't take the interrupt again until we return.	 */	ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);	if (istat & INFINIPATH_I_SPIOBUFAVAIL) {		clear_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl);		ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,				 dd->ipath_sendctrl);		if (dd->ipath_portpiowait)			handle_port_pioavail(dd);		handle_layer_pioavail(dd);	}	/*	 * we check for both transition from empty to non-empty, and urgent	 * packets (those with the interrupt bit set in the header)	 */	if (istat & ((infinipath_i_rcvavail_mask <<		      INFINIPATH_I_RCVAVAIL_SHIFT)		     | (infinipath_i_rcvurg_mask <<			INFINIPATH_I_RCVURG_SHIFT)))		handle_rcv(dd, istat);	ret = IRQ_HANDLED;bail:	return ret;}

⌨️ 快捷键说明

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