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;