wimax/i2400m: fix the bootmode RX deadlock in SDIO driver
authorCindy H Kao <cindy.h.kao@intel.com>
Thu, 27 Aug 2009 22:25:12 +0000 (15:25 -0700)
committerInaky Perez-Gonzalez <inaky@linux.intel.com>
Mon, 19 Oct 2009 06:55:33 +0000 (15:55 +0900)
i2400ms_bus_bm_wait_for_ack() causes a race condition. It happens
because this function clears i2400ms->bm_ack_size before waiting for
an interrupt, which is set by the interrupt service routine i2400ms_rx()
to indicate reception and size of received data; thus, if the interrupt
came right before the clearing/waiting, it is lost.

The fix is clear the bm_ack_size to -EINPROGRESS before we are enabling
the RX interrupt configuration in i2400ms_rx_setup(). Then everytime
when the interrupt service routine i2400ms_rx() is invoked during bootmode,
bm_ack_size is updated with the actual rx_size and it is cleared to
-EINPROGRESS again after the RX data is handled.

Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
drivers/net/wimax/i2400m/sdio-fw.c
drivers/net/wimax/i2400m/sdio-rx.c

index 7d6ec0f475f83b17d483b311cb1baef656bcefb9..c8dc538d40c1e500a9ac4704d54e5a450178380d 100644 (file)
@@ -177,10 +177,6 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
        d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
                  i2400m, ack, ack_size);
 
-       spin_lock(&i2400m->rx_lock);
-       i2400ms->bm_ack_size = -EINPROGRESS;
-       spin_unlock(&i2400m->rx_lock);
-
        result = wait_event_timeout(i2400ms->bm_wfa_wq,
                                    i2400ms->bm_ack_size != -EINPROGRESS,
                                    2 * HZ);
@@ -199,6 +195,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
                size = min(ack_size, i2400ms->bm_ack_size);
                memcpy(ack, i2400m->bm_ack_buf, size);
        }
+       /*
+        * Remember always to clear the bm_ack_size to -EINPROGRESS
+        * after the RX data is processed
+        */
        i2400ms->bm_ack_size = -EINPROGRESS;
        spin_unlock(&i2400m->rx_lock);
 
index 321beadf6e475d2fa8758dcdde1972195559abe8..f6ca51ab216d263b39b8716bbcd41693b808f5bf 100644 (file)
@@ -234,6 +234,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms)
        init_waitqueue_head(&i2400ms->bm_wfa_wq);
        spin_lock(&i2400m->rx_lock);
        i2400ms->bm_wait_result = -EINPROGRESS;
+       /*
+        * Before we are about to enable the RX interrupt, make sure
+        * bm_ack_size is cleared to -EINPROGRESS which indicates
+        * no RX interrupt happened yet or the previous interrupt
+        * has been handled, we are ready to take the new interrupt
+        */
+       i2400ms->bm_ack_size = -EINPROGRESS;
        spin_unlock(&i2400m->rx_lock);
 
        sdio_claim_host(func);