diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2013-06-15 09:36:08 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-23 16:47:08 -0700 |
commit | 7bfe0b7116be207cf2204ae06335cc89d8f8ee02 (patch) | |
tree | 03175cd2ce9dd8013c4aaa8e2dd426767aa1a4c7 /drivers/tty | |
parent | 7391ee16950e772076d321792d9fbf030f921345 (diff) |
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 <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/pty.c | 10 | ||||
-rw-r--r-- | drivers/tty/tty_buffer.c | 37 |
2 files changed, 34 insertions, 13 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 1b39dd639ee9..b38a28bd9511 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 231b7a8710f1..5d5a56407aa8 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); } |