mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] usb: cdc-wdm: avoid setting WDM_READ for ZLP-s
@ 2025-04-03 14:40 Robert Hodaszi
  2025-04-07  9:23 ` Oliver Neukum
  0 siblings, 1 reply; 2+ messages in thread
From: Robert Hodaszi @ 2025-04-03 14:40 UTC (permalink / raw)
  To: gregkh, oneukum; +Cc: linux-kernel, linux-usb, stern, viro, Robert Hodaszi

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>
---
 drivers/usb/class/cdc-wdm.c | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 86ee39db013f..28c5ed840a40 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -92,7 +92,6 @@ struct wdm_device {
 	u16			wMaxCommand;
 	u16			wMaxPacketSize;
 	__le16			inum;
-	int			reslength;
 	int			length;
 	int			read;
 	int			count;
@@ -214,6 +213,11 @@ static void wdm_in_callback(struct urb *urb)
 	if (desc->rerr == 0 && status != -EPIPE)
 		desc->rerr = status;
 
+	if (length == 0) {
+		dev_dbg(&desc->intf->dev, "received ZLP\n");
+		goto skip_zlp;
+	}
+
 	if (length + desc->length > desc->wMaxCommand) {
 		/* The buffer would overflow */
 		set_bit(WDM_OVERFLOW, &desc->flags);
@@ -222,18 +226,18 @@ static void wdm_in_callback(struct urb *urb)
 		if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
 			memmove(desc->ubuf + desc->length, desc->inbuf, length);
 			desc->length += length;
-			desc->reslength = length;
 		}
 	}
 skip_error:
 
 	if (desc->rerr) {
 		/*
-		 * Since there was an error, userspace may decide to not read
-		 * any data after poll'ing.
+		 * If there was a ZLP or an error, userspace may decide to not
+		 * read any data after poll'ing.
 		 * We should respond to further attempts from the device to send
 		 * data, so that we can get unstuck.
 		 */
+skip_zlp:
 		schedule_work(&desc->service_outs_intr);
 	} else {
 		set_bit(WDM_READ, &desc->flags);
@@ -585,15 +589,6 @@ static ssize_t wdm_read
 			goto retry;
 		}
 
-		if (!desc->reslength) { /* zero length read */
-			dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n");
-			clear_bit(WDM_READ, &desc->flags);
-			rv = service_outstanding_interrupt(desc);
-			spin_unlock_irq(&desc->iuspin);
-			if (rv < 0)
-				goto err;
-			goto retry;
-		}
 		cntr = desc->length;
 		spin_unlock_irq(&desc->iuspin);
 	}
@@ -1005,7 +1000,7 @@ static void service_interrupt_work(struct work_struct *work)
 
 	spin_lock_irq(&desc->iuspin);
 	service_outstanding_interrupt(desc);
-	if (!desc->resp_count) {
+	if (!desc->resp_count && (desc->length || desc->rerr)) {
 		set_bit(WDM_READ, &desc->flags);
 		wake_up(&desc->wait);
 	}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] usb: cdc-wdm: avoid setting WDM_READ for ZLP-s
  2025-04-03 14:40 [PATCH] usb: cdc-wdm: avoid setting WDM_READ for ZLP-s Robert Hodaszi
@ 2025-04-07  9:23 ` Oliver Neukum
  0 siblings, 0 replies; 2+ messages in thread
From: Oliver Neukum @ 2025-04-07  9:23 UTC (permalink / raw)
  To: Robert Hodaszi, gregkh, oneukum; +Cc: linux-kernel, linux-usb, stern, viro



On 03.04.25 16:40, Robert Hodaszi wrote:
> 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>


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-04-07  9:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-04-03 14:40 [PATCH] usb: cdc-wdm: avoid setting WDM_READ for ZLP-s Robert Hodaszi
2025-04-07  9:23 ` Oliver Neukum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®