Skip to content

Commit 06653d5

Browse files
KAGA-KOKOpmladek
authored andcommitted
printk: nbcon: Add emit function and callback function for atomic printing
Implement an emit function for nbcon consoles to output printk messages. It utilizes the lockless printk_get_next_message() and console_prepend_dropped() functions to retrieve/build the output message. The emit function includes the required safety points to check for handover/takeover and calls a new write_atomic callback of the console driver to output the message. It also includes proper handling for updating the nbcon console sequence number. A new nbcon_write_context struct is introduced. This is provided to the write_atomic callback and includes only the information necessary for performing atomic writes. 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-8-john.ogness@linutronix.de
1 parent ad56ebd commit 06653d5

File tree

4 files changed

+134
-8
lines changed

4 files changed

+134
-8
lines changed

include/linux/console.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ struct printk_buffers;
242242
* be used only with NBCON_PRIO_PANIC @prio. It
243243
* might cause a system freeze when the console
244244
* is used later.
245+
* @backlog: Ringbuffer has pending records
245246
* @pbufs: Pointer to the text buffer for this context
246247
* @seq: The sequence number to print for this context
247248
*/
@@ -252,11 +253,28 @@ struct nbcon_context {
252253
enum nbcon_prio prio;
253254
unsigned int allow_unsafe_takeover : 1;
254255

256+
/* members set by emit */
257+
unsigned int backlog : 1;
258+
255259
/* members set by acquire */
256260
struct printk_buffers *pbufs;
257261
u64 seq;
258262
};
259263

264+
/**
265+
* struct nbcon_write_context - Context handed to the nbcon write callbacks
266+
* @ctxt: The core console context
267+
* @outbuf: Pointer to the text buffer for output
268+
* @len: Length to write
269+
* @unsafe_takeover: If a hostile takeover in an unsafe state has occurred
270+
*/
271+
struct nbcon_write_context {
272+
struct nbcon_context __private ctxt;
273+
char *outbuf;
274+
unsigned int len;
275+
bool unsafe_takeover;
276+
};
277+
260278
/**
261279
* struct console - The console descriptor structure
262280
* @name: The name of the console driver
@@ -277,6 +295,7 @@ struct nbcon_context {
277295
* @data: Driver private data
278296
* @node: hlist node for the console list
279297
*
298+
* @write_atomic: Write callback for atomic context
280299
* @nbcon_state: State for nbcon consoles
281300
* @nbcon_seq: Sequence number of the next record for nbcon to print
282301
* @pbufs: Pointer to nbcon private buffer
@@ -301,6 +320,8 @@ struct console {
301320
struct hlist_node node;
302321

303322
/* nbcon console specific members */
323+
bool (*write_atomic)(struct console *con,
324+
struct nbcon_write_context *wctxt);
304325
atomic_t __private nbcon_state;
305326
atomic_long_t __private nbcon_seq;
306327
struct printk_buffers *pbufs;

kernel/printk/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,9 @@ struct printk_message {
130130
};
131131

132132
bool other_cpu_in_panic(void);
133+
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
134+
bool is_extended, bool may_supress);
135+
136+
#ifdef CONFIG_PRINTK
137+
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped);
138+
#endif

kernel/printk/nbcon.c

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ void nbcon_seq_force(struct console *con, u64 seq)
221221
* nbcon_seq_force() was used or the current context no longer owns the
222222
* console. In the later case, it will stop printing anyway.
223223
*/
224-
__maybe_unused
225224
static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq)
226225
{
227226
unsigned long nbcon_seq = __seq_to_nbcon_seq(ctxt->seq);
@@ -755,7 +754,6 @@ static bool nbcon_context_can_proceed(struct nbcon_context *ctxt, struct nbcon_s
755754
*
756755
* Internal helper to avoid duplicated code.
757756
*/
758-
__maybe_unused
759757
static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsafe)
760758
{
761759
struct console *con = ctxt->console;
@@ -784,6 +782,110 @@ static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsaf
784782
return nbcon_context_can_proceed(ctxt, &cur);
785783
}
786784

785+
/**
786+
* nbcon_emit_next_record - Emit a record in the acquired context
787+
* @wctxt: The write context that will be handed to the write function
788+
*
789+
* Return: True if this context still owns the console. False if
790+
* ownership was handed over or taken.
791+
*
792+
* When this function returns false then the calling context no longer owns
793+
* the console and is no longer allowed to go forward. In this case it must
794+
* back out immediately and carefully. The buffer content is also no longer
795+
* trusted since it no longer belongs to the calling context. If the caller
796+
* wants to do more it must reacquire the console first.
797+
*
798+
* When true is returned, @wctxt->ctxt.backlog indicates whether there are
799+
* still records pending in the ringbuffer,
800+
*/
801+
__maybe_unused
802+
static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt)
803+
{
804+
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
805+
struct console *con = ctxt->console;
806+
bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
807+
struct printk_message pmsg = {
808+
.pbufs = ctxt->pbufs,
809+
};
810+
unsigned long con_dropped;
811+
struct nbcon_state cur;
812+
unsigned long dropped;
813+
bool done;
814+
815+
/*
816+
* The printk buffers are filled within an unsafe section. This
817+
* prevents NBCON_PRIO_NORMAL and NBCON_PRIO_EMERGENCY from
818+
* clobbering each other.
819+
*/
820+
821+
if (!nbcon_context_enter_unsafe(ctxt))
822+
return false;
823+
824+
ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, true);
825+
if (!ctxt->backlog)
826+
return nbcon_context_exit_unsafe(ctxt);
827+
828+
/*
829+
* @con->dropped is not protected in case of an unsafe hostile
830+
* takeover. In that situation the update can be racy so
831+
* annotate it accordingly.
832+
*/
833+
con_dropped = data_race(READ_ONCE(con->dropped));
834+
835+
dropped = con_dropped + pmsg.dropped;
836+
if (dropped && !is_extended)
837+
console_prepend_dropped(&pmsg, dropped);
838+
839+
if (!nbcon_context_exit_unsafe(ctxt))
840+
return false;
841+
842+
/* For skipped records just update seq/dropped in @con. */
843+
if (pmsg.outbuf_len == 0)
844+
goto update_con;
845+
846+
/* Initialize the write context for driver callbacks. */
847+
wctxt->outbuf = &pmsg.pbufs->outbuf[0];
848+
wctxt->len = pmsg.outbuf_len;
849+
nbcon_state_read(con, &cur);
850+
wctxt->unsafe_takeover = cur.unsafe_takeover;
851+
852+
if (con->write_atomic) {
853+
done = con->write_atomic(con, wctxt);
854+
} else {
855+
nbcon_context_release(ctxt);
856+
WARN_ON_ONCE(1);
857+
done = false;
858+
}
859+
860+
/* If not done, the emit was aborted. */
861+
if (!done)
862+
return false;
863+
864+
/*
865+
* Since any dropped message was successfully output, reset the
866+
* dropped count for the console.
867+
*/
868+
dropped = 0;
869+
update_con:
870+
/*
871+
* The dropped count and the sequence number are updated within an
872+
* unsafe section. This limits update races to the panic context and
873+
* allows the panic context to win.
874+
*/
875+
876+
if (!nbcon_context_enter_unsafe(ctxt))
877+
return false;
878+
879+
if (dropped != con_dropped) {
880+
/* Counterpart to the READ_ONCE() above. */
881+
WRITE_ONCE(con->dropped, dropped);
882+
}
883+
884+
nbcon_seq_try_update(ctxt, pmsg.seq + 1);
885+
886+
return nbcon_context_exit_unsafe(ctxt);
887+
}
888+
787889
/**
788890
* nbcon_alloc - Allocate buffers needed by the nbcon console
789891
* @con: Console to allocate buffers for

kernel/printk/printk.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -698,9 +698,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
698698
return len;
699699
}
700700

701-
static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
702-
bool is_extended, bool may_supress);
703-
704701
/* /dev/kmsg - userspace message inject/listen interface */
705702
struct devkmsg_user {
706703
atomic64_t seq;
@@ -2733,7 +2730,7 @@ static void __console_unlock(void)
27332730
* If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated.
27342731
*/
27352732
#ifdef CONFIG_PRINTK
2736-
static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped)
2733+
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped)
27372734
{
27382735
struct printk_buffers *pbufs = pmsg->pbufs;
27392736
const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf);
@@ -2787,8 +2784,8 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d
27872784
* of @pmsg are valid. (See the documentation of struct printk_message
27882785
* for information about the @pmsg fields.)
27892786
*/
2790-
static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
2791-
bool is_extended, bool may_suppress)
2787+
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
2788+
bool is_extended, bool may_suppress)
27922789
{
27932790
static int panic_console_dropped;
27942791

0 commit comments

Comments
 (0)