diff options
author | Simon Arlott <simon@fire.lp0.eu> | 2007-04-30 19:59:38 +0100 |
---|---|---|
committer | Chris Wright <chrisw@sous-sol.org> | 2007-05-23 14:32:41 -0700 |
commit | 296d13d3b93c22bd0eaf6d0dc5f131a243aed7ce (patch) | |
tree | afacab2dcbcb4e97d905391ce74ef77a94b2faa0 /drivers/usb | |
parent | 27c99eb74744c71c162b68d5b3ed7c682eb55e3e (diff) |
[PATCH] cxacru: Fix infinite loop when trying to cancel polling task
As part of the device initialisation cxacru_atm_start starts
a rearming status polling task, which is cancelled in
cxacru_unbind. Failure to ever start the task means an
infinite loop occurs trying to cancel it.
Possible reasons for not starting the polling task:
* Firmware files missing
* Device initialisation fails
* User unplugs device or unloads module
Effect:
* Infinite loop in khubd trying to add/remove the device (or rmmod if timed right)
Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/atm/cxacru.c | 40 |
1 files changed, 36 insertions, 4 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 3dfa3e40e14..3257d940a51 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -146,6 +146,12 @@ enum cxacru_info_idx { CXINF_MAX = 0x1c, }; +enum poll_state { + CX_INIT, + CX_POLLING, + CX_ABORT +}; + struct cxacru_modem_type { u32 pll_f_clk; u32 pll_b_clk; @@ -159,6 +165,8 @@ struct cxacru_data { int line_status; struct delayed_work poll_work; + struct mutex poll_state_serialize; + enum poll_state poll_state; /* contol handles */ struct mutex cm_serialize; @@ -356,7 +364,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, /* struct atm_dev *atm_dev = usbatm_instance->atm_dev; */ - int ret; + int ret, start_polling = 1; dbg("cxacru_atm_start"); @@ -376,7 +384,15 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, } /* Start status polling */ - cxacru_poll_status(&instance->poll_work.work); + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state == CX_INIT) + instance->poll_state = CX_POLLING; + else /* poll_state == CX_ABORT */ + start_polling = 0; + mutex_unlock(&instance->poll_state_serialize); + + if (start_polling) + cxacru_poll_status(&instance->poll_work.work); return 0; } @@ -685,6 +701,9 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, instance->usbatm = usbatm_instance; instance->modem_type = (struct cxacru_modem_type *) id->driver_info; + mutex_init(&instance->poll_state_serialize); + instance->poll_state = CX_INIT; + instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); if (!instance->rcv_buf) { dbg("cxacru_bind: no memory for rcv_buf"); @@ -744,6 +763,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, struct usb_interface *intf) { struct cxacru_data *instance = usbatm_instance->driver_data; + int stop_polling = 1; dbg("cxacru_unbind entered"); @@ -752,8 +772,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, return; } - while (!cancel_delayed_work(&instance->poll_work)) - flush_scheduled_work(); + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state != CX_POLLING) { + /* Polling hasn't started yet and with + * the mutex locked it can be prevented + * from starting. + */ + instance->poll_state = CX_ABORT; + stop_polling = 0; + } + mutex_unlock(&instance->poll_state_serialize); + + if (stop_polling) + while (!cancel_delayed_work(&instance->poll_work)) + flush_scheduled_work(); usb_kill_urb(instance->snd_urb); usb_kill_urb(instance->rcv_urb); |