ipath_intr.c

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

C
1,209
字号
/* * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */#include <linux/pci.h>#include "ipath_kernel.h"#include "ipath_verbs.h"#include "ipath_common.h"/* * clear (write) a pio buffer, to clear a parity error.   This routine * should only be called when in freeze mode, and the buffer should be * canceled afterwards. */static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum){	u32 __iomem *pbuf;	u32 dwcnt; /* dword count to write */	if (pnum < dd->ipath_piobcnt2k) {		pbuf = (u32 __iomem *) (dd->ipath_pio2kbase + pnum *			dd->ipath_palign);		dwcnt = dd->ipath_piosize2k >> 2;	}	else {		pbuf = (u32 __iomem *) (dd->ipath_pio4kbase +			(pnum - dd->ipath_piobcnt2k) * dd->ipath_4kalign);		dwcnt = dd->ipath_piosize4k >> 2;	}	dev_info(&dd->pcidev->dev,		"Rewrite PIO buffer %u, to recover from parity error\n",		pnum);	*pbuf = dwcnt+1; /* no flush required, since already in freeze */	while(--dwcnt)		*pbuf++ = 0;}/* * Called when we might have an error that is specific to a particular * PIO buffer, and may need to cancel that buffer, so it can be re-used. * If rewrite is true, and bits are set in the sendbufferror registers, * we'll write to the buffer, for error recovery on parity errors. */static void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite){	u32 piobcnt;	unsigned long sbuf[4];	/*	 * it's possible that sendbuffererror could have bits set; might	 * have already done this as a result of hardware error handling	 */	piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;	/* read these before writing errorclear */	sbuf[0] = ipath_read_kreg64(		dd, dd->ipath_kregs->kr_sendbuffererror);	sbuf[1] = ipath_read_kreg64(		dd, dd->ipath_kregs->kr_sendbuffererror + 1);	if (piobcnt > 128) {		sbuf[2] = ipath_read_kreg64(			dd, dd->ipath_kregs->kr_sendbuffererror + 2);		sbuf[3] = ipath_read_kreg64(			dd, dd->ipath_kregs->kr_sendbuffererror + 3);	}	if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {		int i;		if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG) &&			dd->ipath_lastcancel > jiffies) {			__IPATH_DBG_WHICH(__IPATH_PKTDBG|__IPATH_DBG,					  "SendbufErrs %lx %lx", sbuf[0],					  sbuf[1]);			if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)				printk(" %lx %lx ", sbuf[2], sbuf[3]);			printk("\n");		}		for (i = 0; i < piobcnt; i++)			if (test_bit(i, sbuf)) {				if (rewrite)					ipath_clrpiobuf(dd, i);				ipath_disarm_piobufs(dd, i, 1);			}		/* ignore armlaunch errs for a bit */		dd->ipath_lastcancel = jiffies+3;	}}/* These are all rcv-related errors which we want to count for stats */#define E_SUM_PKTERRS \	(INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \	 INFINIPATH_E_RBADVERSION | INFINIPATH_E_RHDR | \	 INFINIPATH_E_RLONGPKTLEN | INFINIPATH_E_RSHORTPKTLEN | \	 INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RMINPKTLEN | \	 INFINIPATH_E_RFORMATERR | INFINIPATH_E_RUNSUPVL | \	 INFINIPATH_E_RUNEXPCHAR | INFINIPATH_E_REBP)/* These are all send-related errors which we want to count for stats */#define E_SUM_ERRS \	(INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | \	 INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \	 INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SUNSUPVL | \	 INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN | \	 INFINIPATH_E_INVALIDADDR)/* * this is similar to E_SUM_ERRS, but can't ignore armlaunch, don't ignore * errors not related to freeze and cancelling buffers.  Can't ignore * armlaunch because could get more while still cleaning up, and need * to cancel those as they happen. */#define E_SPKT_ERRS_IGNORE \	 (INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \	 INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SMINPKTLEN | \	 INFINIPATH_E_SPKTLEN)/* * these are errors that can occur when the link changes state while * a packet is being sent or received.  This doesn't cover things * like EBP or VCRC that can be the result of a sending having the * link change state, so we receive a "known bad" packet. */#define E_SUM_LINK_PKTERRS \	(INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \	 INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN | \	 INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RMINPKTLEN | \	 INFINIPATH_E_RUNEXPCHAR)static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs){	u64 ignore_this_time = 0;	ipath_disarm_senderrbufs(dd, 0);	if ((errs & E_SUM_LINK_PKTERRS) &&	    !(dd->ipath_flags & IPATH_LINKACTIVE)) {		/*		 * This can happen when SMA is trying to bring the link		 * up, but the IB link changes state at the "wrong" time.		 * The IB logic then complains that the packet isn't		 * valid.  We don't want to confuse people, so we just		 * don't print them, except at debug		 */		ipath_dbg("Ignoring packet errors %llx, because link not "			  "ACTIVE\n", (unsigned long long) errs);		ignore_this_time = errs & E_SUM_LINK_PKTERRS;	}	return ignore_this_time;}/* generic hw error messages... */#define INFINIPATH_HWE_TXEMEMPARITYERR_MSG(a) \	{ \		.mask = ( INFINIPATH_HWE_TXEMEMPARITYERR_##a <<    \			  INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT ),   \		.msg = "TXE " #a " Memory Parity"	     \	}#define INFINIPATH_HWE_RXEMEMPARITYERR_MSG(a) \	{ \		.mask = ( INFINIPATH_HWE_RXEMEMPARITYERR_##a <<    \			  INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT ),   \		.msg = "RXE " #a " Memory Parity"	     \	}static const struct ipath_hwerror_msgs ipath_generic_hwerror_msgs[] = {	INFINIPATH_HWE_MSG(IBCBUSFRSPCPARITYERR, "IPATH2IB Parity"),	INFINIPATH_HWE_MSG(IBCBUSTOSPCPARITYERR, "IB2IPATH Parity"),	INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOBUF),	INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOPBC),	INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOLAUNCHFIFO),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(RCVBUF),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(LOOKUPQ),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EAGERTID),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EXPTID),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(FLAGBUF),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(DATAINFO),	INFINIPATH_HWE_RXEMEMPARITYERR_MSG(HDRINFO),};/** * ipath_format_hwmsg - format a single hwerror message * @msg message buffer * @msgl length of message buffer * @hwmsg message to add to message buffer */static void ipath_format_hwmsg(char *msg, size_t msgl, const char *hwmsg){	strlcat(msg, "[", msgl);	strlcat(msg, hwmsg, msgl);	strlcat(msg, "]", msgl);}/** * ipath_format_hwerrors - format hardware error messages for display * @hwerrs hardware errors bit vector * @hwerrmsgs hardware error descriptions * @nhwerrmsgs number of hwerrmsgs * @msg message buffer * @msgl message buffer length */void ipath_format_hwerrors(u64 hwerrs,			   const struct ipath_hwerror_msgs *hwerrmsgs,			   size_t nhwerrmsgs,			   char *msg, size_t msgl){	int i;	const int glen =	    sizeof(ipath_generic_hwerror_msgs) /	    sizeof(ipath_generic_hwerror_msgs[0]);	for (i=0; i<glen; i++) {		if (hwerrs & ipath_generic_hwerror_msgs[i].mask) {			ipath_format_hwmsg(msg, msgl,					   ipath_generic_hwerror_msgs[i].msg);		}	}	for (i=0; i<nhwerrmsgs; i++) {		if (hwerrs & hwerrmsgs[i].mask) {			ipath_format_hwmsg(msg, msgl, hwerrmsgs[i].msg);		}	}}/* return the strings for the most common link states */static char *ib_linkstate(u32 linkstate){	char *ret;	switch (linkstate) {	case IPATH_IBSTATE_INIT:		ret = "Init";		break;	case IPATH_IBSTATE_ARM:		ret = "Arm";		break;	case IPATH_IBSTATE_ACTIVE:		ret = "Active";		break;	default:		ret = "Down";	}	return ret;}void signal_ib_event(struct ipath_devdata *dd, enum ib_event_type ev){	struct ib_event event;	event.device = &dd->verbs_dev->ibdev;	event.element.port_num = 1;	event.event = ev;	ib_dispatch_event(&event);}static void handle_e_ibstatuschanged(struct ipath_devdata *dd,				     ipath_err_t errs, int noprint){	u64 val;	u32 ltstate, lstate;	/*	 * even if diags are enabled, we want to notice LINKINIT, etc.	 * We just don't want to change the LED state, or	 * dd->ipath_kregs->kr_ibcctrl	 */	val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);	lstate = val & IPATH_IBSTATE_MASK;	/*	 * this is confusing enough when it happens that I want to always put it	 * on the console and in the logs.  If it was a requested state change,	 * we'll have already cleared the flags, so we won't print this warning	 */	if ((lstate != IPATH_IBSTATE_ARM && lstate != IPATH_IBSTATE_ACTIVE)		&& (dd->ipath_flags & (IPATH_LINKARMED | IPATH_LINKACTIVE))) {		dev_info(&dd->pcidev->dev, "Link state changed from %s to %s\n",				 (dd->ipath_flags & IPATH_LINKARMED) ? "ARM" : "ACTIVE",				 ib_linkstate(lstate));		/*		 * Flush all queued sends when link went to DOWN or INIT,		 * to be sure that they don't block SMA and other MAD packets		 */		ipath_cancel_sends(dd, 1);	}	else if (lstate == IPATH_IBSTATE_INIT || lstate == IPATH_IBSTATE_ARM ||	    lstate == IPATH_IBSTATE_ACTIVE) {		/*		 * only print at SMA if there is a change, debug if not		 * (sometimes we want to know that, usually not).		 */		if (lstate == ((unsigned) dd->ipath_lastibcstat			       & IPATH_IBSTATE_MASK)) {			ipath_dbg("Status change intr but no change (%s)\n",				  ib_linkstate(lstate));		}		else			ipath_cdbg(VERBOSE, "Unit %u link state %s, last "				   "was %s\n", dd->ipath_unit,				   ib_linkstate(lstate),				   ib_linkstate((unsigned)						dd->ipath_lastibcstat						& IPATH_IBSTATE_MASK));	}	else {		lstate = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK;		if (lstate == IPATH_IBSTATE_INIT ||		    lstate == IPATH_IBSTATE_ARM ||		    lstate == IPATH_IBSTATE_ACTIVE)			ipath_cdbg(VERBOSE, "Unit %u link state down"				   " (state 0x%x), from %s\n",				   dd->ipath_unit,				   (u32)val & IPATH_IBSTATE_MASK,				   ib_linkstate(lstate));		else			ipath_cdbg(VERBOSE, "Unit %u link state changed "				   "to 0x%x from down (%x)\n",				   dd->ipath_unit, (u32) val, lstate);	}	ltstate = (val >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &		INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;	lstate = (val >> INFINIPATH_IBCS_LINKSTATE_SHIFT) &		INFINIPATH_IBCS_LINKSTATE_MASK;	if (ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE ||	    ltstate == INFINIPATH_IBCS_LT_STATE_POLLQUIET) {		u32 last_ltstate;		/*		 * Ignore cycling back and forth from Polling.Active		 * to Polling.Quiet while waiting for the other end of		 * the link to come up. We will cycle back and forth		 * between them if no cable is plugged in,		 * the other device is powered off or disabled, etc.		 */		last_ltstate = (dd->ipath_lastibcstat >>				INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT)			& INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;		if (last_ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE		    || last_ltstate ==		    INFINIPATH_IBCS_LT_STATE_POLLQUIET) {			if (dd->ipath_ibpollcnt > 40) {				dd->ipath_flags |= IPATH_NOCABLE;				*dd->ipath_statusp |=					IPATH_STATUS_IB_NOCABLE;			} else				dd->ipath_ibpollcnt++;			goto skip_ibchange;		}	}	dd->ipath_ibpollcnt = 0;	/* some state other than 2 or 3 */	ipath_stats.sps_iblink++;	if (ltstate != INFINIPATH_IBCS_LT_STATE_LINKUP) {		if (dd->ipath_flags & IPATH_LINKACTIVE)			signal_ib_event(dd, IB_EVENT_PORT_ERR);		dd->ipath_flags |= IPATH_LINKDOWN;		dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT				     | IPATH_LINKACTIVE |				     IPATH_LINKARMED);		*dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;		dd->ipath_lli_counter = 0;		if (!noprint) {			if (((dd->ipath_lastibcstat >>			      INFINIPATH_IBCS_LINKSTATE_SHIFT) &			     INFINIPATH_IBCS_LINKSTATE_MASK)			    == INFINIPATH_IBCS_L_STATE_ACTIVE)				/* if from up to down be more vocal */				ipath_cdbg(VERBOSE,					   "Unit %u link now down (%s)\n",					   dd->ipath_unit,					   ipath_ibcstatus_str[ltstate]);

⌨️ 快捷键说明

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