diff options
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 44 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 23 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/rx.c | 16 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 7 | ||||
-rw-r--r-- | include/net/wimax.h | 6 |
5 files changed, 70 insertions, 26 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index a0ae19966c0c..6280646d7d7f 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -455,6 +455,8 @@ retry: | |||
455 | result = i2400m->bus_dev_start(i2400m); | 455 | result = i2400m->bus_dev_start(i2400m); |
456 | if (result < 0) | 456 | if (result < 0) |
457 | goto error_bus_dev_start; | 457 | goto error_bus_dev_start; |
458 | i2400m->ready = 1; | ||
459 | wmb(); /* see i2400m->ready's documentation */ | ||
458 | result = i2400m_firmware_check(i2400m); /* fw versions ok? */ | 460 | result = i2400m_firmware_check(i2400m); /* fw versions ok? */ |
459 | if (result < 0) | 461 | if (result < 0) |
460 | goto error_fw_check; | 462 | goto error_fw_check; |
@@ -462,7 +464,6 @@ retry: | |||
462 | result = i2400m_check_mac_addr(i2400m); | 464 | result = i2400m_check_mac_addr(i2400m); |
463 | if (result < 0) | 465 | if (result < 0) |
464 | goto error_check_mac_addr; | 466 | goto error_check_mac_addr; |
465 | i2400m->ready = 1; | ||
466 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | 467 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); |
467 | result = i2400m_dev_initialize(i2400m); | 468 | result = i2400m_dev_initialize(i2400m); |
468 | if (result < 0) | 469 | if (result < 0) |
@@ -475,6 +476,9 @@ retry: | |||
475 | 476 | ||
476 | error_dev_initialize: | 477 | error_dev_initialize: |
477 | error_check_mac_addr: | 478 | error_check_mac_addr: |
479 | i2400m->ready = 0; | ||
480 | wmb(); /* see i2400m->ready's documentation */ | ||
481 | flush_workqueue(i2400m->work_queue); | ||
478 | error_fw_check: | 482 | error_fw_check: |
479 | i2400m->bus_dev_stop(i2400m); | 483 | i2400m->bus_dev_stop(i2400m); |
480 | error_bus_dev_start: | 484 | error_bus_dev_start: |
@@ -498,11 +502,15 @@ error_bootstrap: | |||
498 | static | 502 | static |
499 | int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) | 503 | int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) |
500 | { | 504 | { |
501 | int result; | 505 | int result = 0; |
502 | mutex_lock(&i2400m->init_mutex); /* Well, start the device */ | 506 | mutex_lock(&i2400m->init_mutex); /* Well, start the device */ |
503 | result = __i2400m_dev_start(i2400m, bm_flags); | 507 | if (i2400m->updown == 0) { |
504 | if (result >= 0) | 508 | result = __i2400m_dev_start(i2400m, bm_flags); |
505 | i2400m->updown = 1; | 509 | if (result >= 0) { |
510 | i2400m->updown = 1; | ||
511 | wmb(); /* see i2400m->updown's documentation */ | ||
512 | } | ||
513 | } | ||
506 | mutex_unlock(&i2400m->init_mutex); | 514 | mutex_unlock(&i2400m->init_mutex); |
507 | return result; | 515 | return result; |
508 | } | 516 | } |
@@ -529,7 +537,14 @@ void __i2400m_dev_stop(struct i2400m *i2400m) | |||
529 | wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); | 537 | wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); |
530 | i2400m_net_wake_stop(i2400m); | 538 | i2400m_net_wake_stop(i2400m); |
531 | i2400m_dev_shutdown(i2400m); | 539 | i2400m_dev_shutdown(i2400m); |
532 | i2400m->ready = 0; | 540 | /* |
541 | * Make sure no report hooks are running *before* we stop the | ||
542 | * communication infrastructure with the device. | ||
543 | */ | ||
544 | i2400m->ready = 0; /* nobody can queue work anymore */ | ||
545 | wmb(); /* see i2400m->ready's documentation */ | ||
546 | flush_workqueue(i2400m->work_queue); | ||
547 | |||
533 | i2400m->bus_dev_stop(i2400m); | 548 | i2400m->bus_dev_stop(i2400m); |
534 | destroy_workqueue(i2400m->work_queue); | 549 | destroy_workqueue(i2400m->work_queue); |
535 | i2400m_rx_release(i2400m); | 550 | i2400m_rx_release(i2400m); |
@@ -551,6 +566,7 @@ void i2400m_dev_stop(struct i2400m *i2400m) | |||
551 | if (i2400m->updown) { | 566 | if (i2400m->updown) { |
552 | __i2400m_dev_stop(i2400m); | 567 | __i2400m_dev_stop(i2400m); |
553 | i2400m->updown = 0; | 568 | i2400m->updown = 0; |
569 | wmb(); /* see i2400m->updown's documentation */ | ||
554 | } | 570 | } |
555 | mutex_unlock(&i2400m->init_mutex); | 571 | mutex_unlock(&i2400m->init_mutex); |
556 | } | 572 | } |
@@ -632,7 +648,6 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) | |||
632 | const char *reason; | 648 | const char *reason; |
633 | struct i2400m *i2400m = iw->i2400m; | 649 | struct i2400m *i2400m = iw->i2400m; |
634 | struct device *dev = i2400m_dev(i2400m); | 650 | struct device *dev = i2400m_dev(i2400m); |
635 | enum wimax_st wimax_state; | ||
636 | struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; | 651 | struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; |
637 | 652 | ||
638 | if (WARN_ON(iw->pl_size != sizeof(reason))) | 653 | if (WARN_ON(iw->pl_size != sizeof(reason))) |
@@ -647,29 +662,28 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) | |||
647 | /* We are still in i2400m_dev_start() [let it fail] or | 662 | /* We are still in i2400m_dev_start() [let it fail] or |
648 | * i2400m_dev_stop() [we are shutting down anyway, so | 663 | * i2400m_dev_stop() [we are shutting down anyway, so |
649 | * ignore it] or we are resetting somewhere else. */ | 664 | * ignore it] or we are resetting somewhere else. */ |
650 | dev_err(dev, "device rebooted\n"); | 665 | dev_err(dev, "device rebooted somewhere else?\n"); |
651 | i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); | 666 | i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); |
652 | complete(&i2400m->msg_completion); | 667 | complete(&i2400m->msg_completion); |
653 | goto out; | 668 | goto out; |
654 | } | 669 | } |
655 | wimax_state = wimax_state_get(&i2400m->wimax_dev); | 670 | if (i2400m->updown == 0) { |
656 | if (wimax_state < WIMAX_ST_UNINITIALIZED) { | 671 | dev_info(dev, "%s: device is down, doing nothing\n", reason); |
657 | dev_info(dev, "%s: it is down, ignoring\n", reason); | 672 | goto out_unlock; |
658 | goto out_unlock; /* ifconfig up/down wasn't called */ | ||
659 | } | 673 | } |
660 | dev_err(dev, "%s: reinitializing driver\n", reason); | 674 | dev_err(dev, "%s: reinitializing driver\n", reason); |
661 | __i2400m_dev_stop(i2400m); | 675 | __i2400m_dev_stop(i2400m); |
662 | i2400m->updown = 0; | ||
663 | result = __i2400m_dev_start(i2400m, | 676 | result = __i2400m_dev_start(i2400m, |
664 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | 677 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); |
665 | if (result < 0) { | 678 | if (result < 0) { |
679 | i2400m->updown = 0; | ||
680 | wmb(); /* see i2400m->updown's documentation */ | ||
666 | dev_err(dev, "%s: cannot start the device: %d\n", | 681 | dev_err(dev, "%s: cannot start the device: %d\n", |
667 | reason, result); | 682 | reason, result); |
668 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); | 683 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); |
669 | if (result >= 0) | 684 | if (result >= 0) |
670 | result = -ENODEV; | 685 | result = -ENODEV; |
671 | } else | 686 | } |
672 | i2400m->updown = 1; | ||
673 | out_unlock: | 687 | out_unlock: |
674 | if (i2400m->reset_ctx) { | 688 | if (i2400m->reset_ctx) { |
675 | ctx->result = result; | 689 | ctx->result = result; |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 2b9c400810b5..fbc156db5bfd 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -307,6 +307,27 @@ struct i2400m_barker_db; | |||
307 | * force this to be the first field so that we can get from | 307 | * force this to be the first field so that we can get from |
308 | * netdev_priv() the right pointer. | 308 | * netdev_priv() the right pointer. |
309 | * | 309 | * |
310 | * @updown: the device is up and ready for transmitting control and | ||
311 | * data packets. This implies @ready (communication infrastructure | ||
312 | * with the device is ready) and the device's firmware has been | ||
313 | * loaded and the device initialized. | ||
314 | * | ||
315 | * Write to it only inside a i2400m->init_mutex protected area | ||
316 | * followed with a wmb(); rmb() before accesing (unless locked | ||
317 | * inside i2400m->init_mutex). Read access can be loose like that | ||
318 | * [just using rmb()] because the paths that use this also do | ||
319 | * other error checks later on. | ||
320 | * | ||
321 | * @ready: Communication infrastructure with the device is ready, data | ||
322 | * frames can start to be passed around (this is lighter than | ||
323 | * using the WiMAX state for certain hot paths). | ||
324 | * | ||
325 | * Write to it only inside a i2400m->init_mutex protected area | ||
326 | * followed with a wmb(); rmb() before accesing (unless locked | ||
327 | * inside i2400m->init_mutex). Read access can be loose like that | ||
328 | * [just using rmb()] because the paths that use this also do | ||
329 | * other error checks later on. | ||
330 | * | ||
310 | * @rx_reorder: 1 if RX reordering is enabled; this can only be | 331 | * @rx_reorder: 1 if RX reordering is enabled; this can only be |
311 | * set at probe time. | 332 | * set at probe time. |
312 | * | 333 | * |
@@ -458,7 +479,7 @@ struct i2400m { | |||
458 | unsigned updown:1; /* Network device is up or down */ | 479 | unsigned updown:1; /* Network device is up or down */ |
459 | unsigned boot_mode:1; /* is the device in boot mode? */ | 480 | unsigned boot_mode:1; /* is the device in boot mode? */ |
460 | unsigned sboot:1; /* signed or unsigned fw boot */ | 481 | unsigned sboot:1; /* signed or unsigned fw boot */ |
461 | unsigned ready:1; /* all probing steps done */ | 482 | unsigned ready:1; /* Device comm infrastructure ready */ |
462 | unsigned rx_reorder:1; /* RX reorder is enabled */ | 483 | unsigned rx_reorder:1; /* RX reorder is enabled */ |
463 | u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ | 484 | u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ |
464 | /* typed u8 so /sys/kernel/debug/u8 can tweak */ | 485 | /* typed u8 so /sys/kernel/debug/u8 can tweak */ |
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index bcd411f1a854..82c200ad9fdc 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c | |||
@@ -177,8 +177,7 @@ void i2400m_report_hook_work(struct work_struct *ws) | |||
177 | struct i2400m_work *iw = | 177 | struct i2400m_work *iw = |
178 | container_of(ws, struct i2400m_work, ws); | 178 | container_of(ws, struct i2400m_work, ws); |
179 | struct i2400m_report_hook_args *args = (void *) iw->pl; | 179 | struct i2400m_report_hook_args *args = (void *) iw->pl; |
180 | if (iw->i2400m->ready) | 180 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); |
181 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); | ||
182 | kfree_skb(args->skb_rx); | 181 | kfree_skb(args->skb_rx); |
183 | i2400m_put(iw->i2400m); | 182 | i2400m_put(iw->i2400m); |
184 | kfree(iw); | 183 | kfree(iw); |
@@ -305,11 +304,12 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, | |||
305 | .l3l4_hdr = l3l4_hdr, | 304 | .l3l4_hdr = l3l4_hdr, |
306 | .size = size | 305 | .size = size |
307 | }; | 306 | }; |
308 | if (unlikely(i2400m->ready == 0)) /* only send if up */ | 307 | rmb(); /* see i2400m->ready's documentation */ |
309 | return; | 308 | if (likely(i2400m->ready)) { /* only send if up */ |
310 | skb_get(skb_rx); | 309 | skb_get(skb_rx); |
311 | i2400m_queue_work(i2400m, i2400m_report_hook_work, | 310 | i2400m_queue_work(i2400m, i2400m_report_hook_work, |
312 | GFP_KERNEL, &args, sizeof(args)); | 311 | GFP_KERNEL, &args, sizeof(args)); |
312 | } | ||
313 | if (unlikely(i2400m->trace_msg_from_user)) | 313 | if (unlikely(i2400m->trace_msg_from_user)) |
314 | wimax_msg(&i2400m->wimax_dev, "echo", | 314 | wimax_msg(&i2400m->wimax_dev, "echo", |
315 | l3l4_hdr, size, GFP_KERNEL); | 315 | l3l4_hdr, size, GFP_KERNEL); |
@@ -363,8 +363,6 @@ void i2400m_rx_trace(struct i2400m *i2400m, | |||
363 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", | 363 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", |
364 | msg_type, size); | 364 | msg_type, size); |
365 | d_dump(2, dev, l3l4_hdr, size); | 365 | 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); | 366 | result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); |
369 | if (result < 0) | 367 | if (result < 0) |
370 | dev_err(dev, "error sending trace to userspace: %d\n", | 368 | dev_err(dev, "error sending trace to userspace: %d\n", |
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 07653ded6c59..f4dfb60bb628 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -516,7 +516,10 @@ void i2400mu_disconnect(struct usb_interface *iface) | |||
516 | * So at the end, the three cases require common handling. | 516 | * So at the end, the three cases require common handling. |
517 | * | 517 | * |
518 | * If at the time of this call the device's firmware is not loaded, | 518 | * If at the time of this call the device's firmware is not loaded, |
519 | * nothing has to be done. | 519 | * nothing has to be done. Note we can be "loose" about not reading |
520 | * i2400m->updown under i2400m->init_mutex. If it happens to change | ||
521 | * inmediately, other parts of the call flow will fail and effectively | ||
522 | * catch it. | ||
520 | * | 523 | * |
521 | * If the firmware is loaded, we need to: | 524 | * If the firmware is loaded, we need to: |
522 | * | 525 | * |
@@ -555,6 +558,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) | |||
555 | #endif | 558 | #endif |
556 | 559 | ||
557 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); | 560 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); |
561 | rmb(); /* see i2400m->updown's documentation */ | ||
558 | if (i2400m->updown == 0) | 562 | if (i2400m->updown == 0) |
559 | goto no_firmware; | 563 | goto no_firmware; |
560 | if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { | 564 | if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { |
@@ -608,6 +612,7 @@ int i2400mu_resume(struct usb_interface *iface) | |||
608 | struct i2400m *i2400m = &i2400mu->i2400m; | 612 | struct i2400m *i2400m = &i2400mu->i2400m; |
609 | 613 | ||
610 | d_fnstart(3, dev, "(iface %p)\n", iface); | 614 | d_fnstart(3, dev, "(iface %p)\n", iface); |
615 | rmb(); /* see i2400m->updown's documentation */ | ||
611 | if (i2400m->updown == 0) { | 616 | if (i2400m->updown == 0) { |
612 | d_printf(1, dev, "fw was down, no resume neeed\n"); | 617 | d_printf(1, dev, "fw was down, no resume neeed\n"); |
613 | goto out; | 618 | goto out; |
diff --git a/include/net/wimax.h b/include/net/wimax.h index 2af7bf839f23..d69c4a7a1267 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h | |||
@@ -195,6 +195,12 @@ | |||
195 | * defining the `struct nla_policy` for each message, it has to have | 195 | * defining the `struct nla_policy` for each message, it has to have |
196 | * an array size of WIMAX_GNL_ATTR_MAX+1. | 196 | * an array size of WIMAX_GNL_ATTR_MAX+1. |
197 | * | 197 | * |
198 | * The op_*() function pointers will not be called if the wimax_dev is | ||
199 | * in a state <= %WIMAX_ST_UNINITIALIZED. The exception is: | ||
200 | * | ||
201 | * - op_reset: can be called at any time after wimax_dev_add() has | ||
202 | * been called. | ||
203 | * | ||
198 | * THE PIPE INTERFACE: | 204 | * THE PIPE INTERFACE: |
199 | * | 205 | * |
200 | * This interface is kept intentionally simple. The driver can send | 206 | * This interface is kept intentionally simple. The driver can send |