Commit 296d13d3 authored by Simon Arlott's avatar Simon Arlott Committed by Chris Wright

[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: default avatarSimon Arlott <simon@fire.lp0.eu>
Signed-off-by: default avatarChris Wright <chrisw@sous-sol.org>
parent 27c99eb7
...@@ -146,6 +146,12 @@ enum cxacru_info_idx { ...@@ -146,6 +146,12 @@ enum cxacru_info_idx {
CXINF_MAX = 0x1c, CXINF_MAX = 0x1c,
}; };
enum poll_state {
CX_INIT,
CX_POLLING,
CX_ABORT
};
struct cxacru_modem_type { struct cxacru_modem_type {
u32 pll_f_clk; u32 pll_f_clk;
u32 pll_b_clk; u32 pll_b_clk;
...@@ -159,6 +165,8 @@ struct cxacru_data { ...@@ -159,6 +165,8 @@ struct cxacru_data {
int line_status; int line_status;
struct delayed_work poll_work; struct delayed_work poll_work;
struct mutex poll_state_serialize;
enum poll_state poll_state;
/* contol handles */ /* contol handles */
struct mutex cm_serialize; struct mutex cm_serialize;
...@@ -356,7 +364,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, ...@@ -356,7 +364,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
/* /*
struct atm_dev *atm_dev = usbatm_instance->atm_dev; struct atm_dev *atm_dev = usbatm_instance->atm_dev;
*/ */
int ret; int ret, start_polling = 1;
dbg("cxacru_atm_start"); dbg("cxacru_atm_start");
...@@ -376,7 +384,15 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, ...@@ -376,7 +384,15 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
} }
/* Start status polling */ /* 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; return 0;
} }
...@@ -685,6 +701,9 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, ...@@ -685,6 +701,9 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
instance->usbatm = usbatm_instance; instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info; 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); instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->rcv_buf) { if (!instance->rcv_buf) {
dbg("cxacru_bind: no memory for rcv_buf"); dbg("cxacru_bind: no memory for rcv_buf");
...@@ -744,6 +763,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, ...@@ -744,6 +763,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf) struct usb_interface *intf)
{ {
struct cxacru_data *instance = usbatm_instance->driver_data; struct cxacru_data *instance = usbatm_instance->driver_data;
int stop_polling = 1;
dbg("cxacru_unbind entered"); dbg("cxacru_unbind entered");
...@@ -752,8 +772,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, ...@@ -752,8 +772,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
return; return;
} }
while (!cancel_delayed_work(&instance->poll_work)) mutex_lock(&instance->poll_state_serialize);
flush_scheduled_work(); 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->snd_urb);
usb_kill_urb(instance->rcv_urb); usb_kill_urb(instance->rcv_urb);
......
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