From patchwork Sat Sep 24 00:04:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Ogness X-Patchwork-Id: 12987280 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 9005FC07E9D for ; Sat, 24 Sep 2022 00:05:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233006AbiIXAFM (ORCPT ); Fri, 23 Sep 2022 20:05:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54726 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232124AbiIXAFK (ORCPT ); Fri, 23 Sep 2022 20:05:10 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4EA8A9AFA8; Fri, 23 Sep 2022 17:05:06 -0700 (PDT) From: John Ogness DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1663977900; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=WB3BybeSJAXwy9sZVb/waLS+Dyffg8F8NybWwGa1X/A=; b=xcriPjgLQzRYZ4xUbtrjHhhbSY6X8gfZJ30zvTyo5dN8Exk7uj2MmjaWOSJlNRl7Kx7QgB 9zQMGBTGy3Igy87KJJoiQBMEBq1ZfUyJ3+ONwK3Q2gWgYzXNwtard3Pe2VWpuYp1AjVYud g4vXL5kWEDF7Xo1JpP+wPHuBA6Biyoynv4rES/A0LDxmt5x3U6Khr0Gb1dCj357zAK5TrC 1x5uAsDfEZkZMo21gLUlF5sJX5MVPGjKTRNRPvpHjKdH4vn54Jxdxv93lMZv4COb/J/lqF 2N+8ZBtK8RV+utbDmp1JekmGb65ZIrgEh4rTStW8s+HKiMPBgXfBMmkIZL6g/A== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1663977900; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=WB3BybeSJAXwy9sZVb/waLS+Dyffg8F8NybWwGa1X/A=; b=zrUfFUWtkjjZ3Z3lX1frvgxG5HxmxzTiAMVgTfy0X477YaoYnRe+pIKC81HrwV1A1YFwfh Q9yqPZQbhwkJzJCQ== To: Petr Mladek Cc: Sergey Senozhatsky , Steven Rostedt , Thomas Gleixner , linux-kernel@vger.kernel.org, "James E.J. Bottomley" , Helge Deller , Sven Schnelle , John David Anglin , "Eric W. Biederman" , Julia Lawall , linux-parisc@vger.kernel.org Subject: [PATCH printk 08/18] parisc: Put console abuse into one place Date: Sat, 24 Sep 2022 02:10:44 +0206 Message-Id: <20220924000454.3319186-9-john.ogness@linutronix.de> In-Reply-To: <20220924000454.3319186-1-john.ogness@linutronix.de> References: <20220924000454.3319186-1-john.ogness@linutronix.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-parisc@vger.kernel.org From: Thomas Gleixner PARISC has a hope based mechanism to restore consoles in case of a HPMC (machine check exception) which is scattered over several places. Move it into one place to make further changes simpler and add comments. Signed-off-by: Thomas Gleixner Signed-off-by: John Ogness Reviewed-by: Sergey Senozhatsky Reviewed-by: Petr Mladek --- arch/parisc/include/asm/pdc.h | 2 +- arch/parisc/kernel/pdc_cons.c | 38 ++++++++++++++++++++++++++++------- arch/parisc/kernel/traps.c | 17 +++++----------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index b643092d4b98..12bffd8a5d05 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -20,7 +20,7 @@ extern unsigned long parisc_pat_pdc_cap; /* PDC capabilities (PAT) */ #define PDC_TYPE_SNAKE 2 /* Doesn't support SYSTEM_MAP */ void pdc_console_init(void); /* in pdc_console.c */ -void pdc_console_restart(void); +void pdc_console_restart(bool hpmc); void setup_pdc(void); /* in inventory.c */ diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index 2661cdd256ae..9a0c0932d2f9 100644 --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -237,20 +237,44 @@ void __init pdc_console_init(void) /* - * Used for emergencies. Currently only used if an HPMC occurs. If an - * HPMC occurs, it is possible that the current console may not be - * properly initialised after the PDC IO reset. This routine unregisters - * all of the current consoles, reinitializes the pdc console and - * registers it. + * + * + * Used for emergencies. + * + * - If an HPMC occurs, it is possible that the current console may not be + * properly initialised after the PDC IO reset. This routine unregisters + * all of the current consoles, reinitializes the pdc console and registers + * it. + * + * - Maybe the kernel hasn't booted very far yet and hasn't been able + * to initialize the serial or STI console. In that case we should + * re-enable the pdc console, so that the user will be able to + * identify the problem. + * + * + * + * The above is all wishful thinking: + * + * - Invoking [un]register_console() from exception contexts is obviously + * unsafe. + * + * - If the HPMC left the machine in unpleasant state and the pdc console + * was already initialized, but later removed due to CON_BOOT then this + * will do nothing. + * + * Pretend that any of the below works in the same way as we pretend that + * any of PARISC works. */ - -void pdc_console_restart(void) +void pdc_console_restart(bool hpmc) { struct console *console; if (pdc_console_initialized) return; + if (!hpmc && console_drivers) + return; + /* If we've already seen the output, don't bother to print it again */ if (console_drivers != NULL) pdc_cons.flags &= ~CON_PRINTBUFFER; diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index b78f1b9d45c1..81e23f589d3d 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -235,17 +235,12 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) " (__)\\ )\\/\\\n" " U ||----w |\n" " || ||\n"); - + /* unlock the pdc lock if necessary */ pdc_emergency_unlock(); - /* maybe the kernel hasn't booted very far yet and hasn't been able - * to initialize the serial or STI console. In that case we should - * re-enable the pdc console, so that the user will be able to - * identify the problem. */ - if (!console_drivers) - pdc_console_restart(); - + pdc_console_restart(false); + if (err) printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n", current->comm, task_pid_nr(current), str, err); @@ -429,9 +424,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o /* unlock the pdc lock if necessary */ pdc_emergency_unlock(); - /* restart pdc console if necessary */ - if (!console_drivers) - pdc_console_restart(); + pdc_console_restart(false); /* Not all paths will gutter the processor... */ switch(code){ @@ -483,7 +476,7 @@ void notrace handle_interruption(int code, struct pt_regs *regs) int si_code; if (code == 1) - pdc_console_restart(); /* switch back to pdc if HPMC */ + pdc_console_restart(true); /* switch back to pdc if HPMC */ else if (!irqs_disabled_flags(regs->gr[0])) local_irq_enable(); From patchwork Sat Sep 24 00:04:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Ogness X-Patchwork-Id: 12987281 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 908DBC6FA90 for ; Sat, 24 Sep 2022 00:05:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233030AbiIXAFP (ORCPT ); Fri, 23 Sep 2022 20:05:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54778 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233001AbiIXAFM (ORCPT ); Fri, 23 Sep 2022 20:05:12 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 59BCAA5712; Fri, 23 Sep 2022 17:05:06 -0700 (PDT) From: John Ogness DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1663977902; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hP5hgE3NqqWAe7NsJh2Zg4V0BgzlJ82nPhD5UvfqL1c=; b=ZhjExFnt3DHjT6MAszFYGWDIA+0wrrgI55Wiz/TKYY7681Ifuj3NZatGKcFv+V24ss9EjN nnN9VuHE8djfIzuxXeCAdrKVQOc/0xQsbQrV5VjLH4H7CKvhj7lJqUDSMyZR+v2oXKPm4g mg5J3lbKrVhB7xaO/OyzCU+4uaOUax0b6jptRyy1YtTSeYgoavf4QsCfgoRUrhjFRO/9Cw si5NANo0SfuQKZa0SxWsUgAHo7Z4S/V5jcUjseRBxZ3PVlQDW/zwxV188OF+jONkAwtDJG c3qc5d4tUUa2jFibijWRl822uD7E8F8enucx2ScKdzT04dnZEGxZvJwkFIIMwg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1663977902; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hP5hgE3NqqWAe7NsJh2Zg4V0BgzlJ82nPhD5UvfqL1c=; b=p6mv1TLxByq/wgGx21NhPWw2upD3WUZ/VdIJV4lxWZ71oHLwkrysDugo/cKRsLctIcetVH U7JbpTDTxH/BM/Bw== To: Petr Mladek Cc: Sergey Senozhatsky , Steven Rostedt , Thomas Gleixner , linux-kernel@vger.kernel.org, "James E.J. Bottomley" , Helge Deller , Greg Kroah-Hartman , linux-parisc@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [PATCH printk 11/18] printk: Convert console_drivers list to hlist Date: Sat, 24 Sep 2022 02:10:47 +0206 Message-Id: <20220924000454.3319186-12-john.ogness@linutronix.de> In-Reply-To: <20220924000454.3319186-1-john.ogness@linutronix.de> References: <20220924000454.3319186-1-john.ogness@linutronix.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-parisc@vger.kernel.org From: Thomas Gleixner 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 Signed-off-by: John Ogness Signed-off-by: Helge Deller --- arch/parisc/kernel/pdc_cons.c | 19 +++---- fs/proc/consoles.c | 5 +- include/linux/console.h | 15 ++++-- kernel/printk/printk.c | 99 +++++++++++++++++++---------------- 4 files changed, 75 insertions(+), 63 deletions(-) diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index 9a0c0932d2f9..3f9abf0263ee 100644 --- 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_init(void) struct console *tmp; - console_lock(); - for_each_console(tmp) - if (tmp == &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 creating %s\n", pdc_cons.name); return -ENODEV; } @@ -272,15 +267,17 @@ void pdc_console_restart(bool hpmc) if (pdc_console_initialized) return; - if (!hpmc && console_drivers) + if (!hpmc && !hlist_empty(&console_list)) return; /* If we've already seen the output, don't bother to print it again */ - if (console_drivers != NULL) + if (!hlist_empty(&console_list)) pdc_cons.flags &= ~CON_PRINTBUFFER; - while ((console = console_drivers) != NULL) - unregister_console(console_drivers); + while (!hlist_empty(&console_list)) { + unregister_console(READ_ONCE(hlist_entry(console_list.first, + struct console, node))); + } /* force registering the pdc console */ pdc_console_init_force(); diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c index 6775056eecd5..70994d1e52f6 100644 --- a/fs/proc/consoles.c +++ b/fs/proc/consoles.c @@ -74,8 +74,11 @@ static void *c_start(struct seq_file *m, loff_t *pos) static void *c_next(struct seq_file *m, void *v, loff_t *pos) { struct console *con = v; + ++*pos; - return con->next; + hlist_for_each_entry_continue(con, node) + break; + return con; } static void c_stop(struct seq_file *m, void *v) diff --git a/include/linux/console.h b/include/linux/console.h index 86a6125512b9..1e3d0a50cef1 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -15,6 +15,7 @@ #define _LINUX_CONSOLE_H_ 1 #include +#include #include struct vc_data; @@ -154,15 +155,19 @@ struct console { u64 seq; unsigned long dropped; void *data; - struct console *next; + struct hlist_node node; }; #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 +extern struct hlist_head console_list; + extern void console_list_lock(void) __acquires(console_mutex); extern void console_list_unlock(void) __releases(console_mutex); @@ -175,7 +180,7 @@ extern void console_list_unlock(void) __releases(console_mutex); */ #define for_each_registered_console(con) \ lockdep_assert_console_list_lock_held(); \ - for (con = console_drivers; con != NULL; con = con->next) + hlist_for_each_entry(con, &console_list, node) /** * for_each_console() - Iterator over registered consoles @@ -185,7 +190,8 @@ extern void console_list_unlock(void) __releases(console_mutex); * list is immutable. */ #define for_each_console(con) \ - for (con = console_drivers; con != NULL; con = con->next) + lockdep_assert_console_lock_held(); \ + hlist_for_each_entry(con, &console_list, node) /** * for_each_console_kgdb() - Iterator over registered consoles for KGDB @@ -195,7 +201,7 @@ extern void console_list_unlock(void) __releases(console_mutex); * Don't use outside of the KGDB fairy tale land! */ #define for_each_console_kgdb(con) \ - for (con = console_drivers; con != NULL; con = con->next) + hlist_for_each_entry(con, &console_list, node) 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); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 80a728ef9d96..f1d31dcbd6ba 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -79,20 +79,20 @@ int oops_in_progress; EXPORT_SYMBOL(oops_in_progress); /* - * 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 must be taken inside a console_mutex locked section - * for any list manipulation in order to keep the console BKL + * for any console_list manipulation in order to keep the console BKL * machinery happy. This requirement also applies to manipulation * of console->flags. */ 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); /* * System may need to suppress printk message under certain @@ -111,6 +111,11 @@ static struct lockdep_map console_lock_dep_map = { .name = "console_lock" }; +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); @@ -2591,7 +2596,7 @@ static int console_cpu_notify(unsigned int cpu) * 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. */ @@ -2611,7 +2616,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. */ @@ -2979,7 +2984,15 @@ void console_flush_on_panic(enum con_flush_mode mode) u64 seq; seq = 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 = seq; } console_unlock(); @@ -3120,6 +3133,9 @@ static void try_enable_default_console(struct console *newcon) (con->flags & CON_BOOT) ? "boot" : "", \ con->name, con->index, ##__VA_ARGS__) +#define cons_first() \ + hlist_entry(console_list.first, struct console, node) + static int console_unregister_locked(struct console *console); /* @@ -3182,8 +3198,8 @@ void register_console(struct console *newcon) * 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); } } @@ -3211,21 +3227,17 @@ void register_console(struct console *newcon) } /* - * 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 == NULL) { - newcon->next = console_drivers; - console_drivers = newcon; - if (newcon->next) - newcon->next->flags &= ~CON_CONSDEV; - /* Ensure this flag is always set for the head of the list */ - newcon->flags |= CON_CONSDEV; - } else { - newcon->next = console_drivers->next; - console_drivers->next = 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 |= CON_CONSDEV; newcon->dropped = 0; if (newcon->flags & CON_PRINTBUFFER) { @@ -3251,7 +3263,9 @@ void register_console(struct console *newcon) if (bootcon_enabled && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == 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); } @@ -3263,7 +3277,6 @@ EXPORT_SYMBOL(register_console); static int console_unregister_locked(struct console *console) { - struct console *con; int res; con_printk(KERN_INFO, console, "disabled\n"); @@ -3274,32 +3287,28 @@ static int console_unregister_locked(struct console *console) if (res > 0) return 0; - res = -ENODEV; console_lock(); - if (console_drivers == console) { - console_drivers=console->next; - res = 0; - } else { - for_each_console(con) { - if (con->next == console) { - con->next = console->next; - res = 0; - break; - } - } - } - if (res) - goto out_disable_unlock; + /* Disable it unconditionally */ + console->flags &= ~CON_ENABLED; + + if (hlist_unhashed(&console->node)) + goto out_unlock; + + hlist_del_init(&console->node); /* + * * 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 != NULL && console->flags & CON_CONSDEV) - console_drivers->flags |= CON_CONSDEV; + if (!hlist_empty(&console_list) && console->flags & CON_CONSDEV) + cons_first()->flags |= CON_CONSDEV; - console->flags &= ~CON_ENABLED; console_unlock(); console_sysfs_notify(); @@ -3308,10 +3317,8 @@ static int console_unregister_locked(struct console *console) return res; -out_disable_unlock: - console->flags &= ~CON_ENABLED; +out_unlock: console_unlock(); - return res; }