Skip to content

Commit 76f258b

Browse files
KAGA-KOKOpmladek
authored andcommitted
printk: nbcon: Introduce printer kthreads
Provide the main implementation for running a printer kthread per nbcon console that is takeover/handover aware. This includes: - new mandatory write_thread() callback - kthread creation - kthread main printing loop - kthread wakeup mechanism - kthread shutdown kthread creation is a bit tricky because consoles may register before kthreads can be created. In such cases, registration will succeed, even though no kthread exists. Once kthreads can be created, an early_initcall will set @printk_kthreads_ready. If there are no registered boot consoles, the early_initcall creates the kthreads for all registered nbcon consoles. If kthread creation fails, the related console is unregistered. If there are registered boot consoles when @printk_kthreads_ready is set, no kthreads are created until the final boot console unregisters. Once kthread creation finally occurs, @printk_kthreads_running is set so that the system knows kthreads are available for all registered nbcon consoles. If @printk_kthreads_running is already set when the console is registering, the kthread is created during registration. If kthread creation fails, the registration will fail. Until @printk_kthreads_running is set, console printing occurs directly via the console_lock. kthread shutdown on system shutdown/reboot is necessary to ensure the printer kthreads finish their printing so that the system can cleanly transition back to direct printing via the console_lock in order to reliably push out the final shutdown/reboot messages. @printk_kthreads_running is cleared before shutting down the individual kthreads. The kthread uses a new mandatory write_thread() callback that is called with both device_lock() and the console context acquired. The console ownership handling is necessary for synchronization against write_atomic() which is synchronized only via the console context ownership. The device_lock() serializes acquiring the console context with NBCON_PRIO_NORMAL. It is needed in case the device_lock() does not disable preemption. It prevents the following race: CPU0 CPU1 [ task A ] nbcon_context_try_acquire() # success with NORMAL prio # .unsafe == false; // safe for takeover [ schedule: task A -> B ] WARN_ON() nbcon_atomic_flush_pending() nbcon_context_try_acquire() # success with EMERGENCY prio # flushing nbcon_context_release() # HERE: con->nbcon_state is free # to take by anyone !!! nbcon_context_try_acquire() # success with NORMAL prio [ task B ] [ schedule: task B -> A ] nbcon_enter_unsafe() nbcon_context_can_proceed() BUG: nbcon_context_can_proceed() returns "true" because the console is owned by a context on CPU0 with NBCON_PRIO_NORMAL. But it should return "false". The console is owned by a context from task B and we do the check in a context from task A. Note that with these changes, the printer kthreads do not yet take over full responsibility for nbcon printing during normal operation. These changes only focus on the lifecycle of the kthreads. Co-developed-by: John Ogness <[email protected]> Signed-off-by: John Ogness <[email protected]> Signed-off-by: Thomas Gleixner (Intel) <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Petr Mladek <[email protected]>
1 parent fb9fabf commit 76f258b

File tree

4 files changed

+420
-0
lines changed

4 files changed

+420
-0
lines changed

include/linux/console.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
#include <linux/atomic.h>
1818
#include <linux/bits.h>
19+
#include <linux/irq_work.h>
1920
#include <linux/rculist.h>
21+
#include <linux/rcuwait.h>
2022
#include <linux/types.h>
2123
#include <linux/vesa.h>
2224

@@ -324,6 +326,9 @@ struct nbcon_write_context {
324326
* @nbcon_seq: Sequence number of the next record for nbcon to print
325327
* @nbcon_device_ctxt: Context available for non-printing operations
326328
* @pbufs: Pointer to nbcon private buffer
329+
* @kthread: Printer kthread for this console
330+
* @rcuwait: RCU-safe wait object for @kthread waking
331+
* @irq_work: Defer @kthread waking to IRQ work context
327332
*/
328333
struct console {
329334
char name[16];
@@ -377,6 +382,37 @@ struct console {
377382
*/
378383
void (*write_atomic)(struct console *con, struct nbcon_write_context *wctxt);
379384

385+
/**
386+
* @write_thread:
387+
*
388+
* NBCON callback to write out text in task context.
389+
*
390+
* This callback must be called only in task context with both
391+
* device_lock() and the nbcon console acquired with
392+
* NBCON_PRIO_NORMAL.
393+
*
394+
* The same rules for console ownership verification and unsafe
395+
* sections handling applies as with write_atomic().
396+
*
397+
* The console ownership handling is necessary for synchronization
398+
* against write_atomic() which is synchronized only via the context.
399+
*
400+
* The device_lock() provides the primary serialization for operations
401+
* on the device. It might be as relaxed (mutex)[*] or as tight
402+
* (disabled preemption and interrupts) as needed. It allows
403+
* the kthread to operate in the least restrictive mode[**].
404+
*
405+
* [*] Standalone nbcon_context_try_acquire() is not safe with
406+
* the preemption enabled, see nbcon_owner_matches(). But it
407+
* can be safe when always called in the preemptive context
408+
* under the device_lock().
409+
*
410+
* [**] The device_lock() makes sure that nbcon_context_try_acquire()
411+
* would never need to spin which is important especially with
412+
* PREEMPT_RT.
413+
*/
414+
void (*write_thread)(struct console *con, struct nbcon_write_context *wctxt);
415+
380416
/**
381417
* @device_lock:
382418
*
@@ -423,7 +459,11 @@ struct console {
423459
atomic_t __private nbcon_state;
424460
atomic_long_t __private nbcon_seq;
425461
struct nbcon_context __private nbcon_device_ctxt;
462+
426463
struct printk_buffers *pbufs;
464+
struct task_struct *kthread;
465+
struct rcuwait rcuwait;
466+
struct irq_work irq_work;
427467
};
428468

429469
#ifdef CONFIG_LOCKDEP

kernel/printk/internal.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct printk_ringbuffer;
4848
struct dev_printk_info;
4949

5050
extern struct printk_ringbuffer *prb;
51+
extern bool printk_kthreads_running;
5152

5253
__printf(4, 0)
5354
int vprintk_store(int facility, int level,
@@ -90,6 +91,9 @@ enum nbcon_prio nbcon_get_default_prio(void);
9091
void nbcon_atomic_flush_pending(void);
9192
bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
9293
int cookie);
94+
bool nbcon_kthread_create(struct console *con);
95+
void nbcon_kthread_stop(struct console *con);
96+
void nbcon_kthreads_wake(void);
9397

9498
/*
9599
* Check if the given console is currently capable and allowed to print
@@ -125,12 +129,34 @@ static inline bool console_is_usable(struct console *con, short flags, bool use_
125129
return true;
126130
}
127131

132+
/**
133+
* nbcon_kthread_wake - Wake up a console printing thread
134+
* @con: Console to operate on
135+
*/
136+
static inline void nbcon_kthread_wake(struct console *con)
137+
{
138+
/*
139+
* Guarantee any new records can be seen by tasks preparing to wait
140+
* before this context checks if the rcuwait is empty.
141+
*
142+
* The full memory barrier in rcuwait_wake_up() pairs with the full
143+
* memory barrier within set_current_state() of
144+
* ___rcuwait_wait_event(), which is called after prepare_to_rcuwait()
145+
* adds the waiter but before it has checked the wait condition.
146+
*
147+
* This pairs with nbcon_kthread_func:A.
148+
*/
149+
rcuwait_wake_up(&con->rcuwait); /* LMM(nbcon_kthread_wake:A) */
150+
}
151+
128152
#else
129153

130154
#define PRINTK_PREFIX_MAX 0
131155
#define PRINTK_MESSAGE_MAX 0
132156
#define PRINTKRB_RECORD_MAX 0
133157

158+
#define printk_kthreads_running (false)
159+
134160
/*
135161
* In !PRINTK builds we still export console_sem
136162
* semaphore and some of console functions (console_unlock()/etc.), so
@@ -149,6 +175,7 @@ static inline enum nbcon_prio nbcon_get_default_prio(void) { return NBCON_PRIO_N
149175
static inline void nbcon_atomic_flush_pending(void) { }
150176
static inline bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
151177
int cookie) { return false; }
178+
static inline void nbcon_kthread_wake(struct console *con) { }
152179

153180
static inline bool console_is_usable(struct console *con, short flags,
154181
bool use_atomic) { return false; }

0 commit comments

Comments
 (0)