Commit 39f07223 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by Linus Torvalds

[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: default avatarTilman Schmidt <tilman@imap.cc>
Acked-by: default avatarKarsten Keil <kkeil@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent eff3b634
...@@ -1853,20 +1853,24 @@ static int gigaset_write_cmd(struct cardstate *cs, ...@@ -1853,20 +1853,24 @@ static int gigaset_write_cmd(struct cardstate *cs,
{ {
struct cmdbuf_t *cb; struct cmdbuf_t *cb;
unsigned long flags; unsigned long flags;
int status; int rc;
gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD, DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf); "CMD Transmit", len, buf);
if (len <= 0) if (len <= 0) {
return 0; /* nothing to do */ /* nothing to do */
rc = 0;
goto notqueued;
}
if (len > IF_WRITEBUF) if (len > IF_WRITEBUF)
len = IF_WRITEBUF; len = IF_WRITEBUF;
if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
dev_err(cs->dev, "%s: out of memory\n", __func__); dev_err(cs->dev, "%s: out of memory\n", __func__);
return -ENOMEM; rc = -ENOMEM;
goto notqueued;
} }
memcpy(cb->buf, buf, len); memcpy(cb->buf, buf, len);
...@@ -1891,11 +1895,21 @@ static int gigaset_write_cmd(struct cardstate *cs, ...@@ -1891,11 +1895,21 @@ static int gigaset_write_cmd(struct cardstate *cs,
if (unlikely(!cs->connected)) { if (unlikely(!cs->connected)) {
spin_unlock_irqrestore(&cs->lock, flags); spin_unlock_irqrestore(&cs->lock, flags);
gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); 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; return -ENODEV;
} }
status = start_cbsend(cs); rc = start_cbsend(cs);
spin_unlock_irqrestore(&cs->lock, flags); 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 /* gigaset_write_room
...@@ -1964,17 +1978,12 @@ static int gigaset_freebcshw(struct bc_state *bcs) ...@@ -1964,17 +1978,12 @@ static int gigaset_freebcshw(struct bc_state *bcs)
/* kill URBs and tasklets before freeing - better safe than sorry */ /* kill URBs and tasklets before freeing - better safe than sorry */
atomic_set(&ubc->running, 0); atomic_set(&ubc->running, 0);
for (i = 0; i < BAS_OUTURBS; ++i) gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__);
if (ubc->isoouturbs[i].urb) { for (i = 0; i < BAS_OUTURBS; ++i) {
gig_dbg(DEBUG_INIT, "%s: killing iso out URB %d",
__func__, i);
usb_kill_urb(ubc->isoouturbs[i].urb); usb_kill_urb(ubc->isoouturbs[i].urb);
usb_free_urb(ubc->isoouturbs[i].urb); usb_free_urb(ubc->isoouturbs[i].urb);
} }
for (i = 0; i < BAS_INURBS; ++i) 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_kill_urb(ubc->isoinurbs[i]);
usb_free_urb(ubc->isoinurbs[i]); usb_free_urb(ubc->isoinurbs[i]);
} }
...@@ -2099,55 +2108,32 @@ static void freeurbs(struct cardstate *cs) ...@@ -2099,55 +2108,32 @@ static void freeurbs(struct cardstate *cs)
struct bas_bc_state *ubc; struct bas_bc_state *ubc;
int i, j; int i, j;
gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
for (j = 0; j < 2; ++j) { for (j = 0; j < 2; ++j) {
ubc = cs->bcs[j].hw.bas; ubc = cs->bcs[j].hw.bas;
for (i = 0; i < BAS_OUTURBS; ++i) for (i = 0; i < BAS_OUTURBS; ++i) {
if (ubc->isoouturbs[i].urb) {
usb_kill_urb(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); usb_free_urb(ubc->isoouturbs[i].urb);
ubc->isoouturbs[i].urb = NULL; ubc->isoouturbs[i].urb = NULL;
} }
for (i = 0; i < BAS_INURBS; ++i) for (i = 0; i < BAS_INURBS; ++i) {
if (ubc->isoinurbs[i]) {
usb_kill_urb(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]); usb_free_urb(ubc->isoinurbs[i]);
ubc->isoinurbs[i] = NULL; ubc->isoinurbs[i] = NULL;
} }
} }
if (ucs->urb_int_in) {
usb_kill_urb(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); usb_free_urb(ucs->urb_int_in);
ucs->urb_int_in = NULL; ucs->urb_int_in = NULL;
}
if (ucs->urb_cmd_out) {
usb_kill_urb(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); usb_free_urb(ucs->urb_cmd_out);
ucs->urb_cmd_out = NULL; ucs->urb_cmd_out = NULL;
}
if (ucs->urb_cmd_in) {
usb_kill_urb(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); usb_free_urb(ucs->urb_cmd_in);
ucs->urb_cmd_in = NULL; ucs->urb_cmd_in = NULL;
}
if (ucs->urb_ctrl) {
usb_kill_urb(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); usb_free_urb(ucs->urb_ctrl);
ucs->urb_ctrl = NULL; ucs->urb_ctrl = NULL;
}
} }
/* gigaset_probe /* gigaset_probe
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment