diff options
author | Cindy H Kao <cindy.h.kao@intel.com> | 2010-01-30 04:26:54 -0500 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2010-05-11 17:04:46 -0400 |
commit | f22cf689a6353f072bca15d0a26f870e62dfacf8 (patch) | |
tree | 5a6a4907184bfa09aac6727030a501c3b8447a38 /drivers/net/wimax/i2400m/sdio-tx.c | |
parent | 570eb0ea65db625e0b11ca97f4ae857bc1193250 (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>
Diffstat (limited to 'drivers/net/wimax/i2400m/sdio-tx.c')
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-tx.c | 31 |
1 files changed, 24 insertions, 7 deletions
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 | ||
150 | void i2400ms_tx_release(struct i2400ms *i2400ms) | 159 | void 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 | } |