Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=39f07223d400330b9e08aae4c77a391f2418023e
Commit:     39f07223d400330b9e08aae4c77a391f2418023e
Parent:     eff3b634d9a0cccb6ca8b431819fa415f10804dc
Author:     Tilman Schmidt <[EMAIL PROTECTED]>
AuthorDate: Wed Dec 13 00:33:52 2006 -0800
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Wed Dec 13 09:05:47 2006 -0800

    [PATCH] isdn/gigaset: fix possible missing wakeup
    
    Eliminate some possibilities for user processes writing to the Gigaset
    character device to be left sleeping indefinitely, by adding wakeup calls
    to error paths and properly disposing of pending write requests when the
    device is disconnected.
    
    It also removes unnecessary NULL checks before usb_free_urb() and
    usb_kill_urb() calls.
    
    Signed-off-by: Tilman Schmidt <[EMAIL PROTECTED]>
    Acked-by: Karsten Keil <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 drivers/isdn/gigaset/bas-gigaset.c |  118 ++++++++++++++++--------------------
 1 files changed, 52 insertions(+), 66 deletions(-)

diff --git a/drivers/isdn/gigaset/bas-gigaset.c 
b/drivers/isdn/gigaset/bas-gigaset.c
index 63b629b..b5e7f9c 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -1853,20 +1853,24 @@ static int gigaset_write_cmd(struct cardstate *cs,
 {
        struct cmdbuf_t *cb;
        unsigned long flags;
-       int status;
+       int rc;
 
        gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?
                             DEBUG_TRANSCMD : DEBUG_LOCKCMD,
                           "CMD Transmit", len, buf);
 
-       if (len <= 0)
-               return 0;                       /* nothing to do */
+       if (len <= 0) {
+               /* nothing to do */
+               rc = 0;
+               goto notqueued;
+       }
 
        if (len > IF_WRITEBUF)
                len = IF_WRITEBUF;
        if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
                dev_err(cs->dev, "%s: out of memory\n", __func__);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto notqueued;
        }
 
        memcpy(cb->buf, buf, len);
@@ -1891,11 +1895,21 @@ static int gigaset_write_cmd(struct cardstate *cs,
        if (unlikely(!cs->connected)) {
                spin_unlock_irqrestore(&cs->lock, flags);
                gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+               /* flush command queue */
+               spin_lock_irqsave(&cs->cmdlock, flags);
+               while (cs->cmdbuf != NULL)
+                       complete_cb(cs);
+               spin_unlock_irqrestore(&cs->cmdlock, flags);
                return -ENODEV;
        }
-       status = start_cbsend(cs);
+       rc = start_cbsend(cs);
        spin_unlock_irqrestore(&cs->lock, flags);
-       return status < 0 ? status : len;
+       return rc < 0 ? rc : len;
+
+notqueued:                     /* request handled without queuing */
+       if (wake_tasklet)
+               tasklet_schedule(wake_tasklet);
+       return rc;
 }
 
 /* gigaset_write_room
@@ -1964,20 +1978,15 @@ static int gigaset_freebcshw(struct bc_state *bcs)
 
        /* kill URBs and tasklets before freeing - better safe than sorry */
        atomic_set(&ubc->running, 0);
-       for (i = 0; i < BAS_OUTURBS; ++i)
-               if (ubc->isoouturbs[i].urb) {
-                       gig_dbg(DEBUG_INIT, "%s: killing iso out URB %d",
-                               __func__, i);
-                       usb_kill_urb(ubc->isoouturbs[i].urb);
-                       usb_free_urb(ubc->isoouturbs[i].urb);
-               }
-       for (i = 0; i < BAS_INURBS; ++i)
-               if (ubc->isoinurbs[i]) {
-                       gig_dbg(DEBUG_INIT, "%s: killing iso in URB %d",
-                               __func__, i);
-                       usb_kill_urb(ubc->isoinurbs[i]);
-                       usb_free_urb(ubc->isoinurbs[i]);
-               }
+       gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__);
+       for (i = 0; i < BAS_OUTURBS; ++i) {
+               usb_kill_urb(ubc->isoouturbs[i].urb);
+               usb_free_urb(ubc->isoouturbs[i].urb);
+       }
+       for (i = 0; i < BAS_INURBS; ++i) {
+               usb_kill_urb(ubc->isoinurbs[i]);
+               usb_free_urb(ubc->isoinurbs[i]);
+       }
        tasklet_kill(&ubc->sent_tasklet);
        tasklet_kill(&ubc->rcvd_tasklet);
        kfree(ubc->isooutbuf);
@@ -2099,55 +2108,32 @@ static void freeurbs(struct cardstate *cs)
        struct bas_bc_state *ubc;
        int i, j;
 
+       gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
        for (j = 0; j < 2; ++j) {
                ubc = cs->bcs[j].hw.bas;
-               for (i = 0; i < BAS_OUTURBS; ++i)
-                       if (ubc->isoouturbs[i].urb) {
-                               usb_kill_urb(ubc->isoouturbs[i].urb);
-                               gig_dbg(DEBUG_INIT,
-                                       "%s: isoc output URB %d/%d unlinked",
-                                       __func__, j, i);
-                               usb_free_urb(ubc->isoouturbs[i].urb);
-                               ubc->isoouturbs[i].urb = NULL;
-                       }
-               for (i = 0; i < BAS_INURBS; ++i)
-                       if (ubc->isoinurbs[i]) {
-                               usb_kill_urb(ubc->isoinurbs[i]);
-                               gig_dbg(DEBUG_INIT,
-                                       "%s: isoc input URB %d/%d unlinked",
-                                       __func__, j, i);
-                               usb_free_urb(ubc->isoinurbs[i]);
-                               ubc->isoinurbs[i] = NULL;
-                       }
-       }
-       if (ucs->urb_int_in) {
-               usb_kill_urb(ucs->urb_int_in);
-               gig_dbg(DEBUG_INIT, "%s: interrupt input URB unlinked",
-                       __func__);
-               usb_free_urb(ucs->urb_int_in);
-               ucs->urb_int_in = NULL;
-       }
-       if (ucs->urb_cmd_out) {
-               usb_kill_urb(ucs->urb_cmd_out);
-               gig_dbg(DEBUG_INIT, "%s: command output URB unlinked",
-                       __func__);
-               usb_free_urb(ucs->urb_cmd_out);
-               ucs->urb_cmd_out = NULL;
-       }
-       if (ucs->urb_cmd_in) {
-               usb_kill_urb(ucs->urb_cmd_in);
-               gig_dbg(DEBUG_INIT, "%s: command input URB unlinked",
-                       __func__);
-               usb_free_urb(ucs->urb_cmd_in);
-               ucs->urb_cmd_in = NULL;
-       }
-       if (ucs->urb_ctrl) {
-               usb_kill_urb(ucs->urb_ctrl);
-               gig_dbg(DEBUG_INIT, "%s: control output URB unlinked",
-                       __func__);
-               usb_free_urb(ucs->urb_ctrl);
-               ucs->urb_ctrl = NULL;
+               for (i = 0; i < BAS_OUTURBS; ++i) {
+                       usb_kill_urb(ubc->isoouturbs[i].urb);
+                       usb_free_urb(ubc->isoouturbs[i].urb);
+                       ubc->isoouturbs[i].urb = NULL;
+               }
+               for (i = 0; i < BAS_INURBS; ++i) {
+                       usb_kill_urb(ubc->isoinurbs[i]);
+                       usb_free_urb(ubc->isoinurbs[i]);
+                       ubc->isoinurbs[i] = NULL;
+               }
        }
+       usb_kill_urb(ucs->urb_int_in);
+       usb_free_urb(ucs->urb_int_in);
+       ucs->urb_int_in = NULL;
+       usb_kill_urb(ucs->urb_cmd_out);
+       usb_free_urb(ucs->urb_cmd_out);
+       ucs->urb_cmd_out = NULL;
+       usb_kill_urb(ucs->urb_cmd_in);
+       usb_free_urb(ucs->urb_cmd_in);
+       ucs->urb_cmd_in = NULL;
+       usb_kill_urb(ucs->urb_ctrl);
+       usb_free_urb(ucs->urb_ctrl);
+       ucs->urb_ctrl = NULL;
 }
 
 /* gigaset_probe
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to