Re: [PATCH] serial: Fail TIOCMGET/SET if port is suspended

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Gabriel,

On 11/11/2015 09:18 AM, Peter Hurley wrote:
> On 11/11/2015 08:15 AM, Gabriel Krisman Bertazi wrote:
>> When a PCI error is detected, reset code in 8250_pci.c suspends the uart
>> ports to block IO while recovery procedure is performed.  Nevertheless,
>> TIOCMGET and TIOCMSET ioctls are still able to reach the device even
>> when it is suspended, which might cause EEH recovery of serial adapters
>> to fail on Power systems.  This patch blocks any TIOCMGET/TIOCMSET calls
>> from reaching the device if the port is suspended, to avoid racing with
>> the recovery procedure.
> 
> This really should be handled by the 8250_pci sub-driver ticking
> TTY_IO_ERROR so that almost all tty operations fail (a minor bug fix
> to properly order the clearing of ASYNC_INITIALIZED w/ setting TTY_IO_ERROR
> in uart_shutdown() would be req'd).
> 
> Unfortunately the machinery to do that doesn't exist.
> I could trivially add a means given a uart_port ptr but the difficulty
> of _safely_ getting a uart_port from line index still remains;
> serial8250_get_port() should have just added a full ref count mechanism.

Re-checking the 8250_pci sub-driver, I see it goes right through
serial8250_suspend_port() which assumes the 8250 port will not be unregistered
concurrently, so I assume the PCI subsystem serializes i/o error handling
with device removal, in which case the translation from
line => uart_8250_port => uart_port is straightforward.

Below is the serial core machinery that can be used to tick TTY_IO_ERROR.

Regards,
Peter Hurley

--- >% ---
Subject: [PATCH] serial: core: Add notification interface for i/o errors

Add uart_handle_io_error() for notifying tty core the device has
an error which prevents i/o, or the error has cleared and i/o can
resume.

Care is taken to preserve the tty error state if the device error
occurs or is cleared during port startup

Signed-off-by: Peter Hurley <peter@xxxxxxxxxxxxxxxxxx>
---
 drivers/tty/serial/serial_core.c | 40 ++++++++++++++++++++++++++++++++++++++--
 include/linux/serial_core.h      |  3 +++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 418587f..34bcca0 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -195,6 +195,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
 static int uart_startup(struct tty_struct *tty, struct uart_state *state,
 		int init_hw)
 {
+	struct uart_port *uport = state->uart_port;
 	struct tty_port *port = &state->port;
 	int retval;
 
@@ -209,8 +210,11 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state,
 
 	retval = uart_port_startup(tty, state, init_hw);
 	if (!retval) {
+		spin_lock_irq(&uport->lock);
 		set_bit(ASYNCB_INITIALIZED, &port->flags);
-		clear_bit(TTY_IO_ERROR, &tty->flags);
+		if (~uport->status & UPSTAT_IO_ERROR)
+			clear_bit(TTY_IO_ERROR, &tty->flags);
+		spin_unlock_irq(&uport->lock);
 	} else if (retval > 0)
 		retval = 0;
 
@@ -226,6 +230,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
 {
 	struct uart_port *uport = state->uart_port;
 	struct tty_port *port = &state->port;
+	int initialized;
+
+	initialized = test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags);
 
 	/*
 	 * Set the TTY IO error marker
@@ -233,7 +240,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
 	if (tty)
 		set_bit(TTY_IO_ERROR, &tty->flags);
 
-	if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) {
+	if (initialized) {
 		/*
 		 * Turn off DTR and RTS early.
 		 */
@@ -2839,6 +2846,35 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2)
 EXPORT_SYMBOL(uart_match_port);
 
 /**
+ *	uart_handle_io_error - report device io error status change
+ *	@uport: uart_port structure for the open port
+ *	@status: io error status, nonzero if error
+ *
+ *	Caller must hold uport->lock
+ */
+
+void uart_handle_io_error(struct uart_port *uport, unsigned int status)
+{
+	struct tty_port *port = &uport->state->port;
+	struct tty_struct *tty = port->tty;
+
+	lockdep_assert_held_once(&uport->lock);
+
+	if (status)
+		uport->status |= UPSTAT_IO_ERROR;
+	else
+		uport->status &= ~UPSTAT_IO_ERROR;
+
+	if (tty) {
+		if (status)
+			set_bit(TTY_IO_ERROR, &tty->flags);
+		else if (port->flags & ASYNC_INITIALIZED)
+			clear_bit(TTY_IO_ERROR, &tty->flags);
+	}
+}
+EXPORT_SYMBOL_GPL(uart_handle_io_error);
+
+/**
  *	uart_handle_dcd_change - handle a change of carrier detect state
  *	@uport: uart_port structure for the open port
  *	@status: new carrier detect status, nonzero if active
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 297d4fa..1969ca2 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -227,6 +227,7 @@ struct uart_port {
 #define UPSTAT_AUTORTS		((__force upstat_t) (1 << 2))
 #define UPSTAT_AUTOCTS		((__force upstat_t) (1 << 3))
 #define UPSTAT_AUTOXOFF		((__force upstat_t) (1 << 4))
+#define UPSTAT_IO_ERROR		((__force upstat_t) (1 << 5))
 
 	int			hw_stopped;		/* sw-assisted CTS flow state */
 	unsigned int		mctrl;			/* current modem ctrl settings */
@@ -418,6 +419,8 @@ static inline bool uart_softcts_mode(struct uart_port *uport)
  * The following are helper functions for the low level drivers.
  */
 
+extern void uart_handle_io_error(struct uart_port *uport,
+				 unsigned int status);
 extern void uart_handle_dcd_change(struct uart_port *uport,
 		unsigned int status);
 extern void uart_handle_cts_change(struct uart_port *uport,
-- 
2.6.3


--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux