aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCindy H Kao <cindy.h.kao@intel.com>2010-01-30 04:26:54 -0500
committerInaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>2010-05-11 17:04:46 -0400
commitf22cf689a6353f072bca15d0a26f870e62dfacf8 (patch)
tree5a6a4907184bfa09aac6727030a501c3b8447a38
parent570eb0ea65db625e0b11ca97f4ae857bc1193250 (diff)
wimax/i2400m: fix the race condition for accessing TX queue
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>
-rw-r--r--drivers/net/wimax/i2400m/i2400m-sdio.h5
-rw-r--r--drivers/net/wimax/i2400m/sdio-tx.c31
2 files changed, 28 insertions, 8 deletions
diff --git a/drivers/net/wimax/i2400m/i2400m-sdio.h b/drivers/net/wimax/i2400m/i2400m-sdio.h
index b9c4bed3b457..360d4fb195f4 100644
--- a/drivers/net/wimax/i2400m/i2400m-sdio.h
+++ b/drivers/net/wimax/i2400m/i2400m-sdio.h
@@ -99,7 +99,10 @@ enum {
99 * 99 *
100 * @tx_workqueue: workqeueue used for data TX; we don't use the 100 * @tx_workqueue: workqeueue used for data TX; we don't use the
101 * system's workqueue as that might cause deadlocks with code in 101 * system's workqueue as that might cause deadlocks with code in
102 * the bus-generic driver. 102 * the bus-generic driver. The read/write operation to the queue
103 * is protected with spinlock (tx_lock in struct i2400m) to avoid
104 * the queue being destroyed in the middle of a the queue read/write
105 * operation.
103 * 106 *
104 * @debugfs_dentry: dentry for the SDIO specific debugfs files 107 * @debugfs_dentry: dentry for the SDIO specific debugfs files
105 * 108 *
diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c
index de66d068c9cb..412b6a8eaef2 100644
--- a/drivers/net/wimax/i2400m/sdio-tx.c
+++ b/drivers/net/wimax/i2400m/sdio-tx.c
@@ -114,13 +114,17 @@ void i2400ms_bus_tx_kick(struct i2400m *i2400m)
114{ 114{
115 struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); 115 struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
116 struct device *dev = &i2400ms->func->dev; 116 struct device *dev = &i2400ms->func->dev;
117 unsigned long flags;
117 118
118 d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); 119 d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
119 120
120 /* schedule tx work, this is because tx may block, therefore 121 /* schedule tx work, this is because tx may block, therefore
121 * it has to run in a thread context. 122 * it has to run in a thread context.
122 */ 123 */
123 queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker); 124 spin_lock_irqsave(&i2400m->tx_lock, flags);
125 if (i2400ms->tx_workqueue != NULL)
126 queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker);
127 spin_unlock_irqrestore(&i2400m->tx_lock, flags);
124 128
125 d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); 129 d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
126} 130}
@@ -130,27 +134,40 @@ int i2400ms_tx_setup(struct i2400ms *i2400ms)
130 int result; 134 int result;
131 struct device *dev = &i2400ms->func->dev; 135 struct device *dev = &i2400ms->func->dev;
132 struct i2400m *i2400m = &i2400ms->i2400m; 136 struct i2400m *i2400m = &i2400ms->i2400m;
137 struct workqueue_struct *tx_workqueue;
138 unsigned long flags;
133 139
134 d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); 140 d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
135 141
136 INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit); 142 INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit);
137 snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name), 143 snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name),
138 "%s-tx", i2400m->wimax_dev.name); 144 "%s-tx", i2400m->wimax_dev.name);
139 i2400ms->tx_workqueue = 145 tx_workqueue =
140 create_singlethread_workqueue(i2400ms->tx_wq_name); 146 create_singlethread_workqueue(i2400ms->tx_wq_name);
141 if (NULL == i2400ms->tx_workqueue) { 147 if (tx_workqueue == NULL) {
142 dev_err(dev, "TX: failed to create workqueue\n"); 148 dev_err(dev, "TX: failed to create workqueue\n");
143 result = -ENOMEM; 149 result = -ENOMEM;
144 } else 150 } else
145 result = 0; 151 result = 0;
152 spin_lock_irqsave(&i2400m->tx_lock, flags);
153 i2400ms->tx_workqueue = tx_workqueue;
154 spin_unlock_irqrestore(&i2400m->tx_lock, flags);
146 d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); 155 d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
147 return result; 156 return result;
148} 157}
149 158
150void i2400ms_tx_release(struct i2400ms *i2400ms) 159void i2400ms_tx_release(struct i2400ms *i2400ms)
151{ 160{
152 if (i2400ms->tx_workqueue) { 161 struct i2400m *i2400m = &i2400ms->i2400m;
153 destroy_workqueue(i2400ms->tx_workqueue); 162 struct workqueue_struct *tx_workqueue;
154 i2400ms->tx_workqueue = NULL; 163 unsigned long flags;
155 } 164
165 tx_workqueue = i2400ms->tx_workqueue;
166
167 spin_lock_irqsave(&i2400m->tx_lock, flags);
168 i2400ms->tx_workqueue = NULL;
169 spin_unlock_irqrestore(&i2400m->tx_lock, flags);
170
171 if (tx_workqueue)
172 destroy_workqueue(tx_workqueue);
156} 173}