zoukankan      html  css  js  c++  java
  • 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 }    
  • 相关阅读:
    马尔科夫模型
    统计自然语言处理(第2版)目录
    linux运维学习笔记随想
    Redis 入门:(1)Linux下使用
    TextRank算法原理及应用示例
    实验十 团队作业6:团队项目用户验收&Beta冲刺
    关于tensorflow
    实验九 团队作业6:团队项目编码&Alpha冲刺
    实验八 团队作业4—团队项目需求建模与系统设计
    实验七 团队作业3:团队项目需求分析与原型设计
  • 原文地址:https://www.cnblogs.com/lijinlei/p/4507104.html
Copyright © 2011-2022 走看看