diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r-- | drivers/tty/tty_buffer.c | 22 |
1 files changed, 17 insertions, 5 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 5a3fa8913880..a660ab181cca 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c | |||
@@ -242,7 +242,10 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) | |||
242 | atomic_inc(&buf->priority); | 242 | atomic_inc(&buf->priority); |
243 | 243 | ||
244 | mutex_lock(&buf->lock); | 244 | mutex_lock(&buf->lock); |
245 | while ((next = buf->head->next) != NULL) { | 245 | /* paired w/ release in __tty_buffer_request_room; ensures there are |
246 | * no pending memory accesses to the freed buffer | ||
247 | */ | ||
248 | while ((next = smp_load_acquire(&buf->head->next)) != NULL) { | ||
246 | tty_buffer_free(port, buf->head); | 249 | tty_buffer_free(port, buf->head); |
247 | buf->head = next; | 250 | buf->head = next; |
248 | } | 251 | } |
@@ -290,7 +293,10 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, | |||
290 | if (n != NULL) { | 293 | if (n != NULL) { |
291 | n->flags = flags; | 294 | n->flags = flags; |
292 | buf->tail = n; | 295 | buf->tail = n; |
293 | b->commit = b->used; | 296 | /* paired w/ acquire in flush_to_ldisc(); ensures |
297 | * flush_to_ldisc() sees buffer data. | ||
298 | */ | ||
299 | smp_store_release(&b->commit, b->used); | ||
294 | /* paired w/ acquire in flush_to_ldisc(); ensures the | 300 | /* paired w/ acquire in flush_to_ldisc(); ensures the |
295 | * latest commit value can be read before the head is | 301 | * latest commit value can be read before the head is |
296 | * advanced to the next buffer | 302 | * advanced to the next buffer |
@@ -393,7 +399,10 @@ void tty_schedule_flip(struct tty_port *port) | |||
393 | { | 399 | { |
394 | struct tty_bufhead *buf = &port->buf; | 400 | struct tty_bufhead *buf = &port->buf; |
395 | 401 | ||
396 | buf->tail->commit = buf->tail->used; | 402 | /* paired w/ acquire in flush_to_ldisc(); ensures |
403 | * flush_to_ldisc() sees buffer data. | ||
404 | */ | ||
405 | smp_store_release(&buf->tail->commit, buf->tail->used); | ||
397 | schedule_work(&buf->work); | 406 | schedule_work(&buf->work); |
398 | } | 407 | } |
399 | EXPORT_SYMBOL(tty_schedule_flip); | 408 | EXPORT_SYMBOL(tty_schedule_flip); |
@@ -467,7 +476,7 @@ static void flush_to_ldisc(struct work_struct *work) | |||
467 | struct tty_struct *tty; | 476 | struct tty_struct *tty; |
468 | struct tty_ldisc *disc; | 477 | struct tty_ldisc *disc; |
469 | 478 | ||
470 | tty = port->itty; | 479 | tty = READ_ONCE(port->itty); |
471 | if (tty == NULL) | 480 | if (tty == NULL) |
472 | return; | 481 | return; |
473 | 482 | ||
@@ -491,7 +500,10 @@ static void flush_to_ldisc(struct work_struct *work) | |||
491 | * is advancing to the next buffer | 500 | * is advancing to the next buffer |
492 | */ | 501 | */ |
493 | next = smp_load_acquire(&head->next); | 502 | next = smp_load_acquire(&head->next); |
494 | count = head->commit - head->read; | 503 | /* paired w/ release in __tty_buffer_request_room() or in |
504 | * tty_buffer_flush(); ensures we see the committed buffer data | ||
505 | */ | ||
506 | count = smp_load_acquire(&head->commit) - head->read; | ||
495 | if (!count) { | 507 | if (!count) { |
496 | if (next == NULL) { | 508 | if (next == NULL) { |
497 | check_other_closed(tty); | 509 | check_other_closed(tty); |