Am Montag 14 November 2005 14:57 schrieb jamal:

> My suggestion is at this point to ignore any L3 issues and have people
> post their patches. RFC 2863 states MUST be taken into consideration.
> Proper naming must be taken into account.

here we go. I've removed OPER_DORMANTL3* as requested and changed it to one 
dormant state. I've changed VLAN to support OPER_LOWERLAYERDOWN to show how 
the code can work with stacked interfaces. Locking fixes.

Main reasons why I chose this approach:

-kernel operstate is consistent when calling into linkwatch_fire_event()
-userspace override is independant, no different codepaths on wireless 
association with or without WPA enabled

Todo:

-userspace interface, netlink message/ioctl to read netif_get_operstate(), 
netif_get_operstate() for userspace supplicant, operstate_useroverride
-userspace interface, netlink message/ioctl to set operstate_useroverride, 
possibly with automatic reset to OPERU_IGNORE when application closes fd.

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-14 15:47:13.000000000 +0100
@@ -228,11 +228,31 @@ 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 from RFC2863 */
+enum netdev_operstate_t
+{
+	OPER_UNKNOWN = 0,
+	OPER_NOTPRESENT, /* unused, placeholder */
+	OPER_DOWN = 16,
+	OPER_LOWERLAYERDOWN,
+	OPER_TESTING = 32,
+	OPER_DORMANT = 48, /* OS queue start from here */
+	OPER_UP = 96
+};
+
+/* Userspace application may limit visible external state to
+ * DORMANT */
+enum netdev_operoverride_t {
+	OPERU_IGNORE, /* report state as defined by operstate_kernel */
+	OPERU_DORMANT, /* never report >= DORMANT */
+	OPERU_UP, /* report kernel state up to UP */
+	OPERU_UP_UNTIL_DORMANT /* change user override to OPERU_DORMANT on kernel <= OPER_DORMANT */
+};
 
 /*
  * This structure holds at boot time configured netdevice settings. They
@@ -346,7 +366,10 @@ struct net_device
 	struct net_device	*master; /* Pointer to master device of a group,
 					  * which this device is member of.
 					  */
-
+	/* Operation state */
+	int 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 +732,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_DORMANT ||
+		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-14 16:44:51.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 && 
+			    dev->operstate_useroverride == OPERU_UP_UNTIL_DORMANT) {
+				write_lock_bh(&dev_base_lock);
+				dev->operstate_useroverride = OPERU_DORMANT;
+				write_unlock_bh(&dev_base_lock);
+			}
+
+			if (netif_get_operstate(dev) >= OPER_DORMANT) {
 				WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
 				dev_activate(dev);
 			} else
@@ -141,4 +148,28 @@ 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;
+		smp_wmb();
+		linkwatch_fire_event(dev);
+	}
+}
+
+unsigned char netif_get_operstate(const struct net_device *dev) {
+	unsigned char state;
+	
+	if (likely(dev->flags & IFF_UP)) {
+		state = dev->operstate_kernel;
+		if (dev->operstate_useroverride == OPERU_DORMANT &&
+		    state > OPER_DORMANT)
+		    	state = OPER_DORMANT;
+	} else
+		state = OPER_DOWN;
+
+	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-14 16:39:37.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,7 @@ unsigned dev_get_flags(const struct net_
 		(dev->gflags & (IFF_PROMISC |
 				IFF_ALLMULTI));
 
-	if (netif_running(dev) && netif_carrier_ok(dev))
+	if (state == OPER_UNKNOWN || state >= OPER_DORMANT)
 		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);
--- linux-2.6.14/net/8021q/vlan.c.old	2005-11-02 11:07:35.000000000 +0100
+++ linux-2.6.14/net/8021q/vlan.c	2005-11-14 16:10:50.000000000 +0100
@@ -68,7 +68,7 @@ static struct packet_type vlan_packet_ty
 
 /* Bits of netdev state that are propagated from real device to virtual */
 #define VLAN_LINK_STATE_MASK \
-	((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER))
+	((1<<__LINK_STATE_PRESENT))
 
 /* End of global variables definitions. */
 
@@ -561,6 +561,9 @@ static int vlan_device_event(struct noti
 	struct vlan_group *grp = __vlan_find_group(dev->ifindex);
 	int i, flgs;
 	struct net_device *vlandev;
+	unsigned char ostate = netif_get_operstate(dev);
+
+	if (ostate == OPER_DOWN) ostate = OPER_LOWERLAYERDOWN;
 
 	if (!grp)
 		goto out;
@@ -578,13 +581,7 @@ static int vlan_device_event(struct noti
 			if (!vlandev)
 				continue;
 
-			if (netif_carrier_ok(dev)) {
-				if (!netif_carrier_ok(vlandev))
-					netif_carrier_on(vlandev);
-			} else {
-				if (netif_carrier_ok(vlandev))
-					netif_carrier_off(vlandev);
-			}
+			netif_set_kernel_operstate(vlandev, ostate);
 
 			if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
 				vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK) 
@@ -620,6 +617,7 @@ static int vlan_device_event(struct noti
 			if (flgs & IFF_UP)
 				continue;
 
+			netif_set_kernel_operstate(vlandev, ostate);
 			dev_change_flags(vlandev, flgs | IFF_UP);
 		}
 		break;

Reply via email to