Commit fa98eb0e authored by Jonas Bonn's avatar Jonas Bonn Committed by David S. Miller

ethoc: rework interrupt handling

The old interrupt handling was incorrect in that it did not account for the
fact that the interrupt source bits get set irregardless of whether or not
their corresponding mask is set.  This patch fixes that by masking off the
source bits for masked interrupts.

Furthermore, the handling of transmission events is moved to the NAPI polling
handler alongside the reception handler, thus preventing a whole bunch of
interrupts during heavy traffic.
Signed-off-by: default avatarJonas Bonn <jonas@southpole.se>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 20f70ddd
......@@ -495,29 +495,42 @@ static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
return 0;
}
static void ethoc_tx(struct net_device *dev)
static int ethoc_tx(struct net_device *dev, int limit)
{
struct ethoc *priv = netdev_priv(dev);
int count;
struct ethoc_bd bd;
spin_lock(&priv->lock);
for (count = 0; count < limit; ++count) {
unsigned int entry;
while (priv->dty_tx != priv->cur_tx) {
unsigned int entry = priv->dty_tx % priv->num_tx;
struct ethoc_bd bd;
entry = priv->dty_tx % priv->num_tx;
ethoc_read_bd(priv, entry, &bd);
if (bd.stat & TX_BD_READY)
break;
entry = (++priv->dty_tx) % priv->num_tx;
if (bd.stat & TX_BD_READY || (priv->dty_tx == priv->cur_tx)) {
ethoc_ack_irq(priv, INT_MASK_TX);
/* If interrupt came in between reading in the BD
* and clearing the interrupt source, then we risk
* missing the event as the TX interrupt won't trigger
* right away when we reenable it; hence, check
* BD_EMPTY here again to make sure there isn't such an
* event pending...
*/
ethoc_read_bd(priv, entry, &bd);
if (bd.stat & TX_BD_READY ||
(priv->dty_tx == priv->cur_tx))
break;
}
(void)ethoc_update_tx_stats(priv, &bd);
priv->dty_tx++;
}
if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2))
netif_wake_queue(dev);
ethoc_ack_irq(priv, INT_MASK_TX);
spin_unlock(&priv->lock);
return count;
}
static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
......@@ -525,32 +538,38 @@ static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
struct net_device *dev = dev_id;
struct ethoc *priv = netdev_priv(dev);
u32 pending;
ethoc_disable_irq(priv, INT_MASK_ALL);
u32 mask;
/* Figure out what triggered the interrupt...
* The tricky bit here is that the interrupt source bits get
* set in INT_SOURCE for an event irregardless of whether that
* event is masked or not. Thus, in order to figure out what
* triggered the interrupt, we need to remove the sources
* for all events that are currently masked. This behaviour
* is not particularly well documented but reasonable...
*/
mask = ethoc_read(priv, INT_MASK);
pending = ethoc_read(priv, INT_SOURCE);
pending &= mask;
if (unlikely(pending == 0)) {
ethoc_enable_irq(priv, INT_MASK_ALL);
return IRQ_NONE;
}
ethoc_ack_irq(priv, pending);
/* We always handle the dropped packet interrupt */
if (pending & INT_MASK_BUSY) {
dev_err(&dev->dev, "packet dropped\n");
dev->stats.rx_dropped++;
}
if (pending & INT_MASK_RX) {
if (napi_schedule_prep(&priv->napi))
__napi_schedule(&priv->napi);
} else {
ethoc_enable_irq(priv, INT_MASK_RX);
/* Handle receive/transmit event by switching to polling */
if (pending & (INT_MASK_TX | INT_MASK_RX)) {
ethoc_disable_irq(priv, INT_MASK_TX | INT_MASK_RX);
napi_schedule(&priv->napi);
}
if (pending & INT_MASK_TX)
ethoc_tx(dev);
ethoc_enable_irq(priv, INT_MASK_ALL & ~INT_MASK_RX);
return IRQ_HANDLED;
}
......@@ -576,15 +595,18 @@ static int ethoc_get_mac_address(struct net_device *dev, void *addr)
static int ethoc_poll(struct napi_struct *napi, int budget)
{
struct ethoc *priv = container_of(napi, struct ethoc, napi);
int work_done = 0;
int rx_work_done = 0;
int tx_work_done = 0;
rx_work_done = ethoc_rx(priv->netdev, budget);
tx_work_done = ethoc_tx(priv->netdev, budget);
work_done = ethoc_rx(priv->netdev, budget);
if (work_done < budget) {
if (rx_work_done < budget && tx_work_done < budget) {
napi_complete(napi);
ethoc_enable_irq(priv, INT_MASK_RX);
ethoc_enable_irq(priv, INT_MASK_TX | INT_MASK_RX);
}
return work_done;
return rx_work_done;
}
static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment