Commit 3ef4ea3d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull printk updates from Petr Mladek:

 - Make %pK behave the same as %p for kptr_restrict == 0 also with
   no_hash_pointers parameter

 - Ignore the default console in the device tree also when console=null
   or console="" is used on the command line

 - Document console=null and console="" behavior

 - Prevent a deadlock and a livelock caused by console_lock in panic()

 - Make console_lock available for panicking CPU

 - Fast query for the next to-be-used sequence number

 - Use the expected return values in printk.devkmsg __setup handler

 - Use the correct atomic operations in wake_up_klogd() irq_work handler

 - Avoid possible unaligned access when handling %4cc printing format

* tag 'printk-for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  printk: fix return value of printk.devkmsg __setup handler
  vsprintf: Fix %pK with kptr_restrict == 0
  printk: make suppress_panic_printk static
  printk: Set console_set_on_cmdline=1 when __add_preferred_console() is called with user_specified == true
  Docs: printk: add 'console=null|""' to admin/kernel-parameters
  printk: use atomic updates for klogd work
  printk: Drop console_sem during panic
  printk: Avoid livelock with heavy printk during panic
  printk: disable optimistic spin during panic
  printk: Add panic_in_progress helper
  vsprintf: Move space out of string literals in fourcc_string()
  vsprintf: Fix potential unaligned access
  printk: ringbuffer: Improve prb_next_seq() performance
parents 30d024b5 5eb17c1f
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -724,6 +724,12 @@
		hvc<n>	Use the hypervisor console device <n>. This is for
			both Xen and PowerPC hypervisors.

		{ null | "" }
			Use to disable console output, i.e., to have kernel
			console messages discarded.
			This must be the only console= parameter used on the
			kernel command line.

		If the device connected to the port is not a TTY but a braille
		device, prepend "brl," before the device type, for instance
			console=brl,ttyS0
@@ -3516,8 +3522,7 @@
			difficult since unequal pointers can no longer be
			compared.  However, if this command-line option is
			specified, then all normal pointers will have their true
			value printed.  Pointers printed via %pK may still be
			hashed.  This option should only be specified when
			value printed. This option should only be specified when
			debugging the kernel.  Please do not use on production
			kernels.

+76 −9
Original line number Diff line number Diff line
@@ -93,6 +93,12 @@ EXPORT_SYMBOL_GPL(console_drivers);
 */
int __read_mostly suppress_printk;

/*
 * During panic, heavy printk by other CPUs can delay the
 * panic and risk deadlock on console resources.
 */
static int __read_mostly suppress_panic_printk;

#ifdef CONFIG_LOCKDEP
static struct lockdep_map console_lock_dep_map = {
	.name = "console_lock"
@@ -146,8 +152,10 @@ static int __control_devkmsg(char *str)

static int __init control_devkmsg(char *str)
{
	if (__control_devkmsg(str) < 0)
	if (__control_devkmsg(str) < 0) {
		pr_warn("printk.devkmsg: bad option string '%s'\n", str);
		return 1;
	}

	/*
	 * Set sysctl string accordingly:
@@ -166,7 +174,7 @@ static int __init control_devkmsg(char *str)
	 */
	devkmsg_log |= DEVKMSG_LOG_MASK_LOCK;

	return 0;
	return 1;
}
__setup("printk.devkmsg=", control_devkmsg);

@@ -257,6 +265,11 @@ static void __up_console_sem(unsigned long ip)
}
#define up_console_sem() __up_console_sem(_RET_IP_)

static bool panic_in_progress(void)
{
	return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
}

/*
 * This is used for debugging the mess that is the VT code by
 * keeping track if we have the console semaphore held. It's
@@ -1843,6 +1856,16 @@ static int console_trylock_spinning(void)
	if (console_trylock())
		return 1;

	/*
	 * It's unsafe to spin once a panic has begun. If we are the
	 * panic CPU, we may have already halted the owner of the
	 * console_sem. If we are not the panic CPU, then we should
	 * avoid taking console_sem, so the panic CPU has a better
	 * chance of cleanly acquiring it later.
	 */
	if (panic_in_progress())
		return 0;

	printk_safe_enter_irqsave(flags);

	raw_spin_lock(&console_owner_lock);
@@ -2218,6 +2241,10 @@ asmlinkage int vprintk_emit(int facility, int level,
	if (unlikely(suppress_printk))
		return 0;

	if (unlikely(suppress_panic_printk) &&
	    atomic_read(&panic_cpu) != raw_smp_processor_id())
		return 0;

	if (level == LOGLEVEL_SCHED) {
		level = LOGLEVEL_DEFAULT;
		in_sched = true;
@@ -2324,6 +2351,20 @@ asmlinkage __visible void early_printk(const char *fmt, ...)
}
#endif

static void set_user_specified(struct console_cmdline *c, bool user_specified)
{
	if (!user_specified)
		return;

	/*
	 * @c console was defined by the user on the command line.
	 * Do not clear when added twice also by SPCR or the device tree.
	 */
	c->user_specified = true;
	/* At least one console defined by the user on the command line. */
	console_set_on_cmdline = 1;
}

static int __add_preferred_console(char *name, int idx, char *options,
				   char *brl_options, bool user_specified)
{
@@ -2340,8 +2381,7 @@ static int __add_preferred_console(char *name, int idx, char *options,
		if (strcmp(c->name, name) == 0 && c->index == idx) {
			if (!brl_options)
				preferred_console = i;
			if (user_specified)
				c->user_specified = true;
			set_user_specified(c, user_specified);
			return 0;
		}
	}
@@ -2351,7 +2391,7 @@ static int __add_preferred_console(char *name, int idx, char *options,
		preferred_console = i;
	strlcpy(c->name, name, sizeof(c->name));
	c->options = options;
	c->user_specified = user_specified;
	set_user_specified(c, user_specified);
	braille_set_options(c, brl_options);

	c->index = idx;
@@ -2417,7 +2457,6 @@ static int __init console_setup(char *str)
	*s = 0;

	__add_preferred_console(buf, idx, options, brl_options, true);
	console_set_on_cmdline = 1;
	return 1;
}
__setup("console=", console_setup);
@@ -2573,6 +2612,25 @@ static int have_callable_console(void)
	return 0;
}

/*
 * Return true when this CPU should unlock console_sem without pushing all
 * messages to the console. This reduces the chance that the console is
 * locked when the panic CPU tries to use it.
 */
static bool abandon_console_lock_in_panic(void)
{
	if (!panic_in_progress())
		return false;

	/*
	 * We can use raw_smp_processor_id() here because it is impossible for
	 * the task to be migrated to the panic_cpu, or away from it. If
	 * panic_cpu has already been set, and we're not currently executing on
	 * that CPU, then we never will be.
	 */
	return atomic_read(&panic_cpu) != raw_smp_processor_id();
}

/*
 * Can we actually use the console at this time on this cpu?
 *
@@ -2603,6 +2661,7 @@ void console_unlock(void)
{
	static char ext_text[CONSOLE_EXT_LOG_MAX];
	static char text[CONSOLE_LOG_MAX];
	static int panic_console_dropped;
	unsigned long flags;
	bool do_cond_resched, retry;
	struct printk_info info;
@@ -2657,6 +2716,10 @@ void console_unlock(void)
		if (console_seq != r.info->seq) {
			console_dropped += r.info->seq - console_seq;
			console_seq = r.info->seq;
			if (panic_in_progress() && panic_console_dropped++ > 10) {
				suppress_panic_printk = 1;
				pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n");
			}
		}

		if (suppress_message_printing(r.info->level)) {
@@ -2716,6 +2779,10 @@ void console_unlock(void)
		if (handover)
			return;

		/* Allow panic_cpu to take over the consoles safely */
		if (abandon_console_lock_in_panic())
			break;

		if (do_cond_resched)
			cond_resched();
	}
@@ -2733,7 +2800,7 @@ void console_unlock(void)
	 * flush, no worries.
	 */
	retry = prb_read_valid(prb, next_seq, NULL);
	if (retry && console_trylock())
	if (retry && !abandon_console_lock_in_panic() && console_trylock())
		goto again;
}
EXPORT_SYMBOL(console_unlock);
@@ -3228,7 +3295,7 @@ static DEFINE_PER_CPU(int, printk_pending);

static void wake_up_klogd_work_func(struct irq_work *irq_work)
{
	int pending = __this_cpu_xchg(printk_pending, 0);
	int pending = this_cpu_xchg(printk_pending, 0);

	if (pending & PRINTK_PENDING_OUTPUT) {
		/* If trylock fails, someone else is doing the printing */
@@ -3262,7 +3329,7 @@ void defer_console_output(void)
		return;

	preempt_disable();
	__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
	this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
	irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
	preempt_enable();
}
+47 −5
Original line number Diff line number Diff line
@@ -474,8 +474,10 @@ static enum desc_state desc_read(struct prb_desc_ring *desc_ring,
	 * state has been re-checked. A memcpy() for all of @desc
	 * cannot be used because of the atomic_t @state_var field.
	 */
	if (desc_out) {
		memcpy(&desc_out->text_blk_lpos, &desc->text_blk_lpos,
		       sizeof(desc_out->text_blk_lpos)); /* LMM(desc_read:C) */
	}
	if (seq_out)
		*seq_out = info->seq; /* also part of desc_read:C */
	if (caller_id_out)
@@ -528,6 +530,7 @@ static enum desc_state desc_read(struct prb_desc_ring *desc_ring,
	state_val = atomic_long_read(state_var); /* LMM(desc_read:E) */
	d_state = get_desc_state(id, state_val);
out:
	if (desc_out)
		atomic_long_set(&desc_out->state_var, state_val);
	return d_state;
}
@@ -1449,6 +1452,9 @@ static void desc_make_final(struct prb_desc_ring *desc_ring, unsigned long id)

	atomic_long_cmpxchg_relaxed(&d->state_var, prev_state_val,
			DESC_SV(id, desc_finalized)); /* LMM(desc_make_final:A) */

	/* Best effort to remember the last finalized @id. */
	atomic_long_set(&desc_ring->last_finalized_id, id);
}

/**
@@ -1657,7 +1663,12 @@ void prb_commit(struct prb_reserved_entry *e)
 */
void prb_final_commit(struct prb_reserved_entry *e)
{
	struct prb_desc_ring *desc_ring = &e->rb->desc_ring;

	_prb_commit(e, desc_finalized);

	/* Best effort to remember the last finalized @id. */
	atomic_long_set(&desc_ring->last_finalized_id, e->id);
}

/*
@@ -2005,9 +2016,39 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb)
 */
u64 prb_next_seq(struct printk_ringbuffer *rb)
{
	u64 seq = 0;
	struct prb_desc_ring *desc_ring = &rb->desc_ring;
	enum desc_state d_state;
	unsigned long id;
	u64 seq;

	/* Check if the cached @id still points to a valid @seq. */
	id = atomic_long_read(&desc_ring->last_finalized_id);
	d_state = desc_read(desc_ring, id, NULL, &seq, NULL);

	/* Search forward from the oldest descriptor. */
	if (d_state == desc_finalized || d_state == desc_reusable) {
		/*
		 * Begin searching after the last finalized record.
		 *
		 * On 0, the search must begin at 0 because of hack#2
		 * of the bootstrapping phase it is not known if a
		 * record at index 0 exists.
		 */
		if (seq != 0)
			seq++;
	} else {
		/*
		 * The information about the last finalized sequence number
		 * has gone. It should happen only when there is a flood of
		 * new messages and the ringbuffer is rapidly recycled.
		 * Give up and start from the beginning.
		 */
		seq = 0;
	}

	/*
	 * The information about the last finalized @seq might be inaccurate.
	 * Search forward to find the current one.
	 */
	while (_prb_read_valid(rb, &seq, NULL, NULL))
		seq++;

@@ -2044,6 +2085,7 @@ void prb_init(struct printk_ringbuffer *rb,
	rb->desc_ring.infos = infos;
	atomic_long_set(&rb->desc_ring.head_id, DESC0_ID(descbits));
	atomic_long_set(&rb->desc_ring.tail_id, DESC0_ID(descbits));
	atomic_long_set(&rb->desc_ring.last_finalized_id, DESC0_ID(descbits));

	rb->text_data_ring.size_bits = textbits;
	rb->text_data_ring.data = text_buf;
+2 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ struct prb_desc_ring {
	struct printk_info	*infos;
	atomic_long_t		head_id;
	atomic_long_t		tail_id;
	atomic_long_t		last_finalized_id;
};

/*
@@ -258,6 +259,7 @@ static struct printk_ringbuffer name = { \
		.infos		= &_##name##_infos[0],						\
		.head_id	= ATOMIC_INIT(DESC0_ID(descbits)),				\
		.tail_id	= ATOMIC_INIT(DESC0_ID(descbits)),				\
		.last_finalized_id = ATOMIC_INIT(DESC0_ID(descbits)),				\
	},											\
	.text_data_ring = {									\
		.size_bits	= (avgtextbits) + (descbits),					\
+29 −20
Original line number Diff line number Diff line
@@ -49,10 +49,15 @@

#include <asm/page.h>		/* for PAGE_SIZE */
#include <asm/byteorder.h>	/* cpu_to_le16 */
#include <asm/unaligned.h>

#include <linux/string_helpers.h>
#include "kstrtox.h"

/* Disable pointer hashing if requested */
bool no_hash_pointers __ro_after_init;
EXPORT_SYMBOL_GPL(no_hash_pointers);

static noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base)
{
	const char *cp;
@@ -850,6 +855,19 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr,
	return pointer_string(buf, end, (const void *)hashval, spec);
}

static char *default_pointer(char *buf, char *end, const void *ptr,
			     struct printf_spec spec)
{
	/*
	 * default is to _not_ leak addresses, so hash before printing,
	 * unless no_hash_pointers is specified on the command line.
	 */
	if (unlikely(no_hash_pointers))
		return pointer_string(buf, end, ptr, spec);

	return ptr_to_id(buf, end, ptr, spec);
}

int kptr_restrict __read_mostly;

static noinline_for_stack
@@ -859,7 +877,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
	switch (kptr_restrict) {
	case 0:
		/* Handle as %p, hash and do _not_ leak addresses. */
		return ptr_to_id(buf, end, ptr, spec);
		return default_pointer(buf, end, ptr, spec);
	case 1: {
		const struct cred *cred;

@@ -1763,7 +1781,7 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc,
	char output[sizeof("0123 little-endian (0x01234567)")];
	char *p = output;
	unsigned int i;
	u32 val;
	u32 orig, val;

	if (fmt[1] != 'c' || fmt[2] != 'c')
		return error_string(buf, end, "(%p4?)", spec);
@@ -1771,21 +1789,23 @@ char *fourcc_string(char *buf, char *end, const u32 *fourcc,
	if (check_pointer(&buf, end, fourcc, spec))
		return buf;

	val = *fourcc & ~BIT(31);
	orig = get_unaligned(fourcc);
	val = orig & ~BIT(31);

	for (i = 0; i < sizeof(*fourcc); i++) {
	for (i = 0; i < sizeof(u32); i++) {
		unsigned char c = val >> (i * 8);

		/* Print non-control ASCII characters as-is, dot otherwise */
		*p++ = isascii(c) && isprint(c) ? c : '.';
	}

	strcpy(p, *fourcc & BIT(31) ? " big-endian" : " little-endian");
	*p++ = ' ';
	strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
	p += strlen(p);

	*p++ = ' ';
	*p++ = '(';
	p = special_hex_number(p, output + sizeof(output) - 2, *fourcc, sizeof(u32));
	p = special_hex_number(p, output + sizeof(output) - 2, orig, sizeof(u32));
	*p++ = ')';
	*p = '\0';

@@ -2225,10 +2245,6 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
	return widen_string(buf, buf - buf_start, end, spec);
}

/* Disable pointer hashing if requested */
bool no_hash_pointers __ro_after_init;
EXPORT_SYMBOL_GPL(no_hash_pointers);

int __init no_hash_pointers_enable(char *str)
{
	if (no_hash_pointers)
@@ -2457,7 +2473,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
	case 'e':
		/* %pe with a non-ERR_PTR gets treated as plain %p */
		if (!IS_ERR(ptr))
			break;
			return default_pointer(buf, end, ptr, spec);
		return err_ptr(buf, end, ptr, spec);
	case 'u':
	case 'k':
@@ -2467,16 +2483,9 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
		default:
			return error_string(buf, end, "(einval)", spec);
		}
	default:
		return default_pointer(buf, end, ptr, spec);
	}

	/*
	 * default is to _not_ leak addresses, so hash before printing,
	 * unless no_hash_pointers is specified on the command line.
	 */
	if (unlikely(no_hash_pointers))
		return pointer_string(buf, end, ptr, spec);
	else
		return ptr_to_id(buf, end, ptr, spec);
}

/*