Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2369

kernel-2.6.18-238.el5.src.rpm

From: Andy Gospodarek <gospo@redhat.com>
Date: Thu, 2 Jul 2009 15:00:15 -0400
Subject: [net] be2net: fix races in napi and interrupt handling
Message-id: 20090702190015.GW20245@gospo.rdu.redhat.com
O-Subject: [RHEL5.4 PATCH] be2net: fix races in napi and interrupt handling
Bugzilla: 508839
RH-Acked-by: David Miller <davem@redhat.com>
RH-Acked-by: John W. Linville <linville@redhat.com>

This patch comes from directly from the hardware vendor after a two
problems were discovered during testing.  Here are their comments:

"The race that we fixed here is that when we are in be_poll() because of
an RX completion, an interrupt could come for TX completion for which we
will schedule NAPI; but when we return to be_poll from ISR, we could end
up doing a napi_complete() cancelling the NAPI schedule done in TX
completion interrupt.  This will result in no NAPI scheduled as well as
no interrupt enabled in device brining the traffic to a halt."

"Occasionally we may see an interrupt without an event in the eq.  In
intx, we currently see the event queue and return IRQ_NONE causing a the
irq to be disabled ("no one cared".) Instead, read the CEV_ISR reg to
check the existence of the interrupt."

The patch to resolve the second paragraph has been posted upstream and
the first one is an error found from the backport to RHEL5.

I do not currently have the hardware to test this, but I presume they
tested this before submitting it to us.

This will resolve RHBZ 508839.

diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h
index b02e805..59e465a 100644
--- a/drivers/net/benet/be_hw.h
+++ b/drivers/net/benet/be_hw.h
@@ -55,6 +55,10 @@
 #define MEMBAR_CTRL_INT_CTRL_PFUNC_MASK  	0x7 	/* bits 26 - 28 */
 #define MEMBAR_CTRL_INT_CTRL_PFUNC_SHIFT	26
 
+/********* ISR0 Register Offset *************/
+#define CEV_ISR0_OFFSET			0xC18
+#define CEV_ISR_SIZE			4
+
 /********* Event Q door bell *************/
 #define DB_EQ_OFFSET			DB_CQ_OFFSET
 #define DB_EQ_RING_ID_MASK		0x1FF	/* bits 0 - 8 */
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c
index c763240..4e99887 100644
--- a/drivers/net/benet/be_main.c
+++ b/drivers/net/benet/be_main.c
@@ -1212,40 +1212,29 @@ static bool event_get(struct be_eq_obj *eq_obj, u16 *rid)
 	return true;
 }
 
-static int event_handle(struct be_adapter *adapter)
-{
-	struct be_ctrl_info *ctrl = &adapter->ctrl;
-	struct be_eq_obj *eq_obj = &adapter->be_eq;
-	u16 rid = 0, num = 0;
-
-	while (event_get(eq_obj, &rid))
-		num++;
-
-	/* We can see an interrupt and no event */
-	be_eq_notify(ctrl, eq_obj->q.id, true, true, num);
-	if (num)
-		compat_napi_schedule(&adapter->napi);
-
-	drvr_stats(adapter)->be_num_events += num;
-
-	return num;
-}
-
 static irqreturn_t be_intx(int irq, void *dev)
 {
 	struct be_adapter *adapter = dev;
+	struct be_ctrl_info *ctrl = &adapter->ctrl;
+	int isr;
 
-	if (event_handle(adapter))
-		return IRQ_HANDLED;
-	else
+	isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
+				PCI_FUNC(adapter->pdev->devfn) * CEV_ISR_SIZE);
+	if (unlikely(!isr))
 		return IRQ_NONE;
+
+	compat_napi_schedule(&adapter->napi);
+	return IRQ_HANDLED;
 }
 
-static irqreturn_t be_msix_rx(int irq, void *dev)
+static irqreturn_t be_msix(int irq, void *dev)
 {
 	struct be_adapter *adapter = dev;
+	struct be_eq_obj *eq_obj = &adapter->be_eq;
+	struct be_eq_entry *entry = queue_tail_node(&eq_obj->q);
 
-	event_handle(adapter);
+	if (entry->evt)
+		compat_napi_schedule(&adapter->napi);
 
 	return IRQ_HANDLED;
 }
@@ -1265,10 +1254,11 @@ static inline bool do_lro(struct be_adapter *adapter,
 
 int be_poll_rx(struct be_adapter *adapter, int budget)
 {
+	struct be_queue_info *rx_cq = &adapter->rx_obj.cq;
 	struct be_eth_rx_compl *rxcp;
-	u32 work_done;
+	u32 work;
 
-	for (work_done = 0; work_done < budget; work_done++) {
+	for (work = 0; work < budget; work++) {
 		rxcp = be_rx_compl_get(adapter);
 		if (!rxcp)
 			break;
@@ -1286,13 +1276,21 @@ int be_poll_rx(struct be_adapter *adapter, int budget)
 	if (atomic_read(&adapter->rx_obj.q.used) < RX_FRAGS_REFILL_WM)
 		be_post_rx_frags(adapter);
 
-	return work_done;
+	if (work) {
+		if (work < budget)
+			be_cq_notify(&adapter->ctrl, rx_cq->id, true, work);
+		else
+			be_cq_notify(&adapter->ctrl, rx_cq->id, false, work);
+	}
+
+	return work;
 }
 
 /* For TX we don't honour budget; consume everything */
 int be_poll_tx(struct be_adapter *adapter, int budget)
 {
 	struct be_tx_obj *tx_obj = &adapter->tx_obj;
+	struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
 	struct be_queue_info *txq = &tx_obj->q;
 	struct be_eth_tx_compl *txcp;
 	u32 num_cmpl = 0;
@@ -1316,6 +1314,9 @@ int be_poll_tx(struct be_adapter *adapter, int budget)
 	drvr_stats(adapter)->be_tx_events++;
 	drvr_stats(adapter)->be_tx_compl += num_cmpl;
 
+	if (num_cmpl)
+		be_cq_notify(&adapter->ctrl, tx_cq->id, true, num_cmpl);
+
 	return num_cmpl;
 }
 
@@ -1323,26 +1324,25 @@ int be_poll(struct napi_struct *napi, int budget)
 {
 	struct be_adapter *adapter =
 				container_of(napi, struct be_adapter, napi);
-	struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
-	struct be_queue_info *rx_cq = &adapter->rx_obj.cq;
+	struct be_ctrl_info *ctrl = &adapter->ctrl;
+	struct be_eq_obj *eq_obj = &adapter->be_eq;
+	u16 rid = 0, num = 0;
 	u32 tx_work, rx_work;
 
+	while (event_get(eq_obj, &rid))
+		num++;
+
 	tx_work = be_poll_tx(adapter, budget);
 	rx_work = be_poll_rx(adapter, budget);
 	be_process_mcc(&adapter->ctrl);
 
-
 	/* All consumed */
-	if (rx_work < budget) {
+	if (rx_work < budget)
 		compat_napi_complete(napi);
-		be_cq_notify(&adapter->ctrl, rx_cq->id, true, rx_work);
-	} else {
-		/* More to be consumed; continue with interrupts disabled */
-		be_cq_notify(&adapter->ctrl, rx_cq->id, false, rx_work);
-	}
 
-	if (tx_work)
-		be_cq_notify(&adapter->ctrl, tx_cq->id, true, tx_work);
+	drvr_stats(adapter)->be_num_events += num;
+	if (num)
+		be_eq_notify(ctrl, eq_obj->q.id, true, true, num);
 
 	return rx_work;
 }
@@ -1400,7 +1400,7 @@ static int be_msix_register(struct be_adapter *adapter)
 
 	sprintf(be_eq->desc, "%s", netdev->name);
 	vec = be_msix_vec_get(adapter, be_eq->q.id);
-	status = request_irq(vec, be_msix_rx, 0, be_eq->desc, adapter);
+	status = request_irq(vec, be_msix, 0, be_eq->desc, adapter);
 	if (status)
 		goto err;
 	return 0;