From nobody Mon Apr 6 04:57:29 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DA743ECAAD3 for ; Sat, 10 Sep 2022 22:29:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230414AbiIJW3v (ORCPT ); Sat, 10 Sep 2022 18:29:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46698 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230395AbiIJW2l (ORCPT ); Sat, 10 Sep 2022 18:28:41 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4B8C945040 for ; Sat, 10 Sep 2022 15:28:07 -0700 (PDT) Message-ID: <20220910222301.597440803@linutronix.de> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1662848885; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=yRyMpUYT+kmNyPrU5TuOkxnIPqW5hmLjaAM5Dx5YUoo=; b=1A7aaJDo5ez7UQG931AM8p7H8p6BWjG63SKE1+1TPnXaVDmopf1Esy9YgKCz9z7VQ2KHu/ w9Wctya/3qu04CV3Y/iPRYP9JGm+1hkaTUItnJfB7gUrChLs9TU5+is8FhwZ5SGAtcD1Wo L3aqmnQa5uy+FuHnc4mhLsBV2nialmlPvXnddzylyWy5JAcwRyC/p8rYptlRL+BGnDBn6u Qk1h+dmGIMVQJysK6VcJDUmLxbSRWV9yJvyQtZQIAfbR3beT0cvCzek/Dgjte5IDAdzt/J E3hry0isBo7OT+0tP+wM8/wI6oFV4kLcSUHIXyPGuv9HKse+UsdvM8Lpyvwd9g== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1662848885; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=yRyMpUYT+kmNyPrU5TuOkxnIPqW5hmLjaAM5Dx5YUoo=; b=J6KUbzvZVvtdwgZ9oumV19NJwy3XtkgtPj+dhTtWS+2XytNviPsASH/b9X/AaSxZWx6yEf N0mJYDKrvnmoTQCQ== From: Thomas Gleixner To: LKML Cc: John Ogness , Petr Mladek , Sergey Senozhatsky , Steven Rostedt , Linus Torvalds , Peter Zijlstra , "Paul E. McKenney" , Daniel Vetter , Greg Kroah-Hartman , Helge Deller , Jason Wessel , Daniel Thompson , John Ogness Subject: [patch RFC 21/29] printk: Add buffer management for noBKL consoles References: <20220910221947.171557773@linutronix.de> MIME-Version: 1.0 Date: Sun, 11 Sep 2022 00:28:04 +0200 (CEST) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" In case of hostile takeovers it must be ensured that the previous owner cannot scribble over the output buffer of the emergency/panic context. This is achieved by: - Allocating per CPU output buffers per console and add the required handl= ing into the acquire/release functions. - Adding a single instance to struct console for early boot (pre per CPU data being available). The builtin instance is also used for threaded printing once printer threads become available. Wrapped into a seperate data structure so other context related fields can be added in later steps. Co-Developed-by: John Ogness Signed-off-by: John Ogness Signed-off-by: Thomas Gleixner --- include/linux/console.h | 21 ++++++++++++- kernel/printk/printk.c | 18 ++++++++--- kernel/printk/printk_nobkl.c | 69 ++++++++++++++++++++++++++++++++++++++= +++++ 3 files changed, 102 insertions(+), 6 deletions(-) --- a/include/linux/console.h +++ b/include/linux/console.h @@ -276,6 +276,7 @@ struct console; * @req_state: The request state for spin and cleanup * @spinwait_max_us: Limit for spinwait acquire * @prio: Priority of the context + * @txtbuf: Pointer to the text buffer for this context * @thread: The acquire is printk thread context * @hostile: Hostile takeover requested. Cleared on normal * acquire or friendly handover @@ -289,11 +290,25 @@ struct cons_context { struct cons_state req_state; unsigned int spinwait_max_us; enum cons_prio prio; + struct cons_text_buf *txtbuf; unsigned int thread : 1; unsigned int hostile : 1; unsigned int spinwait : 1; }; =20 +#define CONS_MAX_NEST_LVL 8 + +/** + * struct cons_context_data - console context data + * @txtbuf: Buffer for storing the text + * + * Used for early boot embedded into struct console and for + * per CPU data. + */ +struct cons_context_data { + struct cons_text_buf txtbuf; +}; + /** * struct console - The console descriptor structure * @name: The name of the console driver @@ -315,6 +330,8 @@ struct cons_context { * @node: hlist node for the console list * * @atomic_state: State array for non-BKL consoles. Real and handover + * @pcpu_data: Pointer to percpu context data + * @ctxt_data: Builtin context data for early boot and threaded printing */ struct console { char name[16]; @@ -336,8 +353,10 @@ struct console { struct hlist_node node; =20 /* NOBKL console specific members */ - atomic_long_t __private atomic_state[2]; + atomic_long_t __private atomic_state[2]; =20 + struct cons_context_data __percpu *pcpu_data; + struct cons_context_data ctxt_data; }; =20 #ifdef CONFIG_LOCKDEP --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1071,9 +1071,17 @@ static void __init log_buf_add_cpu(void) static inline void log_buf_add_cpu(void) {} #endif /* CONFIG_SMP */ =20 +static void cons_alloc_percpu_data(struct console *con); + static void __init set_percpu_data_ready(void) { + struct console *con; + + console_list_lock(); + for_each_registered_console(con) + cons_alloc_percpu_data(con); __printk_percpu_data_ready =3D true; + console_list_unlock(); } =20 static unsigned int __init add_to_rb(struct printk_ringbuffer *rb, @@ -2341,6 +2349,11 @@ static bool __pr_flush(struct console *c =20 #endif /* !CONFIG_PRINTK */ =20 +#define con_printk(lvl, con, fmt, ...) \ + printk(lvl pr_fmt("%sconsole [%s%d] " fmt), \ + (con->flags & CON_BOOT) ? "boot" : "", \ + con->name, con->index, ##__VA_ARGS__) + #include "printk_nobkl.c" =20 #ifdef CONFIG_EARLY_PRINTK @@ -3191,11 +3204,6 @@ static void try_enable_default_console(s newcon->flags |=3D CON_CONSDEV; } =20 -#define con_printk(lvl, con, fmt, ...) \ - printk(lvl pr_fmt("%sconsole [%s%d] " fmt), \ - (con->flags & CON_BOOT) ? "boot" : "", \ - con->name, con->index, ##__VA_ARGS__) - #define cons_first() \ hlist_entry(console_list.first, struct console, node) =20 --- a/kernel/printk/printk_nobkl.c +++ b/kernel/printk/printk_nobkl.c @@ -207,6 +207,43 @@ static inline bool cons_check_panic(void } =20 /** + * cons_context_set_text_buf - Set the output text buffer for the current = context + * @ctxt: Pointer to the aquire context + * + * Buffer selection: + * 1) Early boot uses the console builtin buffer + * 2) Threads use the console builtin buffer + * 3) All other context use the per CPU buffers + * + * This guarantees that there is no concurrency on the output records + * ever. Per CPU nesting is not a problem at all. The takeover logic + * tells the interrupted context that the buffer has been overwritten. + * + * There are two critical regions which matter: + * + * 1) Context is filling the buffer with a record. After interruption + * it continues to sprintf() the record and before it goes to + * write it out, it checks the state, notices the takeover, discards + * the content and backs out. + * + * 2) Context is in a unsafe critical region in the driver. After + * interruption it might read overwritten data from the output + * buffer. When it leaves the critical region it notices and backs + * out. Hostile takeovers in driver critical regions are best effort + * and there is not much which can be done about that. + */ +static void cons_context_set_text_buf(struct cons_context *ctxt) +{ + struct console *con =3D ctxt->console; + + /* Early boot or allocation fail? */ + if (!con->pcpu_data) + ctxt->txtbuf =3D &con->ctxt_data.txtbuf; + else + ctxt->txtbuf =3D &(this_cpu_ptr(con->pcpu_data)->txtbuf); +} + +/** * cons_cleanup_handover - Cleanup a handover request * @ctxt: Pointer to acquire context * @@ -482,6 +519,7 @@ static bool __cons_try_acquire(struct co return false; success: /* Common updates on success */ + cons_context_set_text_buf(ctxt); return true; =20 check_hostile: @@ -610,6 +648,35 @@ static bool __maybe_unused cons_release( } =20 /** + * cons_alloc_percpu_data - Allocate percpu data for a console + * @con: Console to allocate for + */ +static void cons_alloc_percpu_data(struct console *con) +{ + if (!printk_percpu_data_ready()) + return; + + con->pcpu_data =3D alloc_percpu(typeof(*con->pcpu_data)); + if (con->pcpu_data) + return; + + con_printk(KERN_WARNING, con, "Failed to allocate percpu buffers\n"); +} + +/** + * cons_free_percpu_data - Free percpu data of a console on unregister + * @con: Console to clean up + */ +static void cons_free_percpu_data(struct console *con) +{ + if (!con->pcpu_data) + return; + + free_percpu(con->pcpu_data); + con->pcpu_data =3D NULL; +} + +/** * cons_nobkl_init - Initialize the NOBKL console state * @con: Console to initialize */ @@ -620,6 +687,7 @@ static void cons_nobkl_init(struct conso .enabled =3D !!(con->flags & CON_ENABLED), }; =20 + cons_alloc_percpu_data(con); cons_state_set(con, STATE_REAL, &state); } =20 @@ -632,6 +700,7 @@ static void cons_nobkl_cleanup(struct co struct cons_state state =3D { }; =20 cons_state_set(con, STATE_REAL, &state); + cons_free_percpu_data(con); } =20 #else /* CONFIG_PRINTK */