欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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();
}