be2net: fix a possible events_get() race on BE2
authorSathya Perla <sathya.perla@emulex.com>
Fri, 23 Nov 2012 00:27:18 +0000 (00:27 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 23 Nov 2012 19:26:08 +0000 (14:26 -0500)
On BE2 chip, an interrupt being raised even when EQ is in un-armed state has
been observed a few times.  This is not expected and has never been
observed on BE3/Lancer chips.

As a consequence, be_msix()::events_get() and be_poll()::events_get()
can race and notify an EQ wrongly causing a CEV UE. The other possible
side-effect would be traffic stalling because after notifying EQ,
napi_schedule() is ignored as NAPI is already running.

This patch fixes this issue by counting events only in be_poll().

Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/emulex/benet/be_main.c

index c365722218ffe6ba1627f9148eaca30a023ce799..adef536c1586f7552f09bccb92e35b57e09b8bc5 100644 (file)
@@ -2029,7 +2029,8 @@ static irqreturn_t be_msix(int irq, void *dev)
 {
        struct be_eq_obj *eqo = dev;
 
-       event_handle(eqo);
+       be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0);
+       napi_schedule(&eqo->napi);
        return IRQ_HANDLED;
 }
 
@@ -2125,9 +2126,11 @@ int be_poll(struct napi_struct *napi, int budget)
 {
        struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi);
        struct be_adapter *adapter = eqo->adapter;
-       int max_work = 0, work, i;
+       int max_work = 0, work, i, num_evts;
        bool tx_done;
 
+       num_evts = events_get(eqo);
+
        /* Process all TXQs serviced by this EQ */
        for (i = eqo->idx; i < adapter->num_tx_qs; i += adapter->num_evt_qs) {
                tx_done = be_process_tx(adapter, &adapter->tx_obj[i],
@@ -2150,10 +2153,10 @@ int be_poll(struct napi_struct *napi, int budget)
 
        if (max_work < budget) {
                napi_complete(napi);
-               be_eq_notify(adapter, eqo->q.id, true, false, 0);
+               be_eq_notify(adapter, eqo->q.id, true, false, num_evts);
        } else {
                /* As we'll continue in polling mode, count and clear events */
-               be_eq_notify(adapter, eqo->q.id, false, false, events_get(eqo));
+               be_eq_notify(adapter, eqo->q.id, false, false, num_evts);
        }
        return max_work;
 }