Linux Device Drivers (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Linux Device Drivers (3rd Edition) [Electronic resources] - نسخه متنی

Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








17.6. Packet Reception


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 atomic
context. There are two modes of packet reception that may be
implemented by network drivers: interrupt driven and polled. Most
drivers implement the interrupt-driven technique, and that is the one
we cover first. Some drivers for high-bandwidth adapters may also
implement the polled technique; we look at this approach in the
Section 17.8.

The implementation of snull separates the
"hardware" details from the
device-independent housekeeping. Therefore, the function
snull_rx is called from the
snull
"interrupt" handler after the
hardware has received the packet, and it is already in the
computer's memory. snull_rx
receives a pointer to the data and the length of the packet; its sole
responsibility 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 net_device *dev, struct snull_packet *pkt)
{
struct sk_buff *skb;
struct snull_priv *priv = netdev_priv(dev);
/*
* The packet has been retrieved from the transmission
* medium. Build an skb around it, so upper layers can handle it
*/
skb = dev_alloc_skb(pkt->datalen + 2);
if (!skb) {
if (printk_ratelimit( ))
printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
priv->stats.rx_dropped++;
goto out;
}
memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
/* 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 */
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
netif_rx(skb);
out:
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.



The
first step is to allocate a buffer to hold the packet. Note that the
buffer allocation function (dev_alloc_skb) needs
to know the data length. The information is used by the function to
allocate space for the buffer. dev_alloc_skb
calls kmalloc with atomic priority, so it can 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 17.10.

Of course, the return value from dev_alloc_skb
must be checked, and snull does so. We call
printk_ratelimit before complaining about
failures, however. Generating hundreds or thousands of console
messages per second is a good way to bog down the system entirely and
hide the real source of problems;
printk_ratelimit helps prevent that problem by
returning 0 when too much output has gone to the
console, and things need to be slowed down a bit.

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.

If you are writing a high-performance
driver for an interface that can do full bus-mastering I/O, there is
a possible optimization that is worth considering here. Some drivers
allocate socket buffers for incoming packets prior to their
reception, then instruct the interface to place the packet data
directly into the socket buffer's space. The
networking layer cooperates with this strategy by allocating all
socket buffers in DMA-capable space (which may be in high memory if
your device has the NETIF_F_HIGHDMA feature flag
set). Doing things this way avoids the need for a separate copy
operation to fill the socket buffer, but requires being careful with
buffer sizes because you won't know in advance how
big the incoming packet is. The implementation of a
change_mtu method is also important in this
situation, since it allows the driver to respond to a change in the
maximum packet size.

The network layer needs to have some
information spelled out before it can make sense of the packet. To
this end, the dev and protocol
fields must be assigned before the buffer is passed upstairs. The
Ethernet support code exports a helper function
(eth_type_trans), which finds an appropriate
value to put into protocol. Then we need to
specify how checksumming is to be performed or has been performed on
the packet (snull does not need to perform any
checksums). The possible policies for
skb->ip_summed are:

CHECKSUM_HW



The
device has already performed checksums in hardware. An example of a
hardware checksum is the SPARC HME interface.


CHECKSUM_NONE


Checksums have not yet been verified, and the task must be
accomplished by system 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.



You may be wondering why the checksum status must be specified here
when we have already set a flag in the features
field of our net_device structure. The answer is
that the features flag tells the kernel about how
our device treats outgoing packets. It is not used for incoming
packets, which must, instead, be marked individually.

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,
rx_bytes, tx_packets, and
tx_bytes, which contain the number of packets
received and transmitted and the total number of octets transferred.
All the fields are thoroughly described in Section 17.13.

The last step in packet reception is performed by
netif_rx, which hands off the socket buffer to
the upper layers. netif_rx actually returns an
integer value; NET_RX_SUCCESS
(0) means that the packet was successfully
received; any other value indicates trouble. There are three return
values (NET_RX_CN_LOW,
NET_RX_CN_MOD, and
NET_RX_CN_HIGH) that indicate increasing levels of
congestion in the networking subsystem;
NET_RX_DROP means the packet was dropped. A driver
could use these values to stop feeding packets into the kernel when
congestion gets high, but, in practice, most drivers ignore the
return value from netif_rx. If you are writing a
driver for a high-bandwidth device and wish to do the right thing in
response to congestion, the best approach is to implement NAPI, which
we get to after a quick discussion of interrupt handlers.


    / 202