@@ -394,7 +394,7 @@ static void macvlan_transfer_operstate(struct net_device *dev)
struct macvlan_dev *vlan = netdev_priv(dev);
const struct net_device *lowerdev = vlan->lowerdev;
- if (lowerdev->operstate == IF_OPER_DORMANT)
+ if (netif_dormant(lowerdev) || netif_userspace_dormant(lowerdev))
netif_dormant_on(dev);
else
netif_dormant_off(dev);
@@ -277,6 +277,7 @@ enum netdev_state_t
__LINK_STATE_NOCARRIER,
__LINK_STATE_LINKWATCH_PENDING,
__LINK_STATE_DORMANT,
+ __LINK_STATE_USERSPACE_DORMANT,
};
@@ -582,8 +583,7 @@ struct net_device
unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */
unsigned short padded; /* How much padding added by alloc_netdev() */
- unsigned char operstate; /* RFC2863 operstate */
- unsigned char link_mode; /* mapping policy to operstate */
+ unsigned char link_mode; /* control dormant/up transition */
unsigned mtu; /* interface MTU value */
unsigned short type; /* interface hardware type */
@@ -1256,6 +1256,7 @@ extern void netif_nit_deliver(struct sk_buff *skb);
extern int dev_valid_name(const char *name);
extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
extern int dev_ethtool(struct net *net, struct ifreq *);
+extern unsigned char dev_get_operstate(const struct net_device *);
extern unsigned dev_get_flags(const struct net_device *);
extern int dev_change_flags(struct net_device *, unsigned);
extern int dev_change_name(struct net_device *, const char *);
@@ -1366,6 +1367,42 @@ static inline int netif_dormant(const struct net_device *dev)
return test_bit(__LINK_STATE_DORMANT, &dev->state);
}
+/**
+ * netif_userspace_dormant_on - mark device as dormant
+ * @dev: network device
+ *
+ * Mark device as dormant (as per RFC2863).
+ *
+ * User-space may have a device set to dormant until it performs some form
+ * of authentication or other initialization. This flag is only used for
+ * interacting with user-space. Drivers should not change this flag.
+ */
+static inline void netif_userspace_dormant_on(struct net_device *dev)
+{
+ set_bit(__LINK_STATE_USERSPACE_DORMANT, &dev->state);
+}
+
+/**
+ * netif_userspace_dormant_off - set device as not dormant.
+ * @dev: network device
+ *
+ * User-space has marked the device is not dormant.
+ */
+static inline void netif_userspace_dormant_off(struct net_device *dev)
+{
+ clear_bit(__LINK_STATE_USERSPACE_DORMANT, &dev->state);
+}
+
+/**
+ * netif_dormant - test if a device is dormant
+ * @dev: network device
+ *
+ * Check if device is marked as dormant by user-space.
+ */
+static inline int netif_userspace_dormant(const struct net_device *dev)
+{
+ return test_bit(__LINK_STATE_USERSPACE_DORMANT, &dev->state);
+}
/**
* netif_oper_up - test if device is operational
@@ -1374,8 +1411,8 @@ static inline int netif_dormant(const struct net_device *dev)
* Check if carrier is operational
*/
static inline int netif_oper_up(const struct net_device *dev) {
- return (dev->operstate == IF_OPER_UP ||
- dev->operstate == IF_OPER_UNKNOWN /* backward compat */);
+ return (netif_carrier_ok(dev) && !netif_dormant(dev) &&
+ !netif_userspace_dormant(dev));
}
/**
@@ -189,7 +189,7 @@ static void vlan_transfer_operstate(const struct net_device *dev,
* of real device, also must allow supplicant running
* on VLAN device
*/
- if (dev->operstate == IF_OPER_DORMANT)
+ if (netif_dormant(dev) || netif_userspace_dormant(dev))
netif_dormant_on(vlandev);
else
netif_dormant_off(vlandev);
@@ -39,7 +39,6 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
const struct net_device *dev = port->dev;
struct ifinfomsg *hdr;
struct nlmsghdr *nlh;
- u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
pr_debug("br_fill_info event %d port %s master %s\n",
event, dev->name, br->dev->name);
@@ -59,7 +58,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
NLA_PUT_U32(skb, IFLA_MASTER, br->dev->ifindex);
NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);
- NLA_PUT_U8(skb, IFLA_OPERSTATE, operstate);
+ NLA_PUT_U8(skb, IFLA_OPERSTATE, dev_get_operstate(dev));
if (dev->addr_len)
NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
@@ -3341,6 +3341,30 @@ static void dev_addr_discard(struct net_device *dev)
}
/**
+ * dev_get_operstate - get the state to report to userspace
+ * @dev: device
+ *
+ * Get the current interface state, as per RFC2863.
+ * See Documentation/networking/operstates.txt
+ */
+unsigned char dev_get_operstate(const struct net_device *dev)
+{
+ if (netif_running(dev)) {
+ if (!netif_carrier_ok(dev))
+ if (dev->ifindex != dev->iflink)
+ return IF_OPER_LOWERLAYERDOWN;
+ else
+ return IF_OPER_DOWN;
+ else if (netif_dormant(dev) || netif_userspace_dormant(dev))
+ return IF_OPER_DORMANT;
+ else
+ return IF_OPER_UP;
+ }
+ else
+ return IF_OPER_DOWN;
+}
+
+/**
* dev_get_flags - get flags reported to userspace
* @dev: device
*
@@ -4958,6 +4982,7 @@ EXPORT_SYMBOL(unregister_netdevice);
EXPORT_SYMBOL(unregister_netdevice_notifier);
EXPORT_SYMBOL(net_enable_timestamp);
EXPORT_SYMBOL(net_disable_timestamp);
+EXPORT_SYMBOL(dev_get_operstate);
EXPORT_SYMBOL(dev_get_flags);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
@@ -38,42 +38,14 @@ static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
static struct net_device *lweventlist;
static DEFINE_SPINLOCK(lweventlist_lock);
-static unsigned char default_operstate(const struct net_device *dev)
+/* Set the user-space dormant flag if link_mode calls for it so that
+ * the correct operstate is reported. */
+static void set_default_operstate(struct net_device *dev)
{
- if (!netif_carrier_ok(dev))
- return (dev->ifindex != dev->iflink ?
- IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
-
- if (netif_dormant(dev))
- return IF_OPER_DORMANT;
-
- return IF_OPER_UP;
-}
-
-
-static void rfc2863_policy(struct net_device *dev)
-{
- unsigned char operstate = default_operstate(dev);
-
- if (operstate == dev->operstate)
- return;
-
- write_lock_bh(&dev_base_lock);
-
- switch(dev->link_mode) {
- case IF_LINK_MODE_DORMANT:
- if (operstate == IF_OPER_UP)
- operstate = IF_OPER_DORMANT;
- break;
-
- case IF_LINK_MODE_DEFAULT:
- default:
- break;
- }
-
- dev->operstate = operstate;
-
- write_unlock_bh(&dev_base_lock);
+ if (dev->link_mode == IF_LINK_MODE_DORMANT && netif_oper_up(dev))
+ netif_userspace_dormant_on(dev);
+ else
+ netif_userspace_dormant_off(dev);
}
@@ -178,7 +150,8 @@ static void __linkwatch_run_queue(int urgent_only)
*/
clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
- rfc2863_policy(dev);
+ set_default_operstate(dev);
+
if (dev->flags & IFF_UP) {
if (netif_carrier_ok(dev))
dev_activate(dev);
@@ -156,9 +156,7 @@ static ssize_t show_operstate(struct device *dev,
unsigned char operstate;
read_lock(&dev_base_lock);
- operstate = netdev->operstate;
- if (!netif_running(netdev))
- operstate = IF_OPER_DOWN;
+ operstate = dev_get_operstate(netdev);
read_unlock(&dev_base_lock);
if (operstate >= ARRAY_SIZE(operstates))
@@ -525,29 +525,23 @@ EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
static void set_operstate(struct net_device *dev, unsigned char transition)
{
- unsigned char operstate = dev->operstate;
+ int oldstate = netif_userspace_dormant(dev);
+
+ if (!netif_oper_up(dev))
+ return;
switch(transition) {
case IF_OPER_UP:
- if ((operstate == IF_OPER_DORMANT ||
- operstate == IF_OPER_UNKNOWN) &&
- !netif_dormant(dev))
- operstate = IF_OPER_UP;
+ netif_userspace_dormant_off(dev);
break;
case IF_OPER_DORMANT:
- if (operstate == IF_OPER_UP ||
- operstate == IF_OPER_UNKNOWN)
- operstate = IF_OPER_DORMANT;
+ netif_userspace_dormant_on(dev);
break;
}
- if (dev->operstate != operstate) {
- write_lock_bh(&dev_base_lock);
- dev->operstate = operstate;
- write_unlock_bh(&dev_base_lock);
+ if (oldstate != netif_userspace_dormant(dev))
netdev_state_change(dev);
- }
}
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
@@ -626,8 +620,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
NLA_PUT_U32(skb, IFLA_TXQLEN, dev->tx_queue_len);
- NLA_PUT_U8(skb, IFLA_OPERSTATE,
- netif_running(dev) ? dev->operstate : IF_OPER_DOWN);
+ NLA_PUT_U8(skb, IFLA_OPERSTATE, dev_get_operstate(dev));
NLA_PUT_U8(skb, IFLA_LINKMODE, dev->link_mode);
NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);