safe printk
程序员文章站
2022-04-02 19:00:51
...
什么是safe printk,就是 Use extra buffer to prevent a recursion deadlock in safe mode
的流程如下:
入口在printk中
asmlinkage __visible int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
#这里核心是调用vprintk_func
r = vprintk_func(fmt, args);
va_end(args);
return r;
}
vprintk_func的实现如下:
可以看到在vprintk_func中分了四种print_context,一般情况下我们走的是default
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{
/*
* Try to use the main logbuf even in NMI. But avoid calling console
* drivers that might have their own locks.
*/
if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK) &&
raw_spin_trylock(&logbuf_lock)) {
int len;
len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);
raw_spin_unlock(&logbuf_lock);
defer_console_output();
return len;
}
/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
return vprintk_nmi(fmt, args);
#根据printk_context的不同的值,走不同的分支,我们这里分析safe print
/* Use extra buffer to prevent a recursion deadlock in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
return vprintk_safe(fmt, args);
#默认情况下走的这个flow,各个cpu 对printk中的锁竞争严重
/* No obstacles. */
return vprintk_default(fmt, args);
}
这里vprintk_safe->printk_safe_log_store
static __printf(2, 0) int printk_safe_log_store(struct printk_safe_seq_buf *s,
const char *fmt, va_list args)
{
int add;
size_t len;
va_list ap;
again:
len = atomic_read(&s->len);
/* The trailing '\0' is not counted into len. */
#如果要打印的log的长度大于额外buffer的size,则调用queue_flush_work来flush log
if (len >= sizeof(s->buffer) - 1) {
atomic_inc(&s->message_lost);
queue_flush_work(s);
return 0;
}
/*
* Make sure that all old data have been read before the buffer
* was reset. This is not needed when we just append data.
*/
if (!len)
smp_rmb();
#否则直接打印log,这里也就是说要打印的log size 小于s->buffer
va_copy(ap, args);
add = vscnprintf(s->buffer + len, sizeof(s->buffer) - len, fmt, ap);
va_end(ap);
if (!add)
return 0;
/*
* Do it once again if the buffer has been flushed in the meantime.
* Note that atomic_cmpxchg() is an implicit memory barrier that
* makes sure that the data were written before updating s->len.
*/
#如果在打印的同时又有log进来,则返回到前面继续打印
if (atomic_cmpxchg(&s->len, len, len + add) != len)
goto again;
#log打印完成后,还是调用queue_flush_work 包flush log
queue_flush_work(s);
return add;
}
这里要说明的是每个cpu 都有一个queue来flush log,具体在下面这个函数中为每个cpu
来建立queue 用来flush log
void __init printk_safe_init(void)
{
int cpu;
for_each_possible_cpu(cpu) {
struct printk_safe_seq_buf *s;
#为每个cpu 初始化queue来flush log
s = &per_cpu(safe_print_seq, cpu);
init_irq_work(&s->work, __printk_safe_flush);
#ifdef CONFIG_PRINTK_NMI
s = &per_cpu(nmi_print_seq, cpu);
init_irq_work(&s->work, __printk_safe_flush);
#endif
}
/*
* In the highly unlikely event that a NMI were to trigger at
* this moment. Make sure IRQ work is set up before this
* variable is set.
*/
barrier();
#下面这个变量设置为1,表示可以在irq中安全打印log
printk_safe_irq_ready = 1;
/* Flush pending messages that did not have scheduled IRQ works. */
#flush在queue 建立之前已经存在的log
printk_safe_flush();
}
推荐阅读
-
解析PHP中VC6 X86和VC9 X86的区别及 Non Thread Safe的意思
-
PHP 5.3 下载时 VC9、VC6、Thread Safe、Non Thread Safe的区别分析
-
如何保障MySQL主从复制关系的稳定性?关键词(新特性、crash-safe)
-
解决php的“It is not safe to rely on the system’s timezone settings”问题
-
集合系列之fail-fast 与fail-safe 区别
-
ipad提示exit safe mode是什么原因该如何解决
-
PHP3 safe_mode 失效漏洞
-
iOS SHA1 和 Base64 url_safe加密解析
-
mysqld_safe error: log-error set to '/data/log/mysqld.log', however file don't exists. Create writable for user 'mysql'.The server
-
mysql 批量删除/更新数据报错 you can‘t specify target / every derrived table must / you are using safe update