Skip to content

Commit

Permalink
uwb: fix races between events and neh timers
Browse files Browse the repository at this point in the history
Always use del_timer_sync() before freeing nehs.  Destroy all nehs after
stopping the radio controller and before cleaning up the reservation
manager.  Handle the timer running after an event has removed the neh.

This fixes various oopses that may occur if a radio controller is removed
while a neh timer is still active.

Signed-off-by: David Vrabel <david.vrabel@csr.com>
  • Loading branch information
David Vrabel committed Nov 7, 2008
1 parent 307ba6d commit 58be81e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 16 deletions.
5 changes: 3 additions & 2 deletions drivers/uwb/lc-rc.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ static void uwb_rc_sys_release(struct device *dev)
struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev);
struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev);

uwb_rc_neh_destroy(rc);
uwb_rc_ie_release(rc);
kfree(rc);
}
Expand Down Expand Up @@ -311,7 +310,7 @@ void uwb_rc_rm(struct uwb_rc *rc)
rc->ready = 0;

uwb_dbg_del_rc(rc);
uwb_rsv_cleanup(rc);
uwb_rsv_remove_all(rc);
uwb_rc_ie_rm(rc, UWB_IDENTIFICATION_IE);
if (rc->beaconing >= 0)
uwb_rc_beacon(rc, -1, 0);
Expand All @@ -322,6 +321,7 @@ void uwb_rc_rm(struct uwb_rc *rc)
rc->stop(rc);

uwbd_stop(rc);
uwb_rc_neh_destroy(rc);

uwb_dev_lock(&rc->uwb_dev);
rc->priv = NULL;
Expand All @@ -331,6 +331,7 @@ void uwb_rc_rm(struct uwb_rc *rc)
uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL);
__uwb_rc_sys_rm(rc);
mutex_unlock(&rc->uwb_beca.mutex);
uwb_rsv_cleanup(rc);
uwb_beca_release(rc);
uwb_dev_rm(&rc->uwb_dev);
}
Expand Down
46 changes: 32 additions & 14 deletions drivers/uwb/neh.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,

static void __uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
{
del_timer(&neh->timer);
__uwb_rc_ctx_put(rc, neh);
list_del(&neh->list_node);
}
Expand All @@ -275,6 +274,7 @@ void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
__uwb_rc_neh_rm(rc, neh);
spin_unlock_irqrestore(&rc->neh_lock, flags);

del_timer_sync(&neh->timer);
uwb_rc_neh_put(neh);
}

Expand Down Expand Up @@ -438,9 +438,10 @@ static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size
rceb->bEventContext, size);
} else {
neh = uwb_rc_neh_lookup(rc, rceb);
if (neh)
if (neh) {
del_timer_sync(&neh->timer);
uwb_rc_neh_cb(neh, rceb, size);
else
} else
dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n",
rceb->bEventType, le16_to_cpu(rceb->wEvent),
rceb->bEventContext, size);
Expand Down Expand Up @@ -562,16 +563,22 @@ EXPORT_SYMBOL_GPL(uwb_rc_neh_grok);
*/
void uwb_rc_neh_error(struct uwb_rc *rc, int error)
{
struct uwb_rc_neh *neh, *next;
struct uwb_rc_neh *neh;
unsigned long flags;

BUG_ON(error >= 0);
spin_lock_irqsave(&rc->neh_lock, flags);
list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
for (;;) {
spin_lock_irqsave(&rc->neh_lock, flags);
if (list_empty(&rc->neh_list)) {
spin_unlock_irqrestore(&rc->neh_lock, flags);
break;
}
neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node);
__uwb_rc_neh_rm(rc, neh);
spin_unlock_irqrestore(&rc->neh_lock, flags);

del_timer_sync(&neh->timer);
uwb_rc_neh_cb(neh, NULL, error);
}
spin_unlock_irqrestore(&rc->neh_lock, flags);
}
EXPORT_SYMBOL_GPL(uwb_rc_neh_error);

Expand All @@ -583,10 +590,14 @@ static void uwb_rc_neh_timer(unsigned long arg)
unsigned long flags;

spin_lock_irqsave(&rc->neh_lock, flags);
__uwb_rc_neh_rm(rc, neh);
if (neh->context)
__uwb_rc_neh_rm(rc, neh);
else
neh = NULL;
spin_unlock_irqrestore(&rc->neh_lock, flags);

uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
if (neh)
uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
}

/** Initializes the @rc's neh subsystem
Expand All @@ -605,12 +616,19 @@ void uwb_rc_neh_create(struct uwb_rc *rc)
void uwb_rc_neh_destroy(struct uwb_rc *rc)
{
unsigned long flags;
struct uwb_rc_neh *neh, *next;
struct uwb_rc_neh *neh;

spin_lock_irqsave(&rc->neh_lock, flags);
list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
for (;;) {
spin_lock_irqsave(&rc->neh_lock, flags);
if (list_empty(&rc->neh_list)) {
spin_unlock_irqrestore(&rc->neh_lock, flags);
break;
}
neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node);
__uwb_rc_neh_rm(rc, neh);
spin_unlock_irqrestore(&rc->neh_lock, flags);

del_timer_sync(&neh->timer);
uwb_rc_neh_put(neh);
}
spin_unlock_irqrestore(&rc->neh_lock, flags);
}

0 comments on commit 58be81e

Please sign in to comment.