forked from mirrors/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
					
				
					 4 changed files with 16 additions and 16 deletions
				
			
		| 
						 | 
					@ -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 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -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 a new issue