Skip to content

Commit

Permalink
tty: stop using "delayed_work" in the tty layer
Browse files Browse the repository at this point in the history
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: Jef Driesen <jefdriesen@telenet.be>
Acked-by: Greg KH <gregkh@suse.de>
Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
torvalds committed Mar 22, 2011
1 parent f741a79 commit f23eb2b
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 16 deletions.
14 changes: 7 additions & 7 deletions drivers/tty/tty_buffer.c
Expand Up @@ -322,7 +322,7 @@ void tty_schedule_flip(struct tty_struct *tty)
if (tty->buf.tail != NULL) if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used; tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags); spin_unlock_irqrestore(&tty->buf.lock, flags);
schedule_delayed_work(&tty->buf.work, 1); schedule_work(&tty->buf.work);
} }
EXPORT_SYMBOL(tty_schedule_flip); EXPORT_SYMBOL(tty_schedule_flip);


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


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


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


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


Expand All @@ -529,6 +529,6 @@ void tty_buffer_init(struct tty_struct *tty)
tty->buf.tail = NULL; tty->buf.tail = NULL;
tty->buf.free = NULL; tty->buf.free = NULL;
tty->buf.memory_used = 0; tty->buf.memory_used = 0;
INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); INIT_WORK(&tty->buf.work, flush_to_ldisc);
} }


14 changes: 7 additions & 7 deletions drivers/tty/tty_ldisc.c
Expand Up @@ -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) static int tty_ldisc_halt(struct tty_struct *tty)
{ {
clear_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_LDISC, &tty->flags);
return cancel_delayed_work_sync(&tty->buf.work); return cancel_work_sync(&tty->buf.work);
} }


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


/** /**
Expand Down Expand Up @@ -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 /* Restart the work queue in case no characters kick it off. Safe if
already running */ already running */
if (work) if (work)
schedule_delayed_work(&tty->buf.work, 1); schedule_work(&tty->buf.work);
if (o_work) if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1); schedule_work(&o_tty->buf.work);
mutex_unlock(&tty->ldisc_mutex); mutex_unlock(&tty->ldisc_mutex);
tty_unlock(); tty_unlock();
return retval; return retval;
Expand Down Expand Up @@ -830,12 +830,12 @@ void tty_ldisc_hangup(struct tty_struct *tty)


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


tty_lock(); tty_lock();
Expand Down
2 changes: 1 addition & 1 deletion include/linux/kbd_kern.h
Expand Up @@ -159,7 +159,7 @@ static inline void con_schedule_flip(struct tty_struct *t)
if (t->buf.tail != NULL) if (t->buf.tail != NULL)
t->buf.tail->commit = t->buf.tail->used; t->buf.tail->commit = t->buf.tail->used;
spin_unlock_irqrestore(&t->buf.lock, flags); spin_unlock_irqrestore(&t->buf.lock, flags);
schedule_delayed_work(&t->buf.work, 0); schedule_work(&t->buf.work);
} }


#endif #endif
2 changes: 1 addition & 1 deletion include/linux/tty.h
Expand Up @@ -82,7 +82,7 @@ struct tty_buffer {




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

0 comments on commit f23eb2b

Please sign in to comment.