Chinaunix首页 | 论坛 | 博客
  • 博客访问: 334429
  • 博文数量: 125
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 160
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-06 15:18
文章分类

全部博文(125)

文章存档

2014年(29)

2013年(93)

2012年(3)

分类: LINUX

2013-05-22 14:34:13

NIC设备在内核中相关联的net_device结构初始化,并添加到内核网络设备数据块中注册之后,用户才能通过用户空间的命令开启设备,使其可用。设备的注册和注销是由内核完成的,而设备的开启和关闭是由用户控制的。

网络设备注册的触发事件:

  1. 加载NIC设备驱动程序:若NIC设备驱动程序编译到内核中,则驱动程序将在系统引导期间初始化;若以模块加载的方式,则会在系统运行期间初始化。每当设备驱动程序初始化时,该驱动程序所控制的所有NIC设备就会被注册到系统设备数据库中。
  2. 插入可插拔网络设备:在设备的驱动程序已加载到系统中后,若用户将可热插拔NIC设备插入,内核会通知其驱动程序,这时NIC设备也会被注册到系统中。

相对应的,NIC设备注销的触发事件:

  1. 卸载NIC驱动程序:只针对以模块方式加载的驱动程序,不适用于编译进内核的驱动程序。当root卸载NIC设备驱动程序时,所有相关联以注册的NIC设备都会被注销。
  2. 拔出支持可热插拔的网络设备:当用户从系统中拔出可热插拔的NIC设备时,该网络设备就会在系统中被注销。

分配net_device数据结构

在系统中,网络设备用net_device数据结构定义,该数据结构由alloc_netdev函数分配,定义在net/core/dev.c源文件中。

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

  1. 其中sizeof_priv是网络设备驱动程序私有数据块的大小,在alloc_netdev_mqs函数中将和net_device数据结构一起分配,但驱动程序也可以设置sizeof_priv为0,不需要私有数据块,或自己分配私有数据块内存。

          若何net_device数据结构一起分配驱动程序的私有数据块,则其私有数据块的内存地址通过函数net_dec_priv获取:

/**
*    netdev_priv - access network device private data
*    @dev: network device
*
* Get network device private data
*/
static inline void *netdev_priv(const struct net_device *dev)
{
    return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
}

可见,驱动程序的私有数据块就跟在net_device数据结构后面,从alloc_netdev_mqs函数中也可以看到。

   2.   name是网络设备的名称,如eth*等等,用户空间的配置工具会引用内核所分派的设备名称。在用户空间,net-tools套件中的nameif可以基于MAC地址分派固定的名称给网络接口。

         实际上,在调用alloc_netdev函数时,传入的设备名称可能是name%d的形式如“eth%d”,内核在注册设备时,检查到%d标识符,会调用函数dev_alloc_name在系统中查找尚未使用的序列号,将eth%d替换为eth6等。

   3.   setup函数的原型是void (*setup)(struct net_device *),由驱动程序实现,在分配net_device结构后对结构中的一些未初始化成员进行初始化操作。

内核为不同的网络设备提供了一层封装函数,更便于使用:

网络设备类型

封装函数

函数定义

以太网设备(Ethernet) alloc_etherdev(sizeof_priv) alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
光纤分布式数据接口(FDDI) alloc_fddidev(int sizeof_priv) alloc_netdev(sizeof_priv, "fddi%d", fddi_setup)
高性能并行接口(HPPI) alloc_hippi_dev(int sizeof_priv) alloc_netdev(sizeof_priv, "hip%d", hippi_setup)
令牌环(Token Ring) alloc_trdev(int sizeof_priv) alloc_netdev(sizeof_priv, "tr%d", tr_setup)
光纤通道(Fibre Channel) alloc_fcdev(int sizeof_priv) alloc_netdev(sizeof_priv, "fc%d", fc_setup)
红外数据接口(InDA) alloc_irdadev(int sizeof_priv) alloc_netdev(sizeof_priv, "irda%d", irda_device_setup)

每一类网络设备都定义了自己的setup函数,如以太网设备的setup函数:

/**
* ether_setup - setup Ethernet network device
* @dev: network device
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
    dev->header_ops        = ð_header_ops;
    dev->type        = ARPHRD_ETHER;
    dev->hard_header_len     = ETH_HLEN;
    dev->mtu        = ETH_DATA_LEN;
    dev->addr_len        = ETH_ALEN;
    dev->tx_queue_len    = 1000;    /* Ethernet wants good queues */
    dev->flags        = IFF_BROADCAST|IFF_MULTICAST;

    memset(dev->broadcast, 0xFF, ETH_ALEN);

}
EXPORT_SYMBOL(ether_setup);

alloc_netdev函数的实现:

/**
*    alloc_netdev_mqs - allocate network device
*    @sizeof_priv:    size of private data to allocate space for
*    @name:        device name format string
*    @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 subquue structs
*    for each queue on the device.
*/
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        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)); //net_device数据结构中设备名称的最大长度是16个字节

    /*将net_device数据结构的大小按32字节对齐后,和sizeof_priv私有数据大小相加,产生分配的总内存字节大小*/

    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;
    }

     /*在这里增加31个字节,是为后面将分配后net_device数据结构的地址调整到32字节边界对齐,预留空间*/
    /* ensure 32-byte alignment of whole construct */
    alloc_size += NETDEV_ALIGN - 1;

   

    p = kzalloc(alloc_size, GFP_KERNEL); //分配内存
    if (!p) {
        printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
        return NULL;
    }

  /*将net_device数据结构的地址对齐到32字节边界,并记录下调整后的地址和实际分配的地址之间的长度,便于释放空间时使用分配的实际起始地址*/

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

/*分配一个per_cpu变量,记录该结构的引用计数*/ 

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

    /*初始化设备的硬件地址列表,并分配一个硬件地址成员*/

    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;

    INIT_LIST_HEAD(&dev->ethtool_ntuple_list.list);
    dev->ethtool_ntuple_list.count = 0;
    INIT_LIST_HEAD(&dev->napi_list);
    INIT_LIST_HEAD(&dev->unreg_list);
    INIT_LIST_HEAD(&dev->link_watch_list);
    dev->priv_flags = IFF_XMIT_DST_RELEASE;

   /*调用setup函数,初始化net_device结构中与设备类型密切相关的成员*/
    setup(dev);

   /*分配接收队列和发送队列*/ 

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

#ifdef CONFIG_RPS
    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->group = INIT_NETDEV_GROUP;
    return dev;

free_all:
    free_netdev(dev);
    return NULL;

free_pcpu:
    free_percpu(dev->pcpu_refcnt);
    kfree(dev->_tx);
#ifdef CONFIG_RPS
    kfree(dev->_rx);
#endif

free_p:
    kfree(p);
    return NULL;
}
EXPORT_SYMBOL(alloc_netdev_mqs);

阅读(408) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~