diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 531e539dbfcf..60b076cc4e20 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -149,8 +149,11 @@ static void n_tty_set_room(struct tty_struct *tty) tty->receive_room = left; /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) - schedule_work(&tty->buf.work); + if (left && !old_left) { + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty"); + schedule_work(&tty->port->buf.work); + } } static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index c32690862671..4219f040adb8 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -93,7 +93,7 @@ static void pty_unthrottle(struct tty_struct *tty) static int pty_space(struct tty_struct *to) { - int n = 8192 - to->buf.memory_used; + int n = 8192 - to->port->buf.memory_used; if (n < 0) return 0; return n; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index ddd74d41cbb2..06725f5cc819 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -27,9 +27,9 @@ * Locking: none */ -void tty_buffer_free_all(struct tty_struct *tty) +void tty_buffer_free_all(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { @@ -56,11 +56,11 @@ void tty_buffer_free_all(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { struct tty_buffer *p; - if (tty->buf.memory_used + size > 65536) + if (port->buf.memory_used + size > 65536) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -72,7 +72,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) p->read = 0; p->char_buf_ptr = (char *)(p->data); p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; + port->buf.memory_used += size; return p; } @@ -87,9 +87,9 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) * Locking: Caller must hold tty->buf.lock */ -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ buf->memory_used -= b->size; @@ -114,14 +114,14 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) * Locking: Caller must hold tty->buf.lock */ -static void __tty_buffer_flush(struct tty_struct *tty) +static void __tty_buffer_flush(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { buf->head = thead->next; - tty_buffer_free(tty, thead); + tty_buffer_free(port, thead); } buf->tail = NULL; } @@ -140,7 +140,7 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -155,7 +155,7 @@ void tty_buffer_flush(struct tty_struct *tty) test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else - __tty_buffer_flush(tty); + __tty_buffer_flush(port); spin_unlock_irqrestore(&buf->lock, flags); } @@ -171,9 +171,9 @@ void tty_buffer_flush(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &tty->buf.free; + struct tty_buffer **tbh = &port->buf.free; while ((*tbh) != NULL) { struct tty_buffer *t = *tbh; if (t->size >= size) { @@ -182,14 +182,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) t->used = 0; t->commit = 0; t->read = 0; - tty->buf.memory_used += t->size; + port->buf.memory_used += t->size; return t; } tbh = &((*tbh)->next); } /* Round the buffer size out */ size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); + return tty_buffer_alloc(port, size); /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ } @@ -200,11 +200,11 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * Locking: Caller must hold tty->buf.lock + * Locking: Caller must hold port->buf.lock */ -static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; int left; /* OPTIMISATION: We could keep a per tty "zero" sized buffer to @@ -218,7 +218,7 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { + if ((n = tty_buffer_find(port, size)) != NULL) { if (b != NULL) { b->next = n; b->commit = b->used; @@ -241,16 +241,17 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ int tty_buffer_request_room(struct tty_struct *tty, size_t size) { + struct tty_port *port = tty->port; unsigned long flags; int length; - spin_lock_irqsave(&tty->buf.lock, flags); - length = __tty_buffer_request_room(tty, size); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_lock_irqsave(&port->buf.lock, flags); + length = __tty_buffer_request_room(port, size); + spin_unlock_irqrestore(&port->buf.lock, flags); return length; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -265,13 +266,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, const unsigned char *chars, char flag, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -280,7 +281,7 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -311,13 +312,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * the flags array indicates the status of the character. Returns the * number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -326,7 +327,7 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -357,12 +358,12 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Note that this function can only be used when the low_latency flag * is unset. Otherwise the workqueue won't be flushed. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ void tty_schedule_flip(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -385,19 +386,19 @@ EXPORT_SYMBOL(tty_schedule_flip); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) + size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -423,19 +424,19 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long __flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -464,13 +465,16 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void flush_to_ldisc(struct work_struct *work) { - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work); - struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_port *port = container_of(work, struct tty_port, buf.work); + struct tty_bufhead *buf = &port->buf; + struct tty_struct *tty; unsigned long flags; struct tty_ldisc *disc; + tty = port->itty; + if (WARN_RATELIMIT(tty == NULL, "tty is NULL")) + return; + disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; @@ -489,7 +493,7 @@ static void flush_to_ldisc(struct work_struct *work) if (head->next == NULL) break; buf->head = head->next; - tty_buffer_free(tty, head); + tty_buffer_free(port, head); continue; } /* Ldisc or user is trying to flush the buffers @@ -515,7 +519,7 @@ static void flush_to_ldisc(struct work_struct *work) /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(tty); + __tty_buffer_flush(port); clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } @@ -535,7 +539,7 @@ static void flush_to_ldisc(struct work_struct *work) void tty_flush_to_ldisc(struct tty_struct *tty) { if (!tty->low_latency) - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -553,7 +557,7 @@ void tty_flush_to_ldisc(struct tty_struct *tty) void tty_flip_buffer_push(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -578,9 +582,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none */ -void tty_buffer_init(struct tty_struct *tty) +void tty_buffer_init(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; spin_lock_init(&buf->lock); buf->head = NULL; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 202008f38ca3..a3eba7f359ed 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -186,7 +186,6 @@ void free_tty_struct(struct tty_struct *tty) if (tty->dev) put_device(tty->dev); kfree(tty->write_buf); - tty_buffer_free_all(tty); tty->magic = 0xDEADDEAD; kfree(tty); } @@ -2935,7 +2934,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 47e3968df10b..f4e6754525dc 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -512,7 +512,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->buf.work); + return cancel_work_sync(&tty->port->buf.work); } /** @@ -525,7 +525,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) { flush_work(&tty->hangup_work); flush_work(&tty->SAK_work); - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -704,9 +704,9 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ if (work) - schedule_work(&tty->buf.work); + schedule_work(&tty->port->buf.work); if (o_work) - schedule_work(&o_tty->buf.work); + schedule_work(&o_tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -817,7 +817,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); - cancel_work_sync(&tty->buf.work); + cancel_work_sync(&tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d7bdd8d0c23f..416b42f7c346 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -21,6 +21,7 @@ void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port)); + tty_buffer_init(port); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->delta_msr_wait); @@ -126,6 +127,7 @@ static void tty_port_destructor(struct kref *kref) struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); + tty_buffer_free_all(port); if (port->ops->destruct) port->ops->destruct(port); else diff --git a/include/linux/tty.h b/include/linux/tty.h index 9be74d649a51..d7ff88fb8967 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -188,6 +188,7 @@ struct tty_port_operations { }; struct tty_port { + struct tty_bufhead buf; /* Locked internally */ struct tty_struct *tty; /* Back pointer */ struct tty_struct *itty; /* internal back ptr */ const struct tty_port_operations *ops; /* Port operations */ @@ -259,7 +260,6 @@ struct tty_struct { struct tty_struct *link; struct fasync_struct *fasync; - struct tty_bufhead buf; /* Locked internally */ int alt_speed; /* For magic substitution of 38400 bps */ wait_queue_head_t write_wait; wait_queue_head_t read_wait; @@ -388,9 +388,9 @@ extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); extern void tty_flush_to_ldisc(struct tty_struct *tty); -extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty); -extern void tty_buffer_init(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_port *port); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 9239d033a0a3..2002344ed36a 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -11,7 +11,7 @@ void tty_schedule_flip(struct tty_struct *tty); static inline int tty_insert_flip_char(struct tty_struct *tty, unsigned char ch, char flag) { - struct tty_buffer *tb = tty->buf.tail; + struct tty_buffer *tb = tty->port->buf.tail; if (tb && tb->used < tb->size) { tb->flag_buf_ptr[tb->used] = flag; tb->char_buf_ptr[tb->used++] = ch;