From: Simon Arlott Date: Mon, 30 Apr 2007 18:59:38 +0000 (+0100) Subject: [PATCH] cxacru: Fix infinite loop when trying to cancel polling task X-Git-Tag: v2.6.21.2~64 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=296d13d3b93c22bd0eaf6d0dc5f131a243aed7ce;p=thirdparty%2Fkernel%2Fstable.git [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 Signed-off-by: Chris Wright --- diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 3dfa3e40e1489..3257d940a5149 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);