]> Pileus Git - ~andy/linux/blobdiff - drivers/staging/fwserial/fwserial.c
Merge tag 'regulator-v3.14-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git...
[~andy/linux] / drivers / staging / fwserial / fwserial.c
index 62df009e5ac7c8e9bcd5779bf57e9b8c1d2c309d..8af136e9c9dc16e1ab5f5f5cecf7dfe1579c528b 100644 (file)
@@ -136,14 +136,14 @@ static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card,
 
 #ifdef FWTTY_PROFILING
 
-static void profile_fifo_avail(struct fwtty_port *port, unsigned *stat)
+static void fwtty_profile_fifo(struct fwtty_port *port, unsigned *stat)
 {
        spin_lock_bh(&port->lock);
-       profile_size_distrib(stat, dma_fifo_avail(&port->tx_fifo));
+       fwtty_profile_data(stat, dma_fifo_avail(&port->tx_fifo));
        spin_unlock_bh(&port->lock);
 }
 
-static void dump_profile(struct seq_file *m, struct stats *stats)
+static void fwtty_dump_profile(struct seq_file *m, struct stats *stats)
 {
        /* for each stat, print sum of 0 to 2^k, then individually */
        int k = 4;
@@ -183,8 +183,8 @@ static void dump_profile(struct seq_file *m, struct stats *stats)
 }
 
 #else
-#define profile_fifo_avail(port, stat)
-#define dump_profile(m, stats)
+#define fwtty_profile_fifo(port, stat)
+#define fwtty_dump_profile(m, stats)
 #endif
 
 /*
@@ -456,16 +456,27 @@ static int fwtty_write_port_status(struct fwtty_port *port)
        return err;
 }
 
-static void __fwtty_throttle(struct fwtty_port *port, struct tty_struct *tty)
+static void fwtty_throttle_port(struct fwtty_port *port)
 {
+       struct tty_struct *tty;
        unsigned old;
 
+       tty = tty_port_tty_get(&port->port);
+       if (!tty)
+               return;
+
+       spin_lock_bh(&port->lock);
+
        old = port->mctrl;
        port->mctrl |= OOB_RX_THROTTLE;
        if (C_CRTSCTS(tty))
                port->mctrl &= ~TIOCM_RTS;
        if (~old & OOB_RX_THROTTLE)
                __fwtty_write_port_status(port);
+
+       spin_unlock_bh(&port->lock);
+
+       tty_kref_put(tty);
 }
 
 /**
@@ -532,80 +543,14 @@ static void fwtty_emit_breaks(struct work_struct *work)
        port->icount.brk += brk;
 }
 
-static void fwtty_pushrx(struct work_struct *work)
-{
-       struct fwtty_port *port = to_port(work, push);
-       struct tty_struct *tty;
-       struct buffered_rx *buf, *next;
-       int n, c = 0;
-
-       spin_lock_bh(&port->lock);
-       list_for_each_entry_safe(buf, next, &port->buf_list, list) {
-               n = tty_insert_flip_string_fixed_flag(&port->port, buf->data,
-                                                     TTY_NORMAL, buf->n);
-               c += n;
-               port->buffered -= n;
-               if (n < buf->n) {
-                       if (n > 0) {
-                               memmove(buf->data, buf->data + n, buf->n - n);
-                               buf->n -= n;
-                       }
-                       tty = tty_port_tty_get(&port->port);
-                       if (tty) {
-                               __fwtty_throttle(port, tty);
-                               tty_kref_put(tty);
-                       }
-                       break;
-               } else {
-                       list_del(&buf->list);
-                       kfree(buf);
-               }
-       }
-       if (c > 0)
-               tty_flip_buffer_push(&port->port);
-
-       if (list_empty(&port->buf_list))
-               clear_bit(BUFFERING_RX, &port->flags);
-       spin_unlock_bh(&port->lock);
-}
-
-static int fwtty_buffer_rx(struct fwtty_port *port, unsigned char *d, size_t n)
-{
-       struct buffered_rx *buf;
-       size_t size = (n + sizeof(struct buffered_rx) + 0xFF) & ~0xFF;
-
-       if (port->buffered + n > HIGH_WATERMARK) {
-               fwtty_err_ratelimited(port, "overflowed rx buffer: buffered: %d new: %zu wtrmk: %d\n",
-                                     port->buffered, n, HIGH_WATERMARK);
-               return 0;
-       }
-       buf = kmalloc(size, GFP_ATOMIC);
-       if (!buf)
-               return 0;
-       INIT_LIST_HEAD(&buf->list);
-       buf->n = n;
-       memcpy(buf->data, d, n);
-
-       spin_lock_bh(&port->lock);
-       list_add_tail(&buf->list, &port->buf_list);
-       port->buffered += n;
-       if (port->buffered > port->stats.watermark)
-               port->stats.watermark = port->buffered;
-       set_bit(BUFFERING_RX, &port->flags);
-       spin_unlock_bh(&port->lock);
-
-       return n;
-}
-
 static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len)
 {
-       struct tty_struct *tty;
        int c, n = len;
        unsigned lsr;
        int err = 0;
 
        fwtty_dbg(port, "%d\n", n);
-       profile_size_distrib(port->stats.reads, n);
+       fwtty_profile_data(port->stats.reads, n);
 
        if (port->write_only) {
                n = 0;
@@ -636,31 +581,24 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len)
                goto out;
        }
 
-       if (!test_bit(BUFFERING_RX, &port->flags)) {
-               c = tty_insert_flip_string_fixed_flag(&port->port, data,
-                               TTY_NORMAL, n);
-               if (c > 0)
-                       tty_flip_buffer_push(&port->port);
-               n -= c;
-
-               if (n) {
-                       /* start buffering and throttling */
-                       n -= fwtty_buffer_rx(port, &data[c], n);
-
-                       tty = tty_port_tty_get(&port->port);
-                       if (tty) {
-                               spin_lock_bh(&port->lock);
-                               __fwtty_throttle(port, tty);
-                               spin_unlock_bh(&port->lock);
-                               tty_kref_put(tty);
-                       }
-               }
-       } else
-               n -= fwtty_buffer_rx(port, data, n);
+       c = tty_insert_flip_string_fixed_flag(&port->port, data, TTY_NORMAL, n);
+       if (c > 0)
+               tty_flip_buffer_push(&port->port);
+       n -= c;
 
        if (n) {
                port->overrun = true;
                err = -EIO;
+               fwtty_err_ratelimited(port, "flip buffer overrun\n");
+
+       } else {
+               /* throttle the sender if remaining flip buffer space has
+                * reached high watermark to avoid losing data which may be
+                * in-flight. Since the AR request context is 32k, that much
+                * data may have _already_ been acked.
+                */
+               if (tty_buffer_space_avail(&port->port) < HIGH_WATERMARK)
+                       fwtty_throttle_port(port);
        }
 
 out:
@@ -821,7 +759,7 @@ static int fwtty_tx(struct fwtty_port *port, bool drain)
                        if (n == -EAGAIN)
                                ++port->stats.tx_stall;
                        else if (n == -ENODATA)
-                               profile_size_distrib(port->stats.txns, 0);
+                               fwtty_profile_data(port->stats.txns, 0);
                        else {
                                ++port->stats.fifo_errs;
                                fwtty_err_ratelimited(port, "fifo err: %d\n",
@@ -830,7 +768,7 @@ static int fwtty_tx(struct fwtty_port *port, bool drain)
                        break;
                }
 
-               profile_size_distrib(port->stats.txns, txn->dma_pended.len);
+               fwtty_profile_data(port->stats.txns, txn->dma_pended.len);
 
                fwtty_send_txn_async(peer, txn, TCODE_WRITE_BLOCK_REQUEST,
                                     peer->fifo_addr, txn->dma_pended.data,
@@ -1101,20 +1039,13 @@ static int fwtty_port_activate(struct tty_port *tty_port,
 static void fwtty_port_shutdown(struct tty_port *tty_port)
 {
        struct fwtty_port *port = to_port(tty_port, port);
-       struct buffered_rx *buf, *next;
 
        /* TODO: cancel outstanding transactions */
 
        cancel_delayed_work_sync(&port->emit_breaks);
        cancel_delayed_work_sync(&port->drain);
-       cancel_work_sync(&port->push);
 
        spin_lock_bh(&port->lock);
-       list_for_each_entry_safe(buf, next, &port->buf_list, list) {
-               list_del(&buf->list);
-               kfree(buf);
-       }
-       port->buffered = 0;
        port->flags = 0;
        port->break_ctl = 0;
        port->overrun = 0;
@@ -1184,7 +1115,7 @@ static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c)
        int n, len;
 
        fwtty_dbg(port, "%d\n", c);
-       profile_size_distrib(port->stats.writes, c);
+       fwtty_profile_data(port->stats.writes, c);
 
        spin_lock_bh(&port->lock);
        n = dma_fifo_in(&port->tx_fifo, buf, c);
@@ -1262,9 +1193,7 @@ static void fwtty_unthrottle(struct tty_struct *tty)
 
        fwtty_dbg(port, "CRTSCTS: %d\n", (C_CRTSCTS(tty) != 0));
 
-       profile_fifo_avail(port, port->stats.unthrottle);
-
-       schedule_work(&port->push);
+       fwtty_profile_fifo(port, port->stats.unthrottle);
 
        spin_lock_bh(&port->lock);
        port->mctrl &= ~OOB_RX_THROTTLE;
@@ -1523,15 +1452,14 @@ static void fwtty_debugfs_show_port(struct seq_file *m, struct fwtty_port *port)
 
        seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped,
                   stats.tx_stall, stats.fifo_errs, stats.lost);
-       seq_printf(m, " pkts:%d thr:%d wtrmk:%d", stats.sent, stats.throttled,
-                  stats.watermark);
+       seq_printf(m, " pkts:%d thr:%d", stats.sent, stats.throttled);
 
        if (port->port.console) {
                seq_puts(m, "\n    ");
                (*port->fwcon_ops->proc_show)(m, port->con_data);
        }
 
-       dump_profile(m, &port->stats);
+       fwtty_dump_profile(m, &port->stats);
 }
 
 static void fwtty_debugfs_show_peer(struct seq_file *m, struct fwtty_peer *peer)
@@ -2297,13 +2225,12 @@ static int fwserial_create(struct fw_unit *unit)
                port->index = FWTTY_INVALID_INDEX;
                port->port.ops = &fwtty_port_ops;
                port->serial = serial;
+               tty_buffer_set_limit(&port->port, 128 * 1024);
 
                spin_lock_init(&port->lock);
                INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx);
                INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks);
                INIT_WORK(&port->hangup, fwtty_do_hangup);
-               INIT_WORK(&port->push, fwtty_pushrx);
-               INIT_LIST_HEAD(&port->buf_list);
                init_waitqueue_head(&port->wait_tx);
                port->max_payload = link_speed_to_max_payload(SCODE_100);
                dma_fifo_init(&port->tx_fifo);