diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/rx.c')
-rw-r--r-- | drivers/net/wimax/i2400m/rx.c | 170 |
1 files changed, 137 insertions, 33 deletions
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index 07c32e68909f..e3d2a9de023c 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c | |||
@@ -158,30 +158,104 @@ struct i2400m_report_hook_args { | |||
158 | struct sk_buff *skb_rx; | 158 | struct sk_buff *skb_rx; |
159 | const struct i2400m_l3l4_hdr *l3l4_hdr; | 159 | const struct i2400m_l3l4_hdr *l3l4_hdr; |
160 | size_t size; | 160 | size_t size; |
161 | struct list_head list_node; | ||
161 | }; | 162 | }; |
162 | 163 | ||
163 | 164 | ||
164 | /* | 165 | /* |
165 | * Execute i2400m_report_hook in a workqueue | 166 | * Execute i2400m_report_hook in a workqueue |
166 | * | 167 | * |
167 | * Unpacks arguments from the deferred call, executes it and then | 168 | * Goes over the list of queued reports in i2400m->rx_reports and |
168 | * drops the references. | 169 | * processes them. |
169 | * | 170 | * |
170 | * Obvious NOTE: References are needed because we are a separate | 171 | * NOTE: refcounts on i2400m are not needed because we flush the |
171 | * thread; otherwise the buffer changes under us because it is | 172 | * workqueue this runs on (i2400m->work_queue) before destroying |
172 | * released by the original caller. | 173 | * i2400m. |
173 | */ | 174 | */ |
174 | static | ||
175 | void i2400m_report_hook_work(struct work_struct *ws) | 175 | void i2400m_report_hook_work(struct work_struct *ws) |
176 | { | 176 | { |
177 | struct i2400m_work *iw = | 177 | struct i2400m *i2400m = container_of(ws, struct i2400m, rx_report_ws); |
178 | container_of(ws, struct i2400m_work, ws); | 178 | struct device *dev = i2400m_dev(i2400m); |
179 | struct i2400m_report_hook_args *args = (void *) iw->pl; | 179 | struct i2400m_report_hook_args *args, *args_next; |
180 | if (iw->i2400m->ready) | 180 | LIST_HEAD(list); |
181 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); | 181 | unsigned long flags; |
182 | kfree_skb(args->skb_rx); | 182 | |
183 | i2400m_put(iw->i2400m); | 183 | while (1) { |
184 | kfree(iw); | 184 | spin_lock_irqsave(&i2400m->rx_lock, flags); |
185 | list_splice_init(&i2400m->rx_reports, &list); | ||
186 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
187 | if (list_empty(&list)) | ||
188 | break; | ||
189 | else | ||
190 | d_printf(1, dev, "processing queued reports\n"); | ||
191 | list_for_each_entry_safe(args, args_next, &list, list_node) { | ||
192 | d_printf(2, dev, "processing queued report %p\n", args); | ||
193 | i2400m_report_hook(i2400m, args->l3l4_hdr, args->size); | ||
194 | kfree_skb(args->skb_rx); | ||
195 | list_del(&args->list_node); | ||
196 | kfree(args); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | |||
202 | /* | ||
203 | * Flush the list of queued reports | ||
204 | */ | ||
205 | static | ||
206 | void i2400m_report_hook_flush(struct i2400m *i2400m) | ||
207 | { | ||
208 | struct device *dev = i2400m_dev(i2400m); | ||
209 | struct i2400m_report_hook_args *args, *args_next; | ||
210 | LIST_HEAD(list); | ||
211 | unsigned long flags; | ||
212 | |||
213 | d_printf(1, dev, "flushing queued reports\n"); | ||
214 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
215 | list_splice_init(&i2400m->rx_reports, &list); | ||
216 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
217 | list_for_each_entry_safe(args, args_next, &list, list_node) { | ||
218 | d_printf(2, dev, "flushing queued report %p\n", args); | ||
219 | kfree_skb(args->skb_rx); | ||
220 | list_del(&args->list_node); | ||
221 | kfree(args); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | |||
226 | /* | ||
227 | * Queue a report for later processing | ||
228 | * | ||
229 | * @i2400m: device descriptor | ||
230 | * @skb_rx: skb that contains the payload (for reference counting) | ||
231 | * @l3l4_hdr: pointer to the control | ||
232 | * @size: size of the message | ||
233 | */ | ||
234 | static | ||
235 | void i2400m_report_hook_queue(struct i2400m *i2400m, struct sk_buff *skb_rx, | ||
236 | const void *l3l4_hdr, size_t size) | ||
237 | { | ||
238 | struct device *dev = i2400m_dev(i2400m); | ||
239 | unsigned long flags; | ||
240 | struct i2400m_report_hook_args *args; | ||
241 | |||
242 | args = kzalloc(sizeof(*args), GFP_NOIO); | ||
243 | if (args) { | ||
244 | args->skb_rx = skb_get(skb_rx); | ||
245 | args->l3l4_hdr = l3l4_hdr; | ||
246 | args->size = size; | ||
247 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
248 | list_add_tail(&args->list_node, &i2400m->rx_reports); | ||
249 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
250 | d_printf(2, dev, "queued report %p\n", args); | ||
251 | rmb(); /* see i2400m->ready's documentation */ | ||
252 | if (likely(i2400m->ready)) /* only send if up */ | ||
253 | queue_work(i2400m->work_queue, &i2400m->rx_report_ws); | ||
254 | } else { | ||
255 | if (printk_ratelimit()) | ||
256 | dev_err(dev, "%s:%u: Can't allocate %zu B\n", | ||
257 | __func__, __LINE__, sizeof(*args)); | ||
258 | } | ||
185 | } | 259 | } |
186 | 260 | ||
187 | 261 | ||
@@ -295,21 +369,29 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, | |||
295 | msg_type, size); | 369 | msg_type, size); |
296 | d_dump(2, dev, l3l4_hdr, size); | 370 | d_dump(2, dev, l3l4_hdr, size); |
297 | if (msg_type & I2400M_MT_REPORT_MASK) { | 371 | if (msg_type & I2400M_MT_REPORT_MASK) { |
298 | /* These hooks have to be ran serialized; as well, the | 372 | /* |
299 | * handling might force the execution of commands, and | 373 | * Process each report |
300 | * that might cause reentrancy issues with | 374 | * |
301 | * bus-specific subdrivers and workqueues. So we run | 375 | * - has to be ran serialized as well |
302 | * it in a separate workqueue. */ | 376 | * |
303 | struct i2400m_report_hook_args args = { | 377 | * - the handling might force the execution of |
304 | .skb_rx = skb_rx, | 378 | * commands. That might cause reentrancy issues with |
305 | .l3l4_hdr = l3l4_hdr, | 379 | * bus-specific subdrivers and workqueues, so the we |
306 | .size = size | 380 | * run it in a separate workqueue. |
307 | }; | 381 | * |
308 | if (unlikely(i2400m->ready == 0)) /* only send if up */ | 382 | * - when the driver is not yet ready to handle them, |
309 | return; | 383 | * they are queued and at some point the queue is |
310 | skb_get(skb_rx); | 384 | * restarted [NOTE: we can't queue SKBs directly, as |
311 | i2400m_queue_work(i2400m, i2400m_report_hook_work, | 385 | * this might be a piece of a SKB, not the whole |
312 | GFP_KERNEL, &args, sizeof(args)); | 386 | * thing, and this is cheaper than cloning the |
387 | * SKB]. | ||
388 | * | ||
389 | * Note we don't do refcounting for the device | ||
390 | * structure; this is because before destroying | ||
391 | * 'i2400m', we make sure to flush the | ||
392 | * i2400m->work_queue, so there are no issues. | ||
393 | */ | ||
394 | i2400m_report_hook_queue(i2400m, skb_rx, l3l4_hdr, size); | ||
313 | if (unlikely(i2400m->trace_msg_from_user)) | 395 | if (unlikely(i2400m->trace_msg_from_user)) |
314 | wimax_msg(&i2400m->wimax_dev, "echo", | 396 | wimax_msg(&i2400m->wimax_dev, "echo", |
315 | l3l4_hdr, size, GFP_KERNEL); | 397 | l3l4_hdr, size, GFP_KERNEL); |
@@ -363,8 +445,6 @@ void i2400m_rx_trace(struct i2400m *i2400m, | |||
363 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", | 445 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", |
364 | msg_type, size); | 446 | msg_type, size); |
365 | d_dump(2, dev, l3l4_hdr, size); | 447 | d_dump(2, dev, l3l4_hdr, size); |
366 | if (unlikely(i2400m->ready == 0)) /* only send if up */ | ||
367 | return; | ||
368 | result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); | 448 | result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); |
369 | if (result < 0) | 449 | if (result < 0) |
370 | dev_err(dev, "error sending trace to userspace: %d\n", | 450 | dev_err(dev, "error sending trace to userspace: %d\n", |
@@ -748,7 +828,7 @@ void i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, | |||
748 | dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n", | 828 | dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n", |
749 | nsn, lbn, roq->ws); | 829 | nsn, lbn, roq->ws); |
750 | i2400m_roq_log_dump(i2400m, roq); | 830 | i2400m_roq_log_dump(i2400m, roq); |
751 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); | 831 | i2400m_reset(i2400m, I2400M_RT_WARM); |
752 | } else { | 832 | } else { |
753 | __i2400m_roq_queue(i2400m, roq, skb, lbn, nsn); | 833 | __i2400m_roq_queue(i2400m, roq, skb, lbn, nsn); |
754 | i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET, | 834 | i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET, |
@@ -814,7 +894,7 @@ void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, | |||
814 | dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n", | 894 | dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n", |
815 | nsn, sn, roq->ws); | 895 | nsn, sn, roq->ws); |
816 | i2400m_roq_log_dump(i2400m, roq); | 896 | i2400m_roq_log_dump(i2400m, roq); |
817 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); | 897 | i2400m_reset(i2400m, I2400M_RT_WARM); |
818 | } else { | 898 | } else { |
819 | /* if the queue is empty, don't bother as we'd queue | 899 | /* if the queue is empty, don't bother as we'd queue |
820 | * it and inmediately unqueue it -- just deliver it */ | 900 | * it and inmediately unqueue it -- just deliver it */ |
@@ -1194,6 +1274,28 @@ error_msg_hdr_check: | |||
1194 | EXPORT_SYMBOL_GPL(i2400m_rx); | 1274 | EXPORT_SYMBOL_GPL(i2400m_rx); |
1195 | 1275 | ||
1196 | 1276 | ||
1277 | void i2400m_unknown_barker(struct i2400m *i2400m, | ||
1278 | const void *buf, size_t size) | ||
1279 | { | ||
1280 | struct device *dev = i2400m_dev(i2400m); | ||
1281 | char prefix[64]; | ||
1282 | const __le32 *barker = buf; | ||
1283 | dev_err(dev, "RX: HW BUG? unknown barker %08x, " | ||
1284 | "dropping %zu bytes\n", le32_to_cpu(*barker), size); | ||
1285 | snprintf(prefix, sizeof(prefix), "%s %s: ", | ||
1286 | dev_driver_string(dev), dev_name(dev)); | ||
1287 | if (size > 64) { | ||
1288 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
1289 | 8, 4, buf, 64, 0); | ||
1290 | printk(KERN_ERR "%s... (only first 64 bytes " | ||
1291 | "dumped)\n", prefix); | ||
1292 | } else | ||
1293 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
1294 | 8, 4, buf, size, 0); | ||
1295 | } | ||
1296 | EXPORT_SYMBOL(i2400m_unknown_barker); | ||
1297 | |||
1298 | |||
1197 | /* | 1299 | /* |
1198 | * Initialize the RX queue and infrastructure | 1300 | * Initialize the RX queue and infrastructure |
1199 | * | 1301 | * |
@@ -1261,4 +1363,6 @@ void i2400m_rx_release(struct i2400m *i2400m) | |||
1261 | kfree(i2400m->rx_roq[0].log); | 1363 | kfree(i2400m->rx_roq[0].log); |
1262 | kfree(i2400m->rx_roq); | 1364 | kfree(i2400m->rx_roq); |
1263 | } | 1365 | } |
1366 | /* at this point, nothing can be received... */ | ||
1367 | i2400m_report_hook_flush(i2400m); | ||
1264 | } | 1368 | } |