Receiving data from the network is trickier than transmitting
it because an sk_buff
must be allocated and handed off to the
upper layers from within an interrupt handler--the best way to
receive a packet is through an interrupt, unless the interface is a
purely software one like snull or the loopback interface. While it
is possible to write polling drivers, and a few exist in the official
kernel as well, interrupt-driven operation is much better, both
in data throughput and in computational demands. Since the vast
majority of network interfaces is interrupt-driven, I won’t talk
about the polling implementation, which just exploits kernel timers.
The implementation of snull separates the hardware details from the device-independent housekeeping. The function snull_rx is thus called after the hardware has received the packet and it is already in the computer’s memory. snull_rx therefore receives a pointer to the data and the length of the packet. The function’s sole responsiblity is to send the packet and some additional information to the upper layers of networking code. This code is independent of the way the data pointer and length are obtained.
void snull_rx(struct device *dev, int len, unsigned char *buf) { struct sk_buff *skb; struct snull_priv *privp = (struct snull_priv *)dev->priv; /* * The packet has been retrieved from the transmission * medium. Build an skb around it, so upper layers can handle it */ skb = dev_alloc_skb(len+2); if (!skb) { printk("snull rx: low on mem "); return; } memcpy(skb_put(skb, len), buf, len); /* Write metadata, and then pass to the receive level */ skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ privp->stats.rx_packets++; netif_rx(skb); return; }
The function is sufficiently general to act as a template for any network driver, but some explanation is necessary before you can reuse this code fragment with confidence.
Note that the buffer allocation function wants to know the data length. This avoids wasting memory when calling kmalloc. The allocation function is called with atomic priority by dev_alloc_skb, which can therefore be used safely at interrupt time. The kernel offers other interfaces to socket-buffer allocation, but they are not worth introducing here; socket buffers are explained in detail in Section 14.8, later in this chapter.
Once there is a valid skb
pointer, the packet data is copied into
the buffer by calling memcpy; the skb_put function
updates the end-of-data pointer in the buffer and returns a pointer to
the newly created space.
Unfortunately, there isn’t enough information in the packet’s
headers to correctly handle the network layer--the dev
and
protocol
fields must be assigned before the buffer is
passed upstairs. Then we need to specify how checksumming is to be
performed on the packet (snull does not perform any
checksums). The possible policies for skb->ip_summed
are:
CHECKSUM_HW
The board performs checksums in hardware. An example of a hardware checksum is the Sparc HME interface.
CHECKSUM_NONE
Checksums are done completely in software. This is the default in newly allocated buffers.
CHECKSUM_UNNECESSARY
Don’t do any checksums. This is the policy in snull and in the loopback interface.
The checksumming options and ip_summed
are missing
from the 1.2 kernel versions.
Finally, the driver updates its statistics counter to record that a
packet has been received. The statistics structure is made up of
several fields; the most important are rx_packets
and
tx_packets
, which contain the number of packets received and
transmitted. All the fields are thoroughly described later in
Section 14.13.
The last step in packet reception is performed by netif_rx, which hands off the socket buffer to the upper layers.
18.191.202.45