M. Feser KBS GmbH
Date: 14.01.2019

This patch modifies the TTY implementation to make use of a kthread for
low latency configuration

Modifications:
adjusted line numbers
adjusted prio

Based on the following patch series from Steven Walter (08.06.2015):
drivers/tty: refactor functions for flushing/queuing work
drivers/tty: convert tty_port to use kthread_worker
drivers/tty/tty_buffer.c: use a separate kthread for low_latency
-------------------------------------------------------------------------
diff -ruPN a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
--- a/drivers/tty/tty_buffer.c	2015-08-17 05:52:51.000000000 +0200
+++ b/drivers/tty/tty_buffer.c	2015-08-28 09:13:56.604475262 +0200
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/kthread.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.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>
@@ -71,7 +73,7 @@
 	atomic_dec(&buf->priority);
 	mutex_unlock(&buf->lock);
 	if (restart)
-		queue_work(system_unbound_wq, &buf->work);
+		tty_buffer_restart_work(port);
 }
 EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
 
@@ -404,7 +406,7 @@
	 * flush_to_ldisc() sees buffer data.
	 */
	smp_store_release(&buf->tail->commit, buf->tail->used);
-	queue_work(system_unbound_wq, &buf->work);
+	tty_buffer_restart_work(port);
 }
 EXPORT_SYMBOL(tty_schedule_flip);
 
@@ -489,7 +491,7 @@
  *		 '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;
@@ -560,6 +562,20 @@
 }
 EXPORT_SYMBOL(tty_flip_buffer_push);
 
+static DEFINE_KTHREAD_WORKER(tty_buffer_worker);
+static DEFINE_KTHREAD_WORKER(tty_buffer_worker_ll);
+
+void tty_buffer_init_kthread()
+{
+	struct task_struct *task;
+	struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/4 };
+
+ 	kthread_run(kthread_worker_fn, &tty_buffer_worker, "tty");
+	task = kthread_run(kthread_worker_fn, &tty_buffer_worker_ll,
+			   "tty-low-latency");
+	sched_setscheduler(task, SCHED_FIFO, &param);
+}
+
 /**
  *	tty_buffer_init		-	prepare a tty buffer structure
  *	@tty: tty to initialise
@@ -579,7 +595,7 @@
 	init_llist_head(&buf->free);
 	atomic_set(&buf->mem_used, 0);
 	atomic_set(&buf->priority, 0);
-	INIT_WORK(&buf->work, flush_to_ldisc);
+	kthread_init_work(&buf->work, flush_to_ldisc);
 	buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
 }
 
@@ -608,15 +624,19 @@
 
 bool tty_buffer_restart_work(struct tty_port *port)
 {
-	return queue_work(system_unbound_wq, &port->buf.work);
+	if (port->low_latency)
+		return kthread_queue_work(&tty_buffer_worker_ll, &port->buf.work);
+	else
+		return kthread_queue_work(&tty_buffer_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);
+	kthread_flush_work(&port->buf.work);
 }
diff -ruPN a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
--- a/drivers/tty/tty_io.c	2015-08-17 05:52:51.000000000 +0200
+++ b/drivers/tty/tty_io.c	2015-08-28 08:47:18.044489672 +0200
@@ -3696,6 +3696,7 @@
  */
 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 -ruPN a/include/linux/tty.h b/include/linux/tty.h
--- a/include/linux/tty.h	2015-08-17 05:52:51.000000000 +0200
+++ b/include/linux/tty.h	2015-08-28 08:49:52.368488281 +0200
@@ -3,8 +3,8 @@
 
 #include <linux/fs.h>
 #include <linux/major.h>
+#include <linux/kthread.h>
 #include <linux/termios.h>
-#include <linux/workqueue.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_ldisc.h>
 #include <linux/mutex.h>
@@ -82,7 +82,7 @@
 
 struct tty_bufhead {
 	struct tty_buffer *head;	/* Queue head */
-	struct work_struct work;
+	struct kthread_work work;
 	struct mutex	   lock;
 	atomic_t	   priority;
 	struct tty_buffer sentinel;
@@ -481,6 +481,7 @@
 extern void tty_buffer_free_all(struct tty_port *port);
 extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld);
 extern void tty_buffer_init(struct tty_port *port);
+extern void tty_buffer_init_kthread(void);
 extern void tty_buffer_set_lock_subclass(struct tty_port *port);
 extern bool tty_buffer_restart_work(struct tty_port *port);
 extern bool tty_buffer_cancel_work(struct tty_port *port);
