mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 19:26:25 +00:00
uwb: fix races between events and neh timers
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>
This commit is contained in:
parent
307ba6dd73
commit
58be81ed30
2 changed files with 35 additions and 16 deletions
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -254,7 +254,6 @@ error_kzalloc:
|
|||
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue