forked from Minki/linux
[PATCH] Gigaset ISDN driver error handling fixes
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>
This commit is contained in:
parent
e4233dec74
commit
e702ff0ba6
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user