Enea LINX代码分析之二(ECM_RX)

ECM是Ethernet Connection Manager的简称,也就是LINX协议直接运行在链路层之上,不经过TCP/IP层的协议栈,处理效率更高,本文将对其代码进行分析。ecm也是以一个内核驱动模块(linx_eth_cm.ko)的形式存在,如果需要使用LINX Over Eth,除了insmod linx.ko之外,也需要insmod linx_eth_cm.ko。

作为一个内核驱动模块,首先看看其初始化函数。

 1 static int __init ecm_init(void)
 2 {
 3         int status;
 4 
 5         spin_lock_init(&ecm_lock);
 6         memset(ecm_connection_array, 0, sizeof(ecm_connection_array));
 7 
 8         INIT_LIST_HEAD(&ecm_device_list);
 9         INIT_LIST_HEAD(&ecm_orphan_list);
10 
11         init_waitqueue_head(&ecm_waitq);
12 
13         ecm_workq = create_singlethread_workqueue("ecm");
14         if (ecm_workq == NULL)
15                 return -ENOMEM;
16 
17         status = register_netdevice_notifier(&ecm_notifier);
18         if (status < 0) {
19                 destroy_workqueue(ecm_workq);
20                 return status;
21         }
22 
23         db_add_template(DB_KEY_ETHCM, &ecm_template);
24         db_proc_add(DB_KEY_ETHCM);
25 
26         setup_timer(&ecm_release_cid_timer, ecm_release_cid, 0);
27         mod_timer(&ecm_release_cid_timer, tmo_ms(5000));
28 
29         return 0;
30 }
31 module_init(ecm_init);
init

 初始化主要做下面几件事情:

  1. 初始化eth设备链表,因为设备上可能有多个eth设备,例如eth0,lo等。
  2. 创建一个只有一个线程的工作队列,作为一种可以阻塞的下半部机制用于处理各种工作。
  3. 注册 netdevice事件处理函数。 
  4. 将eth的create()/destroy()函数加入到db中,主要在用于调用mkethcon的时候执行。
  5. 创建一个定时器,功能暂时不清楚。

下面主要看下对通知链的处理,注册对ECM_PROTOCOL(0x8911)协议数据的处理函数ecm_rx()。

 1 static int net_event(struct notifier_block *nb, unsigned long event, void *data);
 2 static struct notifier_block ecm_notifier = {
 3         .notifier_call = net_event,
 4 };
 5 
 6 
 7 static int net_event(struct notifier_block *nb, unsigned long event, void *data)
 8 {
 9         struct net_device *dev;
10         struct ecm_work *w;
11         struct ecm_work_net_event *p;
12 
13 
14         (void)nb;
15         dev = data;
16 
17 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
18         if (dev_net(dev) != &init_net)
19                 return NOTIFY_DONE;
20 #endif
21 
22         w = alloc_ecm_work(sizeof(*p), ECM_WORK_NET_EVENT, GFP_KERNEL);
23         if (w == NULL)
24                 return NOTIFY_DONE;
25 
26         p = w->data;
27         p->event = event;
28         p->dev = dev;
29 
30         /*
31          * We need to call dev_hold(dev) before returning from this function,
32          * alloc_ecm_device() takes care of this. Note that dev_put(dev) must
33          * be done from the work queue.
34          */
35         if (event == NETDEV_REGISTER) {
36                 p->ecm_dev = alloc_ecm_device(dev);
37                 if (p->ecm_dev == NULL) {
38                         free_ecm_work(w);
39                         return NOTIFY_DONE;
40                 }
41         }
42         queue_work(ecm_workq, &w->work);
43 
44         return NOTIFY_OK;
45 }

该函数主要处理从eth设备通知的各种事件,例如NETDEV_UP/NETDEV_DOWN/NETDEV_REGISTER/NETDEV_UNREGISTER等事件。驱动模块加载以后,内核即对当前系统中支持的每个eth设别通知这些事件,对事件的处理,将放到工作队列中执行。

下面是workqueue的处理入口,net_event中的ECM_WORK_NET_EVENT工作在这里由handle_work_net_event()处理。届此,发往本机的协议号为0x8911的数据包可以被接收。

 1 ic void ecm_workq_func(struct work_struct *w)
 2 {
 3         struct ecm_work *p;
 4 
 5 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
 6         log_ecm_work(p = container_of(w, struct ecm_work, work));
 7 #else
 8         log_ecm_work(p = (struct ecm_work *)w);
 9 #endif
10 
11         switch (p->opcode) {
12         case ECM_WORK_CONN_TMO:
13                 handle_work_conn_tmo(p);
14                 break;
15         case ECM_WORK_CONN_PKT:
16                 handle_work_conn_pkt(p);
17                 break;
18         case ECM_WORK_NET_EVENT:
19                 handle_work_net_event(p);
20                 break;
21         case ECM_WORK_CREATE:
22                 handle_work_create(p);
23                 break;
24         case ECM_WORK_DESTROY:
25                 handle_work_destroy(p);
26                 break;
27         case ECM_WORK_CLEANUP:
28                 handle_work_cleanup(p);
29                 break;
30         case ECM_WORK_DC_INIT:
31                 handle_work_dc_init(p);
32                 break;
33         case ECM_WORK_DC_FINI:
34                 handle_work_dc_fini(p);
35                 break;
36         case ECM_WORK_DC_CONN:
37                 handle_work_dc_conn(p);
38                 break;
39         case ECM_WORK_DC_DISC:
40                 handle_work_dc_disc(p);
41                 break;
42         case ECM_WORK_DISC:
43                 handle_work_disc(p);
44                 break;
45         default:
46                 ERROR(); /* FIXME: just for now... */
47                 free_ecm_work(p);
48                 break;
49         }
50 }

在netdevice_event中,最重要的不过NETDEV_REGISTER事件。这里主要调用start_ecm_device().该函数中调用dev_add_pack()注册对

 1 static void handle_work_net_event(struct ecm_work *w)
 2 {
 3         struct ecm_work_net_event *p;
 4 
 5         p = w->data;
 6 
 7         switch (p->event) {
 8         case NETDEV_REGISTER:
 9                 handle_netdev_register(p->dev, p->ecm_dev);
10                 break;
11         case NETDEV_UNREGISTER:
12                 handle_netdev_unregister(p->dev);
13                 break;
14         case NETDEV_UP:
15                 break; /* CONN_TMO takes care of this... */
16         case NETDEV_DOWN:
17                 handle_netdev_down(p->dev);
18                 break;
19         case NETDEV_CHANGEMTU:
20                 report_netdev_unsupported(p->dev, "NETDEV_CHANGEMTU");
21                 break;
22         case NETDEV_CHANGEADDR:
23                 report_netdev_unsupported(p->dev, "NETDEV_CHANGEADDR");
24                 break;
25         case NETDEV_CHANGENAME:
26                 report_netdev_unsupported(p->dev, "NETDEV_CHANGENAME");
27                 break;
28         default:
29                 /*
30                  * Silently ignore all other events, e.g. NETDEV_CHANGE,
31                  * NETDEV_FEAT_CHANGE, NETDEV_GOING_DOWN and NETDEV_REBOOT.
32                  */
33                 break;
34         }
35 
36         free_ecm_work(w);
37 }
38 
39 
40 static void handle_netdev_register(struct net_device *dev,
41                                    struct ecm_device *ecm_dev)
42 {
43         struct list_head *item, *tmp;
44         struct RlnhLinkObj *co;
45 
46         /*
47          * Any orphan connections? Move them to device's conn list before
48          * starting the device. No conn_list lock is needed since RX won't
49          * get any Linx packet, i.e. dev_add_pack hasn't been called.
50          */
51         list_for_each_safe(item, tmp, &ecm_orphan_list) {
52                 co = list_entry(item, struct RlnhLinkObj, node);
53                 if (strcmp(co->dev_name, ecm_dev->dev->name) == 0) {
54                         co->ecm_dev = ecm_dev;
55                         list_move_tail(&co->node, &ecm_dev->conn_list);
56                 }
57         }
58 
59         /* Enable RX and TX... */
60         start_ecm_device(ecm_dev);
61 }
62 
63 
64 static void start_ecm_device(struct ecm_device *p)
65 {
66         struct list_head *item;
67         struct RlnhLinkObj *co;
68 
69         printk(KERN_INFO "start_ecm_device
");
70 
71         list_add(&p->node, &ecm_device_list);
72 
73         /*
74          * Reset transmit lock, i.e. allow TX to use the struct net_device
75          * object. RX won't get any LINX packets and destroy-work makes
76          * the connection in-visible, i.e. no lock is needed.
77          */
78         list_for_each(item, &p->conn_list) {
79                 co = list_entry(item, struct RlnhLinkObj, node);
80                 reset_ecm_lock(&co->tx_lock, 1);
81         }
82 
83         /* RX callback will get LINX packets... */
84         dev_add_pack(&p->pt);
85 }

 总结下,这个初始化注册流程为:

net_event()-->ecm_workq_func()-->handle_work_net_event()-->handle_netdev_register()-->start_ecm_device()-->dev_add_pack()。

下面简介下如何将参数传递给ecm_workq_func()的。

首选需要调用alloc_ecm_work分配一个ecm_work "w",然后将该工作类型对应的数据存入到w->data中。接着将w->work放入到工作队列中,工作队列处理函数再用

container_of() 函数找到 w,接着使用w->data。
 1 static int net_event(struct notifier_block *nb, unsigned long event, void *data)
 2 {
 3         struct net_device *dev;
 4         struct ecm_work *w;
 5         struct ecm_work_net_event *p;
 6         w = alloc_ecm_work(sizeof(*p), ECM_WORK_NET_EVENT, GFP_KERNEL);
 7         if (w == NULL)
 8                 return NOTIFY_DONE;
 9 
10         p = w->data;
11         p->event = event;
12         p->dev = dev;
13 
14         queue_work(ecm_workq, &w->work);
15 
16         return NOTIFY_OK;
17 }
18 
19 
20 static void ecm_workq_func(struct work_struct *w)
21 {
22         struct ecm_work *p;
23 
24 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
25         log_ecm_work(p = container_of(w, struct ecm_work, work));
26 #else
27         log_ecm_work(p = (struct ecm_work *)w);
28 #endif
29 }    
原文地址:https://www.cnblogs.com/lijinlei/p/4507104.html