Skip to content

Commit

Permalink
wimax/i2400m: fix the race condition for accessing TX queue
Browse files Browse the repository at this point in the history
The race condition happens when the TX queue is accessed by
the TX work while the same TX queue is being destroyed because
a bus reset is triggered either by debugfs entry or simply
by failing waking up the device from WiMAX IDLE mode.

This fix is to prevent the TX queue from being accessed by
multiple threads

Signed-off-by: Cindy H Kao <cindy.h.kao@intel.com>
  • Loading branch information
Cindy H Kao authored and Inaky Perez-Gonzalez committed May 11, 2010
1 parent 570eb0e commit f22cf68
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 8 deletions.
5 changes: 4 additions & 1 deletion drivers/net/wimax/i2400m/i2400m-sdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ enum {
*
* @tx_workqueue: workqeueue used for data TX; we don't use the
* system's workqueue as that might cause deadlocks with code in
* the bus-generic driver.
* the bus-generic driver. The read/write operation to the queue
* is protected with spinlock (tx_lock in struct i2400m) to avoid
* the queue being destroyed in the middle of a the queue read/write
* operation.
*
* @debugfs_dentry: dentry for the SDIO specific debugfs files
*
Expand Down
31 changes: 24 additions & 7 deletions drivers/net/wimax/i2400m/sdio-tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,17 @@ void i2400ms_bus_tx_kick(struct i2400m *i2400m)
{
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct device *dev = &i2400ms->func->dev;
unsigned long flags;

d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);

/* schedule tx work, this is because tx may block, therefore
* it has to run in a thread context.
*/
queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker);
spin_lock_irqsave(&i2400m->tx_lock, flags);
if (i2400ms->tx_workqueue != NULL)
queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker);
spin_unlock_irqrestore(&i2400m->tx_lock, flags);

d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
Expand All @@ -130,27 +134,40 @@ int i2400ms_tx_setup(struct i2400ms *i2400ms)
int result;
struct device *dev = &i2400ms->func->dev;
struct i2400m *i2400m = &i2400ms->i2400m;
struct workqueue_struct *tx_workqueue;
unsigned long flags;

d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);

INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit);
snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name),
"%s-tx", i2400m->wimax_dev.name);
i2400ms->tx_workqueue =
tx_workqueue =
create_singlethread_workqueue(i2400ms->tx_wq_name);
if (NULL == i2400ms->tx_workqueue) {
if (tx_workqueue == NULL) {
dev_err(dev, "TX: failed to create workqueue\n");
result = -ENOMEM;
} else
result = 0;
spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400ms->tx_workqueue = tx_workqueue;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
return result;
}

void i2400ms_tx_release(struct i2400ms *i2400ms)
{
if (i2400ms->tx_workqueue) {
destroy_workqueue(i2400ms->tx_workqueue);
i2400ms->tx_workqueue = NULL;
}
struct i2400m *i2400m = &i2400ms->i2400m;
struct workqueue_struct *tx_workqueue;
unsigned long flags;

tx_workqueue = i2400ms->tx_workqueue;

spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400ms->tx_workqueue = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);

if (tx_workqueue)
destroy_workqueue(tx_workqueue);
}

0 comments on commit f22cf68

Please sign in to comment.