From nobody Mon Apr 6 04:56:41 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 5CAE0ECAAD3 for ; Sat, 10 Sep 2022 22:28:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230162AbiIJW2p (ORCPT ); Sat, 10 Sep 2022 18:28:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45980 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230118AbiIJW1z (ORCPT ); Sat, 10 Sep 2022 18:27:55 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D0E5E43327 for ; Sat, 10 Sep 2022 15:27:50 -0700 (PDT) Message-ID: <20220910222301.026064288@linutronix.de> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1662848869; 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=lpamWxeD7WXfacPVoNQg0Q6R4SqcQTvaVnGnp8PqLdM=; b=YmPFuIYQ+9V+opmDhlZv871qYOrvkfmjaaoinjHRpOO4MbDMcfvdRmThrwQkPPgcAoK6Se HnyGMCFsWA8MJfWoviDLEqQHOLy5Fzu1lf9wne0QsQNvpATkh+vT3Qt+NAOpnJDdIftbGJ aez2V18hlxCrw5CTkI6OUtjD+j4aCcDTu/l7/pXqnCmgsKXUbK639wj5xQh80g88/ujo2i NfBPnO26aco9HMtbvwgdD1+2Nl6IPmIGnWYQIvqhkxmJ1S3L0OUr0JLSKtXhlOPGzv+oeP 1UVbMfmgMXzKDZyhCbB2qo62hxlryR1hE+AcTnS6M1GjJ8dqgHEej0oU3ypbng== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1662848869; 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=lpamWxeD7WXfacPVoNQg0Q6R4SqcQTvaVnGnp8PqLdM=; b=pdw/ixWADrwbJmqaSrRHP4ujDZNNf0Tt5fTGkbeNM1f5dFSeZs3czHaPDW8JzCvrL64lCQ 2w/r0ChClTo3n3AQ== 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 Subject: [patch RFC 11/29] printk: Convert console_drivers list to hlist References: <20220910221947.171557773@linutronix.de> MIME-Version: 1.0 Date: Sun, 11 Sep 2022 00:27:48 +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" Replace the open coded single linked list with a hlist so a conversion to SRCU protected list walks can reuse the existing primitives. Signed-off-by: Thomas Gleixner --- arch/parisc/kernel/pdc_cons.c | 17 ++----- fs/proc/consoles.c | 5 +- include/linux/console.h | 19 +++++--- kernel/printk/printk.c | 99 ++++++++++++++++++++++---------------= ----- 4 files changed, 75 insertions(+), 65 deletions(-) --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -147,13 +147,8 @@ static int __init pdc_console_tty_driver =20 struct console *tmp; =20 - console_lock(); - for_each_console(tmp) - if (tmp =3D=3D &pdc_cons) - break; - console_unlock(); - - if (!tmp) { + /* Pretend that this works as much as it pretended to work historically */ + if (hlist_unhashed_lockless(&pdc_cons.node)) { printk(KERN_INFO "PDC console driver not registered anymore, not creatin= g %s\n", pdc_cons.name); return -ENODEV; } @@ -272,15 +267,15 @@ void pdc_console_restart(bool hpmc) if (pdc_console_initialized) return; =20 - if (!hpmc && console_drivers) + if (!hpmc && !hlist_empty(&console_list)) return; =20 /* If we've already seen the output, don't bother to print it again */ - if (console_drivers !=3D NULL) + if (!hlist_empty(&console_list)) pdc_cons.flags &=3D ~CON_PRINTBUFFER; =20 - while ((console =3D console_drivers) !=3D NULL) - unregister_console(console_drivers); + while (!hlist_empty(&console_list)) + unregister_console(READ_ONCE(console_list.first)); =20 /* force registering the pdc console */ pdc_console_init_force(); --- a/fs/proc/consoles.c +++ b/fs/proc/consoles.c @@ -74,8 +74,11 @@ static void *c_start(struct seq_file *m, static void *c_next(struct seq_file *m, void *v, loff_t *pos) { struct console *con =3D v; + ++*pos; - return con->next; + hlist_for_each_entry_continue(con, node) + break; + return con; } =20 static void c_stop(struct seq_file *m, void *v) --- a/include/linux/console.h +++ b/include/linux/console.h @@ -15,6 +15,7 @@ #define _LINUX_CONSOLE_H_ 1 =20 #include +#include #include =20 struct vc_data; @@ -154,15 +155,19 @@ struct console { u64 seq; unsigned long dropped; void *data; - struct console *next; + struct hlist_node node; }; =20 #ifdef CONFIG_LOCKDEP +extern void lockdep_assert_console_lock_held(void); extern void lockdep_assert_console_list_lock_held(void); #else +static inline void lockdep_assert_console_lock_held(void) { } static inline void lockdep_assert_console_list_lock_held(void) { } #endif =20 +extern struct hlist_head console_list; + extern void console_list_lock(void) __acquires(console_mutex); extern void console_list_unlock(void) __releases(console_mutex); =20 @@ -175,7 +180,7 @@ extern void console_list_unlock(void) __ */ #define for_each_registered_console(con) \ lockdep_assert_console_list_lock_held(); \ - for (con =3D console_drivers; con !=3D NULL; con =3D con->next) + hlist_for_each_entry(con, &console_list, node) =20 /** * for_each_console() - Iterator over registered consoles @@ -184,8 +189,9 @@ extern void console_list_unlock(void) __ * Requires console_lock to be held which guarantees that the * list is immutable. */ -#define for_each_console(con) \ - for (con =3D console_drivers; con !=3D NULL; con =3D con->next) +#define for_each_console(con) \ + lockdep_assert_console_lock_held(); \ + hlist_for_each_entry(con, &console_list, node) =20 /** * for_each_console_kgdb() - Iterator over registered consoles for KGDB @@ -194,8 +200,8 @@ extern void console_list_unlock(void) __ * Has no serialization requirements and KGDB pretends that this is safe. * Don't use outside of the KGDB fairy tale land! */ -#define for_each_console_kgdb(con) \ - for (con =3D console_drivers; con !=3D NULL; con =3D con->next) +#define for_each_console_kgdb(con) \ + hlist_for_each_entry(con, &console_list, node) =20 extern int console_set_on_cmdline; extern struct console *early_console; @@ -208,7 +214,6 @@ enum con_flush_mode { extern int add_preferred_console(char *name, int idx, char *options); extern void register_console(struct console *); extern int unregister_console(struct console *); -extern struct console *console_drivers; extern void console_lock(void); extern int console_trylock(void); extern void console_unlock(void); --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -79,17 +79,17 @@ int oops_in_progress; EXPORT_SYMBOL(oops_in_progress); =20 /* - * console_sem protects the console_drivers list, and also provides - * serialization for access to the entire console driver system. + * console_sem protects onsole_list, and also provides serialization for + * access to the entire console driver system. * * console_mutex serializes register/unregister. console_sem has to be - * taken for any list manipulation inside the console_mutex locked + * taken for any console_list manipulation inside the console_mutex locked * section to keep the console BKL machinery happy. */ static DEFINE_MUTEX(console_mutex); static DEFINE_SEMAPHORE(console_sem); -struct console *console_drivers; -EXPORT_SYMBOL_GPL(console_drivers); +HLIST_HEAD(console_list); +EXPORT_SYMBOL_GPL(console_list); =20 /* * System may need to suppress printk message under certain @@ -108,6 +108,11 @@ static struct lockdep_map console_lock_d .name =3D "console_lock" }; =20 +void lockdep_assert_console_lock_held(void) +{ + lockdep_assert(lock_is_held(&console_lock_dep_map)); +} + void lockdep_assert_console_list_lock_held(void) { lockdep_assert_held(&console_mutex); @@ -2586,7 +2591,7 @@ static int console_cpu_notify(unsigned i * console_lock - lock the console system for exclusive use. * * Acquires a lock which guarantees that the caller has - * exclusive access to the console system and the console_drivers list. + * exclusive access to the console system and console_list. * * Can sleep, returns nothing. */ @@ -2606,7 +2611,7 @@ EXPORT_SYMBOL(console_lock); * console_trylock - try to lock the console system for exclusive use. * * Try to acquire a lock which guarantees that the caller has exclusive - * access to the console system and the console_drivers list. + * access to the console system and console_list. * * returns 1 on success, and 0 on failure to acquire the lock. */ @@ -2974,7 +2979,15 @@ void console_flush_on_panic(enum con_flu u64 seq; =20 seq =3D prb_first_valid_seq(prb); - for_each_console(c) + /* + * This cannot use for_each_console() because it's not established + * that the current context has console locked and neither there is + * a guarantee that there is no concurrency in that case. + * + * Open code it for documentation purposes and pretend that + * it works. + */ + hlist_for_each_entry(c, &console_list, node) c->seq =3D seq; } console_unlock(); @@ -3115,6 +3128,9 @@ static void try_enable_default_console(s (con->flags & CON_BOOT) ? "boot" : "", \ con->name, con->index, ##__VA_ARGS__) =20 +#define cons_first() \ + hlist_entry(console_list.first, struct console, node) + static int console_unregister_locked(struct console *console); =20 /* @@ -3177,8 +3193,8 @@ void register_console(struct console *ne * flag set and will be first in the list. */ if (preferred_console < 0) { - if (!console_drivers || !console_drivers->device || - console_drivers->flags & CON_BOOT) { + if (hlist_empty(&console_list) || !cons_first()->device || + cons_first()->flags & CON_BOOT) { try_enable_default_console(newcon); } } @@ -3206,21 +3222,17 @@ void register_console(struct console *ne } =20 /* - * Put this console in the list - keep the - * preferred driver at the head of the list. + * Put this console in the list and keep the referred driver at the + * head of the list. */ console_lock(); - if ((newcon->flags & CON_CONSDEV) || console_drivers =3D=3D NULL) { - newcon->next =3D console_drivers; - console_drivers =3D newcon; - if (newcon->next) - newcon->next->flags &=3D ~CON_CONSDEV; - /* Ensure this flag is always set for the head of the list */ - newcon->flags |=3D CON_CONSDEV; - } else { - newcon->next =3D console_drivers->next; - console_drivers->next =3D newcon; - } + if (newcon->flags & CON_CONSDEV || hlist_empty(&console_list)) + hlist_add_head(&newcon->node, &console_list); + else + hlist_add_behind(&newcon->node, console_list.first); + + /* Ensure this flag is always set for the head of the list */ + cons_first()->flags |=3D CON_CONSDEV; =20 newcon->dropped =3D 0; if (newcon->flags & CON_PRINTBUFFER) { @@ -3246,7 +3258,9 @@ void register_console(struct console *ne if (bootcon_enabled && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) =3D=3D CON_CONSDEV) && !keep_bootcon) { - for_each_console(con) { + struct hlist_node *tmp; + + hlist_for_each_entry_safe(con, tmp, &console_list, node) { if (con->flags & CON_BOOT) console_unregister_locked(con); } @@ -3258,7 +3272,6 @@ EXPORT_SYMBOL(register_console); =20 static int console_unregister_locked(struct console *console) { - struct console *con; int res; =20 con_printk(KERN_INFO, console, "disabled\n"); @@ -3269,32 +3282,28 @@ static int console_unregister_locked(str if (res > 0) return 0; =20 - res =3D -ENODEV; console_lock(); - if (console_drivers =3D=3D console) { - console_drivers=3Dconsole->next; - res =3D 0; - } else { - for_each_console(con) { - if (con->next =3D=3D console) { - con->next =3D console->next; - res =3D 0; - break; - } - } - } =20 - if (res) - goto out_disable_unlock; + /* Disable it unconditionally */ + console->flags &=3D ~CON_ENABLED; + + if (hlist_unhashed(&console->node)) + goto out_unlock; + + hlist_del_init(&console->node); =20 /* + * * If this isn't the last console and it has CON_CONSDEV set, we * need to set it on the next preferred console. + * + * + * The above makes no sense as there is no guarantee that the next + * console has any device attached. Oh well.... */ - if (console_drivers !=3D NULL && console->flags & CON_CONSDEV) - console_drivers->flags |=3D CON_CONSDEV; + if (!hlist_empty(&console_list) && console->flags & CON_CONSDEV) + cons_first()->flags |=3D CON_CONSDEV; =20 - console->flags &=3D ~CON_ENABLED; console_unlock(); console_sysfs_notify(); =20 @@ -3303,10 +3312,8 @@ static int console_unregister_locked(str =20 return res; =20 -out_disable_unlock: - console->flags &=3D ~CON_ENABLED; +out_unlock: console_unlock(); - return res; }