Hi,
attached is my current state of RFC2863 implementation. It defines the new
status fields and operations. Yet not implemented but in my 2.6.15 time frame
is
-userspace interaction, including userspace supplicant + locking of
operstate_useroverride
-migrate VLAN + BONDING to use OPER_LOWERLAYERDOWN
-Documentation ;-)
I'd leave making IPV4 not consider routes when interface is <OPER_DORMANTL3UP
for 2.6.16.
Unfortunately, me and Krzysztof have totally different opinions if this
complexity is needed. I think yes, as it will make it possible to use a
userspace supplicant for connection completion (WPA), layer 3 protocols not
considering down interfaces for routing decisions (imporant for quagga), dial
on demand interfaces, clear state forwarding for stacked interfaces. This is
stuff required now, not in a distant future, and if we touch these flags
anyway, let's do it right.
This is not yet for applying, just compilation tested, comments welcome. Is my
wmb() to avoid 4 byte atomic_t the right idea?
Stefan
--- linux-2.6.14/include/linux/netdevice.h.old 2005-11-02 11:08:10.000000000 +0100
+++ linux-2.6.14/include/linux/netdevice.h 2005-11-07 00:23:12.000000000 +0100
@@ -228,11 +228,35 @@ enum netdev_state_t
__LINK_STATE_START,
__LINK_STATE_PRESENT,
__LINK_STATE_SCHED,
- __LINK_STATE_NOCARRIER,
+ __OBSOLETE_LINK_STATE_NOCARRIER,
__LINK_STATE_RX_SCHED,
__LINK_STATE_LINKWATCH_PENDING
};
+/* Operational state of a network device, mostly taken from RFC2863 */
+enum netdev_operstate_t
+{
+ OPER_UNKNOWN = 0,
+ OPER_NOTPRESENT, /* unused, placeholder */
+ OPER_DOWN = 16,
+ OPER_LOWERLAYERDOWN,
+ OPER_TESTING = 32,
+ OPER_DORMANTL3DOWN = 48, /* OS queue start from here */
+ OPER_DORMANTL3UP = 64, /* L3 should try using interface */
+ OPER_UP = 96
+};
+
+/* Userspace application may limit visible external state to
+ * DORMANTL3DOWN or DORMANTL3UP */
+enum netdev_operoverride_t {
+ OPERU_IGNORE, /* report state as defined by operstate_kernel */
+ OPERU_DORMANTL3DOWN, /* never report >= DORMANTL3DOWN */
+ OPERU_DORMANTL3UP, /* never report >= DORMANTL3UP */
+ OPERU_UP, /* report kernel state up to UP */
+ OPERU_UP_UNTIL_DORMANTL3DOWN, /* change user override to OPERU_DORMANTL3DOWN on kernel <= OPER_DORMANTL3UP */
+ OPERU_UP_UNTIL_DORMANTL3UP /* change user override to OPERU_DORMANTL3UP on kernel <= OPER_DORMANTL3UP */
+};
+
/*
* This structure holds at boot time configured netdevice settings. They
@@ -346,7 +370,10 @@ struct net_device
struct net_device *master; /* Pointer to master device of a group,
* which this device is member of.
*/
-
+ /* Operation state */
+ unsigned char operstate_kernel;
+ unsigned char operstate_useroverride;
+
/* Interface address info. */
unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */
unsigned char addr_len; /* hardware address length */
@@ -709,23 +736,36 @@ static inline void dev_put(struct net_de
#define __dev_put(dev) atomic_dec(&(dev)->refcnt)
#define dev_hold(dev) atomic_inc(&(dev)->refcnt)
-/* Carrier loss detection, dial on demand. The functions netif_carrier_on
- * and _off may be called from IRQ context, but it is caller
+/* Carrier loss detection, dial on demand. The function
+ * netif_set_kernel_operstate may be called from IRQ context, but it is caller
* who is responsible for serialization of these calls.
*/
extern void linkwatch_fire_event(struct net_device *dev);
-static inline int netif_carrier_ok(const struct net_device *dev)
-{
- return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
-}
-
extern void __netdev_watchdog_up(struct net_device *dev);
-extern void netif_carrier_on(struct net_device *dev);
+extern void netif_set_kernel_operstate(struct net_device *dev, unsigned char newstate);
+
+static inline unsigned char netif_get_kernel_operstate(const struct net_device *dev) {
+ return dev->operstate_kernel;
+}
+
+/* Consolidated state. Should be called from process context */
+extern unsigned char netif_get_operstate(const struct net_device *dev);
-extern void netif_carrier_off(struct net_device *dev);
+/* Backward compatibility functions, use netif_set_kernel_operstate
+ instead */
+static inline void netif_carrier_on(struct net_device *dev) {
+ netif_set_kernel_operstate(dev, OPER_UP);
+}
+static inline void netif_carrier_off(struct net_device *dev) {
+ netif_set_kernel_operstate(dev, OPER_DOWN);
+}
+static inline int netif_carrier_ok(const struct net_device *dev) {
+ return (dev->operstate_kernel >= OPER_DORMANTL3UP ||
+ dev->operstate_kernel == OPER_UNKNOWN);
+}
/* Hot-plugging. */
static inline int netif_device_present(struct net_device *dev)
--- linux-2.6.14/net/core/link_watch.c.old 2005-06-17 21:48:29.000000000 +0200
+++ linux-2.6.14/net/core/link_watch.c 2005-11-06 18:06:14.000000000 +0100
@@ -75,7 +75,14 @@ void linkwatch_run_queue(void)
clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
if (dev->flags & IFF_UP) {
- if (netif_carrier_ok(dev)) {
+ if (netif_get_kernel_operstate(dev) < OPER_UP) {
+ if (dev->operstate_useroverride == OPERU_UP_UNTIL_DORMANTL3DOWN)
+ dev->operstate_useroverride = OPERU_DORMANTL3DOWN;
+ else if (dev->operstate_useroverride == OPERU_UP_UNTIL_DORMANTL3UP)
+ dev->operstate_useroverride = OPERU_DORMANTL3UP;
+ }
+
+ if (netif_get_operstate(dev) >= OPER_DORMANTL3DOWN) {
WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
dev_activate(dev);
} else
@@ -141,4 +148,32 @@ void linkwatch_fire_event(struct net_dev
}
}
-EXPORT_SYMBOL(linkwatch_fire_event);
+void netif_set_kernel_operstate(struct net_device *dev, unsigned char newstate) {
+ char oldstate = dev->operstate_kernel;
+ if (oldstate != newstate) {
+ dev->operstate_kernel = newstate;
+ wmb();
+ linkwatch_fire_event(dev);
+ }
+}
+
+unsigned char netif_get_operstate(const struct net_device *dev) {
+ unsigned char state = dev->operstate_kernel;
+ switch(dev->operstate_useroverride) {
+ case OPERU_IGNORE:
+ case OPERU_UP:
+ case OPERU_UP_UNTIL_DORMANTL3DOWN:
+ case OPERU_UP_UNTIL_DORMANTL3UP:
+ break;
+ case OPERU_DORMANTL3DOWN:
+ if (state > OPER_DORMANTL3DOWN) state = OPER_DORMANTL3DOWN;
+ break;
+ case OPERU_DORMANTL3UP:
+ if (state > OPER_DORMANTL3UP) state = OPER_DORMANTL3UP;
+ break;
+ }
+ return state;
+}
+
+EXPORT_SYMBOL(netif_set_kernel_operstate);
+EXPORT_SYMBOL(netif_get_operstate);
--- linux-2.6.14/net/core/dev.c.old 2005-11-06 17:35:22.000000000 +0100
+++ linux-2.6.14/net/core/dev.c 2005-11-06 17:36:41.000000000 +0100
@@ -2138,6 +2138,7 @@ void dev_set_allmulti(struct net_device
unsigned dev_get_flags(const struct net_device *dev)
{
unsigned flags;
+ const unsigned char state = netif_get_operstate(dev);
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
@@ -2145,7 +2146,8 @@ unsigned dev_get_flags(const struct net_
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
- if (netif_running(dev) && netif_carrier_ok(dev))
+ if (netif_running(dev) &&
+ (state == OPER_UNKNOWN || state >= OPER_DORMANTL3UP))
flags |= IFF_RUNNING;
return flags;
--- linux-2.6.14/net/sched/sch_generic.c.old 2005-11-02 11:08:12.000000000 +0100
+++ linux-2.6.14/net/sched/sch_generic.c 2005-11-06 17:13:53.000000000 +0100
@@ -238,19 +238,6 @@ static void dev_watchdog_down(struct net
spin_unlock_bh(&dev->xmit_lock);
}
-void netif_carrier_on(struct net_device *dev)
-{
- if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state))
- linkwatch_fire_event(dev);
- if (netif_running(dev))
- __netdev_watchdog_up(dev);
-}
-
-void netif_carrier_off(struct net_device *dev)
-{
- if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state))
- linkwatch_fire_event(dev);
-}
/* "NOOP" scheduler: the best scheduler, recommended for all interfaces
under all circumstances. It is difficult to invent anything faster or
@@ -614,8 +601,6 @@ void dev_shutdown(struct net_device *dev
}
EXPORT_SYMBOL(__netdev_watchdog_up);
-EXPORT_SYMBOL(netif_carrier_on);
-EXPORT_SYMBOL(netif_carrier_off);
EXPORT_SYMBOL(noop_qdisc);
EXPORT_SYMBOL(noop_qdisc_ops);
EXPORT_SYMBOL(qdisc_create_dflt);