Skip to content

Commit 187de7c

Browse files
jognesspmladek
authored andcommitted
printk: nbcon: Allow unsafe write_atomic() for panic
There may be console drivers that have not yet figured out a way to implement safe atomic printing (->write_atomic() callback). These drivers could choose to only implement threaded printing (->write_thread() callback), but then it is guaranteed that _no_ output will be printed during panic. Not even attempted. As a result, developers may be tempted to implement unsafe ->write_atomic() callbacks and/or implement some sort of custom deferred printing trickery to try to make it work. This goes against the principle intention of the nbcon API as well as endangers other nbcon drivers that are doing things correctly (safely). As a compromise, allow nbcon drivers to implement unsafe ->write_atomic() callbacks by providing a new console flag CON_NBCON_ATOMIC_UNSAFE. When specified, the ->write_atomic() callback for that console will _only_ be called during the final "hope and pray" flush attempt at the end of a panic: nbcon_atomic_flush_unsafe(). Signed-off-by: John Ogness <[email protected]> Link: https://lore.kernel.org/lkml/b2qps3uywhmjaym4mht2wpxul4yqtuuayeoq4iv4k3zf5wdgh3@tocu6c7mj4lt Reviewed-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/all/swdpckuwwlv3uiessmtnf2jwlx3jusw6u7fpk5iggqo4t2vdws@7rpjso4gr7qp/ [1] Link: https://lore.kernel.org/all/[email protected]/ [2] Link: https://patch.msgid.link/[email protected] [[email protected]: Fix build with rework/nbcon-in-kdb branch.] Signed-off-by: Petr Mladek <[email protected]>
1 parent 62627bf commit 187de7c

File tree

2 files changed

+48
-18
lines changed

2 files changed

+48
-18
lines changed

include/linux/console.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ static inline void con_debug_leave(void) { }
186186
* printing callbacks must not be called.
187187
* @CON_NBCON: Console can operate outside of the legacy style console_lock
188188
* constraints.
189+
* @CON_NBCON_ATOMIC_UNSAFE: The write_atomic() callback is not safe and is
190+
* therefore only used by nbcon_atomic_flush_unsafe().
189191
*/
190192
enum cons_flags {
191193
CON_PRINTBUFFER = BIT(0),
@@ -197,6 +199,7 @@ enum cons_flags {
197199
CON_EXTENDED = BIT(6),
198200
CON_SUSPENDED = BIT(7),
199201
CON_NBCON = BIT(8),
202+
CON_NBCON_ATOMIC_UNSAFE = BIT(9),
200203
};
201204

202205
/**
@@ -608,6 +611,7 @@ extern void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
608611
extern bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt);
609612
extern bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt);
610613
extern void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt);
614+
extern bool nbcon_allow_unsafe_takeover(void);
611615
extern bool nbcon_kdb_try_acquire(struct console *con,
612616
struct nbcon_write_context *wctxt);
613617
extern void nbcon_kdb_release(struct nbcon_write_context *wctxt);
@@ -627,9 +631,18 @@ static inline bool console_is_usable(struct console *con, short flags, bool use_
627631
return false;
628632

629633
if (flags & CON_NBCON) {
630-
/* The write_atomic() callback is optional. */
631-
if (use_atomic && !con->write_atomic)
632-
return false;
634+
if (use_atomic) {
635+
/* The write_atomic() callback is optional. */
636+
if (!con->write_atomic)
637+
return false;
638+
639+
/*
640+
* An unsafe write_atomic() callback is only usable
641+
* when unsafe takeovers are allowed.
642+
*/
643+
if ((flags & CON_NBCON_ATOMIC_UNSAFE) && !nbcon_allow_unsafe_takeover())
644+
return false;
645+
}
633646

634647
/*
635648
* For the !use_atomic case, @printk_kthreads_running is not

kernel/printk/nbcon.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,26 @@ enum nbcon_prio nbcon_get_default_prio(void)
14081408
return NBCON_PRIO_NORMAL;
14091409
}
14101410

1411+
/*
1412+
* Track if it is allowed to perform unsafe hostile takeovers of console
1413+
* ownership. When true, console drivers might perform unsafe actions while
1414+
* printing. It is externally available via nbcon_allow_unsafe_takeover().
1415+
*/
1416+
static bool panic_nbcon_allow_unsafe_takeover;
1417+
1418+
/**
1419+
* nbcon_allow_unsafe_takeover - Check if unsafe console takeovers are allowed
1420+
*
1421+
* Return: True, when it is permitted to perform unsafe console printing
1422+
*
1423+
* This is also used by console_is_usable() to determine if it is allowed to
1424+
* call write_atomic() callbacks flagged as unsafe (CON_NBCON_ATOMIC_UNSAFE).
1425+
*/
1426+
bool nbcon_allow_unsafe_takeover(void)
1427+
{
1428+
return panic_on_this_cpu() && panic_nbcon_allow_unsafe_takeover;
1429+
}
1430+
14111431
/**
14121432
* nbcon_legacy_emit_next_record - Print one record for an nbcon console
14131433
* in legacy contexts
@@ -1478,7 +1498,6 @@ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
14781498
* write_atomic() callback
14791499
* @con: The nbcon console to flush
14801500
* @stop_seq: Flush up until this record
1481-
* @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
14821501
*
14831502
* Return: 0 if @con was flushed up to @stop_seq Otherwise, error code on
14841503
* failure.
@@ -1497,8 +1516,7 @@ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
14971516
* returned, it cannot be expected that the unfinalized record will become
14981517
* available.
14991518
*/
1500-
static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
1501-
bool allow_unsafe_takeover)
1519+
static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)
15021520
{
15031521
struct nbcon_write_context wctxt = { };
15041522
struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt);
@@ -1507,7 +1525,7 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
15071525
ctxt->console = con;
15081526
ctxt->spinwait_max_us = 2000;
15091527
ctxt->prio = nbcon_get_default_prio();
1510-
ctxt->allow_unsafe_takeover = allow_unsafe_takeover;
1528+
ctxt->allow_unsafe_takeover = nbcon_allow_unsafe_takeover();
15111529

15121530
if (!nbcon_context_try_acquire(ctxt, false))
15131531
return -EPERM;
@@ -1538,15 +1556,13 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
15381556
* write_atomic() callback
15391557
* @con: The nbcon console to flush
15401558
* @stop_seq: Flush up until this record
1541-
* @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
15421559
*
15431560
* This will stop flushing before @stop_seq if another context has ownership.
15441561
* That context is then responsible for the flushing. Likewise, if new records
15451562
* are added while this context was flushing and there is no other context
15461563
* to handle the printing, this context must also flush those records.
15471564
*/
1548-
static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
1549-
bool allow_unsafe_takeover)
1565+
static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq)
15501566
{
15511567
struct console_flush_type ft;
15521568
unsigned long flags;
@@ -1561,7 +1577,7 @@ static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
15611577
*/
15621578
local_irq_save(flags);
15631579

1564-
err = __nbcon_atomic_flush_pending_con(con, stop_seq, allow_unsafe_takeover);
1580+
err = __nbcon_atomic_flush_pending_con(con, stop_seq);
15651581

15661582
local_irq_restore(flags);
15671583

@@ -1593,9 +1609,8 @@ static void nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
15931609
* __nbcon_atomic_flush_pending - Flush all nbcon consoles using their
15941610
* write_atomic() callback
15951611
* @stop_seq: Flush up until this record
1596-
* @allow_unsafe_takeover: True, to allow unsafe hostile takeovers
15971612
*/
1598-
static void __nbcon_atomic_flush_pending(u64 stop_seq, bool allow_unsafe_takeover)
1613+
static void __nbcon_atomic_flush_pending(u64 stop_seq)
15991614
{
16001615
struct console *con;
16011616
int cookie;
@@ -1613,7 +1628,7 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq, bool allow_unsafe_takeove
16131628
if (nbcon_seq_read(con) >= stop_seq)
16141629
continue;
16151630

1616-
nbcon_atomic_flush_pending_con(con, stop_seq, allow_unsafe_takeover);
1631+
nbcon_atomic_flush_pending_con(con, stop_seq);
16171632
}
16181633
console_srcu_read_unlock(cookie);
16191634
}
@@ -1629,7 +1644,7 @@ static void __nbcon_atomic_flush_pending(u64 stop_seq, bool allow_unsafe_takeove
16291644
*/
16301645
void nbcon_atomic_flush_pending(void)
16311646
{
1632-
__nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), false);
1647+
__nbcon_atomic_flush_pending(prb_next_reserve_seq(prb));
16331648
}
16341649

16351650
/**
@@ -1641,7 +1656,9 @@ void nbcon_atomic_flush_pending(void)
16411656
*/
16421657
void nbcon_atomic_flush_unsafe(void)
16431658
{
1644-
__nbcon_atomic_flush_pending(prb_next_reserve_seq(prb), true);
1659+
panic_nbcon_allow_unsafe_takeover = true;
1660+
__nbcon_atomic_flush_pending(prb_next_reserve_seq(prb));
1661+
panic_nbcon_allow_unsafe_takeover = false;
16451662
}
16461663

16471664
/**
@@ -1848,7 +1865,7 @@ void nbcon_device_release(struct console *con)
18481865
* using the legacy loop.
18491866
*/
18501867
if (ft.nbcon_atomic) {
1851-
__nbcon_atomic_flush_pending_con(con, prb_next_reserve_seq(prb), false);
1868+
__nbcon_atomic_flush_pending_con(con, prb_next_reserve_seq(prb));
18521869
} else if (ft.legacy_direct) {
18531870
if (console_trylock())
18541871
console_unlock();
@@ -1918,5 +1935,5 @@ void nbcon_kdb_release(struct nbcon_write_context *wctxt)
19181935
* The console was locked only when the write_atomic() callback
19191936
* was usable.
19201937
*/
1921-
__nbcon_atomic_flush_pending_con(ctxt->console, prb_next_reserve_seq(prb), false);
1938+
__nbcon_atomic_flush_pending_con(ctxt->console, prb_next_reserve_seq(prb));
19221939
}

0 commit comments

Comments
 (0)