Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Kubecek <mkubecek@suse.cz>2019-10-01 06:59:43 +0200
committerMichal Kubecek <mkubecek@suse.cz>2019-10-01 06:59:43 +0200
commitdd7af57642ca313fb7c6b7af5398f09da9a297e5 (patch)
tree3c25a1166981f1b4121a6745b8ecd75b05fe034b
parent68f033a8447bdaafcff11f51674d157e14a53060 (diff)
parentfdaa896e840a8788a2186a3d071d17f5140c7e96 (diff)
Merge branch 'users/pmladek/SLE15-SP2/for-next' into SLE15-SP2
Pull printk updates from Petr Mladek. suse-commit: 37b51f462a38cb9756564bbdaa5c53fc0217988e
-rw-r--r--arch/x86/kernel/crash.c1
-rw-r--r--include/linux/printk.h6
-rw-r--r--kernel/printk/printk.c75
3 files changed, 73 insertions, 9 deletions
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 2bf70a2fed90..90cc72d38dda 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -99,6 +99,7 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
void kdump_nmi_shootdown_cpus(void)
{
nmi_shootdown_cpus(kdump_nmi_callback);
+ printk_bust_locks();
disable_local_APIC();
}
diff --git a/include/linux/printk.h b/include/linux/printk.h
index cefd374c47b1..63c1ecaf227b 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -194,6 +194,8 @@ devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void __user *buf,
extern void wake_up_klogd(void);
+void printk_bust_locks(void);
+
char *log_buf_addr_get(void);
u32 log_buf_len_get(void);
void log_buf_vmcoreinfo_setup(void);
@@ -235,6 +237,10 @@ static inline void wake_up_klogd(void)
{
}
+static void printk_bust_locks(void)
+{
+}
+
static inline char *log_buf_addr_get(void)
{
return NULL;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 1888f6a3b694..674ba0c2552e 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1626,6 +1626,22 @@ static struct task_struct *console_owner;
static bool console_waiter;
/**
+ * printk_bust_locks - forcibly reset all printk-related locks
+ *
+ * This function can be used after CPUs were stopped using NMI.
+ * It is especially useful in kdump_nmi_shootdown_cpus() that
+ * uses NMI but it does not modify the online CPU mask.
+ */
+void printk_bust_locks(void)
+{
+ debug_locks_off();
+ raw_spin_lock_init(&logbuf_lock);
+ raw_spin_lock_init(&console_owner_lock);
+ console_owner = NULL;
+ console_waiter = false;
+}
+
+/**
* console_lock_spinning_enable - mark beginning of code where another
* thread might safely busy wait
*
@@ -2640,16 +2656,23 @@ void register_console(struct console *newcon)
int i;
unsigned long flags;
struct console *bcon = NULL;
+ struct console *con_consdev = NULL;
struct console_cmdline *c;
static bool has_preferred;
+ bool consdev_fallback = false;
- if (console_drivers)
- for_each_console(bcon)
+ if (console_drivers) {
+ for_each_console(bcon) {
if (WARN(bcon == newcon,
"console '%s%d' already registered\n",
bcon->name, bcon->index))
return;
+ if (bcon->flags & CON_CONSDEV && !con_consdev)
+ con_consdev = bcon;
+ }
+ }
+
/*
* before we register a new CON_BOOT console, make sure we don't
* already have a valid console
@@ -2718,8 +2741,17 @@ void register_console(struct console *newcon)
newcon->flags |= CON_ENABLED;
if (i == preferred_console) {
+ /* This is the last console on the command line. */
newcon->flags |= CON_CONSDEV;
has_preferred = true;
+ } else if (newcon->device && !con_consdev) {
+ /*
+ * This is the first console with tty binding. It will
+ * be used for /dev/console when the preferred one
+ * will not get registered for some reason.
+ */
+ newcon->flags |= CON_CONSDEV;
+ consdev_fallback = true;
}
break;
}
@@ -2733,7 +2765,9 @@ void register_console(struct console *newcon)
* the real console are the same physical device, it's annoying to
* see the beginning boot messages twice
*/
- if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
+ if (bcon &&
+ ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
+ !consdev_fallback)
newcon->flags &= ~CON_PRINTBUFFER;
/*
@@ -2741,12 +2775,28 @@ void register_console(struct console *newcon)
* preferred driver at the head of the list.
*/
console_lock();
- if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
+ if ((newcon->flags & CON_CONSDEV && !consdev_fallback) ||
+ console_drivers == NULL) {
+ /* Put the preferred or the first console at the head. */
newcon->next = console_drivers;
console_drivers = newcon;
- if (newcon->next)
- newcon->next->flags &= ~CON_CONSDEV;
+ /* Only one console can have CON_CONSDEV flag set */
+ if (con_consdev)
+ con_consdev->flags &= ~CON_CONSDEV;
+ } else if (newcon->device && con_consdev) {
+ /*
+ * Keep the driver associated with /dev/console.
+ * We are here only when the console was enabled by the cycle
+ * checking console_cmdline and this is neither preferred
+ * console nor the consdev fallback.
+ */
+ newcon->next = con_consdev->next;
+ con_consdev->next = newcon;
} else {
+ /*
+ * Keep a boot console first until the preferred real one
+ * is registered.
+ */
newcon->next = console_drivers->next;
console_drivers->next = newcon;
}
@@ -2790,6 +2840,7 @@ void register_console(struct console *newcon)
newcon->name, newcon->index);
if (bcon &&
((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
+ !consdev_fallback &&
!keep_bootcon) {
/* We need to iterate through all boot consoles, to make
* sure we print everything out, before we unregister them.
@@ -2835,10 +2886,16 @@ int unregister_console(struct console *console)
/*
* If this isn't the last console and it has CON_CONSDEV set, we
- * need to set it on the next preferred console.
+ * need to set it on the first console with tty binding.
*/
- if (console_drivers != NULL && console->flags & CON_CONSDEV)
- console_drivers->flags |= CON_CONSDEV;
+ if (console_drivers != NULL && console->flags & CON_CONSDEV) {
+ for_each_console(a) {
+ if (a->device) {
+ a->flags |= CON_CONSDEV;
+ break;
+ }
+ }
+ }
console->flags &= ~CON_ENABLED;
console_unlock();