最近接到下游的一个需求,要为适配某网卡修改内核的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中,费了较大力气才把这个补丁给做出来了,由于拆分的代码太多,有太多的函数挪用,就给这个修改带来了较大的功能和可靠性的风险。
所以制作热补丁的方案还需要继续研究,如果后面能有更好的制作热补丁的方式,可以支持结构体变化,支持头文件变化,那么对内核的修改和调试将会大大缩短开发时间。
原文地址:https://www.cnblogs.com/xingmuxin/p/9087200.html