link_watch.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Linux network device link state notification
  3. *
  4. * Author:
  5. * Stefan Rompf <sux@loplof.de>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version
  10. * 2 of the License, or (at your option) any later version.
  11. *
  12. */
  13. #include <linux/config.h>
  14. #include <linux/module.h>
  15. #include <linux/netdevice.h>
  16. #include <linux/if.h>
  17. #include <net/sock.h>
  18. #include <net/pkt_sched.h>
  19. #include <linux/rtnetlink.h>
  20. #include <linux/jiffies.h>
  21. #include <linux/spinlock.h>
  22. #include <linux/list.h>
  23. #include <linux/slab.h>
  24. #include <linux/workqueue.h>
  25. #include <linux/bitops.h>
  26. #include <asm/types.h>
  27. enum lw_bits {
  28. LW_RUNNING = 0,
  29. LW_SE_USED
  30. };
  31. static unsigned long linkwatch_flags;
  32. static unsigned long linkwatch_nextevent;
  33. static void linkwatch_event(void *dummy);
  34. static DECLARE_WORK(linkwatch_work, linkwatch_event, NULL);
  35. static LIST_HEAD(lweventlist);
  36. static DEFINE_SPINLOCK(lweventlist_lock);
  37. struct lw_event {
  38. struct list_head list;
  39. struct net_device *dev;
  40. };
  41. /* Avoid kmalloc() for most systems */
  42. static struct lw_event singleevent;
  43. /* Must be called with the rtnl semaphore held */
  44. void linkwatch_run_queue(void)
  45. {
  46. LIST_HEAD(head);
  47. struct list_head *n, *next;
  48. spin_lock_irq(&lweventlist_lock);
  49. list_splice_init(&lweventlist, &head);
  50. spin_unlock_irq(&lweventlist_lock);
  51. list_for_each_safe(n, next, &head) {
  52. struct lw_event *event = list_entry(n, struct lw_event, list);
  53. struct net_device *dev = event->dev;
  54. if (event == &singleevent) {
  55. clear_bit(LW_SE_USED, &linkwatch_flags);
  56. } else {
  57. kfree(event);
  58. }
  59. /* We are about to handle this device,
  60. * so new events can be accepted
  61. */
  62. clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
  63. if (dev->flags & IFF_UP) {
  64. if (netif_carrier_ok(dev)) {
  65. WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
  66. dev_activate(dev);
  67. } else
  68. dev_deactivate(dev);
  69. netdev_state_change(dev);
  70. }
  71. dev_put(dev);
  72. }
  73. }
  74. static void linkwatch_event(void *dummy)
  75. {
  76. /* Limit the number of linkwatch events to one
  77. * per second so that a runaway driver does not
  78. * cause a storm of messages on the netlink
  79. * socket
  80. */
  81. linkwatch_nextevent = jiffies + HZ;
  82. clear_bit(LW_RUNNING, &linkwatch_flags);
  83. rtnl_shlock();
  84. linkwatch_run_queue();
  85. rtnl_shunlock();
  86. }
  87. void linkwatch_fire_event(struct net_device *dev)
  88. {
  89. if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
  90. unsigned long flags;
  91. struct lw_event *event;
  92. if (test_and_set_bit(LW_SE_USED, &linkwatch_flags)) {
  93. event = kmalloc(sizeof(struct lw_event), GFP_ATOMIC);
  94. if (unlikely(event == NULL)) {
  95. clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
  96. return;
  97. }
  98. } else {
  99. event = &singleevent;
  100. }
  101. dev_hold(dev);
  102. event->dev = dev;
  103. spin_lock_irqsave(&lweventlist_lock, flags);
  104. list_add_tail(&event->list, &lweventlist);
  105. spin_unlock_irqrestore(&lweventlist_lock, flags);
  106. if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {
  107. unsigned long thisevent = jiffies;
  108. if (thisevent >= linkwatch_nextevent) {
  109. schedule_work(&linkwatch_work);
  110. } else {
  111. schedule_delayed_work(&linkwatch_work, linkwatch_nextevent - thisevent);
  112. }
  113. }
  114. }
  115. }
  116. EXPORT_SYMBOL(linkwatch_fire_event);