网络设备之开启

__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;
原文地址:https://www.cnblogs.com/wanpengcoder/p/7526398.html