diff --git a/include/linux/console.h b/include/linux/console.h
index d2bcd2c190a7..a50eaa3b8420 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -244,6 +244,7 @@ struct printk_buffers;
  * @hostile:		Acquire console by hostile takeover
  * @takeover_unsafe:	Acquire console by hostile takeover even if unsafe
  * @pbufs:		Pointer to the text buffer for this context
+ * @seq:		The sequence number to print for this context
  */
 struct nbcon_context {
 	/* members set by caller */
@@ -256,6 +257,7 @@ struct nbcon_context {
 
 	/* members set by acquire */
 	struct printk_buffers	*pbufs;
+	u64			seq;
 };
 
 /**
@@ -279,6 +281,7 @@ struct nbcon_context {
  * @node:		hlist node for the console list
  *
  * @nbcon_state:	State for nbcon consoles
+ * @nbcon_seq:		Sequence number of the next record for nbcon to print
  * @pbufs:		Pointer to nbcon private buffer
  */
 struct console {
@@ -302,6 +305,7 @@ struct console {
 
 	/* nbcon console specific members */
 	atomic_t		__private nbcon_state;
+	atomic_long_t		__private nbcon_seq;
 	struct printk_buffers	*pbufs;
 };
 
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 0f2be350600e..1baccddf83b8 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -4,6 +4,7 @@
  */
 #include <linux/percpu.h>
 #include <linux/console.h>
+#include "printk_ringbuffer.h"
 
 #if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
 void __init printk_sysctl_init(void);
@@ -42,6 +43,7 @@ enum printk_info_flags {
 	LOG_CONT	= 8,	/* text is a fragment of a continuation line */
 };
 
+extern struct printk_ringbuffer *prb;
 extern bool have_legacy_console;
 extern bool have_boot_console;
 
@@ -72,6 +74,8 @@ void defer_console_output(void);
 u16 printk_parse_prefix(const char *text, int *level,
 			enum printk_info_flags *flags);
 
+u64 nbcon_seq_read(struct console *con);
+void nbcon_seq_force(struct console *con, u64 seq);
 bool nbcon_init(struct console *con);
 void nbcon_cleanup(struct console *con);
 
@@ -90,6 +94,8 @@ void nbcon_cleanup(struct console *con);
 #define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
 
 static inline bool printk_percpu_data_ready(void) { return false; }
+static inline u64 nbcon_seq_read(struct console *con) { return 0; }
+static inline void nbcon_seq_force(struct console *con, u64 seq) { }
 static inline bool nbcon_init(struct console *con) { return true; }
 static inline void nbcon_cleanup(struct console *con) { }
 
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 582552a96c57..64e404aacbc4 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -512,7 +512,7 @@ _DEFINE_PRINTKRB(printk_rb_static, CONFIG_LOG_BUF_SHIFT - PRB_AVGBITS,
 
 static struct printk_ringbuffer printk_rb_dynamic;
 
-static struct printk_ringbuffer *prb = &printk_rb_static;
+struct printk_ringbuffer *prb = &printk_rb_static;
 
 /*
  * We cannot access per-CPU data (e.g. per-CPU flush irq_work) before
@@ -3171,8 +3171,27 @@ void console_unblank(void)
  */
 void console_flush_on_panic(enum con_flush_mode mode)
 {
+	struct console *c;
 	bool handover;
-	u64 next_seq;
+	short flags;
+	int cookie;
+	u64 seq;
+
+	seq = prb_first_valid_seq(prb);
+
+	/*
+	 * Safely handle the atomic consoles before trying to flush any
+	 * legacy consoles.
+	 */
+	if (mode == CONSOLE_REPLAY_ALL) {
+		cookie = console_srcu_read_lock();
+		for_each_console_srcu(c) {
+			flags = console_srcu_read_flags(c);
+			if (flags & CON_NBCON)
+				nbcon_seq_force(c, seq);
+		}
+		console_srcu_read_unlock(cookie);
+	}
 
 	if (!serialized_printing)
 		return;
@@ -3195,12 +3214,6 @@ void console_flush_on_panic(enum con_flush_mode mode)
 	console_may_schedule = 0;
 
 	if (mode == CONSOLE_REPLAY_ALL) {
-		struct console *c;
-		int cookie;
-		u64 seq;
-
-		seq = prb_first_valid_seq(prb);
-
 		cookie = console_srcu_read_lock();
 		for_each_console_srcu(c) {
 			/*
@@ -3212,7 +3225,7 @@ void console_flush_on_panic(enum con_flush_mode mode)
 		console_srcu_read_unlock(cookie);
 	}
 
-	console_flush_all(false, &next_seq, &handover);
+	console_flush_all(false, &seq, &handover);
 }
 
 /*
@@ -3795,6 +3808,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 	struct console *c;
 	u64 last_diff = 0;
 	u64 printk_seq;
+	short flags;
 	bool locked;
 	int cookie;
 	u64 diff;
@@ -3821,6 +3835,9 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 		for_each_console_srcu(c) {
 			if (con && con != c)
 				continue;
+
+			flags = console_srcu_read_flags(c);
+
 			/*
 			 * If consoles are not usable, it cannot be expected
 			 * that they make forward progress, so only increment
@@ -3829,7 +3846,9 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 			if (!console_is_usable(c))
 				continue;
 
-			if (locked)
+			if (flags & CON_NBCON)
+				printk_seq = nbcon_seq_read(c);
+			else if (locked)
 				printk_seq = c->seq;
 			else
 				continue;
diff --git a/kernel/printk/printk_nbcon.c b/kernel/printk/printk_nbcon.c
index 39fa64891ec6..8229a0a00d5b 100644
--- a/kernel/printk/printk_nbcon.c
+++ b/kernel/printk/printk_nbcon.c
@@ -108,6 +108,116 @@ static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_sta
 	return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom);
 }
 
+/* Convert sequence from u64 to unsigned long. */
+static inline unsigned long __nbcon_seq_to_stored(u64 full_seq)
+{
+	/* On 32bit systems only the lower 32 bits are stored. */
+	return (unsigned long)full_seq;
+}
+
+/* Convert sequence from unsigned long to u64. */
+static inline u64 __nbcon_seq_to_full(unsigned long stored_seq)
+{
+#ifdef CONFIG_64BIT
+	return stored_seq;
+#else
+	u64 full_seq;
+	u64 rb_seq;
+
+	/*
+	 * The provided sequence is only the lower 32 bits of the ringbuffer
+	 * sequence. It needs to be expanded to 64bit. Get the next sequence
+	 * number from the ringbuffer and fold it.
+	 */
+	rb_seq = prb_next_seq(prb);
+	full_seq = rb_seq - ((u32)rb_seq - stored_seq);
+
+	return full_seq;
+#endif
+}
+
+/**
+ * nbcon_seq_init - Helper function to initialize the console sequence
+ * @con:	Console to work on
+ *
+ * Set @con->nbcon_seq to the starting record (specified with con->seq).
+ * If the starting record no longer exists, the oldest available record
+ * is chosen. This is because on 32bit systems only the lower 32 bits of
+ * the sequence number are stored. The upper 32 bits are derived from the
+ * sequence numbers available in the ringbuffer.
+ *
+ * For init only. Do not use for runtime updates.
+ */
+static void nbcon_seq_init(struct console *con)
+{
+	u64 seq = max_t(u64, con->seq, prb_first_valid_seq(prb));
+
+	atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), __nbcon_seq_to_stored(seq));
+
+	/* Clear con->seq since nbcon consoles use con->nbcon_seq instead. */
+	con->seq = 0;
+}
+
+/**
+ * nbcon_read_seq - Read the current console sequence
+ * @con:	Console to read the sequence of
+ *
+ * Return:	Sequence number of the next record to print on @con.
+ */
+u64 nbcon_seq_read(struct console *con)
+{
+	unsigned long seq = atomic_long_read(&ACCESS_PRIVATE(con, nbcon_seq));
+
+	return __nbcon_seq_to_full(seq);
+}
+
+/**
+ * nbcon_seq_force - Force console sequence to a specific value
+ * @con:	Console to work on
+ *
+ * Only to be used in extreme situations (such as panic with
+ * CONSOLE_REPLAY_ALL).
+ */
+void nbcon_seq_force(struct console *con, u64 seq)
+{
+	atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), __nbcon_seq_to_stored(seq));
+}
+
+/**
+ * nbcon_seq_try_update - Try to update the console sequence number
+ * @ctxt:	Pointer to an acquire context that contains
+ *		all information about the acquire mode
+ *
+ * Return:	True if the console sequence was updated, false otherwise.
+ *
+ * On 32bit the sequence in con->nbcon_seq is only the lower 32 bits.
+ * Therefore it must be expanded to 64bit upon a failed cmpxchg in
+ * order to correctly verify that the new sequence (ctxt->seq) is
+ * larger.
+ *
+ * In case of fail the console has been likely taken over. However, the
+ * caller must still assume it has ownership and decide how to proceed.
+ */
+__maybe_unused
+static bool nbcon_seq_try_update(struct nbcon_context *ctxt)
+{
+	struct console *con = ctxt->console;
+	u64 con_seq = nbcon_seq_read(con);
+
+	while (con_seq < ctxt->seq) {
+		unsigned long seq = __nbcon_seq_to_stored(con_seq);
+
+		if (atomic_long_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_seq), &seq,
+					    __nbcon_seq_to_stored(ctxt->seq))) {
+			return true;
+		}
+
+		/* Expand new @seq value for comparing. */
+		con_seq = __nbcon_seq_to_full(seq);
+	}
+	return false;
+}
+
 /**
  * nbcon_context_try_acquire_direct - Try to acquire directly
  * @ctxt:		The context of the caller
@@ -483,6 +593,9 @@ static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
 	else
 		ctxt->pbufs = con->pbufs;
 
+	/* Set the record sequence for this context to print. */
+	ctxt->seq = nbcon_seq_read(ctxt->console);
+
 	return true;
 }
 
@@ -554,6 +667,7 @@ bool nbcon_init(struct console *con)
 		return false;
 	}
 
+	nbcon_seq_init(con);
 	nbcon_state_set(con, &state);
 	return true;
 }
