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);

Reply via email to