Commit c56a00a1 authored by Xiaobing Tu's avatar Xiaobing Tu Committed by Greg Kroah-Hartman

tty: hold lock across tty buffer finding and buffer filling

tty_buffer_request_room is well protected, but while after it returns,
 it releases the port->lock. tty->buf.tail might be modified
by either irq handler or other threads. The patch adds more protection
by holding the lock across tty buffer finding and buffer filling.
Signed-off-by: default avatarAlek Du <alek.du@intel.com>
Signed-off-by: default avatarXiaobing Tu <xiaobing.tu@intel.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 871bdea6
...@@ -185,25 +185,19 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) ...@@ -185,25 +185,19 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
/* Should possibly check if this fails for the largest buffer we /* Should possibly check if this fails for the largest buffer we
have queued and recycle that ? */ 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 * @tty: tty structure
* @size: size desired * @size: size desired
* *
* Make at least size bytes of linear space available for the tty * Make at least size bytes of linear space available for the tty
* buffer. If we fail return the size we managed to find. * buffer. If we fail return the size we managed to find.
* * Locking: Caller must hold tty->buf.lock
* Locking: Takes tty->buf.lock
*/ */
int tty_buffer_request_room(struct tty_struct *tty, size_t size) static int __tty_buffer_request_room(struct tty_struct *tty, size_t size)
{ {
struct tty_buffer *b, *n; struct tty_buffer *b, *n;
int left; int left;
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
/* OPTIMISATION: We could keep a per tty "zero" sized buffer to /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
remove this conditional if its worth it. This would be invisible remove this conditional if its worth it. This would be invisible
to the callers */ to the callers */
...@@ -225,9 +219,30 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) ...@@ -225,9 +219,30 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size)
size = left; size = left;
} }
spin_unlock_irqrestore(&tty->buf.lock, flags);
return size; 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 tty->buf.lock
*/
int tty_buffer_request_room(struct tty_struct *tty, size_t size)
{
unsigned long flags;
int length;
spin_lock_irqsave(&tty->buf.lock, flags);
length = __tty_buffer_request_room(tty, size);
spin_unlock_irqrestore(&tty->buf.lock, flags);
return length;
}
EXPORT_SYMBOL_GPL(tty_buffer_request_room); EXPORT_SYMBOL_GPL(tty_buffer_request_room);
/** /**
...@@ -249,14 +264,22 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, ...@@ -249,14 +264,22 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
int copied = 0; int copied = 0;
do { do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal); int space;
struct tty_buffer *tb = tty->buf.tail; unsigned long flags;
struct tty_buffer *tb;
spin_lock_irqsave(&tty->buf.lock, flags);
space = __tty_buffer_request_room(tty, goal);
tb = tty->buf.tail;
/* If there is no space then tb may be NULL */ /* If there is no space then tb may be NULL */
if (unlikely(space == 0)) if (unlikely(space == 0)) {
spin_unlock_irqrestore(&tty->buf.lock, flags);
break; break;
}
memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->char_buf_ptr + tb->used, chars, space);
memset(tb->flag_buf_ptr + tb->used, flag, space); memset(tb->flag_buf_ptr + tb->used, flag, space);
tb->used += space; tb->used += space;
spin_unlock_irqrestore(&tty->buf.lock, flags);
copied += space; copied += space;
chars += space; chars += space;
/* There is a small chance that we need to split the data over /* There is a small chance that we need to split the data over
...@@ -286,14 +309,22 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, ...@@ -286,14 +309,22 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
int copied = 0; int copied = 0;
do { do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal); int space;
struct tty_buffer *tb = tty->buf.tail; unsigned long __flags;
struct tty_buffer *tb;
spin_lock_irqsave(&tty->buf.lock, __flags);
space = __tty_buffer_request_room(tty, goal);
tb = tty->buf.tail;
/* If there is no space then tb may be NULL */ /* If there is no space then tb may be NULL */
if (unlikely(space == 0)) if (unlikely(space == 0)) {
spin_unlock_irqrestore(&tty->buf.lock, __flags);
break; break;
}
memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->char_buf_ptr + tb->used, chars, space);
memcpy(tb->flag_buf_ptr + tb->used, flags, space); memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space; tb->used += space;
spin_unlock_irqrestore(&tty->buf.lock, __flags);
copied += space; copied += space;
chars += space; chars += space;
flags += space; flags += space;
...@@ -344,13 +375,20 @@ EXPORT_SYMBOL(tty_schedule_flip); ...@@ -344,13 +375,20 @@ EXPORT_SYMBOL(tty_schedule_flip);
int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
size_t size) size_t size)
{ {
int space = tty_buffer_request_room(tty, size); int space;
unsigned long flags;
struct tty_buffer *tb;
spin_lock_irqsave(&tty->buf.lock, flags);
space = __tty_buffer_request_room(tty, size);
tb = tty->buf.tail;
if (likely(space)) { if (likely(space)) {
struct tty_buffer *tb = tty->buf.tail;
*chars = tb->char_buf_ptr + tb->used; *chars = tb->char_buf_ptr + tb->used;
memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
tb->used += space; tb->used += space;
} }
spin_unlock_irqrestore(&tty->buf.lock, flags);
return space; return space;
} }
EXPORT_SYMBOL_GPL(tty_prepare_flip_string); EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
...@@ -374,13 +412,20 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); ...@@ -374,13 +412,20 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
int tty_prepare_flip_string_flags(struct tty_struct *tty, int tty_prepare_flip_string_flags(struct tty_struct *tty,
unsigned char **chars, char **flags, size_t size) unsigned char **chars, char **flags, size_t size)
{ {
int space = tty_buffer_request_room(tty, size); int space;
unsigned long __flags;
struct tty_buffer *tb;
spin_lock_irqsave(&tty->buf.lock, __flags);
space = __tty_buffer_request_room(tty, size);
tb = tty->buf.tail;
if (likely(space)) { if (likely(space)) {
struct tty_buffer *tb = tty->buf.tail;
*chars = tb->char_buf_ptr + tb->used; *chars = tb->char_buf_ptr + tb->used;
*flags = tb->flag_buf_ptr + tb->used; *flags = tb->flag_buf_ptr + tb->used;
tb->used += space; tb->used += space;
} }
spin_unlock_irqrestore(&tty->buf.lock, __flags);
return space; return space;
} }
EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment