Skip to content

Commit

Permalink
xhci: dbc: honor usb transfer size boundaries.
Browse files Browse the repository at this point in the history
Treat each completed full size write to /dev/ttyDBC0 as a separate usb
transfer. Make sure the size of the TRBs matches the size of the tty
write by first queuing as many max packet size TRBs as possible up to
the last TRB which will be cut short to match the size of the tty write.

This solves an issue where userspace writes several transfers back to
back via /dev/ttyDBC0 into a kfifo before dbgtty can find available
request to turn that kfifo data into TRBs on the transfer ring.

The boundary between transfer was lost as xhci-dbgtty then turned
everyting in the kfifo into as many 'max packet size' TRBs as possible.

DbC would then send more data to the host than intended for that
transfer, causing host to issue a babble error.

Refuse to write more data to kfifo until previous tty write data is
turned into properly sized TRBs with data size boundaries matching tty
write size

Tested-by: Uday M Bhat <[email protected]>
Tested-by: Łukasz Bartosik <[email protected]>
Cc: [email protected]
Signed-off-by: Mathias Nyman <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
matnyman authored and gregkh committed Oct 17, 2024
1 parent f42a36b commit 30c9ae5
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
1 change: 1 addition & 0 deletions drivers/usb/host/xhci-dbgcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ struct dbc_port {
struct tasklet_struct push;

struct list_head write_pool;
unsigned int tx_boundary;

bool registered;
};
Expand Down
55 changes: 50 additions & 5 deletions drivers/usb/host/xhci-dbgtty.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
return dbc->priv;
}

static unsigned int
dbc_kfifo_to_req(struct dbc_port *port, char *packet)
{
unsigned int len;

len = kfifo_len(&port->port.xmit_fifo);

if (len == 0)
return 0;

len = min(len, DBC_MAX_PACKET);

if (port->tx_boundary)
len = min(port->tx_boundary, len);

len = kfifo_out(&port->port.xmit_fifo, packet, len);

if (port->tx_boundary)
port->tx_boundary -= len;

return len;
}

static int dbc_start_tx(struct dbc_port *port)
__releases(&port->port_lock)
__acquires(&port->port_lock)
Expand All @@ -36,7 +59,7 @@ static int dbc_start_tx(struct dbc_port *port)

while (!list_empty(pool)) {
req = list_entry(pool->next, struct dbc_request, list_pool);
len = kfifo_out(&port->port.xmit_fifo, req->buf, DBC_MAX_PACKET);
len = dbc_kfifo_to_req(port, req->buf);
if (len == 0)
break;
do_tty_wake = true;
Expand Down Expand Up @@ -200,14 +223,32 @@ static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf,
{
struct dbc_port *port = tty->driver_data;
unsigned long flags;
unsigned int written = 0;

spin_lock_irqsave(&port->port_lock, flags);
if (count)
count = kfifo_in(&port->port.xmit_fifo, buf, count);
dbc_start_tx(port);

/*
* Treat tty write as one usb transfer. Make sure the writes are turned
* into TRB request having the same size boundaries as the tty writes.
* Don't add data to kfifo before previous write is turned into TRBs
*/
if (port->tx_boundary) {
spin_unlock_irqrestore(&port->port_lock, flags);
return 0;
}

if (count) {
written = kfifo_in(&port->port.xmit_fifo, buf, count);

if (written == count)
port->tx_boundary = kfifo_len(&port->port.xmit_fifo);

dbc_start_tx(port);
}

spin_unlock_irqrestore(&port->port_lock, flags);

return count;
return written;
}

static int dbc_tty_put_char(struct tty_struct *tty, u8 ch)
Expand Down Expand Up @@ -241,6 +282,10 @@ static unsigned int dbc_tty_write_room(struct tty_struct *tty)

spin_lock_irqsave(&port->port_lock, flags);
room = kfifo_avail(&port->port.xmit_fifo);

if (port->tx_boundary)
room = 0;

spin_unlock_irqrestore(&port->port_lock, flags);

return room;
Expand Down

0 comments on commit 30c9ae5

Please sign in to comment.