diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
-rw-r--r-- | drivers/tty/tty_buffer.c | 79 |
1 files changed, 67 insertions, 12 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 4706df20191b..49c6cbab984f 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -71,7 +71,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) atomic_dec(&buf->priority); mutex_unlock(&buf->lock); if (restart) - queue_work(system_unbound_wq, &buf->work); + queue_kthread_work(&port->worker, &buf->work); } EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); @@ -132,6 +132,8 @@ void tty_buffer_free_all(struct tty_port *port) buf->tail = &buf->sentinel; atomic_set(&buf->mem_used, 0); + if (!IS_ERR_OR_NULL(port->worker_thread)) + kthread_stop(port->worker_thread); } /** @@ -166,7 +168,8 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) have queued and recycle that ? */ if (atomic_read(&port->buf.mem_used) > port->buf.mem_limit) return NULL; - p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); + p = kmalloc(sizeof(struct tty_buffer) + 2 * size, + GFP_ATOMIC | __GFP_NOWARN); if (p == NULL) return NULL; @@ -387,6 +390,15 @@ int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag) } EXPORT_SYMBOL(__tty_insert_flip_char); +static inline void tty_flip_buffer_commit(struct tty_buffer *tail) +{ + /* + * Paired w/ acquire in flush_to_ldisc(); ensures flush_to_ldisc() sees + * buffer data. + */ + smp_store_release(&tail->commit, tail->used); +} + /** * tty_schedule_flip - push characters to ldisc * @port: tty port to push from @@ -400,11 +412,8 @@ void tty_schedule_flip(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - /* paired w/ acquire in flush_to_ldisc(); ensures - * flush_to_ldisc() sees buffer data. - */ - smp_store_release(&buf->tail->commit, buf->tail->used); - queue_work(system_unbound_wq, &buf->work); + tty_flip_buffer_commit(buf->tail); + queue_kthread_work(&port->worker, &buf->work); } EXPORT_SYMBOL(tty_schedule_flip); @@ -472,7 +481,7 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) * 'consumer' */ -static void flush_to_ldisc(struct work_struct *work) +static void flush_to_ldisc(struct kthread_work *work) { struct tty_port *port = container_of(work, struct tty_port, buf.work); struct tty_bufhead *buf = &port->buf; @@ -519,6 +528,9 @@ static void flush_to_ldisc(struct work_struct *work) if (!count) break; head->read += count; + + if (need_resched()) + cond_resched(); } mutex_unlock(&buf->lock); @@ -544,6 +556,37 @@ void tty_flip_buffer_push(struct tty_port *port) EXPORT_SYMBOL(tty_flip_buffer_push); /** + * tty_insert_flip_string_and_push_buffer - add characters to the tty buffer and + * push + * @port: tty port + * @chars: characters + * @size: size + * + * The function combines tty_insert_flip_string() and tty_flip_buffer_push() + * with the exception of properly holding the @port->lock. + * + * To be used only internally (by pty currently). + * + * Returns: the number added. + */ +int tty_insert_flip_string_and_push_buffer(struct tty_port *port, + const unsigned char *chars, size_t size) +{ + struct tty_bufhead *buf = &port->buf; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + size = tty_insert_flip_string(port, chars, size); + if (size) + tty_flip_buffer_commit(buf->tail); + spin_unlock_irqrestore(&port->lock, flags); + + queue_kthread_work(&port->worker, &buf->work); + + return size; +} + +/** * tty_buffer_init - prepare a tty buffer structure * @tty: tty to initialise * @@ -562,8 +605,20 @@ void tty_buffer_init(struct tty_port *port) init_llist_head(&buf->free); atomic_set(&buf->mem_used, 0); atomic_set(&buf->priority, 0); - INIT_WORK(&buf->work, flush_to_ldisc); buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT; + init_kthread_work(&buf->work, flush_to_ldisc); + init_kthread_worker(&port->worker); + port->worker_thread = kthread_run(kthread_worker_fn, &port->worker, + "tty_worker_thread"); + if (IS_ERR(port->worker_thread)) { + /* + * Not good, we can't unwind, this tty is going to be really + * sad... + */ + pr_err("Unable to start tty_worker_thread\n"); + } + + } /** @@ -591,15 +646,15 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) bool tty_buffer_restart_work(struct tty_port *port) { - return queue_work(system_unbound_wq, &port->buf.work); + return queue_kthread_work(&port->worker, &port->buf.work); } bool tty_buffer_cancel_work(struct tty_port *port) { - return cancel_work_sync(&port->buf.work); + return kthread_cancel_work_sync(&port->buf.work); } void tty_buffer_flush_work(struct tty_port *port) { - flush_work(&port->buf.work); + flush_kthread_work(&port->buf.work); } |