safe printk

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/tiantao2012/article/details/89159941
什么是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();
}

猜你喜欢

转载自blog.csdn.net/tiantao2012/article/details/89159941