Skip to content

Commit fb1d828

Browse files
matnymangregkh
authored andcommitted
xhci: dbc: honor usb transfer size boundaries.
[ Upstream commit 30c9ae5 ] 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 <uday.m.bhat@intel.com> Tested-by: Łukasz Bartosik <ukaszb@chromium.org> Cc: stable@vger.kernel.org Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20241016140000.783905-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 9c298b1 commit fb1d828

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

drivers/usb/host/xhci-dbgcap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ struct dbc_port {
110110
struct tasklet_struct push;
111111

112112
struct list_head write_pool;
113+
unsigned int tx_boundary;
113114

114115
bool registered;
115116
};

drivers/usb/host/xhci-dbgtty.c

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
2424
return dbc->priv;
2525
}
2626

27+
static unsigned int
28+
dbc_kfifo_to_req(struct dbc_port *port, char *packet)
29+
{
30+
unsigned int len;
31+
32+
len = kfifo_len(&port->port.xmit_fifo);
33+
34+
if (len == 0)
35+
return 0;
36+
37+
len = min(len, DBC_MAX_PACKET);
38+
39+
if (port->tx_boundary)
40+
len = min(port->tx_boundary, len);
41+
42+
len = kfifo_out(&port->port.xmit_fifo, packet, len);
43+
44+
if (port->tx_boundary)
45+
port->tx_boundary -= len;
46+
47+
return len;
48+
}
49+
2750
static int dbc_start_tx(struct dbc_port *port)
2851
__releases(&port->port_lock)
2952
__acquires(&port->port_lock)
@@ -36,7 +59,7 @@ static int dbc_start_tx(struct dbc_port *port)
3659

3760
while (!list_empty(pool)) {
3861
req = list_entry(pool->next, struct dbc_request, list_pool);
39-
len = kfifo_out(&port->port.xmit_fifo, req->buf, DBC_MAX_PACKET);
62+
len = dbc_kfifo_to_req(port, req->buf);
4063
if (len == 0)
4164
break;
4265
do_tty_wake = true;
@@ -200,14 +223,32 @@ static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf,
200223
{
201224
struct dbc_port *port = tty->driver_data;
202225
unsigned long flags;
226+
unsigned int written = 0;
203227

204228
spin_lock_irqsave(&port->port_lock, flags);
205-
if (count)
206-
count = kfifo_in(&port->port.xmit_fifo, buf, count);
207-
dbc_start_tx(port);
229+
230+
/*
231+
* Treat tty write as one usb transfer. Make sure the writes are turned
232+
* into TRB request having the same size boundaries as the tty writes.
233+
* Don't add data to kfifo before previous write is turned into TRBs
234+
*/
235+
if (port->tx_boundary) {
236+
spin_unlock_irqrestore(&port->port_lock, flags);
237+
return 0;
238+
}
239+
240+
if (count) {
241+
written = kfifo_in(&port->port.xmit_fifo, buf, count);
242+
243+
if (written == count)
244+
port->tx_boundary = kfifo_len(&port->port.xmit_fifo);
245+
246+
dbc_start_tx(port);
247+
}
248+
208249
spin_unlock_irqrestore(&port->port_lock, flags);
209250

210-
return count;
251+
return written;
211252
}
212253

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

242283
spin_lock_irqsave(&port->port_lock, flags);
243284
room = kfifo_avail(&port->port.xmit_fifo);
285+
286+
if (port->tx_boundary)
287+
room = 0;
288+
244289
spin_unlock_irqrestore(&port->port_lock, flags);
245290

246291
return room;

0 commit comments

Comments
 (0)