文章目录
背景
日常使用最多的就是mlx的打印。不同日志打印有不同功能,比如使用dev_xxx的打印可以使用内核的动态debug开关相关文件打印(echo -n ‘file probe.c +p’ > /sys/kernel/debug/dynamic_debug/control),详细参考兄弟篇:如何给某个文件打开linux内核的动态打印开关
printk是内核中最基本的打印函数,用于将消息输出到内核日志中。它支持多种日志级别,但是printk本身不支持动态打印,但可以通过全局日志级别控制输出。
pr_err本身不支持动态打印
dev_xxx系列支持动态打印
关系
缩略图:
详细图:
函数细节
mlx5_ib_xxx系列
mlx5_ib_dbg
mlx5_ib_warn
mlx5_ib_err
是直接封装的dev_xxx系列的debug日志,添加了dev设备
举例:
#define mlx5_ib_err(_dev, format, arg...) \
dev_err(&(_dev)->ib_dev.dev, "%s:%d:(pid %d): " format, __func__, \
__LINE__, current->pid, ##arg)
mlx5_ib_err(dev, "could not change QP%d state to INIT: %d\n",
qp->qp_num, ret);
mlx5_core_xxx系列
mlx5_core_dbg
mlx5_core_info
mlx5_core_warn
mlx5_core_err
以一个为例:是直接封装的dev_xxx系列的debug日志,添加了dev设备
#define mlx5_core_dbg(__dev, format, ...) \
dev_dbg((__dev)->device, "%s:%d:(pid %d): " format, \
__func__, __LINE__, current->pid, \
##__VA_ARGS__)
mlx5_core_dbg(dev, "err %d, status %d\n", err, status);
mlx5_fpga_xxx系列
mlx5_fpga_dbg
mlx5_fpga_err
mlx5_fpga_warn
mlx5_fpga_notice
mlx5_fpga_info
是封装了 mlx5_core_xxx系列
以一个为例:
#define mlx5_fpga_err(__adev, format, ...) \
mlx5_core_err((__adev)->mdev, "FPGA: %s:%d:(pid %d): " format, \
__func__, __LINE__, current->pid, ##__VA_ARGS__)
mlx5_fpga_err(fdev, "Failed to activate FPGA RC QP: %d\n", err);
mlx5_log
#define mlx5_log(__dev, level, format, ...) \
mlx5_printk(__dev, level, "%s:%d:(pid %d): " format, \
__func__, __LINE__, current->pid, \
##__VA_ARGS__)
mlx5_printk
static inline void mlx5_printk(struct mlx5_core_dev *dev, int level, const char *format, ...)
{
struct device *device = dev->device;
struct va_format vaf;
va_list args;
if (WARN_ONCE(level < LOGLEVEL_EMERG || level > LOGLEVEL_DEBUG,
"Level %d is out of range, set to default level\n", level))
level = LOGLEVEL_DEFAULT;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
dev_printk_emit(level, device, "%s %s: %pV", dev_driver_string(device), dev_name(device),
&vaf);
va_end(args);
}
netdev_info系列(内核接口)
比如:
netdev_info(priv->netdev, "%s: rx_jumbo_pending not supported\n",
__func__);
WARN_ONCE (内核接口)
比如:
WARN_ONCE(1, "%s: unknown reg_state %d\n", dev->name, dev->reg_state);
iser_xxx系列 iser模块的
iser_info
iser_warn
iser_dbg
iser_err
是封装的内核pr_err系列接口
pr_xxx 内核接口
pr_err(PFX "%s: " fmt, __func__ , ## arg)
esw_xxx 系列 esw的打印
esw_info
esw_warn
esw_debug
封装的dev系列的比如dev_info,加了个E-Switch: 的前缀
比如:
#define esw_info(__dev, format, ...) \
dev_info((__dev)->device, "E-Switch: " format, ##__VA_ARGS__)
这里也说明了,不同模块要定义自己的打印函数的根本动机在于定义自定义的字段
定义方式也很简单 前面几个参数,一般有format,以及…,然后套用其他的就用 ""format
, 然后 …用 ##__VA_ARGS__
来替代
mlx5dr_err 系列
mlx5dr_err
mlx5dr_info
mlx5dr_dbg
是封装的mlx5_core_info系列
其他
关于dev_xxx与printk的关系
从下面定义可得知,dev_info
define_dev_printk_level(_dev_emerg, KERN_EMERG);
define_dev_printk_level(_dev_alert, KERN_ALERT);
define_dev_printk_level(_dev_crit, KERN_CRIT);
define_dev_printk_level(_dev_err, KERN_ERR);
define_dev_printk_level(_dev_warn, KERN_WARNING);
define_dev_printk_level(_dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);
#define define_dev_printk_level(func, kern_level) \
void func(const struct device *dev, const char *fmt, ...) \
{ \
struct va_format vaf; \
va_list args; \
\
va_start(args, fmt); \
\
vaf.fmt = fmt; \
vaf.va = &args; \
\
__dev_printk(kern_level, dev, &vaf); \
\
va_end(args); \
} \
EXPORT_SYMBOL(func);
static void __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
if (dev)
dev_printk_emit(level[1] - '0', dev, "%s %s: %pV",
dev_driver_string(dev), dev_name(dev), vaf);
else
printk("%s(NULL device *): %pV", level, vaf);
}
# 可见,这里__dev_printk,增加了dev_driver_string以及dev_name的获取并且打印
int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = dev_vprintk_emit(level, dev, fmt, args);
va_end(args);
return r;
}
asmlinkage int vprintk_emit(int facility, int level,
...
boot_delay_msec(level);
printk_delay();
wake_up_klogd();
实际举例
mlx5_core_info
比如插拔网线的时候会使用mlx5_core_info打印:
这里就会打上模块名字,以及PCIe的bdf号
综述
本文总结了Mellanox OFED驱动中使用的8种日志打印函数以及涉及的6种内核日志打印函数。 方便日常快速使用