背景
在平时项目开发中,经常需要将事件或者ID转化为字符串。本文根据Mellanox驱动代码中的ib_event介绍介绍一种比较优雅的定义,判断的方法以及可以未来直接使用的代码。它的核心有点在于可以定义跳空的事件,以及直接使用字符串数组下标判空和越界的方法。
一般方法是定义事件类型和字符串,然后解析。
一般判断方法是 if event < EVENT_START && event > EVENT_END, return “unknown”
并且定义enum也需要指定start和end类似这种:
enum ib_event_type {
IB_EVENT_START,
IB_EVENT_CQ_ERR = IB_EVENT_START,
IB_EVENT_QP_FATAL, //最后一行也有,
IB_EVENT_END = IB_EVENT_QP_FATAL,
}
该方法需要定义start和end,或者需要动态的用第一个和最后一个,如果第一个和最后一个变化,还需要修改enum,是否有更优雅的一点方法?避免频繁修改,避免if判断?以及做到事件ID可以不连续?
事件字符串解析三部曲
答案是可以用三目运算符来判断evnet是否存在?甚至不用将event从0开始定义,最大event用事件字符串大小来确定(注意不是evnet大小)。并且判断用三目判断是否在其中,在其中用字符串数组取值,否则用指定的字符串。
当然三目运算的本质和if判断一样,最后编译到汇编几乎相同,都是条件判断。但是三目更加优雅。所以在以后很多场景可以用三目来替代if。尤其是双分支场景。
事件ID不连续的解析方法,是根据array的结构化初始化,不连续的跳空位置会自动初始化为0
三部曲定义:
# .h中定义事件和函数
enum ib_event_type {
IB_EVENT_CQ_ERR,
IB_EVENT_QP_FATAL, //最后一行也有,
}
const char *__attribute_const__ ib_event_msg(enum ib_event_type event);
# .c中定义事件字符串
static const char * const str_ib_events[] = {
[IB_EVENT_CQ_ERR] = "CQ error",
[IB_EVENT_QP_FATAL] = "QP fatal error",
}
# .c中定义事件解析函数,入参是事件,如果事件不在范围内直接用三目
const char *__attribute_const__ ib_event_msg(enum ib_event_type event)
{
size_t index = event;
return (index < ARRAY_SIZE(str_ib_events) && str_ib_events[index]) ?
ib_events[index] : "unrecognized event";
}
//注释:注意这里先判断index是否在array的范围内,然后如果在其中,可以直接return str,但是还加一个判断这里为空,这样就非常有效,并且这里的event可以不用连续,编译期间会根据结构化初始化str字符串来空出没有初始化的数据,非常优雅。缺点就是event不能太大,不然str字符串数组占用空间较大。
//注意这里的array_size是内核的标准接口,可以自己定义,也可以包含头文件
// #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
# 使用地方直接调用
printk("Event: %s\n", ib_event_msg(event);
注意这里未知事件并不定义到enum和string中,而是在函数中判断。
Mellanox驱动中的例子
参考文件:include/rdma/ib_verbs.h
https://github.com/torvalds/linux/blob/v6.11/include/rdma/ib_verbs.h
https://github.com/linux-rdma/rdma-core/blob/master/libibverbs/verbs.c
事件定义
enum ib_event_type {
IB_EVENT_CQ_ERR,
IB_EVENT_QP_FATAL,
IB_EVENT_QP_REQ_ERR,
IB_EVENT_QP_ACCESS_ERR,
IB_EVENT_COMM_EST,
IB_EVENT_SQ_DRAINED,
IB_EVENT_PATH_MIG,
IB_EVENT_PATH_MIG_ERR,
IB_EVENT_DEVICE_FATAL,
IB_EVENT_PORT_ACTIVE,
IB_EVENT_PORT_ERR,
IB_EVENT_LID_CHANGE,
IB_EVENT_PKEY_CHANGE,
IB_EVENT_SM_CHANGE,
IB_EVENT_SRQ_ERR,
IB_EVENT_SRQ_LIMIT_REACHED,
IB_EVENT_QP_LAST_WQE_REACHED,
IB_EVENT_CLIENT_REREGISTER,
IB_EVENT_GID_CHANGE,
IB_EVENT_WQ_FATAL,
IB_EXP_EVENT_XRQ_QP_ERR,
IB_EVENT_XRQ_NVMF_BACKEND_CTRL_PCI_ERR,
IB_EVENT_XRQ_NVMF_BACKEND_CTRL_TO_ERR,
};
事件字符串
static const char * const ib_events[] = {
[IB_EVENT_CQ_ERR] = "CQ error",
[IB_EVENT_QP_FATAL] = "QP fatal error",
[IB_EVENT_QP_REQ_ERR] = "QP request error",
[IB_EVENT_QP_ACCESS_ERR] = "QP access error",
[IB_EVENT_COMM_EST] = "communication established",
[IB_EVENT_SQ_DRAINED] = "send queue drained",
[IB_EVENT_PATH_MIG] = "path migration successful",
[IB_EVENT_PATH_MIG_ERR] = "path migration error",
[IB_EVENT_DEVICE_FATAL] = "device fatal error",
[IB_EVENT_PORT_ACTIVE] = "port active",
[IB_EVENT_PORT_ERR] = "port error",
[IB_EVENT_LID_CHANGE] = "LID change",
[IB_EVENT_PKEY_CHANGE] = "P_key change",
[IB_EVENT_SM_CHANGE] = "SM change",
[IB_EVENT_SRQ_ERR] = "SRQ error",
[IB_EVENT_SRQ_LIMIT_REACHED] = "SRQ limit reached",
[IB_EVENT_QP_LAST_WQE_REACHED] = "last WQE reached",
[IB_EVENT_CLIENT_REREGISTER] = "client reregister",
[IB_EVENT_GID_CHANGE] = "GID changed",
[IB_EXP_EVENT_XRQ_QP_ERR] = "XRQ QP error",
[IB_EVENT_XRQ_NVMF_BACKEND_CTRL_PCI_ERR] = "XRQ NVMF backend ctrl PCI error",
[IB_EVENT_XRQ_NVMF_BACKEND_CTRL_TO_ERR] = "XRQ NVMF backend ctrl timeout error",
};
事件字符串解析
const char *__attribute_const__ ib_event_msg(enum ib_event_type event)
{
size_t index = event;
return (index < ARRAY_SIZE(ib_events) && ib_events[index]) ?
ib_events[index] : "unrecognized event";
}
EXPORT_SYMBOL(ib_event_msg);
综述
本文根据Mellanox驱动代码中的ib_event介绍介绍一种比较优雅的事件定义和解析。判断的方法以及可以未来直接使用的代码。它的核心有点在于可以定义跳空的事件,以及直接使用字符串数组下标判空和越界的方法。
用三目运算符来判断evnet是否存在?甚至不用将event从0开始定义,最大event用事件字符串大小来确定,利用C语言结构化初始化定义自动扩展未定义的ID。并且判断用三目判断是否在其中,在其中用字符串数组取值,否则用指定的字符串。
695

被折叠的 条评论
为什么被折叠?



