Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f23eb2b2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

tty: stop using "delayed_work" in the tty layer



Using delayed-work for tty flip buffers ends up causing us to wait for
the next tick to complete some actions.  That's usually not all that
noticeable, but for certain latency-critical workloads it ends up being
totally unacceptable.

As an extreme case of this, passing a token back-and-forth over a pty
will take two ticks per iteration, so even just a thousand iterations
will take 8 seconds assuming a common 250Hz configuration.

Avoiding the whole delayed work issue brings that ping-pong test-case
down to 0.009s on my machine.

In more practical terms, this latency has been a performance problem for
things like dive computer simulators (simulating the serial interface
using the ptys) and for other environments (Alan mentions a CP/M emulator).

Reported-by: default avatarJef Driesen <jefdriesen@telenet.be>
Acked-by: default avatarGreg KH <gregkh@suse.de>
Acked-by: default avatarAlan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f741a79e
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -322,7 +322,7 @@ void tty_schedule_flip(struct tty_struct *tty)
	if (tty->buf.tail != NULL)
		tty->buf.tail->commit = tty->buf.tail->used;
	spin_unlock_irqrestore(&tty->buf.lock, flags);
	schedule_delayed_work(&tty->buf.work, 1);
	schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_schedule_flip);

@@ -402,7 +402,7 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
static void flush_to_ldisc(struct work_struct *work)
{
	struct tty_struct *tty =
		container_of(work, struct tty_struct, buf.work.work);
		container_of(work, struct tty_struct, buf.work);
	unsigned long 	flags;
	struct tty_ldisc *disc;

@@ -443,7 +443,7 @@ static void flush_to_ldisc(struct work_struct *work)
			if (test_bit(TTY_FLUSHPENDING, &tty->flags))
				break;
			if (!tty->receive_room || seen_tail) {
				schedule_delayed_work(&tty->buf.work, 1);
				schedule_work(&tty->buf.work);
				break;
			}
			if (count > tty->receive_room)
@@ -481,7 +481,7 @@ static void flush_to_ldisc(struct work_struct *work)
 */
void tty_flush_to_ldisc(struct tty_struct *tty)
{
	flush_delayed_work(&tty->buf.work);
	flush_work(&tty->buf.work);
}

/**
@@ -506,9 +506,9 @@ void tty_flip_buffer_push(struct tty_struct *tty)
	spin_unlock_irqrestore(&tty->buf.lock, flags);

	if (tty->low_latency)
		flush_to_ldisc(&tty->buf.work.work);
		flush_to_ldisc(&tty->buf.work);
	else
		schedule_delayed_work(&tty->buf.work, 1);
		schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_flip_buffer_push);

@@ -529,6 +529,6 @@ void tty_buffer_init(struct tty_struct *tty)
	tty->buf.tail = NULL;
	tty->buf.free = NULL;
	tty->buf.memory_used = 0;
	INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
	INIT_WORK(&tty->buf.work, flush_to_ldisc);
}
+7 −7
Original line number Diff line number Diff line
@@ -529,7 +529,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
static int tty_ldisc_halt(struct tty_struct *tty)
{
	clear_bit(TTY_LDISC, &tty->flags);
	return cancel_delayed_work_sync(&tty->buf.work);
	return cancel_work_sync(&tty->buf.work);
}

/**
@@ -542,7 +542,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
{
	flush_work_sync(&tty->hangup_work);
	flush_work_sync(&tty->SAK_work);
	flush_delayed_work_sync(&tty->buf.work);
	flush_work_sync(&tty->buf.work);
}

/**
@@ -722,9 +722,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	/* Restart the work queue in case no characters kick it off. Safe if
	   already running */
	if (work)
		schedule_delayed_work(&tty->buf.work, 1);
		schedule_work(&tty->buf.work);
	if (o_work)
		schedule_delayed_work(&o_tty->buf.work, 1);
		schedule_work(&o_tty->buf.work);
	mutex_unlock(&tty->ldisc_mutex);
	tty_unlock();
	return retval;
@@ -830,12 +830,12 @@ void tty_ldisc_hangup(struct tty_struct *tty)

	/*
	 * this is like tty_ldisc_halt, but we need to give up
	 * the BTM before calling cancel_delayed_work_sync,
	 * which may need to wait for another function taking the BTM
	 * the BTM before calling cancel_work_sync, which may
	 * need to wait for another function taking the BTM
	 */
	clear_bit(TTY_LDISC, &tty->flags);
	tty_unlock();
	cancel_delayed_work_sync(&tty->buf.work);
	cancel_work_sync(&tty->buf.work);
	mutex_unlock(&tty->ldisc_mutex);

	tty_lock();
+1 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ static inline void con_schedule_flip(struct tty_struct *t)
	if (t->buf.tail != NULL)
		t->buf.tail->commit = t->buf.tail->used;
	spin_unlock_irqrestore(&t->buf.lock, flags);
	schedule_delayed_work(&t->buf.work, 0);
	schedule_work(&t->buf.work);
}

#endif
+1 −1
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ struct tty_buffer {


struct tty_bufhead {
	struct delayed_work work;
	struct work_struct work;
	spinlock_t lock;
	struct tty_buffer *head;	/* Queue head */
	struct tty_buffer *tail;	/* Active buffer */