Commit 5b45bfe5 authored by Cindy H Kao's avatar Cindy H Kao Committed by Inaky Perez-Gonzalez

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: default avatarCindy H Kao <cindy.h.kao@intel.com>
Signed-off-by: default avatarInaky Perez-Gonzalez <inaky@linux.intel.com>
parent 663ebb4a
...@@ -177,10 +177,6 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, ...@@ -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", d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size); 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, result = wait_event_timeout(i2400ms->bm_wfa_wq,
i2400ms->bm_ack_size != -EINPROGRESS, i2400ms->bm_ack_size != -EINPROGRESS,
2 * HZ); 2 * HZ);
...@@ -199,6 +195,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, ...@@ -199,6 +195,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
size = min(ack_size, i2400ms->bm_ack_size); size = min(ack_size, i2400ms->bm_ack_size);
memcpy(ack, i2400m->bm_ack_buf, 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; i2400ms->bm_ack_size = -EINPROGRESS;
spin_unlock(&i2400m->rx_lock); spin_unlock(&i2400m->rx_lock);
......
...@@ -234,6 +234,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms) ...@@ -234,6 +234,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms)
init_waitqueue_head(&i2400ms->bm_wfa_wq); init_waitqueue_head(&i2400ms->bm_wfa_wq);
spin_lock(&i2400m->rx_lock); spin_lock(&i2400m->rx_lock);
i2400ms->bm_wait_result = -EINPROGRESS; 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); spin_unlock(&i2400m->rx_lock);
sdio_claim_host(func); sdio_claim_host(func);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment