• 记录一次内核热补丁制作流程


    最近接到下游的一个需求,要为适配某网卡修改内核的patch制作热补丁。内核热补丁有较多的约束限制,包括不支持修改数据结构,不允许删除函数内部静态局部变量,不支持头文件修改等等。本次要修改的patch包含了头文件的修改,数据结构的变动,如果要制作热补丁就需要将这些变动尽量移动到.c文件中。本次patch涉及两处数据结构的变化,分别如下:

    +++ b/include/net/bonding.h
    @@ -173,7 +173,8 @@ struct slave {
         u8     backup:1,   /* indicates backup slave. Value corresponds with
                       BOND_STATE_ACTIVE and BOND_STATE_BACKUP */
                inactive:1, /* indicates inactive slave */
    -           should_notify:1; /* indicateds whether the state changed */
    +           should_notify:1, /* indicates whether the state changed */
    +           should_notify_link:1; /* indicates whether the link changed */
         u8     duplex;
         u32    original_mtu;
         u32    link_failure_count;
    @@ -2249,6 +2271,12 @@ struct netdev_notifier_changeupper_info {
             struct net_device *upper_dev; /* new upper dev */
             bool master; /* is upper dev master */
             bool linking; /* is the nofication for link or unlink */
    +     void *upper_info; /* upper dev info */
    +};

        一个是在结构体中新增一个位域成员,patch中对这个位域成员有读取和写的操作。为保持结构体不变,可以通过位运算从当前字节中获取到该位置的值。对应的获取和写这个位的操作可以用宏来实现,如下所示:

    #define SET_SHOULD_NOTIFY_LINK_BIT(pslave, value) 
            (value) == 1 ? (*(char *)(&(pslave)->new_link + 1) |= 0x8) : (*(char *)(&(pslave)->new_link + 1) &= 0x7)
     
    #define GET_SHOULD_NOTIFY_LINK_BIT(pslave)  ((*(char *)(&(pslave)->new_link + 1) & 0x8) >> 3)
     

        另外一个结构体是新增了一个指向void类型的指针,搜索该patch所有引用这个结构体的地方,发现所有使用该结构体时,在函数中传的参数为该结构体的第一个成员,而不是整个结构体。如下所示。

    void netdev_upper_dev_unlink(struct net_device *dev,
                     struct net_device *upper_dev)
    {
            struct netdev_notifier_changeupper_info changeupper_info;   
            changeupper_info.upper_dev = upper_dev;
            changeupper_info.master = master;
            changeupper_info.linking = true;
    +      changeupper_info.upper_info = upper_info;
    +      ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
    +                       &changeupper_info.info);

    这样就可以通过新增一个与netdev_notifier_changeupper_info 结构体相同的结构体定义, 将新增的成员upper_info传入即可,并且可以保持与原有的代码保持兼容,上述修改可以改为:

    void netdev_upper_dev_unlink(struct net_device *dev,
                     struct net_device *upper_dev)
    {
            struct netdev_notifier_changeupper_info_ext changeupper_info;    
            changeupper_info_ext.upper_dev = upper_dev;
            changeupper_info_ext.master = master;
            changeupper_info_ext.linking = true;
    +      changeupper_info_ext.upper_info = upper_info;
    +      ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
    +                       &changeupper_info_ext.info);

    保持原有结构体netdev_notifier_changeupper_info 不变,新增一个netdev_notifier_changeupper_info_ext结构体。

         若要不修改结构体的定义,那么就要满足在使用这个新增成员时,可以通过原有结构体找到该成员,并且要保证所有调用的函数参数接口不变化,一般来讲要保证这些比较困难,但是netdev_notifier_changeupper_info这个结构体的原有定义,以及原有的使用方式,可扩展型很好,就比较方便修改为结构体不变化的形式,满足制作内核热补丁的条件。

         解决了这两个问题后,用这个patch制作热补丁,但热补丁没有制作成功,经过专家定位,主要问题在于,同一个patch中的修改涉及到编译到内核模块的,和编译到内核的。需要把它们进行拆分,要每个patch可以单独编译通过,完成独立的一块功能。并且还要保证修改的头文件不能又被内核模块调用,又被内核调用,否则头文件也要拆分到.c中。后面经过了各种拆分,把大量的在头文件中,或者在.c文件中定义的函数,或函数实现,挪到了引用的.c中,费了较大力气才把这个补丁给做出来了,由于拆分的代码太多,有太多的函数挪用,就给这个修改带来了较大的功能和可靠性的风险。 

        所以制作热补丁的方案还需要继续研究,如果后面能有更好的制作热补丁的方式,可以支持结构体变化,支持头文件变化,那么对内核的修改和调试将会大大缩短开发时间。
  • 相关阅读:
    Eclipse 插件Maven在使用 add dependency,找不到包,解决办法
    SimpleDateFormat是线程不安全的,切忌切忌!
    JNative 传递参数bug
    oracle存储过程递归调用
    oracle调用DLL
    telnet和Netstat使用
    notepad++搭配dev配置运行C++
    ubuntu第一次安装登陆密码不正确问题
    区分形参和实参
    爬虫2
  • 原文地址:https://www.cnblogs.com/xingmuxin/p/9087200.html
Copyright © 2020-2023  润新知