Linux网络设备驱动之设备驱动的注册与注销(二)

  网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为:

/**
 *    register_netdev    - register a network device
 *    @dev: device to register
 *
 *    Take a completed network device structure and add it to the kernel
 *    interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
 *    chain. 0 is returned on success. A negative errno code is returned
 *    on a failure to set up the device, or if the name is a duplicate.
 *
 *    This is a wrapper around register_netdevice that takes the rtnl semaphore
 *    and expands the device name if you passed a format string to
 *    alloc_netdev.
 */
int register_netdev(struct net_device *dev)
{
    int err;

    rtnl_lock();
    err = register_netdevice(dev);
    rtnl_unlock();
    return err;
}
/**
 *    unregister_netdev - remove device from the kernel
 *    @dev: device
 *
 *    This function shuts down a device interface and removes it
 *    from the kernel tables.
 *
 *    This is just a wrapper for unregister_netdevice that takes
 *    the rtnl semaphore.  In general you want to use this and not
 *    unregister_netdevice.
 */
void unregister_netdev(struct net_device *dev)
{
    rtnl_lock();
    unregister_netdevice(dev);
    rtnl_unlock();
}

  这两个函数都接收一个 net_device 结构体指针为参数,可见 net_device 数据结构在网络设备驱动中的核心地位。

  net_device 的生成和成员的赋值并不一定要由我们亲自动手逐个完成,可以利用如下宏帮助填充:

#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
        alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)

#define alloc_netdev_mq(sizeof_priv, name, name_assign_type, setup, count) \
        alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, count, count)

#define alloc_etherdev(sizeof_priv) \
      alloc_etherdev_mq(sizeof_priv, 1) #define alloc_etherdev_mq(sizeof_priv, count) \
      alloc_etherdev_mqs(sizeof_priv, count, count)

  alloc_etherdev_mq 宏引用的 alloc_etherdev_mqs( ) 函数的原型为:

/**
 * alloc_etherdev_mqs - Allocates and sets up an Ethernet device
 * @sizeof_priv: Size of additional driver-private structure to be allocated
 *    for this Ethernet device
 * @txqs: The number of TX queues this device has.
 * @rxqs: The number of RX queues this device has.
 *
 * Fill in the fields of the device structure with Ethernet-generic
 * values. Basically does everything except registering the device.
 *
 * Constructs a new net device, complete with a private data area of
 * size (sizeof_priv).  A 32-byte (not bit) alignment is enforced for
 * this private data area.
 */

struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
                      unsigned int rxqs)
{
    return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,
                ether_setup, txqs, rxqs);
}

 

  alloc_netdev 以及 alloc_netdev_mq 宏引用的 alloc_netdev_mqs( ) 函数的原型为:

/**
 *    alloc_netdev_mqs - allocate network device
 *    @sizeof_priv:        size of private data to allocate space for
 *    @name:            device name format string
 *    @name_assign_type:     origin of device name
 *    @setup:            callback to initialize device
 *    @txqs:            the number of TX subqueues to allocate
 *    @rxqs:            the number of RX subqueues to allocate
 *
 *    Allocates a struct net_device with private data area for driver use
 *    and performs basic initialization.  Also allocates subqueue structs
 *    for each queue on the device.
 */
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        unsigned char name_assign_type,
        void (*setup)(struct net_device *),
        unsigned int txqs, unsigned int rxqs)
{
    struct net_device *dev;
    size_t alloc_size;
    struct net_device *p;

    BUG_ON(strlen(name) >= sizeof(dev->name));

    if (txqs < 1) {
        pr_err("alloc_netdev: Unable to allocate device with zero queues\n");
        return NULL;
    }

#ifdef CONFIG_SYSFS
    if (rxqs < 1) {
        pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n");
        return NULL;
    }
#endif

    alloc_size = sizeof(struct net_device);
    if (sizeof_priv) {
        /* ensure 32-byte alignment of private area */
        alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
        alloc_size += sizeof_priv;
    }
    /* ensure 32-byte alignment of whole construct */
    alloc_size += NETDEV_ALIGN - 1;

    p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
    if (!p)
        p = vzalloc(alloc_size);
    if (!p)
        return NULL;

    dev = PTR_ALIGN(p, NETDEV_ALIGN);
    dev->padded = (char *)dev - (char *)p;

    dev->pcpu_refcnt = alloc_percpu(int);
    if (!dev->pcpu_refcnt)
        goto free_dev;

    if (dev_addr_init(dev))
        goto free_pcpu;

    dev_mc_init(dev);
    dev_uc_init(dev);

    dev_net_set(dev, &init_net);

    dev->gso_max_size = GSO_MAX_SIZE;
    dev->gso_max_segs = GSO_MAX_SEGS;
    dev->gso_min_segs = 0;

    INIT_LIST_HEAD(&dev->napi_list);
    INIT_LIST_HEAD(&dev->unreg_list);
    INIT_LIST_HEAD(&dev->close_list);
    INIT_LIST_HEAD(&dev->link_watch_list);
    INIT_LIST_HEAD(&dev->adj_list.upper);
    INIT_LIST_HEAD(&dev->adj_list.lower);
    INIT_LIST_HEAD(&dev->all_adj_list.upper);
    INIT_LIST_HEAD(&dev->all_adj_list.lower);
    INIT_LIST_HEAD(&dev->ptype_all);
    INIT_LIST_HEAD(&dev->ptype_specific);
    dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
    setup(dev);

    dev->num_tx_queues = txqs;
    dev->real_num_tx_queues = txqs;
    if (netif_alloc_netdev_queues(dev))
        goto free_all;

#ifdef CONFIG_SYSFS
    dev->num_rx_queues = rxqs;
    dev->real_num_rx_queues = rxqs;
    if (netif_alloc_rx_queues(dev))
        goto free_all;
#endif

    strcpy(dev->name, name);
    dev->name_assign_type = name_assign_type;
    dev->group = INIT_NETDEV_GROUP;
    if (!dev->ethtool_ops)
        dev->ethtool_ops = &default_ethtool_ops;
    return dev;

free_all:
    free_netdev(dev);
    return NULL;

free_pcpu:
    free_percpu(dev->pcpu_refcnt);
free_dev:
    netdev_freemem(dev);
    return NULL;
}

  alloc_netdev_mqs( ) 函数生成一个 net_device 结构体,对其成员赋值并返回该结构体的指针。第一个参数为设备私有成员大小,第二个参数为设备名,第三个参数为设备名称来源, 第四个参数为 net_device 的 setup( ) 函数指针,第五、六个参数为要分配的发送和接收子队列的数量。

  setup( ) 接收的参数也为  struct net_device  指针,用于预置 net_device 成员的值。

  free_netdev( ) 完成与 alloc_netdev( ) 和 alloc_etherdev( ) 函数相反的功能,即释放 net_device 结构体的函数:

/**
 *    free_netdev - free network device
 *    @dev: device
 *
 *    This function does the last stage of destroying an allocated device
 *     interface. The reference to the device object is released.
 *    If this is the last reference then it will be freed.
 */
void free_netdev(struct net_device *dev)
{
    struct napi_struct *p, *n;

    release_net(dev_net(dev));

    netif_free_tx_queues(dev);
#ifdef CONFIG_SYSFS
    kvfree(dev->_rx);
#endif

    kfree(rcu_dereference_protected(dev->ingress_queue, 1));

    /* Flush device addresses */
    dev_addr_flush(dev);

    list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
        netif_napi_del(p);

    free_percpu(dev->pcpu_refcnt);
    dev->pcpu_refcnt = NULL;

    /*  Compatibility with error handling in drivers */
    if (dev->reg_state == NETREG_UNINITIALIZED) {
        netdev_freemem(dev);
        return;
    }

    BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
    dev->reg_state = NETREG_RELEASED;

    /* will free via device release */
    put_device(&dev->dev);
}

  net_device 结构体的分配 和 网络设备驱动的注册 需在网络设备驱动程序初始化时进行,而 net_device 结构体的释放 和 网络设备驱动的注销 在设备或驱动被移除的时候执行,如下所示:

/*  
 *  网络设备驱动程序的注册和注销
 *
 */

static int xxx_register(void)
{
    ```
    /* 分配 net_device 结构体并对其成员赋值 */
    xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);
    if (xxx_dev == NULL)
    ``` /* 分配 net_device 失败 */

    /* 注册 net_device 结构体 */
    if ((result = register_netdev(xxx_dev))) 
    ```    
}

static void xxx_unregister(void)
{
    ```
    /* 注销 net_device 结构体 */
    unregister_netdev(xxx_dev);

    /* 释放 net_device 结构体 */ 
    free_netdev(xxx_dev);
}   

 

posted @ 2020-07-25 01:54  闹闹爸爸  阅读(2836)  评论(0编辑  收藏  举报