mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: John Ogness <john.ogness@linutronix.de>
Cc: "Sergey Senozhatsky" <senozhatsky@chromium.org>,
	"Steven Rostedt" <rostedt@goodmis.org>,
	"Marcos Paulo de Souza" <mpdesouza@suse.com>,
	"Samuel Thibault" <samuel.thibault@ens-lyon.org>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Jiri Slaby" <jirislaby@kernel.org>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
	"Hugo Villeneuve" <hvilleneuve@dimonoff.com>,
	"Fushuai Wang" <wangfushuai@baidu.com>,
	"Kees Cook" <kees@kernel.org>,
	"Stepan Ionichev" <sozdayvek@gmail.com>,
	linux-serial@vger.kernel.org,
	"Manuel Lauss" <manuel.lauss@gmail.com>,
	linux-kernel@vger.kernel.org, "Petr Mladek" <pmladek@suse.com>
Subject: [PATCH v2 1/1] braille: nbcon: Allow to use a serial console with NBCON API as Braille console
Date: Fri, 25 Sep 2026 16:17:29 +0200	[thread overview]
Message-ID: <20260925141729.173943-2-pmladek@suse.com> (raw)
In-Reply-To: <20260925141729.173943-1-pmladek@suse.com>

The Braille console is integrated with the virtual terminal (VT) and
writes its data using the legacy con->write() callback of the associated
serial console driver.

When the associated serial console driver gets converted to the NBCON API,
the braille write callback should use con->write_atomic() callback
with an appropriate locking.

It must be the atomic variant because it can be called under a spin_lock,
for example via:

  + kbd_event()
    + kbd_keycode()
      + atomic_notifier_call_chain(&keyboard_notifier_list)
	+ vt_notifier_call()
	  + vc_refresh()
	    + braille_write()

In addition, it can be called from printk() in any context via
the graphical tty driver (vt code) even when the Braille driver is not
in console_list directly.

The locking is inspired with nbcon_legacy_emit_next_record(),
nbcon_kdb_try_acquire()/release(), and the original serial driver locking:

  1. IRQs are explicitly disabled to prevent CPU migration and nested
     calls into the serial driver code.

  2. New nbcon_braille_try_acquire()/release() API allows to initialize
     the write context and acquire the ownership. It is using
     NBCON_PRIO_NORMAL because it competes only with the other operations
     on the serial console driver which are serialized using
     nbcon_device_try_acquire().

  3. It uses a busy loop until it acquires the ownership. Otherwise,
     the messages would get lost. [*]

  4. It does just the best effort when oops_in_progress is set.

Also, adjust __serial8250_console_write() in the 8250 serial driver to
exclude Braille consoles from the newline prepending logic. The serial
port is not used for standard printk logging which might be interrupted
in the middle of the operation. In the Braille mode, the serial driver
is supposed to write exactly what it gets. In fact, it does not print
any newlines at all in this case.

[*] The busy loop is not safe on PREEMPT_RT where the current owner
    might sleep. We will need another solution there.

Fixes: d3539347022a ("serial: 8250: Switch to nbcon console, take 2")
Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 .../accessibility/braille/braille_console.c   | 43 ++++++++++++-
 drivers/tty/serial/8250/8250_port.c           |  5 +-
 include/linux/console.h                       |  8 +++
 kernel/printk/nbcon.c                         | 64 +++++++++++++++++++
 4 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c
index 06b43b678d6e..bae177cb8cf9 100644
--- a/drivers/accessibility/braille/braille_console.c
+++ b/drivers/accessibility/braille/braille_console.c
@@ -62,14 +62,36 @@ static void braille_write(u16 *buf)
 {
 	static u16 lastwrite[WIDTH];
 	unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
+	struct nbcon_write_context wctxt = { };
+	unsigned long flags;
+	bool locked;
+
 	u16 out;
 	int i;
 
 	if (!braille_co)
 		return;
 
+	if (braille_co->flags & CON_NBCON) {
+		/*
+		 * Braille console might be called from unknown context via
+		 * vt_console_print() from console_unlock() from printk().
+		 * Use the atomic callback and synchronize it just using
+		 * the console context. Disable interrupts to prevent a nested
+		 * call into the driver code which might cause a deadlock when
+		 * trying to acquire the console ownership, see
+		 * __nbcon_atomic_flush_pending_con().
+		 */
+		local_irq_save(flags);
+		do {
+			locked = nbcon_braille_try_acquire(braille_co, &wctxt);
+			if (!locked)
+				cpu_relax();
+		} while (!locked && !oops_in_progress);
+	}
+
 	if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
-		return;
+		goto release_nbcon;
 	memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
 
 #define SOH 1
@@ -102,7 +124,24 @@ static void braille_write(u16 *buf)
 	*c++ = csum;
 	*c++ = ETX;
 
-	braille_co->write(braille_co, data, c - data);
+	if (braille_co->flags & CON_NBCON) {
+		if (braille_co->write_atomic &&
+		    !(braille_co->flags & CON_NBCON_ATOMIC_UNSAFE)) {
+			nbcon_write_context_set_buf(&wctxt, (char *)data, c - data);
+			braille_co->write_atomic(braille_co, &wctxt);
+		} else {
+			pr_warn_once("Braille requires a safe braille_co->write_atomic callback\n");
+		}
+	} else {
+		braille_co->write(braille_co, data, c - data);
+	}
+
+release_nbcon:
+	if (braille_co->flags & CON_NBCON) {
+		if (locked)
+			nbcon_braille_release(&wctxt);
+		local_irq_restore(flags);
+	}
 }
 
 /* Follow the VC cursor*/
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 38fa45e74a37..6eb0b439e433 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -3417,8 +3417,11 @@ static void __serial8250_console_write(struct uart_8250_port *up,
 	 * If the console printer did not fully output the previous line, it
 	 * must have been handed or taken over. Insert a newline in order to
 	 * maintain clean output.
+	 *
+	 * Braille consoles are an exception. The serial port is not used
+	 * for printk(). The driver is supposed to write exactly what it gets.
 	 */
-	if (!up->console_line_ended) {
+	if (unlikely(!up->console_line_ended && !nbcon_is_braille(wctxt))) {
 		if (use_fifo)
 			__serial8250_console_fifo_write(up, wctxt, "\n", 1);
 		else
diff --git a/include/linux/console.h b/include/linux/console.h
index 502d1abe3f50..d780f6de303a 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -615,6 +615,10 @@ extern bool nbcon_allow_unsafe_takeover(void);
 extern bool nbcon_kdb_try_acquire(struct console *con,
 				  struct nbcon_write_context *wctxt);
 extern void nbcon_kdb_release(struct nbcon_write_context *wctxt);
+extern bool nbcon_is_braille(struct nbcon_write_context *wctxt);
+extern bool nbcon_braille_try_acquire(struct console *con,
+				      struct nbcon_write_context *wctxt);
+extern void nbcon_braille_release(struct nbcon_write_context *wctxt);
 
 /*
  * Check if the given console is currently capable and allowed to print
@@ -678,8 +682,12 @@ static inline void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt) { }
 static inline bool nbcon_kdb_try_acquire(struct console *con,
 					 struct nbcon_write_context *wctxt) { return false; }
 static inline void nbcon_kdb_release(struct nbcon_write_context *wctxt) { }
+static inline bool nbcon_is_braille(struct nbcon_write_context *wctxt) { return false; }
 static inline bool console_is_usable(struct console *con, short flags,
 				     bool use_atomic) { return false; }
+static inline bool nbcon_braille_try_acquire(struct console *con,
+					     struct nbcon_write_context *wctxt) { return false; }
+static inline void nbcon_braille_release(struct nbcon_write_context *wctxt) { }
 #endif
 
 extern int console_set_on_cmdline;
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index d17704fe93ae..e5a0506330eb 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -1887,6 +1887,7 @@ bool nbcon_device_try_acquire(struct console *con)
 
 	memset(ctxt, 0, sizeof(*ctxt));
 	ctxt->console	= con;
+	/* Keep in sync with nbcon_braille_try_acquire(). */
 	ctxt->prio	= NBCON_PRIO_NORMAL;
 
 	if (!nbcon_context_try_acquire(ctxt, false))
@@ -2002,3 +2003,66 @@ void nbcon_kdb_release(struct nbcon_write_context *wctxt)
 	 */
 	__nbcon_atomic_flush_pending_con(ctxt->console, prb_next_reserve_seq(prb));
 }
+
+/**
+ * nbcon_is_braille - Checks whether the nbcon write context is using Braille console
+ *
+ * @wctxt:	checked nbcon write context
+ *
+ * Return: True when the write context is associated with a Braille console.
+ *	   Othrewise, return false.
+ *
+ * Context: Can be called in any context but only when Braille console is
+ *	registered and the struct console could not disappear.
+ */
+bool nbcon_is_braille(struct nbcon_write_context *wctxt)
+{
+	struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
+	struct console *con = ctxt->console;
+
+	return con && con->flags & CON_BRL;
+}
+
+/**
+ * nbcon_braille_try_acquire - Try to acquire nbcon console for braille_write()
+ *
+ * @con:	The nbcon console to acquire
+ * @wctxt:	The nbcon write context to be used on success
+ *
+ * Context:	braille_write() for emitting a single buffer on Braille console.
+ *
+ * Return:	True if the console was acquired. False otherwise.
+ *
+ * Braille console is not registered as a proper printk consoles. Instead,
+ * it is integrated with the graphical virtual terminal.
+ *
+ * This function is going to synchronize the Braille write against other
+ * operations on the used serial port. The port can be used also for a user
+ * input but printk() won't emit the messages there directly. It means
+ * the other operations will get synchronized using nbcon_device_try_acquire().
+ */
+bool nbcon_braille_try_acquire(struct console *con,
+			   struct nbcon_write_context *wctxt)
+{
+	struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
+
+	memset(ctxt, 0, sizeof(*ctxt));
+	ctxt->console = con;
+	/* Keep in sync with nbcon_device_try_acquire(). */
+	ctxt->prio = NBCON_PRIO_NORMAL;
+
+	return nbcon_context_try_acquire(ctxt, false);
+}
+
+/**
+ * nbcon_braille_release - Release the nbcon console
+ *
+ * @wctxt:	The nbcon write context initialized by a successful
+ *		nbcon_braille_try_acquire()
+ */
+void nbcon_braille_release(struct nbcon_write_context *wctxt)
+{
+	struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
+
+	nbcon_context_release(ctxt);
+}
-- 
2.55.0


      reply	other threads:[~2026-09-25 14:17 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-25 14:17 [PATCH v2 0/1] braille: nbcon: Fix Braille console for NBCON API Petr Mladek
2026-09-25 14:17 ` Petr Mladek [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260925141729.173943-2-pmladek@suse.com \
    --to=pmladek@suse.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=hvilleneuve@dimonoff.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=jirislaby@kernel.org \
    --cc=john.ogness@linutronix.de \
    --cc=kees@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=manuel.lauss@gmail.com \
    --cc=mpdesouza@suse.com \
    --cc=rostedt@goodmis.org \
    --cc=samuel.thibault@ens-lyon.org \
    --cc=senozhatsky@chromium.org \
    --cc=sozdayvek@gmail.com \
    --cc=wangfushuai@baidu.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®