__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.\n", 29 dev->name); 30 return -EOVERFLOW; 31 } 32 } 33 34 /* 新舊標識不相同 */ 35 if (dev->flags != old_flags) { 36 pr_info("device %s %s promiscuous mode\n", 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;