diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r-- | drivers/tty/tty_buffer.c | 131 |
1 files changed, 42 insertions, 89 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 45d916198f78..bb119934e76c 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -16,6 +16,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/ratelimit.h> /** * tty_buffer_free_all - free buffers used by a tty @@ -119,11 +120,14 @@ static void __tty_buffer_flush(struct tty_port *port) struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; - while ((thead = buf->head) != NULL) { - buf->head = thead->next; - tty_buffer_free(port, thead); + if (unlikely(buf->head == NULL)) + return; + while ((thead = buf->head->next) != NULL) { + tty_buffer_free(port, buf->head); + buf->head = thead; } - buf->tail = NULL; + WARN_ON(buf->head != buf->tail); + buf->head->read = buf->head->commit; } /** @@ -194,19 +198,22 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) have queued and recycle that ? */ } /** - * __tty_buffer_request_room - grow tty buffer if needed + * tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure * @size: size desired * * 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 port->buf.lock + * + * Locking: Takes port->buf.lock */ -static int __tty_buffer_request_room(struct tty_port *port, size_t size) +int tty_buffer_request_room(struct tty_port *port, size_t size) { struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; int left; + unsigned long flags; + spin_lock_irqsave(&buf->lock, flags); /* OPTIMISATION: We could keep a per tty "zero" sized buffer to remove this conditional if its worth it. This would be invisible to the callers */ @@ -228,37 +235,14 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size) } else size = left; } - + spin_unlock_irqrestore(&buf->lock, flags); return size; } - - -/** - * tty_buffer_request_room - grow tty buffer if needed - * @tty: tty structure - * @size: size desired - * - * 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 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(&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); /** * tty_insert_flip_string_fixed_flag - Add characters to the tty buffer - * @tty: tty structure + * @port: tty port * @chars: characters * @flag: flag value for each character * @size: size @@ -269,29 +253,21 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Locking: Called functions may take port->buf.lock */ -int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, +int tty_insert_flip_string_fixed_flag(struct tty_port *port, const unsigned char *chars, char flag, size_t size) { - struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space; - unsigned long flags; - struct tty_buffer *tb; - - spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty->port, goal); - tb = buf->tail; + int space = tty_buffer_request_room(port, goal); + struct tty_buffer *tb = port->buf.tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&buf->lock, flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memset(tb->flag_buf_ptr + tb->used, flag, space); tb->used += space; - spin_unlock_irqrestore(&buf->lock, flags); copied += space; chars += space; /* There is a small chance that we need to split the data over @@ -303,7 +279,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); /** * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure + * @port: tty port * @chars: characters * @flags: flag bytes * @size: size @@ -315,29 +291,21 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * Locking: Called functions may take port->buf.lock */ -int tty_insert_flip_string_flags(struct tty_struct *tty, +int tty_insert_flip_string_flags(struct tty_port *port, const unsigned char *chars, const char *flags, size_t size) { - struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space; - unsigned long __flags; - struct tty_buffer *tb; - - spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty->port, goal); - tb = buf->tail; + int space = tty_buffer_request_room(port, goal); + struct tty_buffer *tb = port->buf.tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { - spin_unlock_irqrestore(&buf->lock, __flags); break; } memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->flag_buf_ptr + tb->used, flags, space); tb->used += space; - spin_unlock_irqrestore(&buf->lock, __flags); copied += space; chars += space; flags += space; @@ -350,7 +318,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); /** * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from + * @port: tty port to push from * * Takes any pending buffers and transfers their ownership to the * ldisc side of the queue. It then schedules those characters for @@ -361,11 +329,11 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Locking: Takes port->buf.lock */ -void tty_schedule_flip(struct tty_struct *tty) +void tty_schedule_flip(struct tty_port *port) { - struct tty_bufhead *buf = &tty->port->buf; + struct tty_bufhead *buf = &port->buf; unsigned long flags; - WARN_ON(tty->low_latency); + WARN_ON(port->low_latency); spin_lock_irqsave(&buf->lock, flags); if (buf->tail != NULL) @@ -377,7 +345,7 @@ EXPORT_SYMBOL(tty_schedule_flip); /** * tty_prepare_flip_string - make room for characters - * @tty: tty + * @port: tty port * @chars: return pointer for character write area * @size: desired size * @@ -390,31 +358,23 @@ EXPORT_SYMBOL(tty_schedule_flip); * Locking: May call functions taking port->buf.lock */ -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, +int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, size_t size) { - 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->port, size); - - tb = buf->tail; + int space = tty_buffer_request_room(port, size); if (likely(space)) { + struct tty_buffer *tb = port->buf.tail; *chars = tb->char_buf_ptr + tb->used; memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); tb->used += space; } - spin_unlock_irqrestore(&buf->lock, flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); /** * tty_prepare_flip_string_flags - make room for characters - * @tty: tty + * @port: tty port * @chars: return pointer for character write area * @flags: return pointer for status flag write area * @size: desired size @@ -428,24 +388,16 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * Locking: May call functions taking port->buf.lock */ -int tty_prepare_flip_string_flags(struct tty_struct *tty, +int tty_prepare_flip_string_flags(struct tty_port *port, unsigned char **chars, char **flags, size_t size) { - 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->port, size); - - tb = buf->tail; + int space = tty_buffer_request_room(port, size); if (likely(space)) { + struct tty_buffer *tb = port->buf.tail; *chars = tb->char_buf_ptr + tb->used; *flags = tb->flag_buf_ptr + tb->used; tb->used += space; } - spin_unlock_irqrestore(&buf->lock, __flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); @@ -539,16 +491,17 @@ static void flush_to_ldisc(struct work_struct *work) */ void tty_flush_to_ldisc(struct tty_struct *tty) { - if (!tty->low_latency) + if (!tty->port->low_latency) flush_work(&tty->port->buf.work); } /** * tty_flip_buffer_push - terminal - * @tty: tty to push + * @port: tty port to push * * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. + * function must not be called from IRQ context if port->low_latency is + * set. * * In the event of the queue being busy for flipping the work will be * held off and retried later. @@ -556,9 +509,9 @@ void tty_flush_to_ldisc(struct tty_struct *tty) * Locking: tty buffer lock. Driver locks in low latency mode. */ -void tty_flip_buffer_push(struct tty_struct *tty) +void tty_flip_buffer_push(struct tty_port *port) { - struct tty_bufhead *buf = &tty->port->buf; + struct tty_bufhead *buf = &port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -566,7 +519,7 @@ void tty_flip_buffer_push(struct tty_struct *tty) buf->tail->commit = buf->tail->used; spin_unlock_irqrestore(&buf->lock, flags); - if (tty->low_latency) + if (port->low_latency) flush_to_ldisc(&buf->work); else schedule_work(&buf->work); |