Skip to content

Commit

Permalink
[PATCH] Gigaset ISDN driver error handling fixes
Browse files Browse the repository at this point in the history
Fix several flaws in the error handling of the Siemens Gigaset ISDN driver,
including one that would cause an Oops when connecting more than one device
of the same type.

Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Tilman Schmidt authored and Linus Torvalds committed Jan 26, 2007
1 parent e4233de commit e702ff0
Showing 1 changed file with 33 additions and 28 deletions.
61 changes: 33 additions & 28 deletions drivers/isdn/gigaset/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,16 +356,17 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv)
{
unsigned long flags;
unsigned i;
static struct cardstate *ret = NULL;
struct cardstate *ret = NULL;

spin_lock_irqsave(&drv->lock, flags);
for (i = 0; i < drv->minors; ++i) {
if (!(drv->flags[i] & VALID_MINOR)) {
drv->flags[i] = VALID_MINOR;
ret = drv->cs + i;
}
if (ret)
if (try_module_get(drv->owner)) {
drv->flags[i] = VALID_MINOR;
ret = drv->cs + i;
}
break;
}
}
spin_unlock_irqrestore(&drv->lock, flags);
return ret;
Expand All @@ -376,6 +377,8 @@ static void free_cs(struct cardstate *cs)
unsigned long flags;
struct gigaset_driver *drv = cs->driver;
spin_lock_irqsave(&drv->lock, flags);
if (drv->flags[cs->minor_index] & VALID_MINOR)
module_put(drv->owner);
drv->flags[cs->minor_index] = 0;
spin_unlock_irqrestore(&drv->lock, flags);
}
Expand Down Expand Up @@ -579,7 +582,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
} else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
skb_reserve(bcs->skb, HW_HDR_LEN);
else {
warn("could not allocate skb\n");
warn("could not allocate skb");
bcs->inputstate |= INS_skip_frame;
}

Expand Down Expand Up @@ -632,17 +635,25 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
int i;

gig_dbg(DEBUG_INIT, "allocating cs");
cs = alloc_cs(drv);
if (!cs)
goto error;
if (!(cs = alloc_cs(drv))) {
err("maximum number of devices exceeded");
return NULL;
}
mutex_init(&cs->mutex);
mutex_lock(&cs->mutex);

gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL);
if (!cs->bcs)
if (!cs->bcs) {
err("out of memory");
goto error;
}
gig_dbg(DEBUG_INIT, "allocating inbuf");
cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
if (!cs->inbuf)
if (!cs->inbuf) {
err("out of memory");
goto error;
}

cs->cs_init = 0;
cs->channels = channels;
Expand All @@ -654,8 +665,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
spin_lock_init(&cs->ev_lock);
cs->ev_tail = 0;
cs->ev_head = 0;
mutex_init(&cs->mutex);
mutex_lock(&cs->mutex);

tasklet_init(&cs->event_tasklet, &gigaset_handle_event,
(unsigned long) cs);
Expand Down Expand Up @@ -684,8 +693,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,

for (i = 0; i < channels; ++i) {
gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
if (!gigaset_initbcs(cs->bcs + i, cs, i))
if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
err("could not allocate channel %d data", i);
goto error;
}
}

++cs->cs_init;
Expand Down Expand Up @@ -720,8 +731,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
make_valid(cs, VALID_ID);
++cs->cs_init;
gig_dbg(DEBUG_INIT, "setting up hw");
if (!cs->ops->initcshw(cs))
if (!cs->ops->initcshw(cs)) {
err("could not allocate device specific data");
goto error;
}

++cs->cs_init;

Expand All @@ -743,8 +756,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
mutex_unlock(&cs->mutex);
return cs;

error: if (cs)
mutex_unlock(&cs->mutex);
error:
mutex_unlock(&cs->mutex);
gig_dbg(DEBUG_INIT, "failed");
gigaset_freecs(cs);
return NULL;
Expand Down Expand Up @@ -1040,7 +1053,6 @@ void gigaset_freedriver(struct gigaset_driver *drv)
spin_unlock_irqrestore(&driver_lock, flags);

gigaset_if_freedriver(drv);
module_put(drv->owner);

kfree(drv->cs);
kfree(drv->flags);
Expand Down Expand Up @@ -1072,10 +1084,6 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
if (!drv)
return NULL;

if (!try_module_get(owner))
goto out1;

drv->cs = NULL;
drv->have_tty = 0;
drv->minor = minor;
drv->minors = minors;
Expand All @@ -1087,11 +1095,11 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,

drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL);
if (!drv->cs)
goto out2;
goto error;

drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL);
if (!drv->flags)
goto out3;
goto error;

for (i = 0; i < minors; ++i) {
drv->flags[i] = 0;
Expand All @@ -1108,11 +1116,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,

return drv;

out3:
error:
kfree(drv->cs);
out2:
module_put(owner);
out1:
kfree(drv);
return NULL;
}
Expand Down

0 comments on commit e702ff0

Please sign in to comment.