tty: Track flip buffer memory limit atomically
authorPeter Hurley <peter@hurleysoftware.com>
Sat, 15 Jun 2013 13:36:08 +0000 (09:36 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Jul 2013 23:47:08 +0000 (16:47 -0700)
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>
drivers/tty/pty.c
drivers/tty/tty_buffer.c
include/linux/tty.h
include/linux/tty_flip.h

index 1b39dd639ee9aa0e3290f5b66c17681728f942c0..b38a28bd95116fd916fc3e685e270887ed59139e 100644 (file)
@@ -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);
 }
 
 /**
index 231b7a8710f18770b6e6a5d5e6bb7ee890b8d7ed..5d5a56407aa8bbd9100b16b1b6ace757acdf1a66 100644 (file)
 #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);
 }
 
index 2e93eb831c611136f18ab66e2dfa4fda932e28a9..7c124541f0115d354858b6649ed65e8271b6671c 100644 (file)
@@ -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
index ad0303994c32b75907e839ffa5ca7cf2f5a42d6d..6944ed2ce692641fafe52aa22a581b547582e7c3 100644 (file)
@@ -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);