Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: cdc-wdm: avoid setting WDM_READ for ZLP-s

Don't set WDM_READ flag in wdm_in_callback() for ZLP-s, otherwise when
userspace tries to poll for available data, it might - incorrectly -
believe there is something available, and when it tries to non-blocking
read it, it might get stuck in the read loop.

For example this is what glib does for non-blocking read (briefly):

1. poll()
2. if poll returns with non-zero, starts a read data loop:
a. loop on poll() (EINTR disabled)
b. if revents was set, reads data
I. if read returns with EINTR or EAGAIN, goto 2.a.
II. otherwise return with data

So if ZLP sets WDM_READ (#1), we expect data, and try to read it (#2).
But as that was a ZLP, and we are doing non-blocking read, wdm_read()
returns with EAGAIN (#2.b.I), so loop again, and try to read again
(#2.a.).

With glib, we might stuck in this loop forever, as EINTR is disabled
(#2.a).

Signed-off-by: Robert Hodaszi <robert.hodaszi@digi.com>
Acked-by: Oliver Neukum <oneukum@suse.com>
Link: https://lore.kernel.org/r/20250403144004.3889125-1-robert.hodaszi@digi.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Robert Hodaszi and committed by
Greg Kroah-Hartman
387602d8 1b4dab85

+9 -14
+9 -14
drivers/usb/class/cdc-wdm.c
··· 92 92 u16 wMaxCommand; 93 93 u16 wMaxPacketSize; 94 94 __le16 inum; 95 - int reslength; 96 95 int length; 97 96 int read; 98 97 int count; ··· 213 214 if (desc->rerr == 0 && status != -EPIPE) 214 215 desc->rerr = status; 215 216 217 + if (length == 0) { 218 + dev_dbg(&desc->intf->dev, "received ZLP\n"); 219 + goto skip_zlp; 220 + } 221 + 216 222 if (length + desc->length > desc->wMaxCommand) { 217 223 /* The buffer would overflow */ 218 224 set_bit(WDM_OVERFLOW, &desc->flags); ··· 226 222 if (!test_bit(WDM_OVERFLOW, &desc->flags)) { 227 223 memmove(desc->ubuf + desc->length, desc->inbuf, length); 228 224 desc->length += length; 229 - desc->reslength = length; 230 225 } 231 226 } 232 227 skip_error: 233 228 234 229 if (desc->rerr) { 235 230 /* 236 - * Since there was an error, userspace may decide to not read 237 - * any data after poll'ing. 231 + * If there was a ZLP or an error, userspace may decide to not 232 + * read any data after poll'ing. 238 233 * We should respond to further attempts from the device to send 239 234 * data, so that we can get unstuck. 240 235 */ 236 + skip_zlp: 241 237 schedule_work(&desc->service_outs_intr); 242 238 } else { 243 239 set_bit(WDM_READ, &desc->flags); ··· 589 585 goto retry; 590 586 } 591 587 592 - if (!desc->reslength) { /* zero length read */ 593 - dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); 594 - clear_bit(WDM_READ, &desc->flags); 595 - rv = service_outstanding_interrupt(desc); 596 - spin_unlock_irq(&desc->iuspin); 597 - if (rv < 0) 598 - goto err; 599 - goto retry; 600 - } 601 588 cntr = desc->length; 602 589 spin_unlock_irq(&desc->iuspin); 603 590 } ··· 1000 1005 1001 1006 spin_lock_irq(&desc->iuspin); 1002 1007 service_outstanding_interrupt(desc); 1003 - if (!desc->resp_count) { 1008 + if (!desc->resp_count && (desc->length || desc->rerr)) { 1004 1009 set_bit(WDM_READ, &desc->flags); 1005 1010 wake_up(&desc->wait); 1006 1011 }