Commit e702ff0b authored by Tilman Schmidt's avatar Tilman Schmidt Committed by Linus Torvalds

[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: default avatarTilman Schmidt <tilman@imap.cc>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e4233dec
...@@ -356,16 +356,17 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv) ...@@ -356,16 +356,17 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv)
{ {
unsigned long flags; unsigned long flags;
unsigned i; unsigned i;
static struct cardstate *ret = NULL; struct cardstate *ret = NULL;
spin_lock_irqsave(&drv->lock, flags); spin_lock_irqsave(&drv->lock, flags);
for (i = 0; i < drv->minors; ++i) { for (i = 0; i < drv->minors; ++i) {
if (!(drv->flags[i] & VALID_MINOR)) { if (!(drv->flags[i] & VALID_MINOR)) {
drv->flags[i] = VALID_MINOR; if (try_module_get(drv->owner)) {
ret = drv->cs + i; drv->flags[i] = VALID_MINOR;
} ret = drv->cs + i;
if (ret) }
break; break;
}
} }
spin_unlock_irqrestore(&drv->lock, flags); spin_unlock_irqrestore(&drv->lock, flags);
return ret; return ret;
...@@ -376,6 +377,8 @@ static void free_cs(struct cardstate *cs) ...@@ -376,6 +377,8 @@ static void free_cs(struct cardstate *cs)
unsigned long flags; unsigned long flags;
struct gigaset_driver *drv = cs->driver; struct gigaset_driver *drv = cs->driver;
spin_lock_irqsave(&drv->lock, flags); spin_lock_irqsave(&drv->lock, flags);
if (drv->flags[cs->minor_index] & VALID_MINOR)
module_put(drv->owner);
drv->flags[cs->minor_index] = 0; drv->flags[cs->minor_index] = 0;
spin_unlock_irqrestore(&drv->lock, flags); spin_unlock_irqrestore(&drv->lock, flags);
} }
...@@ -579,7 +582,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, ...@@ -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) } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
skb_reserve(bcs->skb, HW_HDR_LEN); skb_reserve(bcs->skb, HW_HDR_LEN);
else { else {
warn("could not allocate skb\n"); warn("could not allocate skb");
bcs->inputstate |= INS_skip_frame; bcs->inputstate |= INS_skip_frame;
} }
...@@ -632,17 +635,25 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, ...@@ -632,17 +635,25 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
int i; int i;
gig_dbg(DEBUG_INIT, "allocating cs"); gig_dbg(DEBUG_INIT, "allocating cs");
cs = alloc_cs(drv); if (!(cs = alloc_cs(drv))) {
if (!cs) err("maximum number of devices exceeded");
goto error; return NULL;
}
mutex_init(&cs->mutex);
mutex_lock(&cs->mutex);
gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1); gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL); cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL);
if (!cs->bcs) if (!cs->bcs) {
err("out of memory");
goto error; goto error;
}
gig_dbg(DEBUG_INIT, "allocating inbuf"); gig_dbg(DEBUG_INIT, "allocating inbuf");
cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL); cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
if (!cs->inbuf) if (!cs->inbuf) {
err("out of memory");
goto error; goto error;
}
cs->cs_init = 0; cs->cs_init = 0;
cs->channels = channels; cs->channels = channels;
...@@ -654,8 +665,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, ...@@ -654,8 +665,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
spin_lock_init(&cs->ev_lock); spin_lock_init(&cs->ev_lock);
cs->ev_tail = 0; cs->ev_tail = 0;
cs->ev_head = 0; cs->ev_head = 0;
mutex_init(&cs->mutex);
mutex_lock(&cs->mutex);
tasklet_init(&cs->event_tasklet, &gigaset_handle_event, tasklet_init(&cs->event_tasklet, &gigaset_handle_event,
(unsigned long) cs); (unsigned long) cs);
...@@ -684,8 +693,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, ...@@ -684,8 +693,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
for (i = 0; i < channels; ++i) { for (i = 0; i < channels; ++i) {
gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", 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; goto error;
}
} }
++cs->cs_init; ++cs->cs_init;
...@@ -720,8 +731,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, ...@@ -720,8 +731,10 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
make_valid(cs, VALID_ID); make_valid(cs, VALID_ID);
++cs->cs_init; ++cs->cs_init;
gig_dbg(DEBUG_INIT, "setting up hw"); 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; goto error;
}
++cs->cs_init; ++cs->cs_init;
...@@ -743,8 +756,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, ...@@ -743,8 +756,8 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
mutex_unlock(&cs->mutex); mutex_unlock(&cs->mutex);
return cs; return cs;
error: if (cs) error:
mutex_unlock(&cs->mutex); mutex_unlock(&cs->mutex);
gig_dbg(DEBUG_INIT, "failed"); gig_dbg(DEBUG_INIT, "failed");
gigaset_freecs(cs); gigaset_freecs(cs);
return NULL; return NULL;
...@@ -1040,7 +1053,6 @@ void gigaset_freedriver(struct gigaset_driver *drv) ...@@ -1040,7 +1053,6 @@ void gigaset_freedriver(struct gigaset_driver *drv)
spin_unlock_irqrestore(&driver_lock, flags); spin_unlock_irqrestore(&driver_lock, flags);
gigaset_if_freedriver(drv); gigaset_if_freedriver(drv);
module_put(drv->owner);
kfree(drv->cs); kfree(drv->cs);
kfree(drv->flags); kfree(drv->flags);
...@@ -1072,10 +1084,6 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, ...@@ -1072,10 +1084,6 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
if (!drv) if (!drv)
return NULL; return NULL;
if (!try_module_get(owner))
goto out1;
drv->cs = NULL;
drv->have_tty = 0; drv->have_tty = 0;
drv->minor = minor; drv->minor = minor;
drv->minors = minors; drv->minors = minors;
...@@ -1087,11 +1095,11 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, ...@@ -1087,11 +1095,11 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL); drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL);
if (!drv->cs) if (!drv->cs)
goto out2; goto error;
drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL); drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL);
if (!drv->flags) if (!drv->flags)
goto out3; goto error;
for (i = 0; i < minors; ++i) { for (i = 0; i < minors; ++i) {
drv->flags[i] = 0; drv->flags[i] = 0;
...@@ -1108,11 +1116,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, ...@@ -1108,11 +1116,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
return drv; return drv;
out3: error:
kfree(drv->cs); kfree(drv->cs);
out2:
module_put(owner);
out1:
kfree(drv); kfree(drv);
return NULL; return NULL;
} }
......
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