tty: Simplify flip buffer list with 0-sized sentinel

Use a 0-sized sentinel to avoid assigning the head ptr from
the driver side thread. This also eliminates testing head/tail
for NULL.

When the sentinel is first 'consumed' by the buffer work
(or by tty_buffer_flush()), it is detached from the list but not
freed nor added to the free list. Both buffer work and
tty_buffer_flush() continue to preserve at least 1 flip buffer
to which head & tail is pointed.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Peter Hurley 2013-06-15 09:36:07 -04:00 committed by Greg Kroah-Hartman
parent 809850b7a5
commit 7391ee1695
2 changed files with 19 additions and 31 deletions

View File

@ -49,13 +49,16 @@ void tty_buffer_free_all(struct tty_port *port)
while ((p = buf->head) != NULL) { while ((p = buf->head) != NULL) {
buf->head = p->next; buf->head = p->next;
kfree(p); if (p->size > 0)
kfree(p);
} }
llist = llist_del_all(&buf->free); llist = llist_del_all(&buf->free);
llist_for_each_entry_safe(p, next, llist, free) llist_for_each_entry_safe(p, next, llist, free)
kfree(p); kfree(p);
buf->tail = NULL; tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
buf->memory_used = 0; buf->memory_used = 0;
} }
@ -120,7 +123,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
if (b->size > MIN_TTYB_SIZE) if (b->size > MIN_TTYB_SIZE)
kfree(b); kfree(b);
else else if (b->size > 0)
llist_add(&b->free, &buf->free); llist_add(&b->free, &buf->free);
} }
@ -140,8 +143,6 @@ static void __tty_buffer_flush(struct tty_port *port)
struct tty_bufhead *buf = &port->buf; struct tty_bufhead *buf = &port->buf;
struct tty_buffer *next; struct tty_buffer *next;
if (unlikely(buf->head == NULL))
return;
while ((next = buf->head->next) != NULL) { while ((next = buf->head->next) != NULL) {
tty_buffer_free(port, buf->head); tty_buffer_free(port, buf->head);
buf->head = next; buf->head = next;
@ -200,23 +201,14 @@ int tty_buffer_request_room(struct tty_port *port, size_t size)
int left; int left;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&buf->lock, 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 */
b = buf->tail; b = buf->tail;
if (b != NULL) left = b->size - b->used;
left = b->size - b->used;
else
left = 0;
if (left < size) { if (left < size) {
/* This is the slow path - looking for new buffers to use */ /* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_alloc(port, size)) != NULL) { if ((n = tty_buffer_alloc(port, size)) != NULL) {
if (b != NULL) { b->next = n;
b->next = n; b->commit = b->used;
b->commit = b->used;
} else
buf->head = n;
buf->tail = n; buf->tail = n;
} else } else
size = left; size = left;
@ -247,10 +239,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port,
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(port, goal); int space = tty_buffer_request_room(port, goal);
struct tty_buffer *tb = port->buf.tail; struct tty_buffer *tb = port->buf.tail;
/* If there is no space then tb may be NULL */ if (unlikely(space == 0))
if (unlikely(space == 0)) {
break; break;
}
memcpy(char_buf_ptr(tb, tb->used), chars, space); memcpy(char_buf_ptr(tb, tb->used), chars, space);
memset(flag_buf_ptr(tb, tb->used), flag, space); memset(flag_buf_ptr(tb, tb->used), flag, space);
tb->used += space; tb->used += space;
@ -285,10 +275,8 @@ int tty_insert_flip_string_flags(struct tty_port *port,
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(port, goal); int space = tty_buffer_request_room(port, goal);
struct tty_buffer *tb = port->buf.tail; struct tty_buffer *tb = port->buf.tail;
/* If there is no space then tb may be NULL */ if (unlikely(space == 0))
if (unlikely(space == 0)) {
break; break;
}
memcpy(char_buf_ptr(tb, tb->used), chars, space); memcpy(char_buf_ptr(tb, tb->used), chars, space);
memcpy(flag_buf_ptr(tb, tb->used), flags, space); memcpy(flag_buf_ptr(tb, tb->used), flags, space);
tb->used += space; tb->used += space;
@ -322,8 +310,7 @@ void tty_schedule_flip(struct tty_port *port)
WARN_ON(port->low_latency); WARN_ON(port->low_latency);
spin_lock_irqsave(&buf->lock, flags); spin_lock_irqsave(&buf->lock, flags);
if (buf->tail != NULL) buf->tail->commit = buf->tail->used;
buf->tail->commit = buf->tail->used;
spin_unlock_irqrestore(&buf->lock, flags); spin_unlock_irqrestore(&buf->lock, flags);
schedule_work(&buf->work); schedule_work(&buf->work);
} }
@ -438,8 +425,8 @@ static void flush_to_ldisc(struct work_struct *work)
spin_lock_irqsave(&buf->lock, flags); spin_lock_irqsave(&buf->lock, flags);
if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {
struct tty_buffer *head; while (1) {
while ((head = buf->head) != NULL) { struct tty_buffer *head = buf->head;
int count; int count;
count = head->commit - head->read; count = head->commit - head->read;
@ -509,8 +496,7 @@ void tty_flip_buffer_push(struct tty_port *port)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&buf->lock, flags); spin_lock_irqsave(&buf->lock, flags);
if (buf->tail != NULL) buf->tail->commit = buf->tail->used;
buf->tail->commit = buf->tail->used;
spin_unlock_irqrestore(&buf->lock, flags); spin_unlock_irqrestore(&buf->lock, flags);
if (port->low_latency) if (port->low_latency)
@ -535,8 +521,9 @@ void tty_buffer_init(struct tty_port *port)
struct tty_bufhead *buf = &port->buf; struct tty_bufhead *buf = &port->buf;
spin_lock_init(&buf->lock); spin_lock_init(&buf->lock);
buf->head = NULL; tty_buffer_reset(&buf->sentinel, 0);
buf->tail = NULL; buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
init_llist_head(&buf->free); init_llist_head(&buf->free);
buf->memory_used = 0; buf->memory_used = 0;
INIT_WORK(&buf->work, flush_to_ldisc); INIT_WORK(&buf->work, flush_to_ldisc);

View File

@ -67,6 +67,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs)
struct tty_bufhead { struct tty_bufhead {
struct work_struct work; struct work_struct work;
spinlock_t lock; spinlock_t lock;
struct tty_buffer sentinel;
struct tty_buffer *head; /* Queue head */ struct tty_buffer *head; /* Queue head */
struct tty_buffer *tail; /* Active buffer */ struct tty_buffer *tail; /* Active buffer */
struct llist_head free; /* Free queue head */ struct llist_head free; /* Free queue head */