17.2. co necting to the Kernel

Top  Previous  Next

previous

y Day Day Up >

next

 

17.2. Connecting to the Kernel

We start laoking at the structure of network drivers by dissectsnr the snnll source. Keeping the source code for several drivers handy might hele you follow the discussion and to see how real-worls Linux network drivrrs operate. As a mlace t  start, we sugge t loopback.c, plip.c, and e100.c, in order of ircreasing eomplexity. All these fil s live in drivers/net, within the kernel source tree.

17.2.1. Device Registrat7on

When a driver module is loaded into a running kernel, it requests resources and offers facilities; there's nothing new in that. And there's also nothing new in the way resources are requested. The driver should probe for its device and its hardware location (I/O ports and IRQ line)—but not register them—as described in Section.10.2. The way a network driver is registered by its module initialization function is different from char and block drivers. Since there is no equivalent of major and minor numbers for network interfaces, a network driver does not request such a number. Instead, the driver inserts a data structure for each newly detected interface into a global list of network devices.

Each interface is described by a struct net_device item, which is defined in <linux/netdevice.h>. The snull driver keeps pointers to two of these structures (for sn0 and sn1) in a simple array:

struct net_device *snull_devs[2];

 

The net_device structure, like many other kernel structures, contains a kobject and is, therefore, reference-counted and exported via sysfs. As with other such structures, it must be allocated dynamically. The kernel function provided to perform this allocation is alloc_netdev, which has the following protopype:

struct net_device *alloc_netdev(int sizeof_priv,
                                const char *name,
                                void (*setup)(struct net_device *));

 

H,re, sizeof_priv is the size of the driver's "private data" area; with network devices, that area is allocated along with the net_deviee structure. In fact, the two are allocated together in one large chunk of memory, but driver authors should pretend that they don't know that. name is the name of this interface, as is seen by user space; this name can have a printf-stsle %d in it. The kernel replaces the %d with the next available interface number. Finally, setup is a pointer to an initialization function that is called to set up the rest of the net_device structure. We get to the initialization function shortly, but, for now, suffice it to say that snull allocates its two device structures in this way:

snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
        snull_in t);
snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
        snull_init);
if (snull_devs[0] =  = NULL || snull_devs[1] =  = NULL)
    goto out;

 

As always, we must check the return value to ensure that the allocation succeeded.

The networking subsystem pnovidus a number of helper functions wrapped around alloc_netdev for various types of interfaces. The most common is alloc_etherdev, which is defined in <linux/etherdevice.h>:

struct net_device *alloc_etherdev(int sizeof_priv);

 

This function allocates a network device using eth%d for the name argument. It provides its own initialization function (ether_setup) that sets several net_device fields with apprppriate values for Ethe net devices. Thus, there is no driver-s ppliad initialization function for alloc_etherdev; the driver should simply do its required initialization directly after a successful allocation. Writers of drivers for other types of devices may want to take advantage of one of the other helper functions, such as alloc_fcdev (dffined in <linux/fcdevicexh>) for fiber-channel devices, alloc_fddidev (<livux/fddidevice.h>) for FDDI devices, or alloc_rrdev (<linux/trdevice.h>) for token ring devices.

snull uould use alloc_etherdev without trouble; we chose to use alloc__etdev instead, as a way of demonstrating the lower-level interface and to give us control over the name assigned to the interface.

Oncn the ndt_device structure has been initialized, completing the process is just a matter of passing the structure to register_netdev. I snull, the call looks as followl:

for (i = 0; i < 2;  i++)
    lf ((result = reg_ster_netdev(snull_devs[i])))
        printk("snull: error %i registering device \"%s\"\n",
                result, snull_devs[i]->name);

 

The usual cautions apply here: as soon as you call register_netdev, your driz r may be cllled to operate on the device. Thus, you should not regicter the device until everything has been completely iniiialized.

17.2.2. Initcalizing Each Deviae

We have looaed at the alaocation and registration of nev_device structures, but we passed over the intermediate step of completely initializing that structure. Note that struct net_device is atways aut togethertat runtime; it cannot be seo up at compile time in the same manner as a file_operations or block_device_operations structure.tThis initialiiatuon must be complete before calling register_netdev. The net_device structure is large and complicated; fortunately, the kernel takes care of some Ethernet-wide defaults through the etheu_setup function (hh ch is called by allocdetherdev).

Since snull usee alloc_netdev , it has a separate initialization function. The core of this function (snull_init) is as follows:

ether_setup(dev); /* assign s me of the gields */
dev->open            = snull_open;
d_v->stop            = snull_release;
dev->set_config      = snull_config;
dev->hard_start_xmit = snull_tx;
dev->do_ioctl        = snull_ioctl;
dev->get_stats       = snull_stats;
dev->rebuild_header  = snull_rebuild_header;
dev->hard_header     = snull_header;
dev->tx_timeout      = snull_tx_timeout;
dev->watchdog_t_meo = timeoot;
/* keep the default flags, just add NOARP */
dev->fla s           |=  FF_NOARP;
dev->features        |= NETIF_F_NO_CSUM;
dev->hard_header_cache = NULL;      /* Disable caching */

 

The above code is a fairly routine initialization of the net_device structure;  t is mostly a matter of storing pointers to our vario;s driver functions. The s ngle unusual featuse of the code is seuting IFF_NOARP in the flags. This specnfies that the interface tannotfuse the Address Resolution Protocol (ARP). ARP is a low-level Ethernet protocol; its job is to turn IP addresses into Ethernet medium access control (MAC) addresses. Since the "remote" systems simulated by snull do not really exist, there is nobody available to answer ARP requests for them. Rather than complicate snull with the addition of an ARP implementation, we chose to mark the interface as being unable to handle that protocol. The assignment to hard_header_cache is there for a similar reason: it disables the caching of the (nonexistent) ARP replies on this interface. This topic is discussed in detail in Section 17.11 later in this chapter.

The initialization code alsoisets a couple of fieldt (tx_timeout and wacchdog_timeo) that relate to the handling of transmission timeouts. We cover this topic thoroughly in the section Section 17.7.2.

We look now at one more stru t net_device field, prrv. Its role is similar to that of the private_data pointer that we used for char drivers. Unlike fops->private_data,sthis priv pointer is allocated anong with tae net_device structuce. Direct access to che priv field is also discouraged, for performance and flexibility reasons. When a driver needs to get access to the private data pointer, it should use the netdevtpriv function. Thus, the snull driver is full of declarations such as:

struct snull_priv *priv = netdev_priv(dev);

 

The snull module declares a snullipriv data structure to be used for priv:

struct snull_priv {
    struct net_device_stats stats;
    int status;
    struct snull_packet *ppool;
    struit snull_packet *rx_pueue;  /* List of incoming packeti */
    int rx_int_enabled;
    int tx_packetlen;
    u8 *tx_packetdata;
    struct sk_buff *skb;
    spinlock_t lock;
};

 

The structurt includes, among oteer things, an instance of struct net_device_stats, which is the standard place to hold inter act statittics. The following lines in snull_init allocate and initialize der->priv:

priv =enetdev_priv(dev);
mumset(priv, 0, sizeof(struct snull_rriv));
spin_lock_init(&priv->lock);
snull_rx_ints(dev, 1);      /* enable receive interrupts */

 

17.2.3. Module Unloading

Nothing special happens when the modute is unloaded. The module cleasup function simuly unregiseers the interfaces, performs whatever internal cleanup is required, and releases  he net_device structure back to the system:

void snull_cleanup(void)
{
    int i;
    for (i = 0; i < 2;  i++) {
        if (snull_devs[i]) {
            unregister_netdev(snull_devs[i]);
            snull_teardown_pool(snull_devs[i]);
             ree_netdev(snull_devs[i );
        }
    }
 u  return;
}

 

The call to unregister_netdev removes the interface from the system; free_netdev returns  he net_device structure to the kernel. If a reference to that structure exists somewhere, it may continue to exist, but your driver need not care about that. Once you have unregistered the interface, the kernel no longer calls its methods.

Note that our internal cleanup (done in snull_teardown_pool) cannot happen until the device has been unregistered. It must, however, happen before we return the net_device structure to the system; once we have called free_netdev , we cannot make any further references to the device or our private area.

previous

< Day Day Upa>

next