staging/fwserial: Rip out rx buffering
Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
7e1e71d154
commit
c4a8dab580
@ -456,16 +456,27 @@ static int fwtty_write_port_status(struct fwtty_port *port)
|
|||||||
return err;
|
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;
|
unsigned old;
|
||||||
|
|
||||||
|
tty = tty_port_tty_get(&port->port);
|
||||||
|
if (!tty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
spin_lock_bh(&port->lock);
|
||||||
|
|
||||||
old = port->mctrl;
|
old = port->mctrl;
|
||||||
port->mctrl |= OOB_RX_THROTTLE;
|
port->mctrl |= OOB_RX_THROTTLE;
|
||||||
if (C_CRTSCTS(tty))
|
if (C_CRTSCTS(tty))
|
||||||
port->mctrl &= ~TIOCM_RTS;
|
port->mctrl &= ~TIOCM_RTS;
|
||||||
if (~old & OOB_RX_THROTTLE)
|
if (~old & OOB_RX_THROTTLE)
|
||||||
__fwtty_write_port_status(port);
|
__fwtty_write_port_status(port);
|
||||||
|
|
||||||
|
spin_unlock_bh(&port->lock);
|
||||||
|
|
||||||
|
tty_kref_put(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -532,74 +543,8 @@ static void fwtty_emit_breaks(struct work_struct *work)
|
|||||||
port->icount.brk += brk;
|
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)
|
static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len)
|
||||||
{
|
{
|
||||||
struct tty_struct *tty;
|
|
||||||
int c, n = len;
|
int c, n = len;
|
||||||
unsigned lsr;
|
unsigned lsr;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@ -636,31 +581,24 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!test_bit(BUFFERING_RX, &port->flags)) {
|
c = tty_insert_flip_string_fixed_flag(&port->port, data, TTY_NORMAL, n);
|
||||||
c = tty_insert_flip_string_fixed_flag(&port->port, data,
|
if (c > 0)
|
||||||
TTY_NORMAL, n);
|
tty_flip_buffer_push(&port->port);
|
||||||
if (c > 0)
|
n -= c;
|
||||||
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);
|
|
||||||
|
|
||||||
if (n) {
|
if (n) {
|
||||||
port->overrun = true;
|
port->overrun = true;
|
||||||
err = -EIO;
|
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:
|
out:
|
||||||
@ -1101,20 +1039,13 @@ static int fwtty_port_activate(struct tty_port *tty_port,
|
|||||||
static void fwtty_port_shutdown(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 fwtty_port *port = to_port(tty_port, port);
|
||||||
struct buffered_rx *buf, *next;
|
|
||||||
|
|
||||||
/* TODO: cancel outstanding transactions */
|
/* TODO: cancel outstanding transactions */
|
||||||
|
|
||||||
cancel_delayed_work_sync(&port->emit_breaks);
|
cancel_delayed_work_sync(&port->emit_breaks);
|
||||||
cancel_delayed_work_sync(&port->drain);
|
cancel_delayed_work_sync(&port->drain);
|
||||||
cancel_work_sync(&port->push);
|
|
||||||
|
|
||||||
spin_lock_bh(&port->lock);
|
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->flags = 0;
|
||||||
port->break_ctl = 0;
|
port->break_ctl = 0;
|
||||||
port->overrun = 0;
|
port->overrun = 0;
|
||||||
@ -1264,8 +1195,6 @@ static void fwtty_unthrottle(struct tty_struct *tty)
|
|||||||
|
|
||||||
profile_fifo_avail(port, port->stats.unthrottle);
|
profile_fifo_avail(port, port->stats.unthrottle);
|
||||||
|
|
||||||
schedule_work(&port->push);
|
|
||||||
|
|
||||||
spin_lock_bh(&port->lock);
|
spin_lock_bh(&port->lock);
|
||||||
port->mctrl &= ~OOB_RX_THROTTLE;
|
port->mctrl &= ~OOB_RX_THROTTLE;
|
||||||
if (C_CRTSCTS(tty))
|
if (C_CRTSCTS(tty))
|
||||||
@ -1523,8 +1452,7 @@ 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,
|
seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped,
|
||||||
stats.tx_stall, stats.fifo_errs, stats.lost);
|
stats.tx_stall, stats.fifo_errs, stats.lost);
|
||||||
seq_printf(m, " pkts:%d thr:%d wtrmk:%d", stats.sent, stats.throttled,
|
seq_printf(m, " pkts:%d thr:%d", stats.sent, stats.throttled);
|
||||||
stats.watermark);
|
|
||||||
|
|
||||||
if (port->port.console) {
|
if (port->port.console) {
|
||||||
seq_puts(m, "\n ");
|
seq_puts(m, "\n ");
|
||||||
@ -2302,8 +2230,6 @@ static int fwserial_create(struct fw_unit *unit)
|
|||||||
INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx);
|
INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx);
|
||||||
INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks);
|
INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks);
|
||||||
INIT_WORK(&port->hangup, fwtty_do_hangup);
|
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);
|
init_waitqueue_head(&port->wait_tx);
|
||||||
port->max_payload = link_speed_to_max_payload(SCODE_100);
|
port->max_payload = link_speed_to_max_payload(SCODE_100);
|
||||||
dma_fifo_init(&port->tx_fifo);
|
dma_fifo_init(&port->tx_fifo);
|
||||||
|
@ -166,7 +166,6 @@ struct stats {
|
|||||||
unsigned sent;
|
unsigned sent;
|
||||||
unsigned lost;
|
unsigned lost;
|
||||||
unsigned throttled;
|
unsigned throttled;
|
||||||
unsigned watermark;
|
|
||||||
unsigned reads[DISTRIBUTION_MAX_INDEX + 1];
|
unsigned reads[DISTRIBUTION_MAX_INDEX + 1];
|
||||||
unsigned writes[DISTRIBUTION_MAX_INDEX + 1];
|
unsigned writes[DISTRIBUTION_MAX_INDEX + 1];
|
||||||
unsigned txns[DISTRIBUTION_MAX_INDEX + 1];
|
unsigned txns[DISTRIBUTION_MAX_INDEX + 1];
|
||||||
@ -183,12 +182,6 @@ struct fwconsole_ops {
|
|||||||
#define FWCON_NOTIFY_ATTACH 1
|
#define FWCON_NOTIFY_ATTACH 1
|
||||||
#define FWCON_NOTIFY_DETACH 2
|
#define FWCON_NOTIFY_DETACH 2
|
||||||
|
|
||||||
struct buffered_rx {
|
|
||||||
struct list_head list;
|
|
||||||
size_t n;
|
|
||||||
unsigned char data[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fwtty_port: structure used to track/represent underlying tty_port
|
* fwtty_port: structure used to track/represent underlying tty_port
|
||||||
* @port: underlying tty_port
|
* @port: underlying tty_port
|
||||||
@ -223,11 +216,6 @@ struct buffered_rx {
|
|||||||
* The work can race with the writer but concurrent sending is
|
* The work can race with the writer but concurrent sending is
|
||||||
* prevented with the IN_TX flag. Scheduled under lock to
|
* prevented with the IN_TX flag. Scheduled under lock to
|
||||||
* limit scheduling when fifo has just been drained.
|
* limit scheduling when fifo has just been drained.
|
||||||
* @push: work responsible for pushing buffered rx to the ldisc.
|
|
||||||
* rx can become buffered if the tty buffer is filled before the
|
|
||||||
* ldisc throttles the sender.
|
|
||||||
* @buf_list: list of buffered rx yet to be sent to ldisc
|
|
||||||
* @buffered: byte count of buffered rx
|
|
||||||
* @tx_fifo: fifo used to store & block-up writes for dma to remote
|
* @tx_fifo: fifo used to store & block-up writes for dma to remote
|
||||||
* @max_payload: max bytes transmissable per dma (based on peer's max_payload)
|
* @max_payload: max bytes transmissable per dma (based on peer's max_payload)
|
||||||
* @status_mask: UART_LSR_* bitmask significant to rx (based on termios)
|
* @status_mask: UART_LSR_* bitmask significant to rx (based on termios)
|
||||||
@ -267,9 +255,6 @@ struct fwtty_port {
|
|||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
unsigned mctrl;
|
unsigned mctrl;
|
||||||
struct delayed_work drain;
|
struct delayed_work drain;
|
||||||
struct work_struct push;
|
|
||||||
struct list_head buf_list;
|
|
||||||
int buffered;
|
|
||||||
struct dma_fifo tx_fifo;
|
struct dma_fifo tx_fifo;
|
||||||
int max_payload;
|
int max_payload;
|
||||||
unsigned status_mask;
|
unsigned status_mask;
|
||||||
@ -291,7 +276,6 @@ struct fwtty_port {
|
|||||||
/* bit #s for flags field */
|
/* bit #s for flags field */
|
||||||
#define IN_TX 0
|
#define IN_TX 0
|
||||||
#define STOP_TX 1
|
#define STOP_TX 1
|
||||||
#define BUFFERING_RX 2
|
|
||||||
|
|
||||||
/* bitmasks for special mctrl/mstatus bits */
|
/* bitmasks for special mctrl/mstatus bits */
|
||||||
#define OOB_RX_THROTTLE 0x00010000
|
#define OOB_RX_THROTTLE 0x00010000
|
||||||
|
@ -92,6 +92,7 @@ int tty_buffer_space_avail(struct tty_port *port)
|
|||||||
int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used);
|
int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used);
|
||||||
return max(space, 0);
|
return max(space, 0);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tty_buffer_space_avail);
|
||||||
|
|
||||||
static void tty_buffer_reset(struct tty_buffer *p, size_t size)
|
static void tty_buffer_reset(struct tty_buffer *p, size_t size)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user