forked from Minki/linux
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: 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>
This commit is contained in:
parent
f741a79e98
commit
f23eb2b2b2
@ -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);
|
||||||
|
|
||||||
@ -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;
|
||||||
|
|
||||||
@ -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)
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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);
|
||||||
|
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -722,9 +722,9 @@ enable:
|
|||||||
/* 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;
|
||||||
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user