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: Alek Du <alek.du@intel.com>
Signed-off-by: Xiaobing Tu <xiaobing.tu@intel.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Xiaobing Tu 2012-03-16 03:00:26 +00:00 committed by Greg Kroah-Hartman
parent 871bdea6f8
commit c56a00a165

View file

@ -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)
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,
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,
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);
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);
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);