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;