声明:本文为原创
#####请转贴时保留以下内容######
作者:GTT
请提出宝贵意见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 == -1) dev->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 *buf, bool 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表的关系如下
阅读(2037) | 评论(0) | 转发(1) |