Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1007199
  • 博文数量: 442
  • 博客积分: 1146
  • 博客等级: 少尉
  • 技术积分: 1604
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-04 12:52
个人简介

123

文章分类

全部博文(442)

文章存档

2017年(3)

2016年(15)

2015年(132)

2014年(52)

2013年(101)

2012年(110)

2011年(29)

分类:

2012-01-18 22:03:38

声明:本文为原创
#####请转贴时保留以下内容######
作者GTT
本文档归属http://oldtown.cublog.cn/.转载请注明出处!
请提出宝贵意见Mail:mtloveft@hotmail.com
Linux Version:2.6.33
提示本文是介绍关于linux 如何实现loopback NIC 驱动
 
下面看看Loopback NIC的net_device的注册过程。SourceCode入下。

/**
 * 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();
    /* If the name is a format string the caller wants us to do a name allocation. */
    if (strchr(dev->name, '%')) {
        err = dev_alloc_name(dev, dev->name);
//对设备名进行设置
        if (err < 0) goto out;
    }
    err = register_netdevice(dev);
//把网络设备注册到当前网络空间的网络设备链表中
out:
    rtnl_unlock();
    return err;
}

 
首先判断dev->name是否含有%,如果有%,将重新分配名字。
register_netdevice是具体注册的方法

/**
 * register_netdevice - 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.
 *
 * Callers must hold the rtnl semaphore. You may want
 * register_netdev() instead of this.
 *
 * BUGS:
 * The locking appears insufficient to guarantee two parallel registers
 * will not get the same name.
 */

int register_netdevice(struct net_device *dev)
{
    int ret;
    struct net *net = dev_net(dev);

    BUG_ON(dev_boot_phase);
    ASSERT_RTNL();

    might_sleep();

    /* When net_device's are persistent, this will be fatal. */
    BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
    BUG_ON(!net);
    //初始化各种锁
    spin_lock_init(&dev->addr_list_lock);
    netdev_set_addr_lockdep_class(dev);
    netdev_init_queue_locks(dev);

    dev->iflink = -1;

    /* Init, if this function is available */
    if (dev->netdev_ops->ndo_init) {
        ret = dev->netdev_ops->ndo_init(dev);
        if (ret) {
            if (ret > 0) ret = -EIO;
            goto out;
        }
    }
    
//在hash表中检测是否有重复的名字 
    ret = dev_get_valid_name(net, dev->name, dev->name, 0);
    if (ret) goto err_uninit;

    dev->ifindex = dev_new_index(net);
//给设备分配一个唯一的index
    if (dev->iflink == -1dev->iflink = dev->ifindex;

    /* Fix illegal checksum combinations */
    if ((dev->features & NETIF_F_HW_CSUM) &&
        (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
        printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n", dev->name);
        dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
    }

    if ((dev->features & NETIF_F_NO_CSUM) &&
        (dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
        printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n", dev->name);
        dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);
    }
    dev->features = netdev_fix_features(dev->features, dev->name);
    /* Enable software GSO if SG is supported. */
    if (dev->features & NETIF_F_SG) dev->features |= NETIF_F_GSO;

    netdev_initialize_kobject(dev);
//初始化设备驱动的kobject并创建相关的sysfs

    ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
    ret = notifier_to_errno(ret);
    if (ret) goto err_uninit;

    ret = netdev_register_kobject(dev);
    if (ret) goto err_uninit;
    dev->reg_state = NETREG_REGISTERED;
//设置网络设备为已注册状态

    /* Default initial state at registry is that the device is present. */
    set_bit(__LINK_STATE_PRESENT, &dev->state);
//设置网络设备状态

    dev_init_scheduler(dev);
//初始化排队规则

    dev_hold(dev);
    list_netdevice(dev);
//将相应的链表插入到网络命名空间的全局的链表中
    /* Notify protocols, that a new device appeared. */
    ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
//调用netdev_chain通知内核其他子系统。

    ret = notifier_to_errno(ret);
    if (ret) {
        rollback_registered(dev);
        dev->reg_state = NETREG_UNREGISTERED;
    }
    
/* Prevent userspace races by waiting until the network
     * device is fully setup before sending notifications.
     */

    rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
out:
    return ret;
err_uninit:
    if (dev->netdev_ops->ndo_uninit)
        dev->netdev_ops->ndo_uninit(dev);
    goto out;
}

 
dev->netdev_ops->ndo_init是上篇文章里说过的loopback_ops 里定义的方法loopback_dev_init

static int loopback_dev_init(struct net_device *dev)
{
    struct pcpu_lstats *lstats;
    lstats = alloc_percpu(struct pcpu_lstats);
    if (!lstats) return -ENOMEM;

    dev->ml_priv = lstats;
    return 0;
}

这个方法只是分配一个pcpu_lstats结构,用来存送受信的统计信息。作为net_device的私有数据。
即dev->ml_priv。
 
dev_get_valid_name是检测设备名。

static int dev_get_valid_name(struct net *net, const char *name, char *bufbool fmt)
{
    if (!dev_valid_name(name)) return -EINVAL;

    if (fmt && strchr(name, '%')) return __dev_alloc_name(net, name, buf);
    else if (__dev_get_by_name(net, name)) return -EEXIST;
    else if (buf != name) strlcpy(buf, name, IFNAMSIZ);

    return 0;
}

 
dev_valid_name是对网络设备名的Check。

int dev_valid_name(const char *name)
{
    if (*name == '\0') return 0;
    if (strlen(name) >= IFNAMSIZ) return 0;
    if (!strcmp(name, ".") || !strcmp(name, "..")) return 0;

    while (*name) {
        if (*name == '/' || isspace(*name)) return 0;
        name++;
    }
    return 1;
}

 
名字为空,或者为.和..等都是认为非法的。
__dev_get_by_name是根据名字查找设备,如果设备存在,将不能利用这个名字。

struct net_device *__dev_get_by_name(struct net *net, const char *name)
{
    struct hlist_node *p;
    struct net_device *dev;
    struct hlist_head *head = dev_name_hash(net, name);

    hlist_for_each_entry(dev, p, head, name_hlist)
        if (!strncmp(dev->name, name, IFNAMSIZ)) return dev;

    return NULL;
}


netdev_initialize_kobject是在设备文件里增加一个设备,同时在SysFS里增加相应目录等。先不分析这个方法,以后分析。

call_netdevice_notifiers调用netdev_chain通知内核其他子系统。

list_netdevice是把net_device链接到各种链表里。

/* Device list insertion */
static int list_netdevice(struct net_device *dev)
{
    struct net *net = dev_net(dev);
    ASSERT_RTNL();
    write_lock_bh(&dev_base_lock);
    
//插入到dev_base_head双向链表
    list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
    
//根据name插入到name_hlist这个hash表
    hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
    
//根据ifindex插入到index_hlist这个hash表
    hlist_add_head_rcu(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
    write_unlock_bh(&dev_base_lock);
    return 0;
}

各种双向链表和hash表的关系如下



 



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