Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=9c1729db3e6d738f872bcb090212af00473bf666
Commit:     9c1729db3e6d738f872bcb090212af00473bf666
Parent:     c9c64155f5a81b4b41e98f9fb9c464a565c1bf72
Author:     Alan Cox <[EMAIL PROTECTED]>
AuthorDate: Sun Jul 15 23:39:43 2007 -0700
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Mon Jul 16 09:05:41 2007 -0700

    Prevent an O_NDELAY writer from blocking when a tty write is blocked by the 
tty atomic writer mutex
    
    Without this a tty write could block if a previous blocking tty write was
    in progress on the same tty and blocked by a line discipline or hardware
    event.  Originally found and reported by Dave Johnson.
    
    Signed-off-by: Alan Cox <[EMAIL PROTECTED]>
    Acked-by: Dave Johnson <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 drivers/char/n_hdlc.c    |    9 +++++----
 drivers/char/n_tty.c     |    3 ++-
 drivers/char/tty_io.c    |   40 ++++++++++++++++++++++++++++------------
 drivers/char/tty_ioctl.c |    4 ++--
 include/linux/tty.h      |    6 ++++++
 5 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
index 337a87f..37f7d34 100644
--- a/drivers/char/n_hdlc.c
+++ b/drivers/char/n_hdlc.c
@@ -780,13 +780,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct 
*tty, struct file *filp,
                poll_wait(filp, &tty->write_wait, wait);
 
                /* set bits for operations that won't block */
-               if(n_hdlc->rx_buf_list.head)
+               if (n_hdlc->rx_buf_list.head)
                        mask |= POLLIN | POLLRDNORM;    /* readable */
                if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
                        mask |= POLLHUP;
-               if(tty_hung_up_p(filp))
+               if (tty_hung_up_p(filp))
                        mask |= POLLHUP;
-               if(n_hdlc->tx_free_buf_list.head)
+               if (!tty_is_writelocked(tty) &&
+                               n_hdlc->tx_free_buf_list.head)
                        mask |= POLLOUT | POLLWRNORM;   /* writable */
        }
        return mask;
@@ -861,7 +862,7 @@ static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
        spin_lock_irqsave(&list->spinlock,flags);
        
        buf->link=NULL;
-       if(list->tail)
+       if (list->tail)
                list->tail->link = buf;
        else
                list->head = buf;
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 154f422..371631f 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -1538,7 +1538,8 @@ static unsigned int normal_poll(struct tty_struct * tty, 
struct file * file, pol
                else
                        tty->minimum_to_wake = 1;
        }
-       if (tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS &&
+       if (!tty_is_writelocked(tty) &&
+                       tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS &&
                        tty->driver->write_room(tty) > 0)
                mask |= POLLOUT | POLLWRNORM;
        return mask;
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index a96f26a..a37e633 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1726,6 +1726,23 @@ static ssize_t tty_read(struct file * file, char __user 
* buf, size_t count,
        return i;
 }
 
+void tty_write_unlock(struct tty_struct *tty)
+{
+       mutex_unlock(&tty->atomic_write_lock);
+       wake_up_interruptible(&tty->write_wait);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+       if (!mutex_trylock(&tty->atomic_write_lock)) {
+               if (ndelay)
+                       return -EAGAIN;
+               if (mutex_lock_interruptible(&tty->atomic_write_lock))
+                       return -ERESTARTSYS;
+       }
+       return 0;
+}
+
 /*
  * Split writes up in sane blocksizes to avoid
  * denial-of-service type attacks
@@ -1737,13 +1754,12 @@ static inline ssize_t do_tty_write(
        const char __user *buf,
        size_t count)
 {
-       ssize_t ret = 0, written = 0;
+       ssize_t ret, written = 0;
        unsigned int chunk;
        
-       /* FIXME: O_NDELAY ... */
-       if (mutex_lock_interruptible(&tty->atomic_write_lock)) {
-               return -ERESTARTSYS;
-       }
+       ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
+       if (ret < 0)
+               return ret;
 
        /*
         * We chunk up writes into a temporary buffer. This
@@ -1776,8 +1792,8 @@ static inline ssize_t do_tty_write(
 
                buf = kmalloc(chunk, GFP_KERNEL);
                if (!buf) {
-                       mutex_unlock(&tty->atomic_write_lock);
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto out;
                }
                kfree(tty->write_buf);
                tty->write_cnt = chunk;
@@ -1812,7 +1828,8 @@ static inline ssize_t do_tty_write(
                inode->i_mtime = current_fs_time(inode->i_sb);
                ret = written;
        }
-       mutex_unlock(&tty->atomic_write_lock);
+out:
+       tty_write_unlock(tty);
        return ret;
 }
 
@@ -3163,14 +3180,13 @@ static int tiocsetd(struct tty_struct *tty, int __user 
*p)
 
 static int send_break(struct tty_struct *tty, unsigned int duration)
 {
-       if (mutex_lock_interruptible(&tty->atomic_write_lock))
+       if (tty_write_lock(tty, 0) < 0)
                return -EINTR;
        tty->driver->break_ctl(tty, -1);
-       if (!signal_pending(current)) {
+       if (!signal_pending(current))
                msleep_interruptible(duration);
-       }
        tty->driver->break_ctl(tty, 0);
-       mutex_unlock(&tty->atomic_write_lock);
+       tty_write_unlock(tty);
        if (signal_pending(current))
                return -EINTR;
        return 0;
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index fd471cb..918e24c 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -667,7 +667,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)
                return 0;
        }
 
-       if (mutex_lock_interruptible(&tty->atomic_write_lock))
+       if (tty_write_lock(tty, 0) < 0)
                return -ERESTARTSYS;
 
        if (was_stopped)
@@ -675,7 +675,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)
        tty->driver->write(tty, &ch, 1);
        if (was_stopped)
                stop_tty(tty);
-       mutex_unlock(&tty->atomic_write_lock);
+       tty_write_unlock(tty);
        return 0;
 }
 
diff --git a/include/linux/tty.h b/include/linux/tty.h
index bb45760..deaba9e 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -338,6 +338,12 @@ extern struct tty_struct *get_current_tty(void);
 
 extern struct mutex tty_mutex;
 
+extern void tty_write_unlock(struct tty_struct *tty);
+extern int tty_write_lock(struct tty_struct *tty, int ndelay);
+#define tty_is_writelocked(tty)  (mutex_is_locked(&tty->atomic_write_lock))
+
+
+
 /* n_tty.c */
 extern struct tty_ldisc tty_ldisc_N_TTY;
 
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to