public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* gs_start_io() and gs_close()
       [not found] <loom.20100826T032027-25@post.gmane.org>
@ 2010-08-26  7:05 ` Igor Grinberg
  2010-08-26 18:30   ` David Brownell
  0 siblings, 1 reply; 6+ messages in thread
From: Igor Grinberg @ 2010-08-26  7:05 UTC (permalink / raw)
  To: linux-arm-kernel

 On 08/26/10 04:21, Jim Sung wrote:
> I'm debugging a problem with the USB gadget serial on the Marvell PXA168

CC'ed LAKML

> platform, which led to the following observations:
>
> - The way gs_start_io() is implemented, each time it is invoked,
>   port->read_pool and port->write_pool will grow by QUEUE_SIZE, which
>   seems wrong.
>
>   For now, I just added a list_empty() check in gs_start_io() on the two
>   lists.
>
> - "struct gs_port" does not appear to have provision to notify the USB
>   device controller to release any resource (particularly for rx) in
>   gs_close().
>
>   To fix the problem, I have a hard coded call into the USB device
>   controller right now, but that is ugly.
>
> The particular kernel I'm working with is 2.6.28, but I took a look at
> 2.6.35-rc6, and I don't see any change in these areas.
>
> Jim
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

-- 
Regards,
Igor.

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

* gs_start_io() and gs_close()
  2010-08-26  7:05 ` gs_start_io() and gs_close() Igor Grinberg
@ 2010-08-26 18:30   ` David Brownell
  2010-08-31  1:30     ` Jim Sung
  0 siblings, 1 reply; 6+ messages in thread
From: David Brownell @ 2010-08-26 18:30 UTC (permalink / raw)
  To: linux-arm-kernel

> >???seems wrong.


Does it cause an actual problem though?  Back when
that code was written and submitted, ISTR doing a
leak analysis and finding none on start_io() paths
or their cleanup siblings.  Did something change
since then?

I don't recall problems being reported in this area
over the past several years, either...

- Dave

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

* gs_start_io() and gs_close()
  2010-08-26 18:30   ` David Brownell
@ 2010-08-31  1:30     ` Jim Sung
  2010-08-31  3:42       ` David Brownell
  2010-08-31  3:45       ` David Brownell
  0 siblings, 2 replies; 6+ messages in thread
From: Jim Sung @ 2010-08-31  1:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave:

>>>    seems wrong.
> 
> 
> Does it cause an actual problem though?  Back when

The short answer is "yes", but not in a way that is easily noticeable.

> that code was written and submitted, ISTR doing a
> leak analysis and finding none on start_io() paths
> or their cleanup siblings.  Did something change

OK, the USB gadget serial driver actually has a couple of problems.  On
gs_open(), it always allocates and queues an additional QUEUE_SIZE (16)
worth of requests, so with a loop like this:

    i=1 ; while echo $i > /dev/ttyGS0 ; do let i++ ; done

eventually we run into OOM (Out of Memory).

Technically, it is not a leak as everything gets freed up when the USB
connection is broken, but not on gs_close().

With a USB device/gadget controller driver that has limited resources
(e.g., Marvell has a this MAX_XDS_FOR_TR_CALLS of 64 for transmit and
receive), so even after 4

    stty -F /dev/ttyGS0

we cannot transmit anymore.  We can still receive (not necessarily
reliably) as now we have 16 * 4 = 64 descriptors/buffers ready, but the
device is otherwise not usable.

Got the 0001-pxa168-Dequeue-all-pending-USB-Req-s-submitted-to-U.patch
from Marvell on 8-27-10, but it still has problems, so I'm not going to
post it here.

> since then?

I don't believe so.

> I don't recall problems being reported in this area
> over the past several years, either...

My take is that unless you explicitly look for it, you are probably not
going to notice this.

Anyway, I tried not to change too much and to follow the basic design,
but you have to be the judge of that.  I ran through a handful of tests,
and it seemed to behave much better now.  Here is the patch (against
2.6.28), and you can decide whatever you want to do with it.

Jim

Index: drivers/usb/gadget/u_serial.c
===================================================================
--- drivers/usb/gadget/u_serial.c       (revision 10419)
+++ drivers/usb/gadget/u_serial.c       (working copy)
@@ -103,11 +103,15 @@
        wait_queue_head_t       close_wait;     /* wait for last close */

        struct list_head        read_pool;
+       int read_started;
+       int read_allocated;
        struct list_head        read_queue;
        unsigned                n_read;
        struct tasklet_struct   push;

        struct list_head        write_pool;
+       int write_started;
+       int write_allocated;
        struct gs_buf           port_write_buf;
        wait_queue_head_t       drain_wait;     /* wait while writes drain */

@@ -361,6 +365,9 @@
                struct usb_request      *req;
                int                     len;

+               if (port->write_started >= QUEUE_SIZE)
+                       break;
+
                req = list_entry(pool->next, struct usb_request, list);
                len = gs_send_packet(port, req->buf, in->maxpacket);
                if (len == 0) {
@@ -394,6 +401,8 @@
                        break;
                }

+               port->write_started++;
+
                /* abort immediately after disconnect */
                if (!port->port_usb)
                        break;
@@ -415,7 +424,6 @@
 {
        struct list_head        *pool = &port->read_pool;
        struct usb_ep           *out = port->port_usb->out;
-       unsigned                started = 0;

        while (!list_empty(pool)) {
                struct usb_request      *req;
@@ -427,6 +435,9 @@
                if (!tty)
                        break;

+               if (port->read_started >= QUEUE_SIZE)
+                       break;
+
                req = list_entry(pool->next, struct usb_request, list);
                list_del(&req->list);
                req->length = out->maxpacket;
@@ -444,13 +455,13 @@
                        list_add(&req->list, pool);
                        break;
                }
-               started++;
+               port->read_started++;

                /* abort immediately after disconnect */
                if (!port->port_usb)
                        break;
        }
-       return started;
+       return port->read_started;
 }

 /*
@@ -532,6 +543,7 @@
                }
 recycle:
                list_move(&req->list, &port->read_pool);
+               port->read_started--;
        }

        /* Push from tty to ldisc; this is immediate with low_latency, and
@@ -590,6 +602,7 @@

        spin_lock(&port->port_lock);
        list_add(&req->list, &port->write_pool);
+       port->write_started--;

        switch (req->status) {
        default:
@@ -611,7 +624,7 @@
        spin_unlock(&port->port_lock);
 }

-static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head, int *allocated)
 {
        struct usb_request      *req;

@@ -619,25 +632,30 @@
                req = list_entry(head->next, struct usb_request, list);
                list_del(&req->list);
                gs_free_req(ep, req);
+               if (allocated)
+                       (*allocated)--;
        }
 }

 static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
-               void (*fn)(struct usb_ep *, struct usb_request *))
+               void (*fn)(struct usb_ep *, struct usb_request *), int *allocated)
 {
        int                     i;
        struct usb_request      *req;
+       int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;

        /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
         * do quite that many this time, don't fail ... we just won't
         * be as speedy as we might otherwise be.
         */
-       for (i = 0; i < QUEUE_SIZE; i++) {
+       for (i = 0; i < n; i++) {
                req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
                if (!req)
                        return list_empty(head) ? -ENOMEM : 0;
                req->complete = fn;
                list_add_tail(&req->list, head);
+        if (allocated)
+            (*allocated)++;
        }
        return 0;
 }
@@ -664,14 +682,14 @@
         * configurations may use different endpoints with a given port;
         * and high speed vs full speed changes packet sizes too.
         */
-       status = gs_alloc_requests(ep, head, gs_read_complete);
+       status = gs_alloc_requests(ep, head, gs_read_complete, &port->read_allocated);
        if (status)
                return status;

        status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
-                       gs_write_complete);
+                       gs_write_complete, &port->write_allocated);
        if (status) {
-               gs_free_requests(ep, head);
+               gs_free_requests(ep, head, &port->read_allocated);
                return status;
        }

@@ -683,8 +701,8 @@
        if (started) {
                tty_wakeup(port->port_tty);
        } else {
-               gs_free_requests(ep, head);
-               gs_free_requests(port->port_usb->in, &port->write_pool);
+               gs_free_requests(ep, head, &port->read_allocated);
+               gs_free_requests(port->port_usb->in, &port->write_pool, &port->write_allocated);
                status = -EIO;
        }

@@ -1323,8 +1341,11 @@
        spin_lock_irqsave(&port->port_lock, flags);
        if (port->open_count == 0 && !port->openclose)
                gs_buf_free(&port->port_write_buf);
-       gs_free_requests(gser->out, &port->read_pool);
-       gs_free_requests(gser->out, &port->read_queue);
-       gs_free_requests(gser->in, &port->write_pool);
+       gs_free_requests(gser->out, &port->read_pool, NULL);
+       gs_free_requests(gser->out, &port->read_queue, NULL);
+       gs_free_requests(gser->in, &port->write_pool, NULL);
+
+       port->read_allocated = port->read_started = port->write_allocated = port->write_started = 0;
+
        spin_unlock_irqrestore(&port->port_lock, flags);
 }

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

* gs_start_io() and gs_close()
  2010-08-31  1:30     ` Jim Sung
@ 2010-08-31  3:42       ` David Brownell
  2010-08-31  4:08         ` Greg KH
  2010-08-31  3:45       ` David Brownell
  1 sibling, 1 reply; 6+ messages in thread
From: David Brownell @ 2010-08-31  3:42 UTC (permalink / raw)
  To: linux-arm-kernel

OK, the patch looks plausible and you say you've
tested it ...

Greg, can you add this to your queue?

I figure that we should try to collect a
few "Tested-By" attributions before including
this in the next merge window.

But, at worst, I'd expect bugs that can
be fixed by one of the submitters...

I can imagine how having stress tested this on
systems with relatively much memory (not really
tiny embedded hardware, except physically) could
have hidden such problems.


--- On Mon, 8/30/10, Jim Sung <jsung@syncadence.com> wrote:

> From: Jim Sung <jsung@syncadence.com>
> Subject: Re: gs_start_io() and gs_close()
> To: "David Brownell" <david-b@pacbell.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>



> Cc: "Igor Grinberg" <grinberg@compulab.co.il>, linux-usb at vger.kernel.org, "linux-arm-kernel" <linux-arm-kernel@lists.infradead.org>
> Date: Monday, August 30, 2010, 6:30 PM
> Hi Dave:
> 
> >>>? ? seems wrong.
> > 
> > 
> > Does it cause an actual problem though?? Back
> when
> 
> The short answer is "yes", but not in a way that is easily
> noticeable.
> 
> > that code was written and submitted, ISTR doing a
> > leak analysis and finding none on start_io() paths
> > or their cleanup siblings.? Did something change
> 
> OK, the USB gadget serial driver actually has a couple of
> problems.? On
> gs_open(), it always allocates and queues an additional
> QUEUE_SIZE (16)
> worth of requests, so with a loop like this:
> 
> ? ? i=1 ; while echo $i > /dev/ttyGS0 ; do let
> i++ ; done
> 
> eventually we run into OOM (Out of Memory).
> 
> Technically, it is not a leak as everything gets freed up
> when the USB
> connection is broken, but not on gs_close().
> 
> With a USB device/gadget controller driver that has limited
> resources
> (e.g., Marvell has a this MAX_XDS_FOR_TR_CALLS of 64 for
> transmit and
> receive), so even after 4
> 
> ? ? stty -F /dev/ttyGS0
> 
> we cannot transmit anymore.? We can still receive (not
> necessarily
> reliably) as now we have 16 * 4 = 64 descriptors/buffers
> ready, but the
> device is otherwise not usable.
> 
> Got the
> 0001-pxa168-Dequeue-all-pending-USB-Req-s-submitted-to-U.patch
> from Marvell on 8-27-10, but it still has problems, so I'm
> not going to
> post it here.
> 
> > since then?
> 
> I don't believe so.
> 
> > I don't recall problems being reported in this area
> > over the past several years, either...
> 
> My take is that unless you explicitly look for it, you are
> probably not
> going to notice this.
> 
> Anyway, I tried not to change too much and to follow the
> basic design,
> but you have to be the judge of that.? I ran through a
> handful of tests,
> and it seemed to behave much better now.? Here is the
> patch (against
> 2.6.28), and you can decide whatever you want to do with
> it.
> 
> Jim
> 
> Index: drivers/usb/gadget/u_serial.c
> ===================================================================
> --- drivers/usb/gadget/u_serial.c? ?
> ???(revision 10419)
> +++ drivers/usb/gadget/u_serial.c? ?
> ???(working copy)
> @@ -103,11 +103,15 @@
> ? ? ? ? wait_queue_head_t? ?
> ???close_wait;? ???/*
> wait for last close */
> 
> ? ? ? ? struct list_head? ?
> ? ? read_pool;
> +? ? ???int read_started;
> +? ? ???int read_allocated;
> ? ? ? ? struct list_head? ?
> ? ? read_queue;
> ? ? ? ? unsigned? ? ?
> ? ? ? ? ? n_read;
> ? ? ? ? struct
> tasklet_struct???push;
> 
> ? ? ? ? struct list_head? ?
> ? ? write_pool;
> +? ? ???int write_started;
> +? ? ???int write_allocated;
> ? ? ? ? struct gs_buf? ?
> ? ? ???port_write_buf;
> ? ? ? ? wait_queue_head_t? ?
> ???drain_wait;? ???/*
> wait while writes drain */
> 
> @@ -361,6 +365,9 @@
> ? ? ? ? ? ? ? ?
> struct usb_request? ? ? *req;
> ? ? ? ? ? ? ? ?
> int? ? ? ? ? ? ? ?
> ? ???len;
> 
> +? ? ? ? ? ?
> ???if (port->write_started >=
> QUEUE_SIZE)
> +? ? ? ? ? ? ? ?
> ? ? ???break;
> +
> ? ? ? ? ? ? ? ? req
> = list_entry(pool->next, struct usb_request, list);
> ? ? ? ? ? ? ? ? len
> = gs_send_packet(port, req->buf, in->maxpacket);
> ? ? ? ? ? ? ? ? if
> (len == 0) {
> @@ -394,6 +401,8 @@
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? ? ? ? ? }
> 
> +? ? ? ? ? ?
> ???port->write_started++;
> +
> ? ? ? ? ? ? ? ? /*
> abort immediately after disconnect */
> ? ? ? ? ? ? ? ? if
> (!port->port_usb)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> @@ -415,7 +424,6 @@
>  {
> ? ? ? ? struct list_head? ?
> ? ? *pool = &port->read_pool;
> ? ? ? ? struct usb_ep? ?
> ? ? ???*out =
> port->port_usb->out;
> -? ? ???unsigned? ?
> ? ? ? ? ? ? started = 0;
> 
> ? ? ? ? while (!list_empty(pool)) {
> ? ? ? ? ? ? ? ?
> struct usb_request? ? ? *req;
> @@ -427,6 +435,9 @@
> ? ? ? ? ? ? ? ? if
> (!tty)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> 
> +? ? ? ? ? ?
> ???if (port->read_started >=
> QUEUE_SIZE)
> +? ? ? ? ? ? ? ?
> ? ? ???break;
> +
> ? ? ? ? ? ? ? ? req
> = list_entry(pool->next, struct usb_request, list);
> ? ? ? ? ? ? ? ?
> list_del(&req->list);
> ? ? ? ? ? ? ? ?
> req->length = out->maxpacket;
> @@ -444,13 +455,13 @@
> ? ? ? ? ? ? ? ?
> ? ? ? ? list_add(&req->list,
> pool);
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? ? ? ? ? }
> -? ? ? ? ? ?
> ???started++;
> +? ? ? ? ? ?
> ???port->read_started++;
> 
> ? ? ? ? ? ? ? ? /*
> abort immediately after disconnect */
> ? ? ? ? ? ? ? ? if
> (!port->port_usb)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? }
> -? ? ???return started;
> +? ? ???return
> port->read_started;
>  }
> 
>  /*
> @@ -532,6 +543,7 @@
> ? ? ? ? ? ? ? ? }
>  recycle:
> ? ? ? ? ? ? ? ?
> list_move(&req->list, &port->read_pool);
> +? ? ? ? ? ?
> ???port->read_started--;
> ? ? ? ? }
> 
> ? ? ? ? /* Push from tty to ldisc; this
> is immediate with low_latency, and
> @@ -590,6 +602,7 @@
> 
> ? ? ? ?
> spin_lock(&port->port_lock);
> ? ? ? ? list_add(&req->list,
> &port->write_pool);
> +? ? ???port->write_started--;
> 
> ? ? ? ? switch (req->status) {
> ? ? ? ? default:
> @@ -611,7 +624,7 @@
> ? ? ? ?
> spin_unlock(&port->port_lock);
>  }
> 
> -static void gs_free_requests(struct usb_ep *ep, struct
> list_head *head)
> +static void gs_free_requests(struct usb_ep *ep, struct
> list_head *head, int *allocated)
>  {
> ? ? ? ? struct usb_request? ?
> ? *req;
> 
> @@ -619,25 +632,30 @@
> ? ? ? ? ? ? ? ? req
> = list_entry(head->next, struct usb_request, list);
> ? ? ? ? ? ? ? ?
> list_del(&req->list);
> ? ? ? ? ? ? ? ?
> gs_free_req(ep, req);
> +? ? ? ? ? ?
> ???if (allocated)
> +? ? ? ? ? ? ? ?
> ? ? ???(*allocated)--;
> ? ? ? ? }
>  }
> 
>  static int gs_alloc_requests(struct usb_ep *ep, struct
> list_head *head,
> -? ? ? ? ? ?
> ???void (*fn)(struct usb_ep *, struct
> usb_request *))
> +? ? ? ? ? ?
> ???void (*fn)(struct usb_ep *, struct
> usb_request *), int *allocated)
>  {
> ? ? ? ? int? ? ? ?
> ? ? ? ? ? ???i;
> ? ? ? ? struct usb_request? ?
> ? *req;
> +? ? ???int n = allocated ?
> QUEUE_SIZE - *allocated : QUEUE_SIZE;
> 
> ? ? ? ? /* Pre-allocate up to
> QUEUE_SIZE transfers, but if we can't
> ? ? ? ???* do quite that many
> this time, don't fail ... we just won't
> ? ? ? ???* be as speedy as we
> might otherwise be.
> ? ? ? ???*/
> -? ? ???for (i = 0; i <
> QUEUE_SIZE; i++) {
> +? ? ???for (i = 0; i < n; i++)
> {
> ? ? ? ? ? ? ? ? req
> = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
> ? ? ? ? ? ? ? ? if
> (!req)
> ? ? ? ? ? ? ? ?
> ? ? ? ? return list_empty(head) ?
> -ENOMEM : 0;
> ? ? ? ? ? ? ? ?
> req->complete = fn;
> ? ? ? ? ? ? ? ?
> list_add_tail(&req->list, head);
> +? ? ? ? if (allocated)
> +? ? ? ? ? ? (*allocated)++;
> ? ? ? ? }
> ? ? ? ? return 0;
>  }
> @@ -664,14 +682,14 @@
> ? ? ? ???* configurations may
> use different endpoints with a given port;
> ? ? ? ???* and high speed vs
> full speed changes packet sizes too.
> ? ? ? ???*/
> -? ? ???status =
> gs_alloc_requests(ep, head, gs_read_complete);
> +? ? ???status =
> gs_alloc_requests(ep, head, gs_read_complete,
> &port->read_allocated);
> ? ? ? ? if (status)
> ? ? ? ? ? ? ? ?
> return status;
> 
> ? ? ? ? status =
> gs_alloc_requests(port->port_usb->in,
> &port->write_pool,
> -? ? ? ? ? ? ? ?
> ? ? ???gs_write_complete);
> +? ? ? ? ? ? ? ?
> ? ? ???gs_write_complete,
> &port->write_allocated);
> ? ? ? ? if (status) {
> -? ? ? ? ? ?
> ???gs_free_requests(ep, head);
> +? ? ? ? ? ?
> ???gs_free_requests(ep, head,
> &port->read_allocated);
> ? ? ? ? ? ? ? ?
> return status;
> ? ? ? ? }
> 
> @@ -683,8 +701,8 @@
> ? ? ? ? if (started) {
> ? ? ? ? ? ? ? ?
> tty_wakeup(port->port_tty);
> ? ? ? ? } else {
> -? ? ? ? ? ?
> ???gs_free_requests(ep, head);
> -? ? ? ? ? ?
> ???gs_free_requests(port->port_usb->in,
> &port->write_pool);
> +? ? ? ? ? ?
> ???gs_free_requests(ep, head,
> &port->read_allocated);
> +? ? ? ? ? ?
> ???gs_free_requests(port->port_usb->in,
> &port->write_pool, &port->write_allocated);
> ? ? ? ? ? ? ? ?
> status = -EIO;
> ? ? ? ? }
> 
> @@ -1323,8 +1341,11 @@
> ? ? ? ?
> spin_lock_irqsave(&port->port_lock, flags);
> ? ? ? ? if (port->open_count == 0
> && !port->openclose)
> ? ? ? ? ? ? ? ?
> gs_buf_free(&port->port_write_buf);
> -? ?
> ???gs_free_requests(gser->out,
> &port->read_pool);
> -? ?
> ???gs_free_requests(gser->out,
> &port->read_queue);
> -? ?
> ???gs_free_requests(gser->in,
> &port->write_pool);
> +? ?
> ???gs_free_requests(gser->out,
> &port->read_pool, NULL);
> +? ?
> ???gs_free_requests(gser->out,
> &port->read_queue, NULL);
> +? ?
> ???gs_free_requests(gser->in,
> &port->write_pool, NULL);
> +
> +? ? ???port->read_allocated =
> port->read_started = port->write_allocated =
> port->write_started = 0;
> +
> ? ? ? ?
> spin_unlock_irqrestore(&port->port_lock, flags);
>  }
> 

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

* gs_start_io() and gs_close()
  2010-08-31  1:30     ` Jim Sung
  2010-08-31  3:42       ` David Brownell
@ 2010-08-31  3:45       ` David Brownell
  1 sibling, 0 replies; 6+ messages in thread
From: David Brownell @ 2010-08-31  3:45 UTC (permalink / raw)
  To: linux-arm-kernel

As I mentioned, I'd like to see a few
Tested-By: reports confirming this
patch works, before this gets into
Greg's merge queue ...

So -- can folk using the serial gadget code
please give this a whirl and see if the patch
behaves (or instead makes trouble).




--- On Mon, 8/30/10, Jim Sung <jsung@syncadence.com> wrote:

> From: Jim Sung <jsung@syncadence.com>
> Subject: Re: gs_start_io() and gs_close()
> To: "David Brownell" <david-b@pacbell.net>
> Cc: "Igor Grinberg" <grinberg@compulab.co.il>, linux-usb at vger.kernel.org, "linux-arm-kernel" <linux-arm-kernel@lists.infradead.org>
> Date: Monday, August 30, 2010, 6:30 PM
> Hi Dave:
> 
> >>>? ? seems wrong.
> > 
> > 
> > Does it cause an actual problem though?? Back
> when
> 
> The short answer is "yes", but not in a way that is easily
> noticeable.
> 
> > that code was written and submitted, ISTR doing a
> > leak analysis and finding none on start_io() paths
> > or their cleanup siblings.? Did something change
> 
> OK, the USB gadget serial driver actually has a couple of
> problems.? On
> gs_open(), it always allocates and queues an additional
> QUEUE_SIZE (16)
> worth of requests, so with a loop like this:
> 
> ? ? i=1 ; while echo $i > /dev/ttyGS0 ; do let
> i++ ; done
> 
> eventually we run into OOM (Out of Memory).
> 
> Technically, it is not a leak as everything gets freed up
> when the USB
> connection is broken, but not on gs_close().
> 
> With a USB device/gadget controller driver that has limited
> resources
> (e.g., Marvell has a this MAX_XDS_FOR_TR_CALLS of 64 for
> transmit and
> receive), so even after 4
> 
> ? ? stty -F /dev/ttyGS0
> 
> we cannot transmit anymore.? We can still receive (not
> necessarily
> reliably) as now we have 16 * 4 = 64 descriptors/buffers
> ready, but the
> device is otherwise not usable.
> 
> Got the
> 0001-pxa168-Dequeue-all-pending-USB-Req-s-submitted-to-U.patch
> from Marvell on 8-27-10, but it still has problems, so I'm
> not going to
> post it here.
> 
> > since then?
> 
> I don't believe so.
> 
> > I don't recall problems being reported in this area
> > over the past several years, either...
> 
> My take is that unless you explicitly look for it, you are
> probably not
> going to notice this.
> 
> Anyway, I tried not to change too much and to follow the
> basic design,
> but you have to be the judge of that.? I ran through a
> handful of tests,
> and it seemed to behave much better now.? Here is the
> patch (against
> 2.6.28), and you can decide whatever you want to do with
> it.
> 
> Jim
> 
> Index: drivers/usb/gadget/u_serial.c
> ===================================================================
> --- drivers/usb/gadget/u_serial.c? ?
> ???(revision 10419)
> +++ drivers/usb/gadget/u_serial.c? ?
> ???(working copy)
> @@ -103,11 +103,15 @@
> ? ? ? ? wait_queue_head_t? ?
> ???close_wait;? ???/*
> wait for last close */
> 
> ? ? ? ? struct list_head? ?
> ? ? read_pool;
> +? ? ???int read_started;
> +? ? ???int read_allocated;
> ? ? ? ? struct list_head? ?
> ? ? read_queue;
> ? ? ? ? unsigned? ? ?
> ? ? ? ? ? n_read;
> ? ? ? ? struct
> tasklet_struct???push;
> 
> ? ? ? ? struct list_head? ?
> ? ? write_pool;
> +? ? ???int write_started;
> +? ? ???int write_allocated;
> ? ? ? ? struct gs_buf? ?
> ? ? ???port_write_buf;
> ? ? ? ? wait_queue_head_t? ?
> ???drain_wait;? ???/*
> wait while writes drain */
> 
> @@ -361,6 +365,9 @@
> ? ? ? ? ? ? ? ?
> struct usb_request? ? ? *req;
> ? ? ? ? ? ? ? ?
> int? ? ? ? ? ? ? ?
> ? ???len;
> 
> +? ? ? ? ? ?
> ???if (port->write_started >=
> QUEUE_SIZE)
> +? ? ? ? ? ? ? ?
> ? ? ???break;
> +
> ? ? ? ? ? ? ? ? req
> = list_entry(pool->next, struct usb_request, list);
> ? ? ? ? ? ? ? ? len
> = gs_send_packet(port, req->buf, in->maxpacket);
> ? ? ? ? ? ? ? ? if
> (len == 0) {
> @@ -394,6 +401,8 @@
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? ? ? ? ? }
> 
> +? ? ? ? ? ?
> ???port->write_started++;
> +
> ? ? ? ? ? ? ? ? /*
> abort immediately after disconnect */
> ? ? ? ? ? ? ? ? if
> (!port->port_usb)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> @@ -415,7 +424,6 @@
>  {
> ? ? ? ? struct list_head? ?
> ? ? *pool = &port->read_pool;
> ? ? ? ? struct usb_ep? ?
> ? ? ???*out =
> port->port_usb->out;
> -? ? ???unsigned? ?
> ? ? ? ? ? ? started = 0;
> 
> ? ? ? ? while (!list_empty(pool)) {
> ? ? ? ? ? ? ? ?
> struct usb_request? ? ? *req;
> @@ -427,6 +435,9 @@
> ? ? ? ? ? ? ? ? if
> (!tty)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> 
> +? ? ? ? ? ?
> ???if (port->read_started >=
> QUEUE_SIZE)
> +? ? ? ? ? ? ? ?
> ? ? ???break;
> +
> ? ? ? ? ? ? ? ? req
> = list_entry(pool->next, struct usb_request, list);
> ? ? ? ? ? ? ? ?
> list_del(&req->list);
> ? ? ? ? ? ? ? ?
> req->length = out->maxpacket;
> @@ -444,13 +455,13 @@
> ? ? ? ? ? ? ? ?
> ? ? ? ? list_add(&req->list,
> pool);
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? ? ? ? ? }
> -? ? ? ? ? ?
> ???started++;
> +? ? ? ? ? ?
> ???port->read_started++;
> 
> ? ? ? ? ? ? ? ? /*
> abort immediately after disconnect */
> ? ? ? ? ? ? ? ? if
> (!port->port_usb)
> ? ? ? ? ? ? ? ?
> ? ? ? ? break;
> ? ? ? ? }
> -? ? ???return started;
> +? ? ???return
> port->read_started;
>  }
> 
>  /*
> @@ -532,6 +543,7 @@
> ? ? ? ? ? ? ? ? }
>  recycle:
> ? ? ? ? ? ? ? ?
> list_move(&req->list, &port->read_pool);
> +? ? ? ? ? ?
> ???port->read_started--;
> ? ? ? ? }
> 
> ? ? ? ? /* Push from tty to ldisc; this
> is immediate with low_latency, and
> @@ -590,6 +602,7 @@
> 
> ? ? ? ?
> spin_lock(&port->port_lock);
> ? ? ? ? list_add(&req->list,
> &port->write_pool);
> +? ? ???port->write_started--;
> 
> ? ? ? ? switch (req->status) {
> ? ? ? ? default:
> @@ -611,7 +624,7 @@
> ? ? ? ?
> spin_unlock(&port->port_lock);
>  }
> 
> -static void gs_free_requests(struct usb_ep *ep, struct
> list_head *head)
> +static void gs_free_requests(struct usb_ep *ep, struct
> list_head *head, int *allocated)
>  {
> ? ? ? ? struct usb_request? ?
> ? *req;
> 
> @@ -619,25 +632,30 @@
> ? ? ? ? ? ? ? ? req
> = list_entry(head->next, struct usb_request, list);
> ? ? ? ? ? ? ? ?
> list_del(&req->list);
> ? ? ? ? ? ? ? ?
> gs_free_req(ep, req);
> +? ? ? ? ? ?
> ???if (allocated)
> +? ? ? ? ? ? ? ?
> ? ? ???(*allocated)--;
> ? ? ? ? }
>  }
> 
>  static int gs_alloc_requests(struct usb_ep *ep, struct
> list_head *head,
> -? ? ? ? ? ?
> ???void (*fn)(struct usb_ep *, struct
> usb_request *))
> +? ? ? ? ? ?
> ???void (*fn)(struct usb_ep *, struct
> usb_request *), int *allocated)
>  {
> ? ? ? ? int? ? ? ?
> ? ? ? ? ? ???i;
> ? ? ? ? struct usb_request? ?
> ? *req;
> +? ? ???int n = allocated ?
> QUEUE_SIZE - *allocated : QUEUE_SIZE;
> 
> ? ? ? ? /* Pre-allocate up to
> QUEUE_SIZE transfers, but if we can't
> ? ? ? ???* do quite that many
> this time, don't fail ... we just won't
> ? ? ? ???* be as speedy as we
> might otherwise be.
> ? ? ? ???*/
> -? ? ???for (i = 0; i <
> QUEUE_SIZE; i++) {
> +? ? ???for (i = 0; i < n; i++)
> {
> ? ? ? ? ? ? ? ? req
> = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
> ? ? ? ? ? ? ? ? if
> (!req)
> ? ? ? ? ? ? ? ?
> ? ? ? ? return list_empty(head) ?
> -ENOMEM : 0;
> ? ? ? ? ? ? ? ?
> req->complete = fn;
> ? ? ? ? ? ? ? ?
> list_add_tail(&req->list, head);
> +? ? ? ? if (allocated)
> +? ? ? ? ? ? (*allocated)++;
> ? ? ? ? }
> ? ? ? ? return 0;
>  }
> @@ -664,14 +682,14 @@
> ? ? ? ???* configurations may
> use different endpoints with a given port;
> ? ? ? ???* and high speed vs
> full speed changes packet sizes too.
> ? ? ? ???*/
> -? ? ???status =
> gs_alloc_requests(ep, head, gs_read_complete);
> +? ? ???status =
> gs_alloc_requests(ep, head, gs_read_complete,
> &port->read_allocated);
> ? ? ? ? if (status)
> ? ? ? ? ? ? ? ?
> return status;
> 
> ? ? ? ? status =
> gs_alloc_requests(port->port_usb->in,
> &port->write_pool,
> -? ? ? ? ? ? ? ?
> ? ? ???gs_write_complete);
> +? ? ? ? ? ? ? ?
> ? ? ???gs_write_complete,
> &port->write_allocated);
> ? ? ? ? if (status) {
> -? ? ? ? ? ?
> ???gs_free_requests(ep, head);
> +? ? ? ? ? ?
> ???gs_free_requests(ep, head,
> &port->read_allocated);
> ? ? ? ? ? ? ? ?
> return status;
> ? ? ? ? }
> 
> @@ -683,8 +701,8 @@
> ? ? ? ? if (started) {
> ? ? ? ? ? ? ? ?
> tty_wakeup(port->port_tty);
> ? ? ? ? } else {
> -? ? ? ? ? ?
> ???gs_free_requests(ep, head);
> -? ? ? ? ? ?
> ???gs_free_requests(port->port_usb->in,
> &port->write_pool);
> +? ? ? ? ? ?
> ???gs_free_requests(ep, head,
> &port->read_allocated);
> +? ? ? ? ? ?
> ???gs_free_requests(port->port_usb->in,
> &port->write_pool, &port->write_allocated);
> ? ? ? ? ? ? ? ?
> status = -EIO;
> ? ? ? ? }
> 
> @@ -1323,8 +1341,11 @@
> ? ? ? ?
> spin_lock_irqsave(&port->port_lock, flags);
> ? ? ? ? if (port->open_count == 0
> && !port->openclose)
> ? ? ? ? ? ? ? ?
> gs_buf_free(&port->port_write_buf);
> -? ?
> ???gs_free_requests(gser->out,
> &port->read_pool);
> -? ?
> ???gs_free_requests(gser->out,
> &port->read_queue);
> -? ?
> ???gs_free_requests(gser->in,
> &port->write_pool);
> +? ?
> ???gs_free_requests(gser->out,
> &port->read_pool, NULL);
> +? ?
> ???gs_free_requests(gser->out,
> &port->read_queue, NULL);
> +? ?
> ???gs_free_requests(gser->in,
> &port->write_pool, NULL);
> +
> +? ? ???port->read_allocated =
> port->read_started = port->write_allocated =
> port->write_started = 0;
> +
> ? ? ? ?
> spin_unlock_irqrestore(&port->port_lock, flags);
>  }
> 

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

* gs_start_io() and gs_close()
  2010-08-31  3:42       ` David Brownell
@ 2010-08-31  4:08         ` Greg KH
  0 siblings, 0 replies; 6+ messages in thread
From: Greg KH @ 2010-08-31  4:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 30, 2010 at 08:42:49PM -0700, David Brownell wrote:
> OK, the patch looks plausible and you say you've
> tested it ...
> 
> Greg, can you add this to your queue?

If I get it in a form that I can apply it (i.e. proper subject and
signed-off-by and the like.)

Jim, care to resend it fixed up this way?

thanks,

greg k-h

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

end of thread, other threads:[~2010-08-31  4:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <loom.20100826T032027-25@post.gmane.org>
2010-08-26  7:05 ` gs_start_io() and gs_close() Igor Grinberg
2010-08-26 18:30   ` David Brownell
2010-08-31  1:30     ` Jim Sung
2010-08-31  3:42       ` David Brownell
2010-08-31  4:08         ` Greg KH
2010-08-31  3:45       ` David Brownell

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