Skip to content

Commit 5634c90

Browse files
KAGA-KOKOpmladek
authored andcommitted
printk: nbcon: Add buffer management
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: - Adding a global output buffer instance for the panic context. This is the only situation where hostile takeovers can occur and there is always at most 1 panic context. - Allocating an output buffer per non-boot console upon console registration. This buffer is used by the console owner when not in panic context. (For boot consoles, the existing shared global legacy output buffer is used instead. Boot console printing will be synchronized with legacy console printing.) - Choosing the appropriate buffer is handled in the acquire/release functions. Co-developed-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: Thomas Gleixner (Intel) <tglx@linutronix.de> Reviewed-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20230916192007.608398-5-john.ogness@linutronix.de
1 parent d818b56 commit 5634c90

File tree

4 files changed

+99
-15
lines changed

4 files changed

+99
-15
lines changed

include/linux/console.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ enum nbcon_prio {
231231
};
232232

233233
struct console;
234+
struct printk_buffers;
234235

235236
/**
236237
* struct nbcon_context - Context for console acquire/release
@@ -241,13 +242,17 @@ struct console;
241242
* be used only with NBCON_PRIO_PANIC @prio. It
242243
* might cause a system freeze when the console
243244
* is used later.
245+
* @pbufs: Pointer to the text buffer for this context
244246
*/
245247
struct nbcon_context {
246248
/* members set by caller */
247249
struct console *console;
248250
unsigned int spinwait_max_us;
249251
enum nbcon_prio prio;
250252
unsigned int allow_unsafe_takeover : 1;
253+
254+
/* members set by acquire */
255+
struct printk_buffers *pbufs;
251256
};
252257

253258
/**
@@ -271,6 +276,7 @@ struct nbcon_context {
271276
* @node: hlist node for the console list
272277
*
273278
* @nbcon_state: State for nbcon consoles
279+
* @pbufs: Pointer to nbcon private buffer
274280
*/
275281
struct console {
276282
char name[16];
@@ -293,6 +299,7 @@ struct console {
293299

294300
/* nbcon console specific members */
295301
atomic_t __private nbcon_state;
302+
struct printk_buffers *pbufs;
296303
};
297304

298305
#ifdef CONFIG_LOCKDEP

kernel/printk/internal.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
1313
#define printk_sysctl_init() do { } while (0)
1414
#endif
1515

16+
#define con_printk(lvl, con, fmt, ...) \
17+
printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \
18+
(con->flags & CON_NBCON) ? "" : "legacy ", \
19+
(con->flags & CON_BOOT) ? "boot" : "", \
20+
con->name, con->index, ##__VA_ARGS__)
21+
1622
#ifdef CONFIG_PRINTK
1723

1824
#ifdef CONFIG_PRINTK_CALLER
@@ -63,8 +69,9 @@ void defer_console_output(void);
6369
u16 printk_parse_prefix(const char *text, int *level,
6470
enum printk_info_flags *flags);
6571

72+
bool nbcon_alloc(struct console *con);
6673
void nbcon_init(struct console *con);
67-
void nbcon_cleanup(struct console *con);
74+
void nbcon_free(struct console *con);
6875

6976
#else
7077

@@ -81,8 +88,9 @@ void nbcon_cleanup(struct console *con);
8188
#define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
8289

8390
static inline bool printk_percpu_data_ready(void) { return false; }
91+
static inline bool nbcon_alloc(struct console *con) { return false; }
8492
static inline void nbcon_init(struct console *con) { }
85-
static inline void nbcon_cleanup(struct console *con) { }
93+
static inline void nbcon_free(struct console *con) { }
8694

8795
#endif /* CONFIG_PRINTK */
8896

kernel/printk/nbcon.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <linux/kernel.h>
66
#include <linux/console.h>
77
#include <linux/delay.h>
8+
#include <linux/slab.h>
89
#include "internal.h"
910
/*
1011
* Printk console printing implementation for consoles which does not depend
@@ -70,6 +71,10 @@
7071
* console is an unsafe state. It is used only in panic() by the final
7172
* attempt to flush consoles in a try and hope mode.
7273
*
74+
* Note that separate record buffers are used in panic(). As a result,
75+
* the messages can be read and formatted without any risk even after
76+
* using the hostile takeover in unsafe state.
77+
*
7378
* The release function simply clears the 'prio' field.
7479
*
7580
* All operations on @console::nbcon_state are atomic cmpxchg based to
@@ -459,6 +464,8 @@ static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt,
459464
return 0;
460465
}
461466

467+
static struct printk_buffers panic_nbcon_pbufs;
468+
462469
/**
463470
* nbcon_context_try_acquire - Try to acquire nbcon console
464471
* @ctxt: The context of the caller
@@ -473,6 +480,7 @@ static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt,
473480
__maybe_unused
474481
static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
475482
{
483+
unsigned int cpu = smp_processor_id();
476484
struct console *con = ctxt->console;
477485
struct nbcon_state cur;
478486
int err;
@@ -491,7 +499,18 @@ static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
491499

492500
err = nbcon_context_try_acquire_hostile(ctxt, &cur);
493501
out:
494-
return !err;
502+
if (err)
503+
return false;
504+
505+
/* Acquire succeeded. */
506+
507+
/* Assign the appropriate buffer for this context. */
508+
if (atomic_read(&panic_cpu) == cpu)
509+
ctxt->pbufs = &panic_nbcon_pbufs;
510+
else
511+
ctxt->pbufs = con->pbufs;
512+
513+
return true;
495514
}
496515

497516
static bool nbcon_owner_matches(struct nbcon_state *cur, int expected_cpu,
@@ -530,7 +549,7 @@ static void nbcon_context_release(struct nbcon_context *ctxt)
530549

531550
do {
532551
if (!nbcon_owner_matches(&cur, cpu, ctxt->prio))
533-
return;
552+
break;
534553

535554
new.atom = cur.atom;
536555
new.prio = NBCON_PRIO_NONE;
@@ -542,26 +561,70 @@ static void nbcon_context_release(struct nbcon_context *ctxt)
542561
new.unsafe |= cur.unsafe_takeover;
543562

544563
} while (!nbcon_state_try_cmpxchg(con, &cur, &new));
564+
565+
ctxt->pbufs = NULL;
566+
}
567+
568+
/**
569+
* nbcon_alloc - Allocate buffers needed by the nbcon console
570+
* @con: Console to allocate buffers for
571+
*
572+
* Return: True on success. False otherwise and the console cannot
573+
* be used.
574+
*
575+
* This is not part of nbcon_init() because buffer allocation must
576+
* be performed earlier in the console registration process.
577+
*/
578+
bool nbcon_alloc(struct console *con)
579+
{
580+
if (con->flags & CON_BOOT) {
581+
/*
582+
* Boot console printing is synchronized with legacy console
583+
* printing, so boot consoles can share the same global printk
584+
* buffers.
585+
*/
586+
con->pbufs = &printk_shared_pbufs;
587+
} else {
588+
con->pbufs = kmalloc(sizeof(*con->pbufs), GFP_KERNEL);
589+
if (!con->pbufs) {
590+
con_printk(KERN_ERR, con, "failed to allocate printing buffer\n");
591+
return false;
592+
}
593+
}
594+
595+
return true;
545596
}
546597

547598
/**
548599
* nbcon_init - Initialize the nbcon console specific data
549600
* @con: Console to initialize
601+
*
602+
* nbcon_alloc() *must* be called and succeed before this function
603+
* is called.
550604
*/
551605
void nbcon_init(struct console *con)
552606
{
553607
struct nbcon_state state = { };
554608

609+
/* nbcon_alloc() must have been called and successful! */
610+
BUG_ON(!con->pbufs);
611+
555612
nbcon_state_set(con, &state);
556613
}
557614

558615
/**
559-
* nbcon_cleanup - Cleanup the nbcon console specific data
560-
* @con: Console to cleanup
616+
* nbcon_free - Free and cleanup the nbcon console specific data
617+
* @con: Console to free/cleanup nbcon data
561618
*/
562-
void nbcon_cleanup(struct console *con)
619+
void nbcon_free(struct console *con)
563620
{
564621
struct nbcon_state state = { };
565622

566623
nbcon_state_set(con, &state);
624+
625+
/* Boot consoles share global printk buffers. */
626+
if (!(con->flags & CON_BOOT))
627+
kfree(con->pbufs);
628+
629+
con->pbufs = NULL;
567630
}

kernel/printk/printk.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,12 +3331,6 @@ static void try_enable_default_console(struct console *newcon)
33313331
newcon->flags |= CON_CONSDEV;
33323332
}
33333333

3334-
#define con_printk(lvl, con, fmt, ...) \
3335-
printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \
3336-
(con->flags & CON_NBCON) ? "" : "legacy ", \
3337-
(con->flags & CON_BOOT) ? "boot" : "", \
3338-
con->name, con->index, ##__VA_ARGS__)
3339-
33403334
static void console_init_seq(struct console *newcon, bool bootcon_registered)
33413335
{
33423336
struct console *con;
@@ -3450,6 +3444,15 @@ void register_console(struct console *newcon)
34503444
goto unlock;
34513445
}
34523446

3447+
if (newcon->flags & CON_NBCON) {
3448+
/*
3449+
* Ensure the nbcon console buffers can be allocated
3450+
* before modifying any global data.
3451+
*/
3452+
if (!nbcon_alloc(newcon))
3453+
goto unlock;
3454+
}
3455+
34533456
/*
34543457
* See if we want to enable this console driver by default.
34553458
*
@@ -3477,8 +3480,11 @@ void register_console(struct console *newcon)
34773480
err = try_enable_preferred_console(newcon, false);
34783481

34793482
/* printk() messages are not printed to the Braille console. */
3480-
if (err || newcon->flags & CON_BRL)
3483+
if (err || newcon->flags & CON_BRL) {
3484+
if (newcon->flags & CON_NBCON)
3485+
nbcon_free(newcon);
34813486
goto unlock;
3487+
}
34823488

34833489
/*
34843490
* If we have a bootconsole, and are switching to a real console,
@@ -3589,7 +3595,7 @@ static int unregister_console_locked(struct console *console)
35893595
synchronize_srcu(&console_srcu);
35903596

35913597
if (console->flags & CON_NBCON)
3592-
nbcon_cleanup(console);
3598+
nbcon_free(console);
35933599

35943600
console_sysfs_notify();
35953601

0 commit comments

Comments
 (0)