diff options
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m-sdio.h | 5 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-tx.c | 31 |
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 | ||
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 | } |