diff options
| author | Peter Hurley <peter@hurleysoftware.com> | 2013-06-15 09:36:07 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-23 19:47:08 -0400 |
| commit | 7391ee16950e772076d321792d9fbf030f921345 (patch) | |
| tree | c0b17ff0622f801f05bb11228426bdc90e8ffdc2 | |
| parent | 809850b7a5fcc0a96d023e1171a7944c60fd5a71 (diff) | |
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>
| -rw-r--r-- | drivers/tty/tty_buffer.c | 49 | ||||
| -rw-r--r-- | include/linux/tty.h | 1 |
2 files changed, 19 insertions, 31 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 069640e5b9c0..231b7a8710f1 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
| @@ -49,13 +49,16 @@ void tty_buffer_free_all(struct tty_port *port) | |||
| 49 | 49 | ||
| 50 | while ((p = buf->head) != NULL) { | 50 | while ((p = buf->head) != NULL) { |
| 51 | buf->head = p->next; | 51 | buf->head = p->next; |
| 52 | kfree(p); | 52 | if (p->size > 0) |
| 53 | kfree(p); | ||
| 53 | } | 54 | } |
| 54 | llist = llist_del_all(&buf->free); | 55 | llist = llist_del_all(&buf->free); |
| 55 | llist_for_each_entry_safe(p, next, llist, free) | 56 | llist_for_each_entry_safe(p, next, llist, free) |
| 56 | kfree(p); | 57 | kfree(p); |
| 57 | 58 | ||
| 58 | buf->tail = NULL; | 59 | tty_buffer_reset(&buf->sentinel, 0); |
| 60 | buf->head = &buf->sentinel; | ||
| 61 | buf->tail = &buf->sentinel; | ||
| 59 | buf->memory_used = 0; | 62 | buf->memory_used = 0; |
| 60 | } | 63 | } |
| 61 | 64 | ||
| @@ -120,7 +123,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) | |||
| 120 | 123 | ||
| 121 | if (b->size > MIN_TTYB_SIZE) | 124 | if (b->size > MIN_TTYB_SIZE) |
| 122 | kfree(b); | 125 | kfree(b); |
| 123 | else | 126 | else if (b->size > 0) |
| 124 | llist_add(&b->free, &buf->free); | 127 | llist_add(&b->free, &buf->free); |
| 125 | } | 128 | } |
| 126 | 129 | ||
| @@ -140,8 +143,6 @@ static void __tty_buffer_flush(struct tty_port *port) | |||
| 140 | struct tty_bufhead *buf = &port->buf; | 143 | struct tty_bufhead *buf = &port->buf; |
| 141 | struct tty_buffer *next; | 144 | struct tty_buffer *next; |
| 142 | 145 | ||
| 143 | if (unlikely(buf->head == NULL)) | ||
| 144 | return; | ||
| 145 | while ((next = buf->head->next) != NULL) { | 146 | while ((next = buf->head->next) != NULL) { |
| 146 | tty_buffer_free(port, buf->head); | 147 | tty_buffer_free(port, buf->head); |
| 147 | buf->head = next; | 148 | buf->head = next; |
| @@ -200,23 +201,14 @@ int tty_buffer_request_room(struct tty_port *port, size_t size) | |||
| 200 | int left; | 201 | int left; |
| 201 | unsigned long flags; | 202 | unsigned long flags; |
| 202 | spin_lock_irqsave(&buf->lock, flags); | 203 | spin_lock_irqsave(&buf->lock, flags); |
| 203 | /* OPTIMISATION: We could keep a per tty "zero" sized buffer to | ||
| 204 | remove this conditional if its worth it. This would be invisible | ||
| 205 | to the callers */ | ||
| 206 | b = buf->tail; | 204 | b = buf->tail; |
| 207 | if (b != NULL) | 205 | left = b->size - b->used; |
| 208 | left = b->size - b->used; | ||
| 209 | else | ||
| 210 | left = 0; | ||
| 211 | 206 | ||
| 212 | if (left < size) { | 207 | if (left < size) { |
| 213 | /* This is the slow path - looking for new buffers to use */ | 208 | /* This is the slow path - looking for new buffers to use */ |
| 214 | if ((n = tty_buffer_alloc(port, size)) != NULL) { | 209 | if ((n = tty_buffer_alloc(port, size)) != NULL) { |
| 215 | if (b != NULL) { | 210 | b->next = n; |
| 216 | b->next = n; | 211 | b->commit = b->used; |
| 217 | b->commit = b->used; | ||
| 218 | } else | ||
| 219 | buf->head = n; | ||
| 220 | buf->tail = n; | 212 | buf->tail = n; |
| 221 | } else | 213 | } else |
| 222 | size = left; | 214 | size = left; |
| @@ -247,10 +239,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, | |||
| 247 | int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); | 239 | int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); |
| 248 | int space = tty_buffer_request_room(port, goal); | 240 | int space = tty_buffer_request_room(port, goal); |
| 249 | struct tty_buffer *tb = port->buf.tail; | 241 | struct tty_buffer *tb = port->buf.tail; |
| 250 | /* If there is no space then tb may be NULL */ | 242 | if (unlikely(space == 0)) |
| 251 | if (unlikely(space == 0)) { | ||
| 252 | break; | 243 | break; |
| 253 | } | ||
| 254 | memcpy(char_buf_ptr(tb, tb->used), chars, space); | 244 | memcpy(char_buf_ptr(tb, tb->used), chars, space); |
| 255 | memset(flag_buf_ptr(tb, tb->used), flag, space); | 245 | memset(flag_buf_ptr(tb, tb->used), flag, space); |
| 256 | tb->used += space; | 246 | tb->used += space; |
| @@ -285,10 +275,8 @@ int tty_insert_flip_string_flags(struct tty_port *port, | |||
| 285 | int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); | 275 | int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); |
| 286 | int space = tty_buffer_request_room(port, goal); | 276 | int space = tty_buffer_request_room(port, goal); |
| 287 | struct tty_buffer *tb = port->buf.tail; | 277 | struct tty_buffer *tb = port->buf.tail; |
| 288 | /* If there is no space then tb may be NULL */ | 278 | if (unlikely(space == 0)) |
| 289 | if (unlikely(space == 0)) { | ||
| 290 | break; | 279 | break; |
| 291 | } | ||
| 292 | memcpy(char_buf_ptr(tb, tb->used), chars, space); | 280 | memcpy(char_buf_ptr(tb, tb->used), chars, space); |
| 293 | memcpy(flag_buf_ptr(tb, tb->used), flags, space); | 281 | memcpy(flag_buf_ptr(tb, tb->used), flags, space); |
| 294 | tb->used += space; | 282 | tb->used += space; |
| @@ -322,8 +310,7 @@ void tty_schedule_flip(struct tty_port *port) | |||
| 322 | WARN_ON(port->low_latency); | 310 | WARN_ON(port->low_latency); |
| 323 | 311 | ||
| 324 | spin_lock_irqsave(&buf->lock, flags); | 312 | spin_lock_irqsave(&buf->lock, flags); |
| 325 | if (buf->tail != NULL) | 313 | buf->tail->commit = buf->tail->used; |
| 326 | buf->tail->commit = buf->tail->used; | ||
| 327 | spin_unlock_irqrestore(&buf->lock, flags); | 314 | spin_unlock_irqrestore(&buf->lock, flags); |
| 328 | schedule_work(&buf->work); | 315 | schedule_work(&buf->work); |
| 329 | } | 316 | } |
| @@ -438,8 +425,8 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 438 | spin_lock_irqsave(&buf->lock, flags); | 425 | spin_lock_irqsave(&buf->lock, flags); |
| 439 | 426 | ||
| 440 | if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { | 427 | if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { |
| 441 | struct tty_buffer *head; | 428 | while (1) { |
| 442 | while ((head = buf->head) != NULL) { | 429 | struct tty_buffer *head = buf->head; |
| 443 | int count; | 430 | int count; |
| 444 | 431 | ||
| 445 | count = head->commit - head->read; | 432 | count = head->commit - head->read; |
| @@ -509,8 +496,7 @@ void tty_flip_buffer_push(struct tty_port *port) | |||
| 509 | unsigned long flags; | 496 | unsigned long flags; |
| 510 | 497 | ||
| 511 | spin_lock_irqsave(&buf->lock, flags); | 498 | spin_lock_irqsave(&buf->lock, flags); |
| 512 | if (buf->tail != NULL) | 499 | buf->tail->commit = buf->tail->used; |
| 513 | buf->tail->commit = buf->tail->used; | ||
| 514 | spin_unlock_irqrestore(&buf->lock, flags); | 500 | spin_unlock_irqrestore(&buf->lock, flags); |
| 515 | 501 | ||
| 516 | if (port->low_latency) | 502 | if (port->low_latency) |
| @@ -535,8 +521,9 @@ void tty_buffer_init(struct tty_port *port) | |||
| 535 | struct tty_bufhead *buf = &port->buf; | 521 | struct tty_bufhead *buf = &port->buf; |
| 536 | 522 | ||
| 537 | spin_lock_init(&buf->lock); | 523 | spin_lock_init(&buf->lock); |
| 538 | buf->head = NULL; | 524 | tty_buffer_reset(&buf->sentinel, 0); |
| 539 | buf->tail = NULL; | 525 | buf->head = &buf->sentinel; |
| 526 | buf->tail = &buf->sentinel; | ||
| 540 | init_llist_head(&buf->free); | 527 | init_llist_head(&buf->free); |
| 541 | buf->memory_used = 0; | 528 | buf->memory_used = 0; |
| 542 | INIT_WORK(&buf->work, flush_to_ldisc); | 529 | INIT_WORK(&buf->work, flush_to_ldisc); |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 5043b12f23ea..2e93eb831c61 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
| @@ -67,6 +67,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) | |||
| 67 | struct tty_bufhead { | 67 | struct tty_bufhead { |
| 68 | struct work_struct work; | 68 | struct work_struct work; |
| 69 | spinlock_t lock; | 69 | spinlock_t lock; |
| 70 | struct tty_buffer sentinel; | ||
| 70 | struct tty_buffer *head; /* Queue head */ | 71 | struct tty_buffer *head; /* Queue head */ |
| 71 | struct tty_buffer *tail; /* Active buffer */ | 72 | struct tty_buffer *tail; /* Active buffer */ |
| 72 | struct llist_head free; /* Free queue head */ | 73 | struct llist_head free; /* Free queue head */ |
