aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCindy H Kao <cindy.h.kao@intel.com>2009-08-27 18:25:12 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:55:33 -0400
commit5b45bfe5010ae1ddaac463d1bcdb141a4ff4ff66 (patch)
tree8115ce638a420e4ba6610ea6c2e126ddb9180b86
parent663ebb4aa2fd0d19e3d1da0fed3fa3fa134a4ef0 (diff)
wimax/i2400m: fix the bootmode RX deadlock in SDIO driver
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>
-rw-r--r--drivers/net/wimax/i2400m/sdio-fw.c8
-rw-r--r--drivers/net/wimax/i2400m/sdio-rx.c7
2 files changed, 11 insertions, 4 deletions
diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c
index 7d6ec0f475f8..c8dc538d40c1 100644
--- a/drivers/net/wimax/i2400m/sdio-fw.c
+++ b/drivers/net/wimax/i2400m/sdio-fw.c
@@ -177,10 +177,6 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
177 d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", 177 d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
178 i2400m, ack, ack_size); 178 i2400m, ack, ack_size);
179 179
180 spin_lock(&i2400m->rx_lock);
181 i2400ms->bm_ack_size = -EINPROGRESS;
182 spin_unlock(&i2400m->rx_lock);
183
184 result = wait_event_timeout(i2400ms->bm_wfa_wq, 180 result = wait_event_timeout(i2400ms->bm_wfa_wq,
185 i2400ms->bm_ack_size != -EINPROGRESS, 181 i2400ms->bm_ack_size != -EINPROGRESS,
186 2 * HZ); 182 2 * HZ);
@@ -199,6 +195,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
199 size = min(ack_size, i2400ms->bm_ack_size); 195 size = min(ack_size, i2400ms->bm_ack_size);
200 memcpy(ack, i2400m->bm_ack_buf, size); 196 memcpy(ack, i2400m->bm_ack_buf, size);
201 } 197 }
198 /*
199 * Remember always to clear the bm_ack_size to -EINPROGRESS
200 * after the RX data is processed
201 */
202 i2400ms->bm_ack_size = -EINPROGRESS; 202 i2400ms->bm_ack_size = -EINPROGRESS;
203 spin_unlock(&i2400m->rx_lock); 203 spin_unlock(&i2400m->rx_lock);
204 204
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
index 321beadf6e47..f6ca51ab216d 100644
--- a/drivers/net/wimax/i2400m/sdio-rx.c
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -234,6 +234,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms)
234 init_waitqueue_head(&i2400ms->bm_wfa_wq); 234 init_waitqueue_head(&i2400ms->bm_wfa_wq);
235 spin_lock(&i2400m->rx_lock); 235 spin_lock(&i2400m->rx_lock);
236 i2400ms->bm_wait_result = -EINPROGRESS; 236 i2400ms->bm_wait_result = -EINPROGRESS;
237 /*
238 * Before we are about to enable the RX interrupt, make sure
239 * bm_ack_size is cleared to -EINPROGRESS which indicates
240 * no RX interrupt happened yet or the previous interrupt
241 * has been handled, we are ready to take the new interrupt
242 */
243 i2400ms->bm_ack_size = -EINPROGRESS;
237 spin_unlock(&i2400m->rx_lock); 244 spin_unlock(&i2400m->rx_lock);
238 245
239 sdio_claim_host(func); 246 sdio_claim_host(func);