If you followed last issue's "Gearheads" column, all of your block and character devices should be running under Linux 2.2, albeit possibly with warnings about obsolete PCI interfaces. In this article, I will finish up with some of the smaller changes that may catch a driver author, cover networking, and then look at the new PCI layer.
if(skb->protocol == htons(ETH_P_MYPROTO))
{
/* The card requires we mask the addresses for this */
u8 v;
skb = skb_cow(skb, 0);
if(skb==NULL)
return 1; /* Whoops no memory */
skb->data[4]&=0x7F;
skb->data[5]&=0x7F;
}
|
I'll start with the small stuff, since that is nice and easy. The first of these is signal handling. Linux 2.2 has more signals as well as POSIX real-time signal queues. This fact changes the driver code to determine whether a process has received a signal.
In Linux 2.0, drivers check for signals directly. This is done with code such as:
if(current->signal &
~current->blocked)
return -EINTR;
which ensures that pressing Ctrl-C on the terminal will return the EINTR error code from the device driver function.
Linux 2.2 replaces this with a function which hides the implementation of signals, which also means that we can avoid changing drivers again in the future. The above code now becomes:
if(signal_pending(current))
return -EINTR;
which is much cleaner.
The second, related issue is timeouts. Linux provides device drivers with several ways to handle timeouts. The normal mechanism is to use the add_timer() and del_timer() functions. It is also possible to sleep on a wait queue or reschedule with a timeout.
In Linux 2.0, the code for rescheduling with a timeout (essentially, to cause the process to sleep for a certain delay within the kernel) was:
current->state =
TASK_INTERRUPTIBLE;
current->timeout =
jiffies + MY_DELAY;
schedule();
In Linux 2.2, this becomes:
current->state =
TASK_INTERRUPTIBLE;
schedule_timeout(MY_DELAY);
The same pattern is followed for sleeping on a wait queue. This is done quite simply with:
interruptible_sleep_on_
timeout (wait_queue, MY_DELAY);
These changes to the timeout functions are done to improve scheduler performance. Instead of the scheduling code spending time managing processes that are almost never running, the scheduler and the timer handling are now split.
Porting Network Interfaces
Network driver functions have changed between Linux 2.0 and Linux 2.2. The actual routines have changed little in terms of functionality, but the calling conventions have changed considerably due to the addition of extensive SMP support.
The most obvious difference is that the functions for freeing buffers have changed. In order to avoid memory-accounting errors (and to make the programmer's life easier) the network buffers now remember which resources they are using and whether the buffer is in the sending or receiving path. This means that the second FREE_READ or FREE_WRITE argument to dev_kfree_skb() is a thing of the past.
Secondly, the buffers handed to a network driver belong solely to that driver. This gives the driver (almost) total freedom to play with the sk_ buff structure which it is handed. It can't change skb-> data (which is shared) but it can play with the rest of the object. It is best to avoid playing with the data anyway, as this potentially requires a copy. However, you can use the skb_cow() function to obtain a private copy of the buffer. If the buffer was already private it simply hands back the buffer you gave it, taking virtually no time to execute. Thus you might use something like Figure 1.
Another function provided to help device and protocol authors is skb_ realloc_headroom(). The kernel guarantees that when your driver is passed a buffer, that buffer has at leastdev->hard_header_len bytes for the hardware headers. The ether_ setup function sets this to 14 (for example), which leaves space for the Ethernet header to fit.
Sometimes you get low-speed drivers that occasionally need a lot of header space, and it's undesirable to allocate the entire header space all of the time. In these cases you can use:
skb = skb_realloc_headroom
(skb, 128);
if(skb == NULL)
/* Whoops no memory */
return 0;
to make a copy of the buffer if need be, which has at least 128 bytes of space at the beginning. In general, you want to avoid this function as copies impact performance. In some cases, such as tunnel devices, you can never be sure how much header space is needed in advance (e.g., as a tunnel may itself be tunneled). In these cases you set the device header length to cover normal cases and bite the overhead on the occasional unusual frame by using skb_realloc_ headroom().