__dev_open函数,完成对设备的启用操作,并进行一些必要初始化和通知,调用关系如下,本文主要对这几个函数进行分析;
1 /** 2 * _dev_open函数的调用关系 3 * dev_change_flags-->_dev_change_flags-->__dev_open 4 * 5 * dev_open-->__dev_open 6 * 7 * __dev_open 8 * |-->dev_set_rx_mode 9 * |-->__dev_set_promiscuity 10 */
在标志改变时,__dev_change_flags会对新旧标志进行检查处理,若果发现其IFF_UP标识位有所变化,则根据其原来是否处理开启状态做对应处理,若原来处理启用状态,则关闭之,若原来处理关闭状态,则开启之;
1 int __dev_change_flags(struct net_device *dev, unsigned int flags) 2 { 3 unsigned int old_flags = dev->flags; 4 int ret; 5 6 /* 这里省去一些无关的代码细节 */ 7 8 /* 9 * Have we downed the interface. We handle IFF_UP ourselves 10 * according to user attempts to set it, rather than blindly 11 * setting it. 12 */ 13 14 ret = 0; 15 /* 两个标识有一个是IFF_UP */ 16 if ((old_flags ^ flags) & IFF_UP) 17 /* 源标识有IFF_UP则调用关闭,否则调用开启 */ 18 ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev); 19 20 /* 这里省去一些无关的代码细节 */ 21 22 return ret; 23 }
dev_open完成设备的启用,其首先会进行当前状态的判断,若处于关闭状态,则执行启用设备操作,并在启用后发送通知消息;
1 /** 2 * dev_open - prepare an interface for use. 3 * @dev: device to open 4 * 5 * Takes a device from down to up state. The device's private open 6 * function is invoked and then the multicast lists are loaded. Finally 7 * the device is moved into the up state and a %NETDEV_UP message is 8 * sent to the netdev notifier chain. 9 * 10 * Calling this function on an active interface is a nop. On a failure 11 * a negative errno code is returned. 12 */ 13 int dev_open(struct net_device *dev) 14 { 15 int ret; 16 17 /* 如果已经打开返回 */ 18 if (dev->flags & IFF_UP) 19 return 0; 20 21 /* 打开设备 */ 22 ret = __dev_open(dev); 23 if (ret < 0) 24 return ret; 25 26 /* 通知设备打开 */ 27 rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); 28 call_netdevice_notifiers(NETDEV_UP, dev); 29 30 return ret; 31 }
__dev_open为设备启用核心函数,该函数执行设备启用,设置启用标记,并且设置接收模式,排队规则等;
1 static int __dev_open(struct net_device *dev) 2 { 3 const struct net_device_ops *ops = dev->netdev_ops; 4 int ret; 5 6 ASSERT_RTNL(); 7 8 /* 设备不可用 */ 9 if (!netif_device_present(dev)) 10 return -ENODEV; 11 12 /* Block netpoll from trying to do any rx path servicing. 13 * If we don't do this there is a chance ndo_poll_controller 14 * or ndo_poll may be running while we open the device 15 */ 16 /* 禁用netpoll */ 17 netpoll_poll_disable(dev); 18 19 /* 设备打开前通知 */ 20 ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev); 21 ret = notifier_to_errno(ret); 22 if (ret) 23 return ret; 24 25 /* 设置设备打开标记 */ 26 set_bit(__LINK_STATE_START, &dev->state); 27 28 /* 校验地址 */ 29 if (ops->ndo_validate_addr) 30 ret = ops->ndo_validate_addr(dev); 31 32 /* 执行打开 */ 33 if (!ret && ops->ndo_open) 34 ret = ops->ndo_open(dev); 35 36 /* 启用netpoll */ 37 netpoll_poll_enable(dev); 38 39 /* 失败,清除打开标记 */ 40 if (ret) 41 clear_bit(__LINK_STATE_START, &dev->state); 42 43 /* 设备打开操作 */ 44 else { 45 /* 设置打开标记 */ 46 dev->flags |= IFF_UP; 47 48 /* 设置接收模式 */ 49 dev_set_rx_mode(dev); 50 /* 初始化排队规则 */ 51 dev_activate(dev); 52 /* 加入设备数据到熵池 */ 53 add_device_randomness(dev->dev_addr, dev->addr_len); 54 } 55 56 return ret; 57 }
设备启用过程中,会设置接收模式,若设备实现了ndo_set_rx_mode则调用设备的该函数进行模式设置,如果设备没有实现该函数,那么将会根据单播和混杂标志进行设置;
关于该问题的详细介绍,请参考本博客的另外一篇文章<网络设备之uc_promisc>
1 /* 2 * Upload unicast and multicast address lists to device and 3 * configure RX filtering. When the device doesn't support unicast 4 * filtering it is put in promiscuous mode while unicast addresses 5 * are present. 6 */ 7 void __dev_set_rx_mode(struct net_device *dev) 8 { 9 const struct net_device_ops *ops = dev->netdev_ops; 10 11 /* dev_open will call this function so the list will stay sane. */ 12 /* 设备未启动 */ 13 if (!(dev->flags&IFF_UP)) 14 return; 15 16 /* 设备不存在 */ 17 if (!netif_device_present(dev)) 18 return; 19 20 /* 不支持单播过滤 */ 21 /* 未实现ndo_set_rx_mode */ 22 if (!(dev->priv_flags & IFF_UNICAST_FLT)) { 23 /* Unicast addresses changes may only happen under the rtnl, 24 * therefore calling __dev_set_promiscuity here is safe. 25 */ 26 /* 单播硬件地址存在&& 单播混杂模式未开启 */ 27 if (!netdev_uc_empty(dev) && !dev->uc_promisc) { 28 /* 开启混杂模式 */ 29 __dev_set_promiscuity(dev, 1, false); 30 dev->uc_promisc = true; 31 } 32 /* 单播硬件地址不存在&& 设备开启混杂模式 */ 33 else if (netdev_uc_empty(dev) && dev->uc_promisc) { 34 /* */ 35 __dev_set_promiscuity(dev, -1, false); 36 dev->uc_promisc = false; 37 } 38 } 39 40 /* 调用设备设置接收模式函数 */ 41 if (ops->ndo_set_rx_mode) 42 ops->ndo_set_rx_mode(dev); 43 }
上面函数分支会调用__dev_set_promiscuity函数设置混杂模式,该函数根据混杂模式计数开启或者关闭设备的混杂模式;
1 /* 设置混杂模式 */ 2 static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) 3 { 4 unsigned int old_flags = dev->flags; 5 kuid_t uid; 6 kgid_t gid; 7 8 ASSERT_RTNL(); 9 10 /* 打混杂标记 */ 11 dev->flags |= IFF_PROMISC; 12 13 /* 改变混杂计数,inc可能为负 */ 14 dev->promiscuity += inc; 15 16 /* 所有混杂均释放 */ 17 if (dev->promiscuity == 0) { 18 /* 19 * Avoid overflow. 20 * If inc causes overflow, untouch promisc and return error. 21 */ 22 /* 当前是释放混杂计数操作,则关闭混杂模式 */ 23 if (inc < 0) 24 dev->flags &= ~IFF_PROMISC; 25 /* 否则出错 */ 26 else { 27 dev->promiscuity -= inc; 28 pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken. ", 29 dev->name); 30 return -EOVERFLOW; 31 } 32 } 33 34 /* 新旧标识不相同 */ 35 if (dev->flags != old_flags) { 36 pr_info("device %s %s promiscuous mode ", 37 dev->name, 38 dev->flags & IFF_PROMISC ? "entered" : "left"); 39 if (audit_enabled) { 40 current_uid_gid(&uid, &gid); 41 audit_log(current->audit_context, GFP_ATOMIC, 42 AUDIT_ANOM_PROMISCUOUS, 43 "dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u", 44 dev->name, (dev->flags & IFF_PROMISC), 45 (old_flags & IFF_PROMISC), 46 from_kuid(&init_user_ns, audit_get_loginuid(current)), 47 from_kuid(&init_user_ns, uid), 48 from_kgid(&init_user_ns, gid), 49 audit_get_sessionid(current)); 50 } 51 52 /* 调用改变rx标识操作 */ 53 dev_change_rx_flags(dev, IFF_PROMISC); 54 } 55 56 /* 混杂模式通知 */ 57 if (notify) 58 __dev_notify_flags(dev, old_flags, IFF_PROMISC); 59 return 0;