diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
| -rw-r--r-- | drivers/tty/tty_buffer.c | 29 |
1 files changed, 14 insertions, 15 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f1d30f6945af..cf78d1985cd8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
| @@ -255,16 +255,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, | |||
| 255 | if (change || left < size) { | 255 | if (change || left < size) { |
| 256 | /* This is the slow path - looking for new buffers to use */ | 256 | /* This is the slow path - looking for new buffers to use */ |
| 257 | if ((n = tty_buffer_alloc(port, size)) != NULL) { | 257 | if ((n = tty_buffer_alloc(port, size)) != NULL) { |
| 258 | unsigned long iflags; | ||
| 259 | |||
| 260 | n->flags = flags; | 258 | n->flags = flags; |
| 261 | buf->tail = n; | 259 | buf->tail = n; |
| 262 | |||
| 263 | spin_lock_irqsave(&buf->flush_lock, iflags); | ||
| 264 | b->commit = b->used; | 260 | b->commit = b->used; |
| 261 | /* paired w/ barrier in flush_to_ldisc(); ensures the | ||
| 262 | * latest commit value can be read before the head is | ||
| 263 | * advanced to the next buffer | ||
| 264 | */ | ||
| 265 | smp_wmb(); | ||
| 265 | b->next = n; | 266 | b->next = n; |
| 266 | spin_unlock_irqrestore(&buf->flush_lock, iflags); | ||
| 267 | |||
| 268 | } else if (change) | 267 | } else if (change) |
| 269 | size = 0; | 268 | size = 0; |
| 270 | else | 269 | else |
| @@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 448 | mutex_lock(&buf->lock); | 447 | mutex_lock(&buf->lock); |
| 449 | 448 | ||
| 450 | while (1) { | 449 | while (1) { |
| 451 | unsigned long flags; | ||
| 452 | struct tty_buffer *head = buf->head; | 450 | struct tty_buffer *head = buf->head; |
| 451 | struct tty_buffer *next; | ||
| 453 | int count; | 452 | int count; |
| 454 | 453 | ||
| 455 | /* Ldisc or user is trying to gain exclusive access */ | 454 | /* Ldisc or user is trying to gain exclusive access */ |
| 456 | if (atomic_read(&buf->priority)) | 455 | if (atomic_read(&buf->priority)) |
| 457 | break; | 456 | break; |
| 458 | 457 | ||
| 459 | spin_lock_irqsave(&buf->flush_lock, flags); | 458 | next = head->next; |
| 459 | /* paired w/ barrier in __tty_buffer_request_room(); | ||
| 460 | * ensures commit value read is not stale if the head | ||
| 461 | * is advancing to the next buffer | ||
| 462 | */ | ||
| 463 | smp_rmb(); | ||
| 460 | count = head->commit - head->read; | 464 | count = head->commit - head->read; |
| 461 | if (!count) { | 465 | if (!count) { |
| 462 | if (head->next == NULL) { | 466 | if (next == NULL) |
| 463 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
| 464 | break; | 467 | break; |
| 465 | } | 468 | buf->head = next; |
| 466 | buf->head = head->next; | ||
| 467 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
| 468 | tty_buffer_free(port, head); | 469 | tty_buffer_free(port, head); |
| 469 | continue; | 470 | continue; |
| 470 | } | 471 | } |
| 471 | spin_unlock_irqrestore(&buf->flush_lock, flags); | ||
| 472 | 472 | ||
| 473 | count = receive_buf(tty, head, count); | 473 | count = receive_buf(tty, head, count); |
| 474 | if (!count) | 474 | if (!count) |
| @@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port) | |||
| 523 | struct tty_bufhead *buf = &port->buf; | 523 | struct tty_bufhead *buf = &port->buf; |
| 524 | 524 | ||
| 525 | mutex_init(&buf->lock); | 525 | mutex_init(&buf->lock); |
| 526 | spin_lock_init(&buf->flush_lock); | ||
| 527 | tty_buffer_reset(&buf->sentinel, 0); | 526 | tty_buffer_reset(&buf->sentinel, 0); |
| 528 | buf->head = &buf->sentinel; | 527 | buf->head = &buf->sentinel; |
| 529 | buf->tail = &buf->sentinel; | 528 | buf->tail = &buf->sentinel; |
