From 36697529b5bbe36911e39a6309e7a7c9250d280a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:04:48 -0400 Subject: tty: Replace ldisc locking with ldisc_sem Line discipline locking was performed with a combination of a mutex, a status bit, a count, and a waitqueue -- basically, a rw semaphore. Replace the existing combination with an ld_semaphore. Fixes: 1) the 'reference acquire after ldisc locked' bug 2) the over-complicated halt mechanism 3) lock order wrt. tty_lock() 4) dropping locks while changing ldisc 5) previously unidentified deadlock while locking ldisc from both linked ttys concurrently 6) previously unidentified recursive deadlocks Adds much-needed lockdep diagnostics. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 9121c1f7aee..a42a028a9d4 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -429,7 +429,7 @@ static void flush_to_ldisc(struct work_struct *work) return; disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ + if (disc == NULL) return; spin_lock_irqsave(&buf->lock, flags); -- cgit v1.2.3-70-g09d2 From da261e7fe7b0e23a0d4d46039d20dc60fa197b49 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:14:14 -0400 Subject: tty: Simplify tty buffer/ldisc interface with helper function Ldisc interface functions must be called with interrupts enabled. Separating the ldisc calls into a helper function simplies the eventual removal of the spinlock. Note that access to the buf->head ptr outside the spinlock is safe here because; * __tty_buffer_flush() is prevented from running while buffer work performs i/o, * tty_buffer_find() only assigns buf->head if the flip buffer list is empty (which is never the case in flush_to_ldisc() since at least one buffer is always left in the list after use) Access to the read index outside the spinlock is safe here for the same reasons. Update the buffer's read index _after_ the data has been received by the ldisc. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index a42a028a9d4..6c7a1d043c7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -403,6 +403,18 @@ int tty_prepare_flip_string_flags(struct tty_port *port, EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); +static int +receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) +{ + struct tty_ldisc *disc = tty->ldisc; + + count = min_t(int, count, tty->receive_room); + if (count) + disc->ops->receive_buf(tty, head->char_buf_ptr + head->read, + head->flag_buf_ptr + head->read, count); + head->read += count; + return count; +} /** * flush_to_ldisc @@ -438,8 +450,6 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_buffer *head; while ((head = buf->head) != NULL) { int count; - char *char_buf; - unsigned char *flag_buf; count = head->commit - head->read; if (!count) { @@ -449,16 +459,10 @@ static void flush_to_ldisc(struct work_struct *work) tty_buffer_free(port, head); continue; } - if (!tty->receive_room) - break; - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; spin_unlock_irqrestore(&buf->lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); + + count = receive_buf(tty, head, count); + spin_lock_irqsave(&buf->lock, flags); /* Ldisc or user is trying to flush the buffers. We may have a deferred request to flush the @@ -469,7 +473,8 @@ static void flush_to_ldisc(struct work_struct *work) clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); break; - } + } else if (!count) + break; } clear_bit(TTYP_FLUSHING, &port->iflags); } -- cgit v1.2.3-70-g09d2 From 24a89d1cb69b6c488cf16d98dd02e7820f62b40c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:14:15 -0400 Subject: tty: Make ldisc input flow control concurrency-friendly Although line discipline receiving is single-producer/single-consumer, using tty->receive_room to manage flow control creates unnecessary critical regions requiring additional lock use. Instead, introduce the optional .receive_buf2() ldisc method which returns the # of bytes actually received. Serialization is guaranteed by the caller. In turn, the line discipline should schedule the buffer work item whenever space becomes available; ie., when there is room to receive data and receive_room() previously returned 0 (the buffer work item stops processing if receive_buf2() returns 0). Note the 'no room' state need not be atomic despite concurrent use by two threads because only the buffer work thread can set the state and only the read() thread can clear the state. Add n_tty_receive_buf2() as the receive_buf2() method for N_TTY. Provide a public helper function, tty_ldisc_receive_buf(), to use when directly accessing the receive_buf() methods. Line disciplines not using input flow control can continue to set tty->receive_room to a fixed value and only provide the receive_buf() method. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 72 +++++++++++++++++++++++++++++----------------- drivers/tty/tty_buffer.c | 13 ++++++--- drivers/tty/vt/selection.c | 4 +-- include/linux/tty.h | 13 +++++++++ include/linux/tty_ldisc.h | 13 +++++++++ 5 files changed, 82 insertions(+), 33 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4bf0fc0843d..eddeb7889e6 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -79,6 +79,9 @@ struct n_tty_data { unsigned long overrun_time; int num_overrun; + /* non-atomic */ + bool no_room; + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char echo_overrun:1; @@ -114,25 +117,10 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set_room - receive space - * @tty: terminal - * - * Updates tty->receive_room to reflect the currently available space - * in the input buffer, and re-schedules the flip buffer work if space - * just became available. - * - * Locks: Concurrent update is protected with read_lock - */ - -static int set_room(struct tty_struct *tty) +static int receive_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; - int old_left; - unsigned long flags; - - raw_spin_lock_irqsave(&ldata->read_lock, flags); if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to @@ -150,18 +138,27 @@ static int set_room(struct tty_struct *tty) */ if (left <= 0) left = ldata->icanon && !ldata->canon_data; - old_left = tty->receive_room; - tty->receive_room = left; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); - - return left && !old_left; + return left; } +/** + * n_tty_set_room - receive space + * @tty: terminal + * + * Re-schedules the flip buffer work if space just became available. + * + * Locks: Concurrent update is protected with read_lock + */ + static void n_tty_set_room(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + /* Did this open up the receive buffer? We may need to flip */ - if (set_room(tty)) { + if (unlikely(ldata->no_room) && receive_room(tty)) { + ldata->no_room = 0; + WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that @@ -1408,8 +1405,8 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct n_tty_data *ldata = tty->disc_data; const unsigned char *p; @@ -1464,8 +1461,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - set_room(tty); - if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1480,7 +1475,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, */ while (1) { tty_set_flow_change(tty, TTY_THROTTLE_SAFE); - if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE) break; if (!tty_throttle_safe(tty)) break; @@ -1488,6 +1483,28 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, __tty_set_flow_change(tty, 0); } +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + __receive_buf(tty, cp, fp, count); +} + +static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + int room; + + tty->receive_room = room = receive_room(tty); + if (!room) + ldata->no_room = 1; + count = min(count, room); + if (count) + __receive_buf(tty, cp, fp, count); + + return count; +} + int is_ignored(int sig) { return (sigismember(¤t->blocked, sig) || @@ -2203,6 +2220,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup, .fasync = n_tty_fasync, + .receive_buf2 = n_tty_receive_buf2, }; /** diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6c7a1d043c7..ff1b2e37c3c 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -407,11 +407,16 @@ static int receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { struct tty_ldisc *disc = tty->ldisc; + char *p = head->char_buf_ptr + head->read; + unsigned char *f = head->flag_buf_ptr + head->read; - count = min_t(int, count, tty->receive_room); - if (count) - disc->ops->receive_buf(tty, head->char_buf_ptr + head->read, - head->flag_buf_ptr + head->read, count); + if (disc->ops->receive_buf2) + count = disc->ops->receive_buf2(tty, p, f, count); + else { + count = min_t(int, count, tty->receive_room); + if (count) + disc->ops->receive_buf(tty, p, f, count); + } head->read += count; return count; } diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 60b7b692605..2ca8d6b6514 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -356,8 +356,8 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); + count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL, + count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 7269daf7632..8323ee4f95b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -557,6 +557,19 @@ extern void tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern void tty_ldisc_begin(void); +static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, + char *f, int count) +{ + if (ld->ops->receive_buf2) + count = ld->ops->receive_buf2(ld->tty, p, f, count); + else { + count = min_t(int, count, ld->tty->receive_room); + if (count) + ld->ops->receive_buf(ld->tty, p, f, count); + } + return count; +} + /* n_tty.c */ extern struct tty_ldisc_ops tty_ldisc_N_TTY; diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 23bdd9debb8..f15c898ff46 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -109,6 +109,17 @@ * * Tells the discipline that the DCD pin has changed its status. * Used exclusively by the N_PPS (Pulse-Per-Second) line discipline. + * + * int (*receive_buf2)(struct tty_struct *, const unsigned char *cp, + * char *fp, int count); + * + * This function is called by the low-level tty driver to send + * characters received by the hardware to the line discpline for + * processing. is a pointer to the buffer of input + * character received by the device. is a pointer to a + * pointer of flag bytes which indicate whether a character was + * received with a parity error, etc. + * If assigned, prefer this function for automatic flow control. */ #include @@ -195,6 +206,8 @@ struct tty_ldisc_ops { void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int); void (*fasync)(struct tty_struct *tty, int on); + int (*receive_buf2)(struct tty_struct *, const unsigned char *cp, + char *fp, int count); struct module *owner; -- cgit v1.2.3-70-g09d2 From 1fc359fc3ea72314cc3ebdfa94c60e020c152cd2 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:01 -0400 Subject: tty: Compute flip buffer ptrs The char_buf_ptr and flag_buf_ptr values are trivially derived from the .data field offset; compute values as needed. Fixes a long-standing type-mismatch with the char and flag ptrs. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 22 ++++++++++------------ include/linux/tty.h | 12 ++++++++++-- include/linux/tty_flip.h | 4 ++-- 3 files changed, 22 insertions(+), 16 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index ff1b2e37c3c..170674cb68f 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -71,8 +71,6 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) p->next = NULL; p->commit = 0; p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; port->buf.memory_used += size; return p; } @@ -265,8 +263,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, if (unlikely(space == 0)) { break; } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, flag, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; copied += space; chars += space; @@ -303,8 +301,8 @@ int tty_insert_flip_string_flags(struct tty_port *port, if (unlikely(space == 0)) { break; } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; copied += space; chars += space; @@ -364,8 +362,8 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, 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); + *chars = char_buf_ptr(tb, tb->used); + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); tb->used += space; } return space; @@ -394,8 +392,8 @@ int tty_prepare_flip_string_flags(struct tty_port *port, 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; + *chars = char_buf_ptr(tb, tb->used); + *flags = flag_buf_ptr(tb, tb->used); tb->used += space; } return space; @@ -407,8 +405,8 @@ static int receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { struct tty_ldisc *disc = tty->ldisc; - char *p = head->char_buf_ptr + head->read; - unsigned char *f = head->flag_buf_ptr + head->read; + unsigned char *p = char_buf_ptr(head, head->read); + char *f = flag_buf_ptr(head, head->read); if (disc->ops->receive_buf2) count = disc->ops->receive_buf2(tty, p, f, count); diff --git a/include/linux/tty.h b/include/linux/tty.h index 57a70d1d041..87bbaa31ebf 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -31,8 +31,6 @@ struct tty_buffer { struct tty_buffer *next; - char *char_buf_ptr; - unsigned char *flag_buf_ptr; int used; int size; int commit; @@ -41,6 +39,16 @@ struct tty_buffer { unsigned long data[0]; }; +static inline unsigned char *char_buf_ptr(struct tty_buffer *b, int ofs) +{ + return ((unsigned char *)b->data) + ofs; +} + +static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) +{ + return (char *)char_buf_ptr(b, ofs) + b->size; +} + /* * We default to dicing tty buffer allocations to this many characters * in order to avoid multiple page allocations. We know the size of diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index e0f252633b4..ad0303994c3 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -18,8 +18,8 @@ static inline int tty_insert_flip_char(struct tty_port *port, { struct tty_buffer *tb = port->buf.tail; if (tb && tb->used < tb->size) { - tb->flag_buf_ptr[tb->used] = flag; - tb->char_buf_ptr[tb->used++] = ch; + *flag_buf_ptr(tb, tb->used) = flag; + *char_buf_ptr(tb, tb->used++) = ch; return 1; } return tty_insert_flip_string_flags(port, &ch, &flag, 1); -- cgit v1.2.3-70-g09d2 From 1cef50e317c3395c6e8451f2b82a155db6ef2b42 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:02 -0400 Subject: tty: Fix flip buffer free list Since flip buffers are size-aligned to 256 bytes and all flip buffers 512-bytes or larger are not added to the free list, the free list only contains 256-byte flip buffers. Remove the list search when allocating a new flip buffer. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 170674cb68f..a5e396217f7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -18,6 +18,10 @@ #include #include + +#define MIN_TTYB_SIZE 256 +#define TTYB_ALIGN_MASK 255 + /** * tty_buffer_free_all - free buffers used by a tty * @tty: tty to free from @@ -94,7 +98,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) buf->memory_used -= b->size; WARN_ON(buf->memory_used < 0); - if (b->size >= 512) + if (b->size > MIN_TTYB_SIZE) kfree(b); else { b->next = buf->free; @@ -176,9 +180,10 @@ void tty_buffer_flush(struct tty_struct *tty) static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) { struct tty_buffer **tbh = &port->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { + if (size <= MIN_TTYB_SIZE) { + if (*tbh) { + struct tty_buffer *t = *tbh; + *tbh = t->next; t->next = NULL; t->used = 0; @@ -187,10 +192,9 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) port->buf.memory_used += t->size; return t; } - tbh = &((*tbh)->next); } /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; + size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); return tty_buffer_alloc(port, size); /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ -- cgit v1.2.3-70-g09d2 From 9dd5139f973f55ab4b2e9aff8171781f1e55b29d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:03 -0400 Subject: tty: Factor flip buffer initialization into helper function Factor shared code; prepare for adding 0-sized sentinel flip buffer. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index a5e396217f7..56d460295c8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -22,6 +22,15 @@ #define MIN_TTYB_SIZE 256 #define TTYB_ALIGN_MASK 255 +static void tty_buffer_reset(struct tty_buffer *p, size_t size) +{ + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; +} + /** * tty_buffer_free_all - free buffers used by a tty * @tty: tty to free from @@ -70,11 +79,8 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; + + tty_buffer_reset(p, size); port->buf.memory_used += size; return p; } @@ -185,10 +191,7 @@ static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) struct tty_buffer *t = *tbh; *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; + tty_buffer_reset(t, t->size); port->buf.memory_used += t->size; return t; } -- cgit v1.2.3-70-g09d2 From 11b9faa44df76189b8346ff602a2c01c610c37eb Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:04 -0400 Subject: tty: Merge tty_buffer_find() into tty_buffer_alloc() tty_buffer_find() implements a simple free list lookaside cache. Merge this functionality into tty_buffer_alloc() to reflect the more traditional alloc/free symmetry. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 50 +++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 56d460295c8..a428fa2f725 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -64,6 +64,8 @@ void tty_buffer_free_all(struct tty_port *port) * @size: desired size (characters) * * Allocate a new tty buffer to hold the desired number of characters. + * We round our buffers off in 256 character chunks to get better + * allocation behaviour. * Return NULL if out of memory or the allocation would exceed the * per device queue * @@ -72,14 +74,29 @@ void tty_buffer_free_all(struct tty_port *port) static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { + struct tty_buffer **tbh = &port->buf.free; struct tty_buffer *p; + /* Round the buffer size out */ + size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); + + if (size <= MIN_TTYB_SIZE) { + if (*tbh) { + p = *tbh; + *tbh = p->next; + goto found; + } + } + + /* Should possibly check if this fails for the largest buffer we + have queued and recycle that ? */ if (port->buf.memory_used + size > 65536) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) return NULL; +found: tty_buffer_reset(p, size); port->buf.memory_used += size; return p; @@ -171,37 +188,6 @@ void tty_buffer_flush(struct tty_struct *tty) spin_unlock_irqrestore(&buf->lock, flags); } -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) -{ - struct tty_buffer **tbh = &port->buf.free; - if (size <= MIN_TTYB_SIZE) { - if (*tbh) { - struct tty_buffer *t = *tbh; - - *tbh = t->next; - tty_buffer_reset(t, t->size); - port->buf.memory_used += t->size; - return t; - } - } - /* Round the buffer size out */ - size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); - return tty_buffer_alloc(port, size); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ -} /** * tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure @@ -230,7 +216,7 @@ int tty_buffer_request_room(struct tty_port *port, size_t size) if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(port, size)) != NULL) { + if ((n = tty_buffer_alloc(port, size)) != NULL) { if (b != NULL) { b->next = n; b->commit = b->used; -- cgit v1.2.3-70-g09d2 From 2cf7b67e87f0d8db025cff12b5d29c0663bbcd87 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:05 -0400 Subject: tty: Use generic names for flip buffer list cursors Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index a428fa2f725..0259a766b87 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -44,15 +44,15 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) void tty_buffer_free_all(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; + struct tty_buffer *p; - while ((thead = buf->head) != NULL) { - buf->head = thead->next; - kfree(thead); + while ((p = buf->head) != NULL) { + buf->head = p->next; + kfree(p); } - while ((thead = buf->free) != NULL) { - buf->free = thead->next; - kfree(thead); + while ((p = buf->free) != NULL) { + buf->free = p->next; + kfree(p); } buf->tail = NULL; buf->memory_used = 0; @@ -143,13 +143,13 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) static void __tty_buffer_flush(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; + struct tty_buffer *next; if (unlikely(buf->head == NULL)) return; - while ((thead = buf->head->next) != NULL) { + while ((next = buf->head->next) != NULL) { tty_buffer_free(port, buf->head); - buf->head = thead; + buf->head = next; } WARN_ON(buf->head != buf->tail); buf->head->read = buf->head->commit; -- cgit v1.2.3-70-g09d2 From 809850b7a5fcc0a96d023e1171a7944c60fd5a71 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:06 -0400 Subject: tty: Use lockless flip buffer free list In preparation for lockless flip buffers, make the flip buffer free list lockless. NB: using llist is not the optimal solution, as the driver and buffer work may contend over the llist head unnecessarily. However, test measurements indicate this contention is low. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 29 ++++++++++++----------------- include/linux/llist.h | 23 +++++++++++++++++++++++ include/linux/tty.h | 8 ++++++-- 3 files changed, 41 insertions(+), 19 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 0259a766b87..069640e5b9c 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -44,16 +44,17 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) void tty_buffer_free_all(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *p; + struct tty_buffer *p, *next; + struct llist_node *llist; while ((p = buf->head) != NULL) { buf->head = p->next; kfree(p); } - while ((p = buf->free) != NULL) { - buf->free = p->next; + llist = llist_del_all(&buf->free); + llist_for_each_entry_safe(p, next, llist, free) kfree(p); - } + buf->tail = NULL; buf->memory_used = 0; } @@ -68,22 +69,20 @@ void tty_buffer_free_all(struct tty_port *port) * allocation behaviour. * Return NULL if out of memory or the allocation would exceed the * per device queue - * - * Locking: Caller must hold tty->buf.lock */ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &port->buf.free; + struct llist_node *free; struct tty_buffer *p; /* Round the buffer size out */ size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); if (size <= MIN_TTYB_SIZE) { - if (*tbh) { - p = *tbh; - *tbh = p->next; + free = llist_del_first(&port->buf.free); + if (free) { + p = llist_entry(free, struct tty_buffer, free); goto found; } } @@ -109,8 +108,6 @@ found: * * Free a tty buffer, or add it to the free list according to our * internal strategy - * - * Locking: Caller must hold tty->buf.lock */ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) @@ -123,10 +120,8 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) if (b->size > MIN_TTYB_SIZE) kfree(b); - else { - b->next = buf->free; - buf->free = b; - } + else + llist_add(&b->free, &buf->free); } /** @@ -542,7 +537,7 @@ void tty_buffer_init(struct tty_port *port) spin_lock_init(&buf->lock); buf->head = NULL; buf->tail = NULL; - buf->free = NULL; + init_llist_head(&buf->free); buf->memory_used = 0; INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/llist.h b/include/linux/llist.h index cdaa7f02389..8828a78dec9 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -124,6 +124,29 @@ static inline void init_llist_head(struct llist_head *list) &(pos)->member != NULL; \ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) +/** + * llist_for_each_entry_safe - iterate over some deleted entries of lock-less list of given type + * safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @node: the first entry of deleted list entries. + * @member: the name of the llist_node with the struct. + * + * In general, some entries of the lock-less list can be traversed + * safely only after being removed from list, so start with an entry + * instead of list head. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each_entry_safe(pos, n, node, member) \ + for (pos = llist_entry((node), typeof(*pos), member); \ + &pos->member != NULL && \ + (n = llist_entry(pos->member.next, typeof(*n), member), true); \ + pos = n) + /** * llist_empty - tests whether a lock-less list is empty * @head: the list to test diff --git a/include/linux/tty.h b/include/linux/tty.h index 87bbaa31ebf..5043b12f23e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -11,6 +11,7 @@ #include #include #include +#include @@ -30,7 +31,10 @@ #define __DISABLED_CHAR '\0' struct tty_buffer { - struct tty_buffer *next; + union { + struct tty_buffer *next; + struct llist_node free; + }; int used; int size; int commit; @@ -65,7 +69,7 @@ struct tty_bufhead { spinlock_t lock; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ - struct tty_buffer *free; /* Free queue head */ + struct llist_head free; /* Free queue head */ int memory_used; /* Buffer space used excluding free queue */ }; -- cgit v1.2.3-70-g09d2 From 7391ee16950e772076d321792d9fbf030f921345 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:07 -0400 Subject: tty: Simplify flip buffer list with 0-sized sentinel Use a 0-sized sentinel to avoid assigning the head ptr from the driver side thread. This also eliminates testing head/tail for NULL. When the sentinel is first 'consumed' by the buffer work (or by tty_buffer_flush()), it is detached from the list but not freed nor added to the free list. Both buffer work and tty_buffer_flush() continue to preserve at least 1 flip buffer to which head & tail is pointed. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 49 ++++++++++++++++++------------------------------ include/linux/tty.h | 1 + 2 files changed, 19 insertions(+), 31 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 069640e5b9c..231b7a8710f 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -49,13 +49,16 @@ void tty_buffer_free_all(struct tty_port *port) while ((p = buf->head) != NULL) { buf->head = p->next; - kfree(p); + if (p->size > 0) + kfree(p); } llist = llist_del_all(&buf->free); llist_for_each_entry_safe(p, next, llist, free) kfree(p); - buf->tail = NULL; + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; buf->memory_used = 0; } @@ -120,7 +123,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) if (b->size > MIN_TTYB_SIZE) kfree(b); - else + else if (b->size > 0) llist_add(&b->free, &buf->free); } @@ -140,8 +143,6 @@ static void __tty_buffer_flush(struct tty_port *port) struct tty_bufhead *buf = &port->buf; struct tty_buffer *next; - if (unlikely(buf->head == NULL)) - return; while ((next = buf->head->next) != NULL) { tty_buffer_free(port, buf->head); buf->head = next; @@ -200,23 +201,14 @@ int tty_buffer_request_room(struct tty_port *port, size_t size) 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 */ b = buf->tail; - if (b != NULL) - left = b->size - b->used; - else - left = 0; + left = b->size - b->used; if (left < size) { /* This is the slow path - looking for new buffers to use */ if ((n = tty_buffer_alloc(port, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - buf->head = n; + b->next = n; + b->commit = b->used; buf->tail = n; } else size = left; @@ -247,10 +239,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); 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)) { + if (unlikely(space == 0)) break; - } memcpy(char_buf_ptr(tb, tb->used), chars, space); memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; @@ -285,10 +275,8 @@ int tty_insert_flip_string_flags(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); 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)) { + if (unlikely(space == 0)) break; - } memcpy(char_buf_ptr(tb, tb->used), chars, space); memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; @@ -322,8 +310,7 @@ void tty_schedule_flip(struct tty_port *port) WARN_ON(port->low_latency); spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; + buf->tail->commit = buf->tail->used; spin_unlock_irqrestore(&buf->lock, flags); schedule_work(&buf->work); } @@ -438,8 +425,8 @@ static void flush_to_ldisc(struct work_struct *work) spin_lock_irqsave(&buf->lock, flags); if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - struct tty_buffer *head; - while ((head = buf->head) != NULL) { + while (1) { + struct tty_buffer *head = buf->head; int count; count = head->commit - head->read; @@ -509,8 +496,7 @@ void tty_flip_buffer_push(struct tty_port *port) unsigned long flags; spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; + buf->tail->commit = buf->tail->used; spin_unlock_irqrestore(&buf->lock, flags); if (port->low_latency) @@ -535,8 +521,9 @@ void tty_buffer_init(struct tty_port *port) struct tty_bufhead *buf = &port->buf; spin_lock_init(&buf->lock); - buf->head = NULL; - buf->tail = NULL; + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; init_llist_head(&buf->free); buf->memory_used = 0; INIT_WORK(&buf->work, flush_to_ldisc); diff --git a/include/linux/tty.h b/include/linux/tty.h index 5043b12f23e..2e93eb831c6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -67,6 +67,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct work_struct work; spinlock_t lock; + struct tty_buffer sentinel; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct llist_head free; /* Free queue head */ -- cgit v1.2.3-70-g09d2 From 7bfe0b7116be207cf2204ae06335cc89d8f8ee02 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:08 -0400 Subject: tty: Track flip buffer memory limit atomically Lockless flip buffers require atomically updating the bytes-in-use watermark. The pty driver also peeks at the watermark value to limit memory consumption to a much lower value than the default; query the watermark with new fn, tty_buffer_space_avail(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 10 +++------- drivers/tty/tty_buffer.c | 37 +++++++++++++++++++++++++++++++------ include/linux/tty.h | 3 +-- include/linux/tty_flip.h | 1 + 4 files changed, 36 insertions(+), 15 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 1b39dd639ee..b38a28bd951 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -89,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty) * pty_space - report space left for writing * @to: tty we are writing into * - * The tty buffers allow 64K but we sneak a peak and clip at 8K this - * allows a lot of overspill room for echo and other fun messes to - * be handled properly + * Limit the buffer space used by ptys to 8k. */ static int pty_space(struct tty_struct *to) { - int n = 8192 - to->port->buf.memory_used; - if (n < 0) - return 0; - return n; + int n = tty_buffer_space_avail(to->port); + return min(n, 8192); } /** diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 231b7a8710f..5d5a56407aa 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -22,6 +22,31 @@ #define MIN_TTYB_SIZE 256 #define TTYB_ALIGN_MASK 255 +/* + * Byte threshold to limit memory consumption for flip buffers. + * The actual memory limit is > 2x this amount. + */ +#define TTYB_MEM_LIMIT 65536 + + +/** + * tty_buffer_space_avail - return unused buffer space + * @port - tty_port owning the flip buffer + * + * Returns the # of bytes which can be written by the driver without + * reaching the buffer limit. + * + * Note: this does not guarantee that memory is available to write + * the returned # of bytes (use tty_prepare_flip_string_xxx() to + * pre-allocate if memory guarantee is required). + */ + +int tty_buffer_space_avail(struct tty_port *port) +{ + int space = TTYB_MEM_LIMIT - atomic_read(&port->buf.memory_used); + return max(space, 0); +} + static void tty_buffer_reset(struct tty_buffer *p, size_t size) { p->used = 0; @@ -59,7 +84,8 @@ void tty_buffer_free_all(struct tty_port *port) tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; - buf->memory_used = 0; + + atomic_set(&buf->memory_used, 0); } /** @@ -92,7 +118,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ - if (port->buf.memory_used + size > 65536) + if (atomic_read(&port->buf.memory_used) > TTYB_MEM_LIMIT) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -100,7 +126,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) found: tty_buffer_reset(p, size); - port->buf.memory_used += size; + atomic_add(size, &port->buf.memory_used); return p; } @@ -118,8 +144,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ - buf->memory_used -= b->size; - WARN_ON(buf->memory_used < 0); + WARN_ON(atomic_sub_return(b->size, &buf->memory_used) < 0); if (b->size > MIN_TTYB_SIZE) kfree(b); @@ -525,7 +550,7 @@ void tty_buffer_init(struct tty_port *port) buf->head = &buf->sentinel; buf->tail = &buf->sentinel; init_llist_head(&buf->free); - buf->memory_used = 0; + atomic_set(&buf->memory_used, 0); INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/tty.h b/include/linux/tty.h index 2e93eb831c6..7c124541f01 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -71,8 +71,7 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct llist_head free; /* Free queue head */ - int memory_used; /* Buffer space used excluding - free queue */ + atomic_t memory_used; /* In-use buffers excluding free list */ }; /* * When a break, frame error, or parity error happens, these codes are diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index ad0303994c3..6944ed2ce69 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -1,6 +1,7 @@ #ifndef _LINUX_TTY_FLIP_H #define _LINUX_TTY_FLIP_H +extern int tty_buffer_space_avail(struct tty_port *port); extern int tty_buffer_request_room(struct tty_port *port, size_t size); extern int tty_insert_flip_string_flags(struct tty_port *port, const unsigned char *chars, const char *flags, size_t size); -- cgit v1.2.3-70-g09d2 From e8437d7ecbc50198705331449367d401ebb3181f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:09 -0400 Subject: tty: Make driver-side flip buffers lockless Driver-side flip buffer input is already single-threaded; 'publish' the .next link as the last operation on the tail buffer so the 'consumer' sees the already-completed flip buffer. The commit buffer index is already 'published' by driver-side functions. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 5d5a56407aa..685757c6ce8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -62,8 +62,6 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) * * Remove all the buffers pending on a tty whether queued with data * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none */ void tty_buffer_free_all(struct tty_port *port) @@ -216,29 +214,26 @@ void tty_buffer_flush(struct tty_struct *tty) * * 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_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); + b = buf->tail; left = b->size - b->used; if (left < size) { /* This is the slow path - looking for new buffers to use */ if ((n = tty_buffer_alloc(port, size)) != NULL) { - b->next = n; - b->commit = b->used; buf->tail = n; + b->commit = b->used; + smp_mb(); + b->next = n; } else size = left; } - spin_unlock_irqrestore(&buf->lock, flags); return size; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -252,8 +247,6 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. - * - * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_port *port, @@ -288,8 +281,6 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * Queue a series of bytes to the tty buffering. For each character * the flags array indicates the status of the character. Returns the * number added. - * - * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_port *port, @@ -324,19 +315,14 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * processing by the line discipline. * Note that this function can only be used when the low_latency flag * is unset. Otherwise the workqueue won't be flushed. - * - * Locking: Takes port->buf.lock */ void tty_schedule_flip(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - unsigned long flags; WARN_ON(port->low_latency); - spin_lock_irqsave(&buf->lock, flags); buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); schedule_work(&buf->work); } EXPORT_SYMBOL(tty_schedule_flip); @@ -352,8 +338,6 @@ EXPORT_SYMBOL(tty_schedule_flip); * accounted for as ready for normal characters. This is used for drivers * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, @@ -382,8 +366,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * accounted for as ready for characters. This is used for drivers * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_port *port, @@ -511,18 +493,13 @@ void tty_flush_to_ldisc(struct tty_struct *tty) * * In the event of the queue being busy for flipping the work will be * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. */ void tty_flip_buffer_push(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - unsigned long flags; - spin_lock_irqsave(&buf->lock, flags); buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); if (port->low_latency) flush_to_ldisc(&buf->work); -- cgit v1.2.3-70-g09d2 From e9975fdec0138f1b2a85b9624e41660abd9865d4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:10 -0400 Subject: tty: Ensure single-threaded flip buffer consumer with mutex The buffer work may race with parallel tty_buffer_flush. Use a mutex to guarantee exclusive modify access to the head flip buffer. Remove the unneeded spin lock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_tty.c | 2 ++ drivers/tty/tty_buffer.c | 40 +++++++++++++++++++--------------------- include/linux/tty.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index 654f6010b47..0d52de3729c 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -1120,7 +1120,9 @@ static void dgrp_tty_close(struct tty_struct *tty, struct file *file) if (!sent_printer_offstr) dgrp_tty_flush_buffer(tty); + spin_unlock_irqrestore(&nd->nd_lock, lock_flags); tty_ldisc_flush(tty); + spin_lock_irqsave(&nd->nd_lock, lock_flags); break; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 685757c6ce8..c3c606c5272 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -157,8 +157,6 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) * flush all the buffers containing receive data. Caller must * hold the buffer lock and must have ensured no parallel flush to * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock */ static void __tty_buffer_flush(struct tty_port *port) @@ -182,29 +180,29 @@ static void __tty_buffer_flush(struct tty_port *port) * being processed by flush_to_ldisc then we defer the processing * to that function * - * Locking: none + * Locking: takes flush_mutex to ensure single-threaded flip buffer + * 'consumer' */ void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - unsigned long flags; - - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ if (test_bit(TTYP_FLUSHING, &port->iflags)) { set_bit(TTYP_FLUSHPENDING, &port->iflags); - spin_unlock_irqrestore(&buf->lock, flags); + mutex_unlock(&buf->flush_mutex); wait_event(tty->read_wait, test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; - } else - __tty_buffer_flush(port); - spin_unlock_irqrestore(&buf->lock, flags); + } + + __tty_buffer_flush(port); + mutex_unlock(&buf->flush_mutex); } /** @@ -408,9 +406,10 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) * This routine is called out of the software interrupt to flush data * from the buffer chain to the line discipline. * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. + * The receive_buf method is single threaded for each tty instance. + * + * Locking: takes flush_mutex to ensure single-threaded flip buffer + * 'consumer' */ static void flush_to_ldisc(struct work_struct *work) @@ -418,7 +417,6 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_port *port = container_of(work, struct tty_port, buf.work); struct tty_bufhead *buf = &port->buf; struct tty_struct *tty; - unsigned long flags; struct tty_ldisc *disc; tty = port->itty; @@ -429,7 +427,7 @@ static void flush_to_ldisc(struct work_struct *work) if (disc == NULL) return; - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { while (1) { @@ -444,11 +442,13 @@ static void flush_to_ldisc(struct work_struct *work) tty_buffer_free(port, head); continue; } - spin_unlock_irqrestore(&buf->lock, flags); + + mutex_unlock(&buf->flush_mutex); count = receive_buf(tty, head, count); - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); + /* Ldisc or user is trying to flush the buffers. We may have a deferred request to flush the input buffer, if so pull the chain under the lock @@ -464,7 +464,7 @@ static void flush_to_ldisc(struct work_struct *work) clear_bit(TTYP_FLUSHING, &port->iflags); } - spin_unlock_irqrestore(&buf->lock, flags); + mutex_unlock(&buf->flush_mutex); tty_ldisc_deref(disc); } @@ -514,15 +514,13 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * * Set up the initial state of the buffer management for a tty device. * Must be called before the other tty buffer functions are used. - * - * Locking: none */ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - spin_lock_init(&buf->lock); + mutex_init(&buf->flush_mutex); tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; diff --git a/include/linux/tty.h b/include/linux/tty.h index 7c124541f01..1c8fef0e3ff 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -66,7 +66,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct work_struct work; - spinlock_t lock; + struct mutex flush_mutex; struct tty_buffer sentinel; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ -- cgit v1.2.3-70-g09d2 From d7a68be4f265be10e24be931c257af30ca55566b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:11 -0400 Subject: tty: Only perform flip buffer flush from tty_buffer_flush() Now that dropping the buffer lock is not necessary (as result of converting the spin lock to a mutex), the flip buffer flush no longer needs to be handled by the buffer work. Simply signal a flush is required; the buffer work will exit the i/o loop, which allows tty_buffer_flush() to proceed. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 63 ++++++++++++++++-------------------------------- include/linux/tty.h | 1 - 2 files changed, 21 insertions(+), 43 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index c3c606c5272..39cae611fe5 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -189,19 +189,11 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - mutex_lock(&buf->flush_mutex); - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTYP_FLUSHING, &port->iflags)) { - set_bit(TTYP_FLUSHPENDING, &port->iflags); - mutex_unlock(&buf->flush_mutex); - wait_event(tty->read_wait, - test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); - return; - } + set_bit(TTYP_FLUSHPENDING, &port->iflags); + mutex_lock(&buf->flush_mutex); __tty_buffer_flush(port); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); mutex_unlock(&buf->flush_mutex); } @@ -429,39 +421,26 @@ static void flush_to_ldisc(struct work_struct *work) mutex_lock(&buf->flush_mutex); - if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - while (1) { - struct tty_buffer *head = buf->head; - int count; - - count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - buf->head = head->next; - tty_buffer_free(port, head); - continue; - } - - mutex_unlock(&buf->flush_mutex); - - count = receive_buf(tty, head, count); - - mutex_lock(&buf->flush_mutex); - - /* Ldisc or user is trying to flush the buffers. - We may have a deferred request to flush the - input buffer, if so pull the chain under the lock - and empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); - break; - } else if (!count) + while (1) { + struct tty_buffer *head = buf->head; + int count; + + /* Ldisc or user is trying to flush the buffers. */ + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) + break; + + count = head->commit - head->read; + if (!count) { + if (head->next == NULL) break; + buf->head = head->next; + tty_buffer_free(port, head); + continue; } - clear_bit(TTYP_FLUSHING, &port->iflags); + + count = receive_buf(tty, head, count); + if (!count) + break; } mutex_unlock(&buf->flush_mutex); diff --git a/include/linux/tty.h b/include/linux/tty.h index 1c8fef0e3ff..1d5bacca365 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -213,7 +213,6 @@ struct tty_port { wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ unsigned long iflags; /* TTYP_ internal flags */ -#define TTYP_FLUSHING 1 /* Flushing to ldisc in progress */ #define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1, /* port is a console */ low_latency:1; /* direct buffer flush */ -- cgit v1.2.3-70-g09d2 From 0f56bd2f6a97d8b0eb5c8f9bc04b83a6c16d1d48 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:13 -0400 Subject: tty: Use non-atomic state to signal flip buffer flush pending Atomic bit ops are no longer required to indicate a flip buffer flush is pending, as the flush_mutex is sufficient barrier. Remove the unnecessary port .iflags field and localize flip buffer state to struct tty_bufhead. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 7 ++++--- include/linux/tty.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 39cae611fe5..fb042b9a8d6 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -189,11 +189,11 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - set_bit(TTYP_FLUSHPENDING, &port->iflags); + buf->flushpending = 1; mutex_lock(&buf->flush_mutex); __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); + buf->flushpending = 0; mutex_unlock(&buf->flush_mutex); } @@ -426,7 +426,7 @@ static void flush_to_ldisc(struct work_struct *work) int count; /* Ldisc or user is trying to flush the buffers. */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) + if (buf->flushpending) break; count = head->commit - head->read; @@ -505,6 +505,7 @@ void tty_buffer_init(struct tty_port *port) buf->tail = &buf->sentinel; init_llist_head(&buf->free); atomic_set(&buf->memory_used, 0); + buf->flushpending = 0; INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/tty.h b/include/linux/tty.h index b8e8adf95bf..991575fe345 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -68,6 +68,7 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; struct mutex flush_mutex; + unsigned int flushpending:1; struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ @@ -212,8 +213,6 @@ struct tty_port { wait_queue_head_t close_wait; /* Close waiters */ wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ - unsigned long iflags; /* TTYP_ internal flags */ -#define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1, /* port is a console */ low_latency:1; /* direct buffer flush */ struct mutex mutex; /* Locking */ -- cgit v1.2.3-70-g09d2 From 47aa658a015440906def231f54685c4d5d49dc38 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:14 -0400 Subject: tty: Merge __tty_flush_buffer() into lone call site __tty_flush_buffer() is now only called by tty_flush_buffer(); merge functions. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index fb042b9a8d6..dbe4a718e2d 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -150,28 +150,6 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) llist_add(&b->free, &buf->free); } -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - */ - -static void __tty_buffer_flush(struct tty_port *port) -{ - struct tty_bufhead *buf = &port->buf; - struct tty_buffer *next; - - while ((next = buf->head->next) != NULL) { - tty_buffer_free(port, buf->head); - buf->head = next; - } - WARN_ON(buf->head != buf->tail); - buf->head->read = buf->head->commit; -} - /** * tty_buffer_flush - flush full tty buffers * @tty: tty to flush @@ -188,11 +166,16 @@ void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; + struct tty_buffer *next; buf->flushpending = 1; mutex_lock(&buf->flush_mutex); - __tty_buffer_flush(port); + while ((next = buf->head->next) != NULL) { + tty_buffer_free(port, buf->head); + buf->head = next; + } + buf->head->read = buf->head->commit; buf->flushpending = 0; mutex_unlock(&buf->flush_mutex); } -- cgit v1.2.3-70-g09d2 From a7c8d58c79853adeebf0a1ddc9c63e433b4d97f1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:15 -0400 Subject: tty: Fix unsafe vt paste_selection() Convert the tty_buffer_flush() exclusion mechanism to a public interface - tty_buffer_lock/unlock_exclusive() - and use the interface to safely write the paste selection to the line discipline. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 61 ++++++++++++++++++++++++++++++++++++---------- drivers/tty/vt/selection.c | 4 ++- include/linux/tty.h | 4 +-- include/linux/tty_flip.h | 3 +++ 4 files changed, 56 insertions(+), 16 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index dbe4a718e2d..f22e116db10 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -29,6 +29,42 @@ #define TTYB_MEM_LIMIT 65536 +/** + * tty_buffer_lock_exclusive - gain exclusive access to buffer + * tty_buffer_unlock_exclusive - release exclusive access + * + * @port - tty_port owning the flip buffer + * + * Guarantees safe use of the line discipline's receive_buf() method by + * excluding the buffer work and any pending flush from using the flip + * buffer. Data can continue to be added concurrently to the flip buffer + * from the driver side. + * + * On release, the buffer work is restarted if there is data in the + * flip buffer + */ + +void tty_buffer_lock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + + atomic_inc(&buf->priority); + mutex_lock(&buf->lock); +} + +void tty_buffer_unlock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + int restart; + + restart = buf->head->commit != buf->head->read; + + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); + if (restart) + queue_work(system_unbound_wq, &buf->work); +} + /** * tty_buffer_space_avail - return unused buffer space * @port - tty_port owning the flip buffer @@ -158,7 +194,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) * being processed by flush_to_ldisc then we defer the processing * to that function * - * Locking: takes flush_mutex to ensure single-threaded flip buffer + * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' */ @@ -168,16 +204,16 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_bufhead *buf = &port->buf; struct tty_buffer *next; - buf->flushpending = 1; + atomic_inc(&buf->priority); - mutex_lock(&buf->flush_mutex); + mutex_lock(&buf->lock); while ((next = buf->head->next) != NULL) { tty_buffer_free(port, buf->head); buf->head = next; } buf->head->read = buf->head->commit; - buf->flushpending = 0; - mutex_unlock(&buf->flush_mutex); + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); } /** @@ -383,7 +419,7 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) * * The receive_buf method is single threaded for each tty instance. * - * Locking: takes flush_mutex to ensure single-threaded flip buffer + * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' */ @@ -402,14 +438,14 @@ static void flush_to_ldisc(struct work_struct *work) if (disc == NULL) return; - mutex_lock(&buf->flush_mutex); + mutex_lock(&buf->lock); while (1) { struct tty_buffer *head = buf->head; int count; - /* Ldisc or user is trying to flush the buffers. */ - if (buf->flushpending) + /* Ldisc or user is trying to gain exclusive access */ + if (atomic_read(&buf->priority)) break; count = head->commit - head->read; @@ -426,7 +462,7 @@ static void flush_to_ldisc(struct work_struct *work) break; } - mutex_unlock(&buf->flush_mutex); + mutex_unlock(&buf->lock); tty_ldisc_deref(disc); } @@ -482,13 +518,12 @@ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - mutex_init(&buf->flush_mutex); + mutex_init(&buf->lock); tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; init_llist_head(&buf->free); atomic_set(&buf->memory_used, 0); - buf->flushpending = 0; + atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); } - diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 2ca8d6b6514..ea27804d87a 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -24,6 +24,7 @@ #include #include #include +#include /* Don't take this from : 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -346,8 +347,8 @@ int paste_selection(struct tty_struct *tty) console_unlock(); ld = tty_ldisc_ref_wait(tty); + tty_buffer_lock_exclusive(&vc->port); - /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); @@ -363,6 +364,7 @@ int paste_selection(struct tty_struct *tty) remove_wait_queue(&vc->paste_wait, &wait); __set_current_state(TASK_RUNNING); + tty_buffer_unlock_exclusive(&vc->port); tty_ldisc_deref(ld); return 0; } diff --git a/include/linux/tty.h b/include/linux/tty.h index 991575fe345..7a9a3b0a6b5 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -67,8 +67,8 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; - struct mutex flush_mutex; - unsigned int flushpending:1; + struct mutex lock; + atomic_t priority; struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 6944ed2ce69..21ddd7d9ea1 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -32,4 +32,7 @@ static inline int tty_insert_flip_string(struct tty_port *port, return tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size); } +extern void tty_buffer_lock_exclusive(struct tty_port *port); +extern void tty_buffer_unlock_exclusive(struct tty_port *port); + #endif /* _LINUX_TTY_FLIP_H */ -- cgit v1.2.3-70-g09d2 From 9114fe8ccf1871f630d2c14cd60e5f455b015459 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:16 -0400 Subject: tty: Remove private constant from global namespace TTY_BUFFER_PAGE is only used within drivers/tty/tty_buffer.c; relocate to that file scope. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 10 ++++++++++ include/linux/tty.h | 11 ----------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/tty/tty_buffer.c') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f22e116db10..c043136fbe5 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -28,6 +28,16 @@ */ #define TTYB_MEM_LIMIT 65536 +/* + * We default to dicing tty buffer allocations to this many characters + * in order to avoid multiple page allocations. We know the size of + * tty_buffer itself but it must also be taken into account that the + * the buffer is 256 byte aligned. See tty_buffer_find for the allocation + * logic this must match + */ + +#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) + /** * tty_buffer_lock_exclusive - gain exclusive access to buffer diff --git a/include/linux/tty.h b/include/linux/tty.h index 7a9a3b0a6b5..5fd5d6f1ebc 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -53,17 +53,6 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) return (char *)char_buf_ptr(b, ofs) + b->size; } -/* - * We default to dicing tty buffer allocations to this many characters - * in order to avoid multiple page allocations. We know the size of - * tty_buffer itself but it must also be taken into account that the - * the buffer is 256 byte aligned. See tty_buffer_find for the allocation - * logic this must match - */ - -#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) - - struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; -- cgit v1.2.3-70-g09d2