]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
[PATCH] cxacru: Fix infinite loop when trying to cancel polling task
authorSimon Arlott <simon@fire.lp0.eu>
Mon, 30 Apr 2007 18:59:38 +0000 (19:59 +0100)
committerChris Wright <chrisw@sous-sol.org>
Wed, 23 May 2007 21:32:41 +0000 (14:32 -0700)
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>
drivers/usb/atm/cxacru.c

index 3dfa3e40e14890f84e9fe86bbf53b8288e635f5d..3257d940a5149192d7bb6e74d1d1358ecbe81493 100644 (file)
@@ -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);