aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-09-16 20:10:55 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:56:07 -0400
commitc2315b4ea9ac9c3f8caf03c3511d86fabe4a5fcd (patch)
tree17e2d15abfd26fa83f8a9654bf581f6d40fc8c33 /drivers/net/wimax/i2400m
parent8f90f3ee83dc54e182d6a7548727cbae4b523e6e (diff)
wimax/i2400m: clarify and fix i2400m->{ready,updown}
The i2400m driver uses two different bits to distinguish how much the driver is up. i2400m->ready is used to denote that the infrastructure to communicate with the device is up and running. i2400m->updown is used to indicate if 'ready' and the device is up and running, ready to take control and data traffic. However, all this was pretty dirty and not clear, with many open spots where race conditions were present. This commit cleans up the situation by: - documenting the usage of both bits - setting them only in specific, well controlled places (i2400m_dev_start, i2400m_dev_stop) - ensuring the i2400m workqueue can't get in the middle of the setting by flushing it when i2400m->ready is set to zero. This allows the report hook not having to check again for the bit to be set [rx.c:i2400m_report_hook_work()]. - using i2400m->updown to determine if the device is up and running instead of the wimax state in i2400m_dev_reset_handle(). - not loosing missed messages sent by the hardware before i2400m->ready is set. In rx.c, whatever the device sends can be sent to user space over the message pipes as soon as the wimax device is registered, so don't wait for i2400m->ready to be set. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Diffstat (limited to 'drivers/net/wimax/i2400m')
-rw-r--r--drivers/net/wimax/i2400m/driver.c44
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h23
-rw-r--r--drivers/net/wimax/i2400m/rx.c16
-rw-r--r--drivers/net/wimax/i2400m/usb.c7
4 files changed, 64 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
476error_dev_initialize: 477error_dev_initialize:
477error_check_mac_addr: 478error_check_mac_addr:
479 i2400m->ready = 0;
480 wmb(); /* see i2400m->ready's documentation */
481 flush_workqueue(i2400m->work_queue);
478error_fw_check: 482error_fw_check:
479 i2400m->bus_dev_stop(i2400m); 483 i2400m->bus_dev_stop(i2400m);
480error_bus_dev_start: 484error_bus_dev_start:
@@ -498,11 +502,15 @@ error_bootstrap:
498static 502static
499int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) 503int 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;
673out_unlock: 687out_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;