17.7. The Interrupt Handler
Most hardware interfaces are
controlled by means of an interrupt handler. The hardware interrupts
the processor to signal one of two possible events: a new packet has
arrived or transmission of an outgoing packet is complete. Network
interfaces can also generate interrupts to signal errors, link status
changes, and so on.
The usual interrupt routine can tell the difference between a
new-packet-arrived interrupt and a done-transmitting notification by
checking a status register found on the physical device. The
snull interface works similarly, but its status
word is implemented in software and lives in
dev->priv. The interrupt handler for a network
interface looks like this:
static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int statusword;
struct snull_priv *priv;
struct snull_packet *pkt = NULL;
/*
* As usual, check the "device" pointer to be sure it is
* really interrupting.
* Then assign "struct device *dev"
*/
struct net_device *dev = (struct net_device *)dev_id;
/* ... and check with hw if it's really ours */
/* paranoid */
if (!dev)
return;
/* Lock the device */
priv = netdev_priv(dev);
spin_lock(&priv->lock);
/* retrieve statusword: real netdevices use I/O instructions */
statusword = priv->status;
priv->status = 0;
if (statusword & SNULL_RX_INTR) {
/* send it to snull_rx for handling */
pkt = priv->rx_queue;
if (pkt) {
priv->rx_queue = pkt->next;
snull_rx(dev, pkt);
}
}
if (statusword & SNULL_TX_INTR) {
/* a transmission is over: free the skb */
priv->stats.tx_packets++;
priv->stats.tx_bytes += priv->tx_packetlen;
dev_kfree_skb(priv->skb);
}
/* Unlock the device and we are done */
spin_unlock(&priv->lock);
if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */
return;
}
The handler's first task is to retrieve a pointer to
the correct struct net_device.
This pointer usually comes from the dev_id pointer
received as an argument.
The interesting part of this handler deals with the
"transmission done" situation. In
this case, the statistics are updated, and
dev_kfree_skb
is called to return the (no longer needed) socket buffer to the
system. There are, actually, three variants of this function that may
be called:
- dev_kfree_skb(struct sk_buff *skb);
-
This version should be called when you know that your code will not
be running in interrupt context. Since snull has
no actual hardware interrupts, this is the version we use.
- dev_kfree_skb_irq(struct sk_buff *skb);
-
If you know that you will be freeing the buffer in an interrupt
handler, use this version, which is optimized for that case.
- dev_kfree_skb_any(struct sk_buff *skb);
-
This is the version to use if the relevant code could be running in
either interrupt or noninterrupt context.
Finally, if your driver has temporarily stopped the transmission
queue, this is usually the place to restart it with
netif_wake_queue.
Packet reception, in contrast to transmission,
doesn't need any special interrupt handling. Calling
snull_rx (which we have already seen) is all
that's required.
|