@@ -527,7 +527,7 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
{
int oldstate = netif_userspace_dormant(dev);
- if (!netif_oper_up(dev))
+ if (!netif_carrier_ok(dev) || netif_dormant(dev))
return;
switch(transition) {
From: Michael Marineau <mike@marineau.org>
Date: Tue, 6 Jan 2009 00:48:02 -0500
Subject: [PATCH] Fold net_device.operstate into net_device.state
The variable operstate in the net_device structure typically held
nothing beyond what was already in the normal state flags (carrier_ok,
etc). operstate's main purpose is to allow user space to set a network
interface to a dormant state. Keeping operstate and state in sync is a
bit awkward so this patch removes operstate and replaces it by using the
state flags instead. Setting the interface to dormant from user-space is
now handled by netif_userspace_dormant to parallel the netif_dormant set
of functions that drivers use.
This resolves a regression that was introduced by commit
b47300168e770b60ab96c8924854c3 which prevented operstate from being kept
in sync with state before the device is registered. This caused drivers
that set things like the carrier state before registering to mis-report
their initial state, breaking NetworkManager, dhcpcd, etc.
Signed-off-by: Michael Marineau <mike@marineau.org>
---
drivers/net/macvlan.c | 2 +-
include/linux/netdevice.h | 45 +++++++++++++++++++++++++++++++++++++++++----
net/8021q/vlan.c | 2 +-
net/bridge/br_netlink.c | 3 +--
net/core/dev.c | 25 +++++++++++++++++++++++++
net/core/link_watch.c | 45 +++++++++------------------------------------
net/core/net-sysfs.c | 4 +---
net/core/rtnetlink.c | 23 ++++++++---------------
8 files changed, 87 insertions(+), 62 deletions(-)
@@ -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_carrier_ok(dev) || netif_dormant(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);