diff options
author | Cindy H Kao <cindy.h.kao@intel.com> | 2009-08-27 18:25:12 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-19 02:55:33 -0400 |
commit | 5b45bfe5010ae1ddaac463d1bcdb141a4ff4ff66 (patch) | |
tree | 8115ce638a420e4ba6610ea6c2e126ddb9180b86 | |
parent | 663ebb4aa2fd0d19e3d1da0fed3fa3fa134a4ef0 (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.c | 8 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-rx.c | 7 |
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); |