2002-03-28 00:42:56

by Stefan Rompf

[permalink] [raw]
Subject: Patch: Device operative state notification against 2.4.18ac2

diff -X dontdiff -urN linux-2.4.18ac2/Documentation/Configure.help linux-2.4.18ac2-stefan/Documentation/Configure.help
--- linux-2.4.18ac2/Documentation/Configure.help Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/Documentation/Configure.help Sun Mar 24 20:12:08 2002
@@ -24594,6 +24594,15 @@
information: http://www.candelatech.com/~greear/vlan.html If unsure,
you can safely say 'N'.

+Device link state notification
+CONFIG_LINKWATCH
+ When this option is enabled, the kernel will forward changes in the
+ operative ("RUNNING") state of an interface via the netlink socket.
+ This is most useful when running linux as a router. Note that currently
+ not many drivers maintain an administrative state, a few even break this
+ option. Compliant drivers change the RUNNING flag in ifconfig output
+ depending on operative state. If unsure, say 'N'.
+
#
# A couple of things I keep forgetting:
# capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
diff -X dontdiff -urN linux-2.4.18ac2/include/linux/netdevice.h linux-2.4.18ac2-stefan/include/linux/netdevice.h
--- linux-2.4.18ac2/include/linux/netdevice.h Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/include/linux/netdevice.h Mon Mar 25 23:29:37 2002
@@ -208,6 +208,12 @@
__LINK_STATE_NOCARRIER
};

+/* This gets called by netif_carrier_on()/_off() whenever
+ * state of an interface changes
+ */
+#ifdef CONFIG_LINKWATCH
+extern void wake_linkwatch_thread(void);
+#endif

/*
* This structure holds at boot time configured netdevice settings. They
@@ -611,14 +617,24 @@

static inline void netif_carrier_on(struct net_device *dev)
{
+#ifdef CONFIG_LINKWATCH
+ if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state))
+ wake_linkwatch_thread();
+#else
clear_bit(__LINK_STATE_NOCARRIER, &dev->state);
+#endif
if (netif_running(dev))
__netdev_watchdog_up(dev);
}

static inline void netif_carrier_off(struct net_device *dev)
{
+#ifdef CONFIG_LINKWATCH
+ if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state))
+ wake_linkwatch_thread();
+#else
set_bit(__LINK_STATE_NOCARRIER, &dev->state);
+#endif
}

/* Hot-plugging. */
diff -X dontdiff -urN linux-2.4.18ac2/net/Config.in linux-2.4.18ac2-stefan/net/Config.in
--- linux-2.4.18ac2/net/Config.in Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/net/Config.in Sun Mar 24 19:58:20 2002
@@ -46,6 +46,7 @@
fi

dep_tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_8021Q $CONFIG_EXPERIMENTAL
+ bool 'Device link state notification (EXPERIMENTAL)' CONFIG_LINKWATCH

fi

diff -X dontdiff -urN linux-2.4.18ac2/net/core/dev.c linux-2.4.18ac2-stefan/net/core/dev.c
--- linux-2.4.18ac2/net/core/dev.c Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/net/core/dev.c Wed Mar 27 00:32:17 2002
@@ -719,6 +719,12 @@
*/
dev->flags |= IFF_UP;

+ if (netif_carrier_ok(dev)) {
+ dev->flags |= IFF_RUNNING;
+ } else {
+ dev->flags &= ~IFF_RUNNING;
+ }
+
set_bit(__LINK_STATE_START, &dev->state);

/*
@@ -812,7 +818,7 @@
* Device is now down.
*/

- dev->flags &= ~IFF_UP;
+ dev->flags &= ~(IFF_UP | IFF_RUNNING);
#ifdef CONFIG_NET_FASTROUTE
dev_clear_fastroute(dev);
#endif
@@ -2740,7 +2746,9 @@
#ifdef CONFIG_NET_DIVERT
extern void dv_init(void);
#endif /* CONFIG_NET_DIVERT */
-
+#ifdef CONFIG_LINKWATCH
+extern void linkwatch_init(void);
+#endif /* CONFIG_LINKWATCH */

/*
* Callers must hold the rtnl semaphore. See the comment at the
@@ -2877,6 +2885,10 @@
*/

net_device_init();
+
+#ifdef CONFIG_LINKWATCH
+ linkwatch_init();
+#endif /* CONFIG_LINKWATCH */

return 0;
}
diff -X dontdiff -urN linux-2.4.18ac2/net/core/link_watch.c linux-2.4.18ac2-stefan/net/core/link_watch.c
--- linux-2.4.18ac2/net/core/link_watch.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18ac2-stefan/net/core/link_watch.c Thu Mar 28 00:18:43 2002
@@ -0,0 +1,106 @@
+/*
+ * Linux network device state notifaction
+ *
+ * Author:
+ * Stefan Rompf <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/rtnetlink.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+
+/* Used by wake_linkwatch_thread() */
+static wait_queue_head_t linkwatch_wqh;
+static unsigned long linkwatch_nowait;
+
+static int linkwatch_thread(void *dummy)
+{
+ struct net_device *dev;
+ DECLARE_WAITQUEUE(linkwatch_wq, current);
+
+ daemonize();
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ strcpy(current->comm, "linkwatchd");
+
+ while(1) {
+ clear_bit(0, &linkwatch_nowait);
+ rtnl_lock();
+ for (dev=dev_base; dev; dev = dev->next) {
+
+ /* State of netif_carrier_ok() is reflected
+ into dev_flags by this loop, and a netlink
+ message is omitted whenever the state
+ changes */
+
+ if (!(dev->flags & IFF_UP)) continue;
+
+ if (dev->flags & IFF_RUNNING) {
+ if (!netif_carrier_ok(dev)) {
+ write_lock(&dev_base_lock);
+ dev->flags &= ~IFF_RUNNING;
+ write_unlock(&dev_base_lock);
+ netdev_state_change(dev);
+ }
+ } else {
+ if (netif_carrier_ok(dev)) {
+ write_lock(&dev_base_lock);
+ dev->flags |= IFF_RUNNING;
+ write_unlock(&dev_base_lock);
+ netdev_state_change(dev);
+ }
+ }
+ }
+ rtnl_unlock();
+
+ /* Prevent runaway on rapid state changes */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (test_bit(0, &linkwatch_nowait)) {
+ set_current_state(TASK_RUNNING);
+ } else {
+ add_wait_queue(&linkwatch_wqh, &linkwatch_wq);
+ schedule();
+ remove_wait_queue(&linkwatch_wqh, &linkwatch_wq);
+ }
+ }
+
+ return 0; /* Not reached */
+}
+
+
+void wake_linkwatch_thread() {
+ set_bit(0, &linkwatch_nowait);
+ wake_up(&linkwatch_wqh);
+}
+
+
+void __init linkwatch_init(void) {
+ pid_t me;
+
+ init_waitqueue_head(&linkwatch_wqh);
+ linkwatch_nowait = 0;
+ me = kernel_thread(linkwatch_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+
+ if (!me) {
+ printk(KERN_INFO "Failed to init linkwatch thread, continuing without\n");
+ }
+}
+
diff -X dontdiff -urN linux-2.4.18ac2/net/netsyms.c linux-2.4.18ac2-stefan/net/netsyms.c
--- linux-2.4.18ac2/net/netsyms.c Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/net/netsyms.c Mon Mar 25 21:55:34 2002
@@ -588,4 +588,8 @@
EXPORT_SYMBOL(net_call_rx_atomic);
EXPORT_SYMBOL(softnet_data);

+#ifdef CONFIG_LINKWATCH
+EXPORT_SYMBOL(wake_linkwatch_thread);
+#endif
+
#endif /* CONFIG_NET */
--- linux-2.4.18ac2/net/core/Makefile Wed Mar 27 00:06:54 2002
+++ linux-2.4.18ac2-stefan/net/core/Makefile Mon Mar 25 21:54:26 2002
@@ -27,4 +27,6 @@
obj-$(CONFIG_NET_DIVERT) += dv.o
obj-$(CONFIG_NET_PROFILE) += profile.o

+obj-$(CONFIG_LINKWATCH) += link_watch.o
+
include $(TOPDIR)/Rules.make


Attachments:
diff-24ac (7.05 kB)