diff options
Diffstat (limited to 'drivers/tty/tty_buffer.c')
| -rw-r--r-- | drivers/tty/tty_buffer.c | 131 | 
1 files changed, 42 insertions, 89 deletions
| diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 45d916198f78..bb119934e76c 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -16,6 +16,7 @@  #include <linux/bitops.h>  #include <linux/delay.h>  #include <linux/module.h> +#include <linux/ratelimit.h>  /**   *	tty_buffer_free_all		-	free buffers used by a tty @@ -119,11 +120,14 @@ static void __tty_buffer_flush(struct tty_port *port)  	struct tty_bufhead *buf = &port->buf;  	struct tty_buffer *thead; -	while ((thead = buf->head) != NULL) { -		buf->head = thead->next; -		tty_buffer_free(port, thead); +	if (unlikely(buf->head == NULL)) +		return; +	while ((thead = buf->head->next) != NULL) { +		tty_buffer_free(port, buf->head); +		buf->head = thead;  	} -	buf->tail = NULL; +	WARN_ON(buf->head != buf->tail); +	buf->head->read = buf->head->commit;  }  /** @@ -194,19 +198,22 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size)  	   have queued and recycle that ? */  }  /** - *	__tty_buffer_request_room		-	grow tty buffer if needed + *	tty_buffer_request_room		-	grow tty buffer if needed   *	@tty: tty structure   *	@size: size desired   *   *	Make at least size bytes of linear space available for the tty   *	buffer. If we fail return the size we managed to find. - *      Locking: Caller must hold port->buf.lock + * + *	Locking: Takes port->buf.lock   */ -static int __tty_buffer_request_room(struct tty_port *port, size_t size) +int tty_buffer_request_room(struct tty_port *port, size_t size)  {  	struct tty_bufhead *buf = &port->buf;  	struct tty_buffer *b, *n;  	int left; +	unsigned long flags; +	spin_lock_irqsave(&buf->lock, flags);  	/* OPTIMISATION: We could keep a per tty "zero" sized buffer to  	   remove this conditional if its worth it. This would be invisible  	   to the callers */ @@ -228,37 +235,14 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size)  		} else  			size = left;  	} - +	spin_unlock_irqrestore(&buf->lock, flags);  	return size;  } - - -/** - *	tty_buffer_request_room		-	grow tty buffer if needed - *	@tty: tty structure - *	@size: size desired - * - *	Make at least size bytes of linear space available for the tty - *	buffer. If we fail return the size we managed to find. - * - *	Locking: Takes port->buf.lock - */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) -{ -	struct tty_port *port = tty->port; -	unsigned long flags; -	int length; - -	spin_lock_irqsave(&port->buf.lock, flags); -	length = __tty_buffer_request_room(port, size); -	spin_unlock_irqrestore(&port->buf.lock, flags); -	return length; -}  EXPORT_SYMBOL_GPL(tty_buffer_request_room);  /**   *	tty_insert_flip_string_fixed_flag - Add characters to the tty buffer - *	@tty: tty structure + *	@port: tty port   *	@chars: characters   *	@flag: flag value for each character   *	@size: size @@ -269,29 +253,21 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room);   *	Locking: Called functions may take port->buf.lock   */ -int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, +int tty_insert_flip_string_fixed_flag(struct tty_port *port,  		const unsigned char *chars, char flag, size_t size)  { -	struct tty_bufhead *buf = &tty->port->buf;  	int copied = 0;  	do {  		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); -		int space; -		unsigned long flags; -		struct tty_buffer *tb; - -		spin_lock_irqsave(&buf->lock, flags); -		space = __tty_buffer_request_room(tty->port, goal); -		tb = buf->tail; +		int space = tty_buffer_request_room(port, goal); +		struct tty_buffer *tb = port->buf.tail;  		/* If there is no space then tb may be NULL */  		if (unlikely(space == 0)) { -			spin_unlock_irqrestore(&buf->lock, flags);  			break;  		}  		memcpy(tb->char_buf_ptr + tb->used, chars, space);  		memset(tb->flag_buf_ptr + tb->used, flag, space);  		tb->used += space; -		spin_unlock_irqrestore(&buf->lock, flags);  		copied += space;  		chars += space;  		/* There is a small chance that we need to split the data over @@ -303,7 +279,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);  /**   *	tty_insert_flip_string_flags	-	Add characters to the tty buffer - *	@tty: tty structure + *	@port: tty port   *	@chars: characters   *	@flags: flag bytes   *	@size: size @@ -315,29 +291,21 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);   *	Locking: Called functions may take port->buf.lock   */ -int tty_insert_flip_string_flags(struct tty_struct *tty, +int tty_insert_flip_string_flags(struct tty_port *port,  		const unsigned char *chars, const char *flags, size_t size)  { -	struct tty_bufhead *buf = &tty->port->buf;  	int copied = 0;  	do {  		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); -		int space; -		unsigned long __flags; -		struct tty_buffer *tb; - -		spin_lock_irqsave(&buf->lock, __flags); -		space = __tty_buffer_request_room(tty->port, goal); -		tb = buf->tail; +		int space = tty_buffer_request_room(port, goal); +		struct tty_buffer *tb = port->buf.tail;  		/* If there is no space then tb may be NULL */  		if (unlikely(space == 0)) { -			spin_unlock_irqrestore(&buf->lock, __flags);  			break;  		}  		memcpy(tb->char_buf_ptr + tb->used, chars, space);  		memcpy(tb->flag_buf_ptr + tb->used, flags, space);  		tb->used += space; -		spin_unlock_irqrestore(&buf->lock, __flags);  		copied += space;  		chars += space;  		flags += space; @@ -350,7 +318,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);  /**   *	tty_schedule_flip	-	push characters to ldisc - *	@tty: tty to push from + *	@port: tty port to push from   *   *	Takes any pending buffers and transfers their ownership to the   *	ldisc side of the queue. It then schedules those characters for @@ -361,11 +329,11 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);   *	Locking: Takes port->buf.lock   */ -void tty_schedule_flip(struct tty_struct *tty) +void tty_schedule_flip(struct tty_port *port)  { -	struct tty_bufhead *buf = &tty->port->buf; +	struct tty_bufhead *buf = &port->buf;  	unsigned long flags; -	WARN_ON(tty->low_latency); +	WARN_ON(port->low_latency);  	spin_lock_irqsave(&buf->lock, flags);  	if (buf->tail != NULL) @@ -377,7 +345,7 @@ EXPORT_SYMBOL(tty_schedule_flip);  /**   *	tty_prepare_flip_string		-	make room for characters - *	@tty: tty + *	@port: tty port   *	@chars: return pointer for character write area   *	@size: desired size   * @@ -390,31 +358,23 @@ EXPORT_SYMBOL(tty_schedule_flip);   *	Locking: May call functions taking port->buf.lock   */ -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, +int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,  		size_t size)  { -	struct tty_bufhead *buf = &tty->port->buf; -	int space; -	unsigned long flags; -	struct tty_buffer *tb; - -	spin_lock_irqsave(&buf->lock, flags); -	space = __tty_buffer_request_room(tty->port, size); - -	tb = buf->tail; +	int space = tty_buffer_request_room(port, size);  	if (likely(space)) { +		struct tty_buffer *tb = port->buf.tail;  		*chars = tb->char_buf_ptr + tb->used;  		memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);  		tb->used += space;  	} -	spin_unlock_irqrestore(&buf->lock, flags);  	return space;  }  EXPORT_SYMBOL_GPL(tty_prepare_flip_string);  /**   *	tty_prepare_flip_string_flags	-	make room for characters - *	@tty: tty + *	@port: tty port   *	@chars: return pointer for character write area   *	@flags: return pointer for status flag write area   *	@size: desired size @@ -428,24 +388,16 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);   *	Locking: May call functions taking port->buf.lock   */ -int tty_prepare_flip_string_flags(struct tty_struct *tty, +int tty_prepare_flip_string_flags(struct tty_port *port,  			unsigned char **chars, char **flags, size_t size)  { -	struct tty_bufhead *buf = &tty->port->buf; -	int space; -	unsigned long __flags; -	struct tty_buffer *tb; - -	spin_lock_irqsave(&buf->lock, __flags); -	space = __tty_buffer_request_room(tty->port, size); - -	tb = buf->tail; +	int space = tty_buffer_request_room(port, size);  	if (likely(space)) { +		struct tty_buffer *tb = port->buf.tail;  		*chars = tb->char_buf_ptr + tb->used;  		*flags = tb->flag_buf_ptr + tb->used;  		tb->used += space;  	} -	spin_unlock_irqrestore(&buf->lock, __flags);  	return space;  }  EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); @@ -539,16 +491,17 @@ static void flush_to_ldisc(struct work_struct *work)   */  void tty_flush_to_ldisc(struct tty_struct *tty)  { -	if (!tty->low_latency) +	if (!tty->port->low_latency)  		flush_work(&tty->port->buf.work);  }  /**   *	tty_flip_buffer_push	-	terminal - *	@tty: tty to push + *	@port: tty port to push   *   *	Queue a push of the terminal flip buffers to the line discipline. This - *	function must not be called from IRQ context if tty->low_latency is set. + *	function must not be called from IRQ context if port->low_latency is + *	set.   *   *	In the event of the queue being busy for flipping the work will be   *	held off and retried later. @@ -556,9 +509,9 @@ void tty_flush_to_ldisc(struct tty_struct *tty)   *	Locking: tty buffer lock. Driver locks in low latency mode.   */ -void tty_flip_buffer_push(struct tty_struct *tty) +void tty_flip_buffer_push(struct tty_port *port)  { -	struct tty_bufhead *buf = &tty->port->buf; +	struct tty_bufhead *buf = &port->buf;  	unsigned long flags;  	spin_lock_irqsave(&buf->lock, flags); @@ -566,7 +519,7 @@ void tty_flip_buffer_push(struct tty_struct *tty)  		buf->tail->commit = buf->tail->used;  	spin_unlock_irqrestore(&buf->lock, flags); -	if (tty->low_latency) +	if (port->low_latency)  		flush_to_ldisc(&buf->work);  	else  		schedule_work(&buf->work); |