Refactor the implementation of n_tty_write() into do_n_tty_write(),
and change n_tty_write() to call do_n_tty_write() after acquiring
tty.termios_rwsem.

This allows the n_tty line dicipline to write to a user tty via
do_n_tty_write() when already holding tty.termios_rwsem.

Signed-off-by: Walt Drummond <w...@drummond.us>
---
 drivers/tty/n_tty.c | 69 +++++++++++++++++++++++++++------------------
 include/linux/tty.h |  2 +-
 2 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 0ec93f1a61f5..64a058a4c63b 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2231,45 +2231,24 @@ static ssize_t n_tty_read(struct tty_struct *tty, 
struct file *file,
        return retval;
 }
 
-/**
- *     n_tty_write             -       write function for tty
- *     @tty: tty device
- *     @file: file object
- *     @buf: userspace buffer pointer
- *     @nr: size of I/O
- *
- *     Write function of the terminal device.  This is serialized with
- *     respect to other write callers but not to termios changes, reads
- *     and other such events.  Since the receive code will echo characters,
- *     thus calling driver write methods, the output_lock is used in
- *     the output processing functions called here as well as in the
- *     echo processing function to protect the column state and space
- *     left in the buffer.
- *
- *     This code must be sure never to sleep through a hangup.
- *
- *     Locking: output_lock to protect column state and space left
- *              (note that the process_output*() functions take this
- *               lock themselves)
- */
-
-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
-                          const unsigned char *buf, size_t nr)
+static ssize_t do_n_tty_write(struct tty_struct *tty, struct file *file,
+                             const unsigned char *buf, size_t nr)
 {
        const unsigned char *b = buf;
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
        int c;
        ssize_t retval = 0;
 
+       lockdep_assert_held_read(&tty->termios_rwsem);
+
        /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-       if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
+       if (L_TOSTOP(tty) &&
+           !(file && file->f_op->write_iter != redirected_tty_write)) {
                retval = tty_check_change(tty);
                if (retval)
                        return retval;
        }
 
-       down_read(&tty->termios_rwsem);
-
        /* Write out any echoed characters that are still pending */
        process_echoes(tty);
 
@@ -2336,10 +2315,44 @@ static ssize_t n_tty_write(struct tty_struct *tty, 
struct file *file,
        remove_wait_queue(&tty->write_wait, &wait);
        if (nr && tty->fasync)
                set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-       up_read(&tty->termios_rwsem);
+
        return (b - buf) ? b - buf : retval;
 }
 
+/**
+ *     n_tty_write             -       write function for tty
+ *     @tty: tty device
+ *     @file: file object
+ *     @buf: userspace buffer pointer
+ *     @nr: size of I/O
+ *
+ *     Write function of the terminal device.  This is serialized with
+ *     respect to other write callers but not to termios changes, reads
+ *     and other such events.  Since the receive code will echo characters,
+ *     thus calling driver write methods, the output_lock is used in
+ *     the output processing functions called here as well as in the
+ *     echo processing function to protect the column state and space
+ *     left in the buffer.
+ *
+ *     This code must be sure never to sleep through a hangup.
+ *
+ *     Locking: output_lock to protect column state and space left
+ *              (note that the process_output*() functions take this
+ *               lock themselves)
+ */
+
+static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
+                          const unsigned char *buf, size_t nr)
+{
+       ssize_t retval = 0;
+
+       down_read(&tty->termios_rwsem);
+       retval = do_n_tty_write(tty, file, buf, nr);
+       up_read(&tty->termios_rwsem);
+
+       return retval;
+}
+
 /**
  *     n_tty_poll              -       poll method for N_TTY
  *     @tty: terminal device
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 168e57e40bbb..cbe5d535a69d 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -237,7 +237,7 @@ struct tty_file_private {
 
 static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
 {
-       return file->f_flags & O_NONBLOCK ||
+       return (file && file->f_flags & O_NONBLOCK) ||
                test_bit(TTY_LDISC_CHANGING, &tty->flags);
 }
 
-- 
2.30.2

Reply via email to