Skip to contentSkip to conversation

serial core fixes

PATCH In review Peter Hurley 3 participants 15 messages Oct 16, 2014 – Nov 6, 2014

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

cover serial core fixes

Hi Greg,

The first 3 patches are picked out from the closing series that was split up; the first 2 patches lay the groundwork for the fixing the locking for the ->set_termios() method.

The 2 UPF_*/ASYNC* patches are to bind the definitions together since userspace only gets one set of definitions, but the code operates as if the definitions are synch'd.

The remainder except the last patch are cleanups; the last patch fixes port count mismatch in uart_open()/uart_close(), which Geert and I discussed a while back. So one less thing on my todo list.

Still on the todo list:
1. Some tty_port flags usage is non-atomic.
2. uart_remove_one_port will happily remove an in-use port since there's no reference counting.
3. fixing autoRTS for omap (so it clears RTS when tiocmset drops RTS)

Regards,

Peter Hurley (10):
serial: Refactor uart_flush_buffer() from uart_close()
serial: core: Flush ldisc after dropping port mutex in uart_close()
serial: Fix locking for uart driver set_termios() method tty,serial: Unify UPF_* and ASYNC_* flag definitions
tty: Document defunct ASYNC_* bits in uapi header
serial: core: Unwrap >80 char line in uart_close()
serial: core: Remove redundant timeout assignments
serial: core: Colocate crucial structure linkage
serial: core: Remove extra locking in uart_write()
serial: core: Fix port count when uart_open() errors

 Documentation/serial/driver       |  6 +++--
 drivers/tty/serial/amba-pl011.c   |  1 +
 drivers/tty/serial/atmel_serial.c | 28 +++++++++++----------
 drivers/tty/serial/serial-tegra.c | 30 +++++++++++-----------
 drivers/tty/serial/serial_core.c  | 52 ++++++++++++++++++++++-----------------
 drivers/tty/serial/timbuart.c     |  2 ++
 include/linux/serial_core.h       | 47 ++++++++++++++++++++++++-----------
 include/uapi/linux/tty_flags.h    | 15 +++++++----
 8 files changed, 110 insertions(+), 71 deletions(-)
Signature · 1 line
2.1.1
#1 lore raw

PATCH 1/10 serial: Refactor uart_flush_buffer() from uart_close()

In the context of the final tty & port close, flushing the tx ring buffer after the hardware has already been shutdown and the ring buffer freed is neither required nor desirable.

uart_flush_buffer() performs 3 operations:
1. Resets tx ring buffer indices, but the tx ring buffer has already been freed and the indices are reset if the port is re-opened.
2. Calls uart driver's flush_buffer() method
5 in-tree uart drivers define flush_buffer() methods:
amba-pl011, atmel-serial, imx, serial-tegra, timbuart
These have been refactored into the shutdown() method, if required.
3. Kicks the ldisc for more writing, but this is undesirable. The file handle is being released; any waiting writer will will be kicked out by tty_release() with a warning. Further, the N_TTY ldisc may generate SIGIO for a file handle which is no longer valid.

Cc Nicolas Ferre <[email protected]>
Cc Russell King <[email protected]>
Cc Laxman Dewangan <[email protected]>
---
 drivers/tty/serial/amba-pl011.c   |  1 +
 drivers/tty/serial/atmel_serial.c | 28 +++++++++++++++-------------
 drivers/tty/serial/serial-tegra.c | 30 ++++++++++++++++--------------
 drivers/tty/serial/serial_core.c  |  1 -
 drivers/tty/serial/timbuart.c     |  2 ++
 5 files changed, 34 insertions(+), 28 deletions(-)
drivers/tty/serial/amba-pl011.c +1 -0
index 8572f2a..5b82d7a 100644
@@ -1674,6 +1674,7 @@ static void pl011_shutdown(struct uart_port *port)
 			plat->exit();
 	}
 
+	pl011_dma_flush_buffer(port);
 }
 
 static void
drivers/tty/serial/atmel_serial.c +15 -13
index d7d4584..8b13d4d 100644
@@ -1801,6 +1801,20 @@ free_irq:
 }
 
 /*
+ * Flush any TX data submitted for DMA. Called when the TX circular
+ * buffer is reset.
+ */
+static void atmel_flush_buffer(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_pdc_tx(port)) {
+		UART_PUT_TCR(port, 0);
+		atmel_port->pdc_tx.ofs = 0;
+	}
+}
+
+/*
  * Disable the port
  */
 static void atmel_shutdown(struct uart_port *port)
@@ -1851,20 +1865,8 @@ static void atmel_shutdown(struct uart_port *port)
 	atmel_free_gpio_irq(port);
 
 	atmel_port->ms_irq_enabled = false;
-}
 
-/*
- * Flush any TX data submitted for DMA. Called when the TX circular
- * buffer is reset.
- */
-static void atmel_flush_buffer(struct uart_port *port)
-{
-	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
-
-	if (atmel_use_pdc_tx(port)) {
-		UART_PUT_TCR(port, 0);
-		atmel_port->pdc_tx.ofs = 0;
-	}
+	atmel_flush_buffer(port);
 }
 
 /*
drivers/tty/serial/serial-tegra.c +16 -14
index 53d7c31..78a5cf6 100644
@@ -1034,6 +1034,20 @@ fail_rx_dma:
 	return ret;
 }
 
+/*
+ * Flush any TX data submitted for DMA and PIO. Called when the
+ * TX circular buffer is reset.
+ */
+static void tegra_uart_flush_buffer(struct uart_port *u)
+{
+	struct tegra_uart_port *tup = to_tegra_uport(u);
+
+	tup->tx_bytes = 0;
+	if (tup->tx_dma_chan)
+		dmaengine_terminate_all(tup->tx_dma_chan);
+	return;
+}
+
 static void tegra_uart_shutdown(struct uart_port *u)
 {
 	struct tegra_uart_port *tup = to_tegra_uport(u);
@@ -1046,6 +1060,8 @@ static void tegra_uart_shutdown(struct uart_port *u)
 	tegra_uart_dma_channel_free(tup, true);
 	tegra_uart_dma_channel_free(tup, false);
 	free_irq(u->irq, tup);
+
+	tegra_uart_flush_buffer(u);
 }
 
 static void tegra_uart_enable_ms(struct uart_port *u)
@@ -1174,20 +1190,6 @@ static void tegra_uart_set_termios(struct uart_port *u,
 	return;
 }
 
-/*
- * Flush any TX data submitted for DMA and PIO. Called when the
- * TX circular buffer is reset.
- */
-static void tegra_uart_flush_buffer(struct uart_port *u)
-{
-	struct tegra_uart_port *tup = to_tegra_uport(u);
-
-	tup->tx_bytes = 0;
-	if (tup->tx_dma_chan)
-		dmaengine_terminate_all(tup->tx_dma_chan);
-	return;
-}
-
 static const char *tegra_uart_type(struct uart_port *u)
 {
 	return TEGRA_UART_TYPE;
drivers/tty/serial/serial_core.c +0 -1
index 221ca6f..ce0e762 100644
@@ -1360,7 +1360,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 
 	mutex_lock(&port->mutex);
 	uart_shutdown(tty, state);
-	uart_flush_buffer(tty);
 
 	tty_ldisc_flush(tty);
 
drivers/tty/serial/timbuart.c +2 -0
index 0d11d50..e9e2523 100644
@@ -273,6 +273,8 @@ static void timbuart_shutdown(struct uart_port *port)
 	dev_dbg(port->dev, "%s\n", __func__);
 	free_irq(port->irq, uart);
 	iowrite32(0, port->membase + TIMBUART_IER);
+
+	timbuart_flush_buffer(port);
 }
 
 static int get_bindex(int baud)
Signature · 1 line
2.1.1
#2 lore raw

PATCH 2/10 serial: core: Flush ldisc after dropping port mutex in uart_close()

In reply to Peter Hurley

The tty buffers (and any line discipline buffers) must be flushed after the UART hardware has shutdown; otherwise, a racing open on the same tty may receive data from the previous session, which is a security hazard. However, holding the port mutex while flushing the line discipline buffers creates a lock inversion if the set_termios() handler takes the port mutex (as it does in the followup patch, 'serial: Fix locking for uart driver set_termios method'.

Flush the ldisc buffers after dropping the port mutex; the tty lock is still held which prevents a concurrent open() from advancing while flushing. Since no new rx data is possible after uart_shutdown() until a new open reinitializes the port, the later flush has no impact on what data is being discarded.

---
 drivers/tty/serial/serial_core.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/tty/serial/serial_core.c +2 -3
index ce0e762..6203c6c 100644
@@ -1360,9 +1360,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 
 	mutex_lock(&port->mutex);
 	uart_shutdown(tty, state);
-
-	tty_ldisc_flush(tty);
-
 	tty_port_tty_set(port, NULL);
 	tty->closing = 0;
 	spin_lock_irqsave(&port->lock, flags);
@@ -1389,6 +1386,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	wake_up_interruptible(&port->close_wait);
 
 	mutex_unlock(&port->mutex);
+
+	tty_ldisc_flush(tty);
 }
 
 static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
Signature · 1 line
2.1.1
#3 lore raw

PATCH 3/10 serial: Fix locking for uart driver set_termios() method

In reply to Peter Hurley

The low-level uart driver may modify termios settings to override settings that are not compatible with the uart, such as CRTSCTS. Thus, callers of the low-level uart driver's set_termios() method must hold termios_rwsem write lock to prevent concurrent access to termios, in case such override occurs.

The termios_rwsem lock requirement does not extend to console setup (ie., uart_set_options), as console setup cannot race with tty operations. Nor does this lock requirement extend to functions which cannot be concurrent with tty ioctls (ie., uart_port_startup() and uart_resume_port()).

Further, always claim the port mutex to protect hardware re-reprogramming in the set_termios() uart driver method. Note this is unnecessary for console initialization in uart_set_options() which cannot be concurrent with other uart operations.

---
 Documentation/serial/driver      | 6 ++++--
 drivers/tty/serial/serial_core.c | 8 +++++++-
 2 files changed, 11 insertions(+), 3 deletions(-)
Documentation/serial/driver +4 -2
index ba64e4b..c415b0e 100644
@@ -59,7 +59,9 @@ The core driver uses the info->tmpbuf_sem lock to prevent multi-threaded
 access to the info->tmpbuf bouncebuffer used for port writes.
 
 The port_sem semaphore is used to protect against ports being added/
-removed or reconfigured at inappropriate times.
+removed or reconfigured at inappropriate times. Since v2.6.27, this
+semaphore has been the 'mutex' member of the tty_port struct, and
+commonly referred to as the port mutex (or port->mutex).
 
 
 uart_ops
@@ -248,7 +250,7 @@ hardware.
 	Other flags may be used (eg, xon/xoff characters) if your
 	hardware supports hardware "soft" flow control.
 
-	Locking: none.
+	Locking: caller holds port->mutex
 	Interrupts: caller dependent.
 	This call must not sleep
 
drivers/tty/serial/serial_core.c +7 -1
index 6203c6c..6ebe78c 100644
@@ -436,7 +436,7 @@ uart_get_divisor(struct uart_port *port, unsigned int baud)
 
 EXPORT_SYMBOL(uart_get_divisor);
 
-/* FIXME: Consistent locking policy */
+/* Caller holds port mutex */
 static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
 					struct ktermios *old_termios)
 {
@@ -1172,11 +1172,15 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd,
 		break;
 
 	case TIOCSSERIAL:
+		down_write(&tty->termios_rwsem);
 		ret = uart_set_info_user(tty, state, uarg);
+		up_write(&tty->termios_rwsem);
 		break;
 
 	case TIOCSERCONFIG:
+		down_write(&tty->termios_rwsem);
 		ret = uart_do_autoconfig(tty, state);
+		up_write(&tty->termios_rwsem);
 		break;
 
 	case TIOCSERGWILD: /* obsolete */
@@ -1277,7 +1281,9 @@ static void uart_set_termios(struct tty_struct *tty,
 		return;
 	}
 
+	mutex_lock(&state->port.mutex);
 	uart_change_speed(tty, state, old_termios);
+	mutex_unlock(&state->port.mutex);
 	/* reload cflag from termios; port driver may have overriden flags */
 	cflag = tty->termios.c_cflag;
 
Signature · 1 line
2.1.1
#4 lore raw

PATCH 4/10 tty,serial: Unify UPF_* and ASYNC_* flag definitions

In reply to Peter Hurley

The userspace-defined ASYNC_* flags in include/uapi/linux/tty_flags.h are the authoritative bit definitions for the serial_struct flags, and thus for any derivative values or fields.

Although the serial core provides the TIOCSSERIAL and TIOCGSERIAL ioctls to set and retrieve these flags from userspace, it defines these bits independently, as UPF_* macros.

Define the UPF_* macros which are userspace-modifiable directly from the ASYNC_* symbolic constants. Add compile-time test to ensure the bits changeable by TIOCSSERIAL match the defined range in the uapi header.

Add ASYNCB_MAGIC_MULTIPLIER to the uapi header since this bit is programmable by userspace.

---
 include/linux/serial_core.h    | 47 ++++++++++++++++++++++++++++--------------
 include/uapi/linux/tty_flags.h |  6 +++++-
 2 files changed, 37 insertions(+), 16 deletions(-)
include/linux/serial_core.h +32 -15
index bb828a5..ab7180e 100644
@@ -156,21 +156,33 @@ struct uart_port {
 	/* flags must be updated while holding port mutex */
 	upf_t			flags;
 
-#define UPF_FOURPORT		((__force upf_t) (1 << 1))
-#define UPF_SAK			((__force upf_t) (1 << 2))
-#define UPF_SPD_MASK		((__force upf_t) (0x1030))
-#define UPF_SPD_HI		((__force upf_t) (0x0010))
-#define UPF_SPD_VHI		((__force upf_t) (0x0020))
-#define UPF_SPD_CUST		((__force upf_t) (0x0030))
-#define UPF_SPD_SHI		((__force upf_t) (0x1000))
-#define UPF_SPD_WARP		((__force upf_t) (0x1010))
-#define UPF_SKIP_TEST		((__force upf_t) (1 << 6))
-#define UPF_AUTO_IRQ		((__force upf_t) (1 << 7))
-#define UPF_HARDPPS_CD		((__force upf_t) (1 << 11))
-#define UPF_LOW_LATENCY		((__force upf_t) (1 << 13))
-#define UPF_BUGGY_UART		((__force upf_t) (1 << 14))
+	/*
+	 * These flags must be equivalent to the flags defined in
+	 * include/uapi/linux/tty_flags.h which are the userspace definitions
+	 * assigned from the serial_struct flags in uart_set_info()
+	 * [for bit definitions in the UPF_CHANGE_MASK]
+	 *
+	 * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable
+	 * except bit 15 (UPF_NO_TXEN_TEST) which is masked off.
+	 * The remaining bits are serial-core specific and not modifiable by
+	 * userspace.
+	 */
+#define UPF_FOURPORT		((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
+#define UPF_SAK			((__force upf_t) ASYNC_SAK            /* 2  */ )
+#define UPF_SPD_HI		((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
+#define UPF_SPD_VHI		((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
+#define UPF_SPD_CUST		((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
+#define UPF_SPD_WARP		((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
+#define UPF_SPD_MASK		((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
+#define UPF_SKIP_TEST		((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
+#define UPF_AUTO_IRQ		((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
+#define UPF_HARDPPS_CD		((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
+#define UPF_SPD_SHI		((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
+#define UPF_LOW_LATENCY		((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
+#define UPF_BUGGY_UART		((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
 #define UPF_NO_TXEN_TEST	((__force upf_t) (1 << 15))
-#define UPF_MAGIC_MULTIPLIER	((__force upf_t) (1 << 16))
+#define UPF_MAGIC_MULTIPLIER	((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
+
 /* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
 #define UPF_HARD_FLOW		((__force upf_t) (1 << 21))
 /* Port has hardware-assisted s/w flow control */
@@ -186,9 +198,14 @@ struct uart_port {
 #define UPF_DEAD		((__force upf_t) (1 << 30))
 #define UPF_IOREMAP		((__force upf_t) (1 << 31))
 
-#define UPF_CHANGE_MASK		((__force upf_t) (0x17fff))
+#define __UPF_CHANGE_MASK	0x17fff
+#define UPF_CHANGE_MASK		((__force upf_t) __UPF_CHANGE_MASK)
 #define UPF_USR_MASK		((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))
 
+#if __UPF_CHANGE_MASK > ASYNC_FLAGS
+#error Change mask not equivalent to userspace-visible bit defines
+#endif
+
 	/* status must be updated while holding port lock */
 	upstat_t		status;
 
include/uapi/linux/tty_flags.h +5 -1
index eefcb48..7b516f7 100644
@@ -6,6 +6,8 @@
  * shared by the tty_port flags structures.
  *
  * Define ASYNCB_* for convenient use with {test,set,clear}_bit.
+ *
+ * Bits [0..ASYNCB_LAST_USER] are userspace defined/visible/changeable
  */
 #define ASYNCB_HUP_NOTIFY	 0 /* Notify getty on hangups and closes
 				    * on the callout port */
@@ -26,7 +28,8 @@
 #define ASYNCB_BUGGY_UART	14 /* This is a buggy UART, skip some safety
 				    * checks.  Note: can be dangerous! */
 #define ASYNCB_AUTOPROBE	15 /* Port was autoprobed by PCI or PNP code */
-#define ASYNCB_LAST_USER	15
+#define ASYNCB_MAGIC_MULTIPLIER	16 /* Use special CLK or divisor */
+#define ASYNCB_LAST_USER	16
 
 /* Internal flags used only by kernel */
 #define ASYNCB_INITIALIZED	31 /* Serial port was initialized */
@@ -57,6 +60,7 @@
 #define ASYNC_LOW_LATENCY	(1U << ASYNCB_LOW_LATENCY)
 #define ASYNC_BUGGY_UART	(1U << ASYNCB_BUGGY_UART)
 #define ASYNC_AUTOPROBE		(1U << ASYNCB_AUTOPROBE)
+#define ASYNC_MAGIC_MULTIPLIER	(1U << ASYNCB_MAGIC_MULTIPLIER)
 
 #define ASYNC_FLAGS		((1U << (ASYNCB_LAST_USER + 1)) - 1)
 #define ASYNC_USR_MASK		(ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \
Signature · 1 line
2.1.1
#5 lore raw

PATCH 5/10 tty: Document defunct ASYNC_* bits in uapi header

In reply to Peter Hurley

Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls.

---
 include/uapi/linux/tty_flags.h | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
include/uapi/linux/tty_flags.h +5 -4
index 7b516f7..4e021e3 100644
@@ -8,6 +8,7 @@
  * Define ASYNCB_* for convenient use with {test,set,clear}_bit.
  *
  * Bits [0..ASYNCB_LAST_USER] are userspace defined/visible/changeable
+ * [x] in the bit comments indicates the flag is defunct and no longer used.
  */
 #define ASYNCB_HUP_NOTIFY	 0 /* Notify getty on hangups and closes
 				    * on the callout port */
@@ -19,15 +20,15 @@
 #define ASYNCB_SKIP_TEST	 6 /* Skip UART test during autoconfiguration */
 #define ASYNCB_AUTO_IRQ		 7 /* Do automatic IRQ during
 				    * autoconfiguration */
-#define ASYNCB_SESSION_LOCKOUT	 8 /* Lock out cua opens based on session */
-#define ASYNCB_PGRP_LOCKOUT	 9 /* Lock out cua opens based on pgrp */
-#define ASYNCB_CALLOUT_NOHUP	10 /* Don't do hangups for cua device */
+#define ASYNCB_SESSION_LOCKOUT	 8 /* [x] Lock out cua opens based on session */
+#define ASYNCB_PGRP_LOCKOUT	 9 /* [x] Lock out cua opens based on pgrp */
+#define ASYNCB_CALLOUT_NOHUP	10 /* [x] Don't do hangups for cua device */
 #define ASYNCB_HARDPPS_CD	11 /* Call hardpps when CD goes high  */
 #define ASYNCB_SPD_SHI		12 /* Use 230400 instead of 38400 bps */
 #define ASYNCB_LOW_LATENCY	13 /* Request low latency behaviour */
 #define ASYNCB_BUGGY_UART	14 /* This is a buggy UART, skip some safety
 				    * checks.  Note: can be dangerous! */
-#define ASYNCB_AUTOPROBE	15 /* Port was autoprobed by PCI or PNP code */
+#define ASYNCB_AUTOPROBE	15 /* [x] Port was autoprobed by PCI/PNP code */
 #define ASYNCB_MAGIC_MULTIPLIER	16 /* Use special CLK or divisor */
 #define ASYNCB_LAST_USER	16
 
Signature · 1 line
2.1.1
#6 lore raw

Re: [PATCH -next 05/10] tty: Document defunct ASYNC_* bits in uapi header

Quoted 3 lines · Peter Hurley

Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls.

Hello,

would it make sense to mark them deprecated somehow? At build time, or at least warn in the serial core that "current->comm is using a deprecated flag"_ratelimited()?

thanks,

Signature · 2 lines
js
suse labs
#7 lore raw

Re: [PATCH -next 05/10] tty: Document defunct ASYNC_* bits in uapi header

Hi Jiri,

Quoted 8 lines · Jiri Slaby
Quoted 3 lines · Peter Hurley

Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls.

Hello,

would it make sense to mark them deprecated somehow? At build time

A build warning when the macro is expanded would be best, but my c-preprocessor-fu is terrible, so I have no idea how to make that work.

Quoted 3 lines

or
at least warn in the serial core that "current->comm is using a deprecated flag"_ratelimited()?

If we just print the message at TIOCSSERIAL if any of the deprecated bits are set, that would be ok. Probably the only issue would be that setserial could cause this message at will, so log flooding would be a concern.

Regards,
Peter Hurley

#8 lore raw

Re: [PATCH -next 05/10] tty: Document defunct ASYNC_* bits in uapi header

Quoted 8 lines · Peter Hurley

Hi Jiri,

Quoted 5 lines · Jiri Slaby
Quoted 3 lines · Peter Hurley

Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls.

Quoted 6 lines
Quoted 3 lines

Hello,

would it make sense to mark them deprecated somehow? At build time

A build warning when the macro is expanded would be best, but my c-preprocessor-fu is terrible, so I have no idea how to make that work.

We can define a deprecated type like:

typedef __u32 __attribute__((deprecated)) depr_tty_t

and then do define:

#define ASYNCB_FLAG_1 (depr_tty_t)1
#define ASYNCB_FLAG_2 (depr_tty_t)2

Etc.

However, I do not know if it is desirable AND whether all compilers out there used in userspace support that attribute.

Quoted 8 lines
Quoted 3 lines

or
at least warn in the serial core that "current->comm is using a deprecated flag"_ratelimited()?

If we just print the message at TIOCSSERIAL if any of the deprecated bits are set, that would be ok. Probably the only issue would be that setserial could cause this message at will, so log flooding would be a concern.

That's why _ratelimited(). So something like the patch attached. But it needs to be put to a separate function, out of the line.

/me currently builds a kernel to see who, if anybody, uses the flags on a up-to-date system...

thanks,

Signature · 2 lines
js
suse labs

From 16064db6eb64663f8a15f449a7e3bc69ed885320 Mon Sep 17 00:00:00 2001
From: Jiri Slaby <[email protected]>
Date: Fri, 17 Oct 2014 11:05:02 +0200
Subject: [PATCH 1/1] tty: warn on deprecated flags

---
 drivers/tty/tty_io.c           | 16 ++++++++++++++++
 include/uapi/linux/tty_flags.h |  2 ++
 2 files changed, 18 insertions(+)
drivers/tty/tty_io.c +16 -0
index 8fbad3410c75..46b2b3b3555c 100644
@@ -2830,6 +2830,22 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 			break;
 		}
 		break;
+	case TIOCSSERIAL:
+		{
+			static DEFINE_RATELIMIT_STATE(depr_flags,
+					DEFAULT_RATELIMIT_INTERVAL,
+					DEFAULT_RATELIMIT_BURST);
+			int flags;
+			char comm[TASK_COMM_LEN];
+			if (!get_user(flags, (int __user *)arg) &&
+					(flags & ASYNC_DEPRECATED) &&
+					__ratelimit(&depr_flags)) {
+				pr_warning("%s is using deprecated flags: %.8x\n",
+						get_task_comm(comm, current),
+						flags & ASYNC_DEPRECATED);
+			}
+		}
+		break;
 	}
 	if (tty->ops->ioctl) {
 		retval = (tty->ops->ioctl)(tty, cmd, arg);
include/uapi/linux/tty_flags.h +2 -0
index eefcb483a2c0..879b91713e7c 100644
@@ -59,6 +59,8 @@
 #define ASYNC_AUTOPROBE		(1U << ASYNCB_AUTOPROBE)
 
 #define ASYNC_FLAGS		((1U << (ASYNCB_LAST_USER + 1)) - 1)
+#define ASYNC_DEPRECATED	(ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | \
+		ASYNC_CALLOUT_NOHUP | ASYNC_AUTOPROBE)
 #define ASYNC_USR_MASK		(ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \
 		ASYNC_LOW_LATENCY)
 #define ASYNC_SPD_CUST		(ASYNC_SPD_HI|ASYNC_SPD_VHI)
Signature · 1 line
2.1.1
#9 lore raw

Re: [PATCH -next 05/10] tty: Document defunct ASYNC_* bits in uapi header

Quoted 39 lines · Jiri Slaby
Quoted 8 lines · Peter Hurley

Hi Jiri,

Quoted 5 lines · Jiri Slaby
Quoted 3 lines · Peter Hurley

Note the serial_struct flags for which the kernel ignores and performs no action. The flags cannot be removed since they form part of the userspace interface via the TIOCSSERIAL/TIOCGSERIAL ioctls.

Quoted 6 lines
Quoted 3 lines

Hello,

would it make sense to mark them deprecated somehow? At build time

A build warning when the macro is expanded would be best, but my c-preprocessor-fu is terrible, so I have no idea how to make that work.

We can define a deprecated type like:

typedef __u32 __attribute__((deprecated)) depr_tty_t

and then do define:

#define ASYNCB_FLAG_1 (depr_tty_t)1
#define ASYNCB_FLAG_2 (depr_tty_t)2

Etc.

However, I do not know if it is desirable AND whether all compilers out there used in userspace support that attribute.

Quoted 8 lines
Quoted 3 lines

or
at least warn in the serial core that "current->comm is using a deprecated flag"_ratelimited()?

If we just print the message at TIOCSSERIAL if any of the deprecated bits are set, that would be ok. Probably the only issue would be that setserial could cause this message at will, so log flooding would be a concern.

Quoted 5 lines

That's why _ratelimited(). So something like the patch attached. But it needs to be put to a separate function, out of the line.

/me currently builds a kernel to see who, if anybody, uses the flags on a up-to-date system...

What ever happened with this test? Care to resend it as a "real" patch so we can annoy userspace? :)

thanks,

greg k-h

#10 lore raw

PATCH 6/10 serial: core: Unwrap >80 char line in uart_close()

In reply to Peter Hurley

The wrapped line looks wrong and out-of-place; leave it as

Quoted 1 line

80 char line.

---
 drivers/tty/serial/serial_core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/tty/serial/serial_core.c +1 -2
index 6ebe78c..be58916 100644
@@ -1373,8 +1373,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	if (port->blocked_open) {
 		spin_unlock_irqrestore(&port->lock, flags);
 		if (port->close_delay)
-			msleep_interruptible(
-					jiffies_to_msecs(port->close_delay));
+			msleep_interruptible(jiffies_to_msecs(port->close_delay));
 		spin_lock_irqsave(&port->lock, flags);
 	} else if (!uart_console(uport)) {
 		spin_unlock_irqrestore(&port->lock, flags);
Signature · 1 line
2.1.1
#11 lore raw

PATCH 7/10 serial: core: Remove redundant timeout assignments

In reply to Peter Hurley

tty_port_init() initializes close_delay and closing_wait to these same values; remove.

---
 drivers/tty/serial/serial_core.c | 2 --
 1 file changed, 2 deletions(-)
drivers/tty/serial/serial_core.c +0 -2
index be58916..676c03a 100644
@@ -2346,8 +2346,6 @@ int uart_register_driver(struct uart_driver *drv)
 
 		tty_port_init(port);
 		port->ops = &uart_port_ops;
-		port->close_delay     = HZ / 2;	/* .5 seconds */
-		port->closing_wait    = 30 * HZ;/* 30 seconds */
 	}
 
 	retval = tty_register_driver(normal);
Signature · 1 line
2.1.1
#12 lore raw

PATCH 8/10 serial: core: Colocate crucial structure linkage

In reply to Peter Hurley

The key function of uart_add_one_port() is to cross-reference the UART driver's port structure with the serial core's state table; keep the assignments together and document this crucial association.

---
 drivers/tty/serial/serial_core.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/tty/serial/serial_core.c +3 -2
index 676c03a..e04ae58 100644
@@ -2594,11 +2594,12 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 		goto out;
 	}
 
+	/* Link the port to the driver state table and vice versa */
 	state->uart_port = uport;
-	state->pm_state = UART_PM_STATE_UNDEFINED;
+	uport->state = state;
 
+	state->pm_state = UART_PM_STATE_UNDEFINED;
 	uport->cons = drv->cons;
-	uport->state = state;
 
 	/*
 	 * If this port is a console, then the spinlock is already
Signature · 1 line
2.1.1
#13 lore raw

PATCH 9/10 serial: core: Remove extra locking in uart_write()

In reply to Peter Hurley

uart_start() only claims the port->lock to call __uart_start(), which does the actual processing. Eliminate the extra acquire/release in uart_write(); call __uart_start() directly with port->lock already held.

---
 drivers/tty/serial/serial_core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/tty/serial/serial_core.c +2 -1
index e04ae58..6a3571e 100644
@@ -537,9 +537,10 @@ static int uart_write(struct tty_struct *tty,
 		count -= c;
 		ret += c;
 	}
+
+	__uart_start(tty);
 	spin_unlock_irqrestore(&port->lock, flags);
 
-	uart_start(tty);
 	return ret;
 }
 
Signature · 1 line
2.1.1
#14 lore raw

PATCH 10/10 serial: core: Fix port count when uart_open() errors

In reply to Peter Hurley

A port count mismatch occurs if mutex_lock_interruptible() exits uart_open() and the port has already been opened. This may prematurely close a port on an open tty. Since uart_close() is _always_ called if uart_open() fails, the port count must be corrected if errors occur.

Always increment the port count in uart_open(), regardless of errors; always decrement the port count in uart_close(). Note that tty_port_close_start() decrements the port count when uart_open() was successful.

---
 drivers/tty/serial/serial_core.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)
drivers/tty/serial/serial_core.c +15 -10
index 6a3571e..3f02c2b 100644
@@ -1337,8 +1337,16 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 	struct uart_port *uport;
 	unsigned long flags;
 
-	if (!state)
+	if (!state) {
+		struct uart_driver *drv = tty->driver->driver_state;
+
+		state = drv->state + tty->index;
+		port = &state->port;
+		spin_lock_irq(&port->lock);
+		--port->count;
+		spin_unlock_irq(&port->lock);
 		return;
+	}
 
 	uport = state->uart_port;
 	port = &state->port;
@@ -1555,6 +1563,10 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
 
 	pr_debug("uart_open(%d) called\n", line);
 
+	spin_lock_irq(&port->lock);
+	++port->count;
+	spin_unlock_irq(&port->lock);
+
 	/*
 	 * We take the semaphore here to guarantee that we won't be re-entered
 	 * while allocating the state structure, or while we request any IRQs
@@ -1567,17 +1579,11 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
 		goto end;
 	}
 
-	port->count++;
 	if (!state->uart_port || state->uart_port->flags & UPF_DEAD) {
 		retval = -ENXIO;
-		goto err_dec_count;
+		goto err_unlock;
 	}
 
-	/*
-	 * Once we set tty->driver_data here, we are guaranteed that
-	 * uart_close() will decrement the driver module use count.
-	 * Any failures from here onwards should not touch the count.
-	 */
 	tty->driver_data = state;
 	state->uart_port->state = state;
 	state->port.low_latency =
@@ -1598,8 +1604,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
 
 end:
 	return retval;
-err_dec_count:
-	port->count--;
+err_unlock:
 	mutex_unlock(&port->mutex);
 	goto end;
 }
Signature · 1 line
2.1.1