There still can be a race after the commit b027e2298bd588
("tty: fix data race between tty_init_dev and flush of buf"),
if receive_buf call comes before tty initialization completes
in n_tty_open and tty->driver_data may be NULL.
CPU0CPU1
n_tty_open
tty_init_dev
tty_ldisc_unlock
schedule
flush_to_ldisc
n_tty_receive_buf
uart_flush_chars
uart_start
Extending ldisc semaphore lock in tty_init_dev till driver_data
initializes completely after tty->ops->open().
Signed-off-by: Zhang Yu
Signed-off-by: Li RongQing
---
we see this bug in centos 7.4, and think b027e2298bd588 can not
fix it, since driver_data is NULL;
the trace is that:
[exception RIP: uart_start+24]
RIP: 814908b8 RSP: 881c23ab7d18 RFLAGS: 00010282
RAX: RBX: 88182bafc400 RCX:
RDX: 00020001 RSI: 001d RDI: 88182bafc400
RBP: 881c23ab7d30 R8: 881fff956060 R9: 81b5dc20
R10: 5a07 R11: 0001 R12: 88182bafc400
R13: 881ff90c852d R14: 88182baff400 R15:
ORIG_RAX: CS: 0010 SS: 0018
#11 [881c23ab7d38] uart_flush_chars at 81490ade
#12 [881c23ab7d48] n_tty_receive_buf at 81478b1d
#13 [881c23ab7de0] flush_to_ldisc at 8147bdc4
#14 [881c23ab7e28] process_one_work at 8109e210
#15 [881c23ab7e70] worker_thread at 8109e69e
#16 [881c23ab7ec8] kthread at 810a62d1
#17 [881c23ab7f50] ret_from_fork at 8171d677
other one
PID: 922TASK: 881c7fbc1f40 CPU: 29 COMMAND: "agetty"
#0 [8818e9677a20] __schedule at 817114f2
#1 [8818e9677a78] preempt_schedule at 81711f4f
#2 [8818e9677a90] _raw_spin_unlock_irqrestore at 81713f7d
#3 [8818e9677aa8] __wake_up at 810b03c4
#4 [8818e9677ae0] n_tty_set_termios at 814770d3
#5 [8818e9677b10] n_tty_open at 814772ec`
drivers/staging/speakup/spk_ttyio.c | 1 +
drivers/tty/pty.c | 2 ++
drivers/tty/serdev/serdev-ttyport.c | 2 ++
drivers/tty/tty_io.c| 14 +++---
4 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/speakup/spk_ttyio.c
b/drivers/staging/speakup/spk_ttyio.c
index 979e3ae249c1..c31f08c98383 100644
--- a/drivers/staging/speakup/spk_ttyio.c
+++ b/drivers/staging/speakup/spk_ttyio.c
@@ -155,6 +155,7 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth
*synth)
else
ret = -ENODEV;
+ tty_ldisc_unlock(tty);
if (ret) {
tty_unlock(tty);
return ret;
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 00099a8439d2..1b9684d4f718 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -873,9 +873,11 @@ static int ptmx_open(struct inode *inode, struct file
*filp)
tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
+ tty_ldisc_unlock(tty);
tty_unlock(tty);
return 0;
err_release:
+ tty_ldisc_unlock(tty);
tty_unlock(tty);
// This will also put-ref the fsi
tty_release(inode, filp);
diff --git a/drivers/tty/serdev/serdev-ttyport.c
b/drivers/tty/serdev/serdev-ttyport.c
index fa1672993b4c..ce16cb303e28 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -123,6 +123,7 @@ static int ttyport_open(struct serdev_controller *ctrl)
if (ret)
goto err_close;
+ tty_ldisc_unlock(tty);
tty_unlock(serport->tty);
/* Bring the UART into a known 8 bits no parity hw fc state */
@@ -145,6 +146,7 @@ static int ttyport_open(struct serdev_controller *ctrl)
err_close:
tty->ops->close(tty, NULL);
err_unlock:
+ tty_ldisc_unlock(tty);
tty_unlock(tty);
tty_release_struct(tty, serport->tty_idx);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 687250ec8032..199f45e2e1b1 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1351,7 +1351,6 @@ struct tty_struct *tty_init_dev(struct tty_driver
*driver, int idx)
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto err_release_tty;
- tty_ldisc_unlock(tty);
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
@@ -1926,7 +1925,7 @@ EXPORT_SYMBOL_GPL(tty_kopen);
* - concurrent tty removal from driver table
*/
static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
-struct file *filp)
+struct file *filp, bool *unlock)
{
struct