Re: [PATCH v3 2/2] drivers/tty: use a kthread_worker for low-latency

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 06/09/2015 02:05 PM, Steven Walter wrote:
> Use a dedicated kthread for handling ports marked as low_latency.  Since
> this thread is RT_FIFO, it is not subject to the same types of
> starvation as the normal priority kworker threads.

This is not a problem unique to the tty subsystem; many subsystems use
kworkers to handle i/o after the initial ISR.

Without careful design, high-prio userspace RT threads can effectively starve
themselves of i/o.

In any event, solutions to this problem belong either in the core workqueue
(for example, an i/o-specific unbounded workqueue) or in the CONFIG_PREEMPT_RT
patch.

Regards,
Peter Hurley


> Some UART applications are very latency sensitive.  In at least one case
> (motor controller card controlled over RS-232), a reply needs to be sent
> within about 1ms after receiving a byte.  Without this patch, normal
> priority kworker threads can be delayed substantially by userspace
> real-time threads.  This has been observed to cause latencies up to
> 100ms.  With this patch, no occurences of communications failure were
> observed, meaning the 1ms latency is reliably achieved.
> 
> Signed-off-by: Steven Walter <stevenrwalter@xxxxxxxxx>
> ---
>  drivers/tty/tty_buffer.c | 39 +++++++++++++++++++++++++++++++++++----
>  drivers/tty/tty_io.c     |  1 +
>  include/linux/tty.h      |  3 +++
>  3 files changed, 39 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
> index ed7b5c8..0560ca7 100644
> --- a/drivers/tty/tty_buffer.c
> +++ b/drivers/tty/tty_buffer.c
> @@ -3,6 +3,7 @@
>   */
>  
>  #include <linux/types.h>
> +#include <linux/kthread.h>
>  #include <linux/errno.h>
>  #include <linux/tty.h>
>  #include <linux/tty_driver.h>
> @@ -11,6 +12,7 @@
>  #include <linux/string.h>
>  #include <linux/slab.h>
>  #include <linux/sched.h>
> +#include <linux/sched/prio.h>
>  #include <linux/wait.h>
>  #include <linux/bitops.h>
>  #include <linux/delay.h>
> @@ -431,9 +433,8 @@ 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 tty_port *port)
>  {
> -	struct tty_port *port = container_of(work, struct tty_port, buf.work);
>  	struct tty_bufhead *buf = &port->buf;
>  	struct tty_struct *tty;
>  	struct tty_ldisc *disc;
> @@ -482,6 +483,18 @@ static void flush_to_ldisc(struct work_struct *work)
>  	tty_ldisc_deref(disc);
>  }
>  
> +static void flush_to_ldisc_kthread(struct kthread_work *work)
> +{
> +	struct tty_port *port = container_of(work, struct tty_port, buf.kwork);
> +	flush_to_ldisc(port);
> +}
> +
> +static void flush_to_ldisc_workq(struct work_struct *work)
> +{
> +	struct tty_port *port = container_of(work, struct tty_port, buf.work);
> +	flush_to_ldisc(port);
> +}
> +
>  /**
>   *	tty_flush_to_ldisc
>   *	@tty: tty to push
> @@ -520,19 +533,36 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
>   *	Must be called before the other tty buffer functions are used.
>   */
>  
> +static DEFINE_KTHREAD_WORKER(tty_buffer_worker);
>  
>  void tty_buffer_queue_work(struct tty_port *port)
>  {
>  	struct tty_bufhead *buf = &port->buf;
> -	schedule_work(&buf->work);
> +	if (port->low_latency)
> +		queue_kthread_work(&tty_buffer_worker, &buf->kwork);
> +	else
> +		schedule_work(&buf->work);
>  }
>  
>  void tty_buffer_flush_work(struct tty_port *port)
>  {
>  	struct tty_bufhead *buf = &port->buf;
> +	/* Flush both so we don't have to worry about racing with
> +	 * changes to port->low_latency */
> +	flush_kthread_work(&buf->kwork);
>  	flush_work(&buf->work);
>  }
>  
> +void tty_buffer_init_kthread()
> +{
> +	struct task_struct *task;
> +	/* Use same default priority as threaded irq handlers */
> +	struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2 };
> +
> +	task = kthread_run(kthread_worker_fn, &tty_buffer_worker, "tty");
> +	sched_setscheduler(task, SCHED_FIFO, &param);
> +}
> +
>  void tty_buffer_init(struct tty_port *port)
>  {
>  	struct tty_bufhead *buf = &port->buf;
> @@ -544,7 +574,8 @@ 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);
> +	INIT_WORK(&buf->work, flush_to_ldisc_workq);
> +	init_kthread_work(&buf->kwork, flush_to_ldisc_kthread);
>  	buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
>  }
>  
> diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
> index c288473..abcd9a5 100644
> --- a/drivers/tty/tty_io.c
> +++ b/drivers/tty/tty_io.c
> @@ -3602,6 +3602,7 @@ void console_sysfs_notify(void)
>   */
>  int __init tty_init(void)
>  {
> +	tty_buffer_init_kthread();
>  	cdev_init(&tty_cdev, &tty_fops);
>  	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
>  	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
> diff --git a/include/linux/tty.h b/include/linux/tty.h
> index 7bad787..4dc584d 100644
> --- a/include/linux/tty.h
> +++ b/include/linux/tty.h
> @@ -5,6 +5,7 @@
>  #include <linux/major.h>
>  #include <linux/termios.h>
>  #include <linux/workqueue.h>
> +#include <linux/kthread.h>
>  #include <linux/tty_driver.h>
>  #include <linux/tty_ldisc.h>
>  #include <linux/mutex.h>
> @@ -60,6 +61,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs)
>  struct tty_bufhead {
>  	struct tty_buffer *head;	/* Queue head */
>  	struct work_struct work;
> +	struct kthread_work kwork;
>  	struct mutex	   lock;
>  	atomic_t	   priority;
>  	struct tty_buffer sentinel;
> @@ -448,6 +450,7 @@ extern void tty_buffer_flush(struct tty_struct *tty);
>  extern void tty_buffer_init(struct tty_port *port);
>  extern void tty_buffer_queue_work(struct tty_port *port);
>  extern void tty_buffer_flush_work(struct tty_port *port);
> +extern void tty_buffer_init_kthread(void);
>  extern speed_t tty_termios_baud_rate(struct ktermios *termios);
>  extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
>  extern void tty_termios_encode_baud_rate(struct ktermios *termios,
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux