[kernel/f18] Update team driver from net-next from Jiri Pirko

Justin M. Forbes jforbes at fedoraproject.org
Fri Mar 8 14:03:15 UTC 2013


commit 9436a43c32c6b35849d8e7a362886489705557a7
Author: Justin M. Forbes <jforbes at redhat.com>
Date:   Fri Mar 8 07:56:38 2013 -0600

    Update team driver from net-next from Jiri Pirko

 kernel.spec                         |    6 +
 team-net-next-update-20130307.patch |  608 +++++++++++++++++++++++++++++++++++
 2 files changed, 614 insertions(+), 0 deletions(-)
---
diff --git a/kernel.spec b/kernel.spec
index 423793c..d0780e6 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -711,6 +711,8 @@ Patch14000: hibernate-freeze-filesystems.patch
 
 Patch14010: lis3-improve-handling-of-null-rate.patch
 
+Patch14011: team-net-next-update-20130307.patch
+
 
 Patch20000: 0001-efifb-Skip-DMI-checks-if-the-bootloader-knows-what-i.patch
 Patch20001: 0002-x86-EFI-Calculate-the-EFI-framebuffer-size-instead-o.patch
@@ -1508,6 +1510,9 @@ ApplyPatch net-sctp-Validate-parameter-size-for-SCTP_GET_ASSOC_.patch
 #rhbz 917353
 ApplyPatch backlight_revert.patch -R
 
+#Team Driver update
+ApplyPatch team-net-next-update-20130307.patch
+
 # END OF PATCH APPLICATIONS
 
 %endif
@@ -2367,6 +2372,7 @@ fi
 %changelog
 * Fri Mar 08 2013 Justin M. Forbes <jforbes at redhat.com>
 - Revert "write backlight harder" until better solution is found (rhbz 917353)
+- Update team driver from net-next from Jiri Pirko
 
 * Fri Mar 08 2013 Josh Boyer <jwboyer at redhat.com>
 - CVE-2013-1828 sctp: SCTP_GET_ASSOC_STATS stack buffer overflow (rhbz 919315 919316)
diff --git a/team-net-next-update-20130307.patch b/team-net-next-update-20130307.patch
new file mode 100644
index 0000000..ebaa067
--- /dev/null
+++ b/team-net-next-update-20130307.patch
@@ -0,0 +1,608 @@
+Update team driver to 3.9-rc1.
+
+Split patches available here:
+http://people.redhat.com/jpirko/f18_team_update_4/
+
+Flavio Leitner (5):
+  team: implement carrier change
+  team: add ethtool support
+  team: update master carrier state
+  team: use strlcpy with ethtool_drvinfo fields
+  team: allow userspace to take control over carrier
+
+Jiri Pirko (5):
+  rtnl: expose carrier value with possibility to set it
+  net: add change_carrier netdev op
+  team: handle sending port list in the same way option list is sent
+  team: move netlink event notifiers after team_port_leave()
+  team: ab: set active port option as changed when port is leaving
+
+ Documentation/networking/operstates.txt   |   4 +
+ drivers/net/team/team.c                   | 246 +++++++++++++++++++-----------
+ drivers/net/team/team_mode_activebackup.c |  13 +-
+ include/linux/if_team.h                   |   1 +
+ include/linux/netdevice.h                 |  12 ++
+ include/uapi/linux/if_link.h              |   1 +
+ net/core/dev.c                            |  19 +++
+ net/core/rtnetlink.c                      |  10 ++
+ 8 files changed, 215 insertions(+), 91 deletions(-)
+
+Signed-off-by: Jiri Pirko <jpirko at redhat.com>
+
+diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt
+index 1a77a3c..9769457 100644
+--- a/Documentation/networking/operstates.txt
++++ b/Documentation/networking/operstates.txt
+@@ -88,6 +88,10 @@ set this flag. On netif_carrier_off(), the scheduler stops sending
+ packets. The name 'carrier' and the inversion are historical, think of
+ it as lower layer.
+ 
++Note that for certain kind of soft-devices, which are not managing any
++real hardware, there is possible to set this bit from userpsace.
++One should use TVL IFLA_CARRIER to do so.
++
+ netif_carrier_ok() can be used to query that bit.
+ 
+ __LINK_STATE_DORMANT, maps to IFF_DORMANT:
+diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
+index ad86660..9e68014 100644
+--- a/drivers/net/team/team.c
++++ b/drivers/net/team/team.c
+@@ -28,6 +28,7 @@
+ #include <net/genetlink.h>
+ #include <net/netlink.h>
+ #include <net/sch_generic.h>
++#include <generated/utsrelease.h>
+ #include <linux/if_team.h>
+ 
+ #define DRV_NAME "team"
+@@ -507,6 +508,7 @@ static bool team_is_mode_set(struct team *team)
+ 
+ static void team_set_no_mode(struct team *team)
+ {
++	team->user_carrier_enabled = false;
+ 	team->mode = &__team_no_mode;
+ }
+ 
+@@ -1129,10 +1131,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 		return -ENOENT;
+ 	}
+ 
+-	__team_option_inst_mark_removed_port(team, port);
+-	__team_options_change_check(team);
+-	__team_option_inst_del_port(team, port);
+-	__team_port_change_port_removed(port);
+ 	team_port_disable(team, port);
+ 	list_del_rcu(&port->list);
+ 	netdev_rx_handler_unregister(port_dev);
+@@ -1141,6 +1139,12 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
+ 	vlan_vids_del_by_dev(port_dev, dev);
+ 	dev_close(port_dev);
+ 	team_port_leave(team, port);
++
++	__team_option_inst_mark_removed_port(team, port);
++	__team_options_change_check(team);
++	__team_option_inst_del_port(team, port);
++	__team_port_change_port_removed(port);
++
+ 	team_port_set_orig_dev_addr(port);
+ 	dev_set_mtu(port_dev, port->orig.mtu);
+ 	synchronize_rcu();
+@@ -1399,13 +1403,11 @@ static void team_destructor(struct net_device *dev)
+ 
+ static int team_open(struct net_device *dev)
+ {
+-	netif_carrier_on(dev);
+ 	return 0;
+ }
+ 
+ static int team_close(struct net_device *dev)
+ {
+-	netif_carrier_off(dev);
+ 	return 0;
+ }
+ 
+@@ -1707,6 +1709,19 @@ static netdev_features_t team_fix_features(struct net_device *dev,
+ 	return features;
+ }
+ 
++static int team_change_carrier(struct net_device *dev, bool new_carrier)
++{
++	struct team *team = netdev_priv(dev);
++
++	team->user_carrier_enabled = true;
++
++	if (new_carrier)
++		netif_carrier_on(dev);
++	else
++		netif_carrier_off(dev);
++	return 0;
++}
++
+ static const struct net_device_ops team_netdev_ops = {
+ 	.ndo_init		= team_init,
+ 	.ndo_uninit		= team_uninit,
+@@ -1729,8 +1744,24 @@ static const struct net_device_ops team_netdev_ops = {
+ 	.ndo_add_slave		= team_add_slave,
+ 	.ndo_del_slave		= team_del_slave,
+ 	.ndo_fix_features	= team_fix_features,
++	.ndo_change_carrier     = team_change_carrier,
+ };
+ 
++/***********************
++ * ethtool interface
++ ***********************/
++
++static void team_ethtool_get_drvinfo(struct net_device *dev,
++				     struct ethtool_drvinfo *drvinfo)
++{
++	strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
++	strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
++}
++
++static const struct ethtool_ops team_ethtool_ops = {
++	.get_drvinfo		= team_ethtool_get_drvinfo,
++	.get_link		= ethtool_op_get_link,
++};
+ 
+ /***********************
+  * rt netlink interface
+@@ -1780,6 +1811,7 @@ static void team_setup(struct net_device *dev)
+ 	ether_setup(dev);
+ 
+ 	dev->netdev_ops = &team_netdev_ops;
++	dev->ethtool_ops = &team_ethtool_ops;
+ 	dev->destructor	= team_destructor;
+ 	dev->tx_queue_len = 0;
+ 	dev->flags |= IFF_MULTICAST;
+@@ -1941,30 +1973,6 @@ static void team_nl_team_put(struct team *team)
+ 	dev_put(team->dev);
+ }
+ 
+-static int team_nl_send_generic(struct genl_info *info, struct team *team,
+-				int (*fill_func)(struct sk_buff *skb,
+-						 struct genl_info *info,
+-						 int flags, struct team *team))
+-{
+-	struct sk_buff *skb;
+-	int err;
+-
+-	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+-	if (!skb)
+-		return -ENOMEM;
+-
+-	err = fill_func(skb, info, NLM_F_ACK, team);
+-	if (err < 0)
+-		goto err_fill;
+-
+-	err = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
+-	return err;
+-
+-err_fill:
+-	nlmsg_free(skb);
+-	return err;
+-}
+-
+ typedef int team_nl_send_func_t(struct sk_buff *skb,
+ 				struct team *team, u32 portid);
+ 
+@@ -2309,16 +2317,57 @@ team_put:
+ 	return err;
+ }
+ 
+-static int team_nl_fill_port_list_get(struct sk_buff *skb,
+-				      u32 portid, u32 seq, int flags,
+-				      struct team *team,
+-				      bool fillall)
++static int team_nl_fill_one_port_get(struct sk_buff *skb,
++				     struct team_port *port)
++{
++	struct nlattr *port_item;
++
++	port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
++	if (!port_item)
++		goto nest_cancel;
++	if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
++		goto nest_cancel;
++	if (port->changed) {
++		if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
++			goto nest_cancel;
++		port->changed = false;
++	}
++	if ((port->removed &&
++	     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
++	    (port->state.linkup &&
++	     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
++	    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
++	    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
++		goto nest_cancel;
++	nla_nest_end(skb, port_item);
++	return 0;
++
++nest_cancel:
++	nla_nest_cancel(skb, port_item);
++	return -EMSGSIZE;
++}
++
++static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
++				      int flags, team_nl_send_func_t *send_func,
++				      struct team_port *one_port)
+ {
+ 	struct nlattr *port_list;
++	struct nlmsghdr *nlh;
+ 	void *hdr;
+ 	struct team_port *port;
++	int err;
++	struct sk_buff *skb = NULL;
++	bool incomplete;
++	int i;
++
++	port = list_first_entry(&team->port_list, struct team_port, list);
+ 
+-	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags,
++start_again:
++	err = __send_and_alloc_skb(&skb, team, portid, send_func);
++	if (err)
++		return err;
++
++	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
+ 			  TEAM_CMD_PORT_LIST_GET);
+ 	if (!hdr)
+ 		return -EMSGSIZE;
+@@ -2329,47 +2378,54 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
+ 	if (!port_list)
+ 		goto nla_put_failure;
+ 
+-	list_for_each_entry(port, &team->port_list, list) {
+-		struct nlattr *port_item;
++	i = 0;
++	incomplete = false;
+ 
+-		/* Include only changed ports if fill all mode is not on */
+-		if (!fillall && !port->changed)
+-			continue;
+-		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
+-		if (!port_item)
+-			goto nla_put_failure;
+-		if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
+-			goto nla_put_failure;
+-		if (port->changed) {
+-			if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
+-				goto nla_put_failure;
+-			port->changed = false;
++	/* If one port is selected, called wants to send port list containing
++	 * only this port. Otherwise go through all listed ports and send all
++	 */
++	if (one_port) {
++		err = team_nl_fill_one_port_get(skb, one_port);
++		if (err)
++			goto errout;
++	} else {
++		list_for_each_entry(port, &team->port_list, list) {
++			err = team_nl_fill_one_port_get(skb, port);
++			if (err) {
++				if (err == -EMSGSIZE) {
++					if (!i)
++						goto errout;
++					incomplete = true;
++					break;
++				}
++				goto errout;
++			}
++			i++;
+ 		}
+-		if ((port->removed &&
+-		     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
+-		    (port->state.linkup &&
+-		     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
+-		    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
+-		    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
+-			goto nla_put_failure;
+-		nla_nest_end(skb, port_item);
+ 	}
+ 
+ 	nla_nest_end(skb, port_list);
+-	return genlmsg_end(skb, hdr);
++	genlmsg_end(skb, hdr);
++	if (incomplete)
++		goto start_again;
++
++send_done:
++	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
++	if (!nlh) {
++		err = __send_and_alloc_skb(&skb, team, portid, send_func);
++		if (err)
++			goto errout;
++		goto send_done;
++	}
++
++	return send_func(skb, team, portid);
+ 
+ nla_put_failure:
++	err = -EMSGSIZE;
++errout:
+ 	genlmsg_cancel(skb, hdr);
+-	return -EMSGSIZE;
+-}
+-
+-static int team_nl_fill_port_list_get_all(struct sk_buff *skb,
+-					  struct genl_info *info, int flags,
+-					  struct team *team)
+-{
+-	return team_nl_fill_port_list_get(skb, info->snd_portid,
+-					  info->snd_seq, NLM_F_ACK,
+-					  team, true);
++	nlmsg_free(skb);
++	return err;
+ }
+ 
+ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+@@ -2382,7 +2438,8 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+ 	if (!team)
+ 		return -EINVAL;
+ 
+-	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all);
++	err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
++					 NLM_F_ACK, team_nl_send_unicast, NULL);
+ 
+ 	team_nl_team_put(team);
+ 
+@@ -2433,27 +2490,11 @@ static int team_nl_send_event_options_get(struct team *team,
+ 					sel_opt_inst_list);
+ }
+ 
+-static int team_nl_send_event_port_list_get(struct team *team)
++static int team_nl_send_event_port_get(struct team *team,
++				       struct team_port *port)
+ {
+-	struct sk_buff *skb;
+-	int err;
+-	struct net *net = dev_net(team->dev);
+-
+-	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+-	if (!skb)
+-		return -ENOMEM;
+-
+-	err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false);
+-	if (err < 0)
+-		goto err_fill;
+-
+-	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
+-				      GFP_KERNEL);
+-	return err;
+-
+-err_fill:
+-	nlmsg_free(skb);
+-	return err;
++	return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
++					  port);
+ }
+ 
+ static int team_nl_init(void)
+@@ -2526,28 +2567,53 @@ static void __team_port_change_send(struct team_port *port, bool linkup)
+ 	port->state.duplex = 0;
+ 
+ send_event:
+-	err = team_nl_send_event_port_list_get(port->team);
++	err = team_nl_send_event_port_get(port->team, port);
+ 	if (err && err != -ESRCH)
+ 		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
+ 			    port->dev->name, err);
+ 
+ }
+ 
++static void __team_carrier_check(struct team *team)
++{
++	struct team_port *port;
++	bool team_linkup;
++
++	if (team->user_carrier_enabled)
++		return;
++
++	team_linkup = false;
++	list_for_each_entry(port, &team->port_list, list) {
++		if (port->linkup) {
++			team_linkup = true;
++			break;
++		}
++	}
++
++	if (team_linkup)
++		netif_carrier_on(team->dev);
++	else
++		netif_carrier_off(team->dev);
++}
++
+ static void __team_port_change_check(struct team_port *port, bool linkup)
+ {
+ 	if (port->state.linkup != linkup)
+ 		__team_port_change_send(port, linkup);
++	__team_carrier_check(port->team);
+ }
+ 
+ static void __team_port_change_port_added(struct team_port *port, bool linkup)
+ {
+ 	__team_port_change_send(port, linkup);
++	__team_carrier_check(port->team);
+ }
+ 
+ static void __team_port_change_port_removed(struct team_port *port)
+ {
+ 	port->removed = true;
+ 	__team_port_change_send(port, false);
++	__team_carrier_check(port->team);
+ }
+ 
+ static void team_port_change_check(struct team_port *port, bool linkup)
+diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
+index 6262b4d..40fd338 100644
+--- a/drivers/net/team/team_mode_activebackup.c
++++ b/drivers/net/team/team_mode_activebackup.c
+@@ -19,6 +19,7 @@
+ 
+ struct ab_priv {
+ 	struct team_port __rcu *active_port;
++	struct team_option_inst_info *ap_opt_inst_info;
+ };
+ 
+ static struct ab_priv *ab_priv(struct team *team)
+@@ -54,8 +55,17 @@ drop:
+ 
+ static void ab_port_leave(struct team *team, struct team_port *port)
+ {
+-	if (ab_priv(team)->active_port == port)
++	if (ab_priv(team)->active_port == port) {
+ 		RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
++		team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info);
++	}
++}
++
++static int ab_active_port_init(struct team *team,
++			       struct team_option_inst_info *info)
++{
++	ab_priv(team)->ap_opt_inst_info = info;
++	return 0;
+ }
+ 
+ static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)
+@@ -88,6 +98,7 @@ static const struct team_option ab_options[] = {
+ 	{
+ 		.name = "activeport",
+ 		.type = TEAM_OPTION_TYPE_U32,
++		.init = ab_active_port_init,
+ 		.getter = ab_active_port_get,
+ 		.setter = ab_active_port_set,
+ 	},
+diff --git a/include/linux/if_team.h b/include/linux/if_team.h
+index 0245def..4648d80 100644
+--- a/include/linux/if_team.h
++++ b/include/linux/if_team.h
+@@ -186,6 +186,7 @@ struct team {
+ 
+ 	const struct team_mode *mode;
+ 	struct team_mode_ops ops;
++	bool user_carrier_enabled;
+ 	bool queue_override_enabled;
+ 	struct list_head *qom_lists; /* array of queue override mapping lists */
+ 	long mode_priv[TEAM_MODE_PRIV_LONGS];
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 9ef07d0..7ebddc7 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -894,6 +894,14 @@ struct netdev_fcoe_hbainfo {
+  * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh)
+  * int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
+  *			     struct net_device *dev)
++ *
++ * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
++ *	Called to change device carrier. Soft-devices (like dummy, team, etc)
++ *	which do not represent real hardware may define this to allow their
++ *	userspace components to manage their virtual carrier state. Devices
++ *	that determine carrier state from physical hardware properties (eg
++ *	network cables) or protocol-dependent mechanisms (eg
++ *	USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function.
+  */
+ struct net_device_ops {
+ 	int			(*ndo_init)(struct net_device *dev);
+@@ -1011,6 +1019,8 @@ struct net_device_ops {
+ 	int			(*ndo_bridge_getlink)(struct sk_buff *skb,
+ 						      u32 pid, u32 seq,
+ 						      struct net_device *dev);
++	int			(*ndo_change_carrier)(struct net_device *dev,
++						      bool new_carrier);
+ };
+ 
+ /*
+@@ -2197,6 +2207,8 @@ extern int		dev_set_mtu(struct net_device *, int);
+ extern void		dev_set_group(struct net_device *, int);
+ extern int		dev_set_mac_address(struct net_device *,
+ 					    struct sockaddr *);
++extern int		dev_change_carrier(struct net_device *,
++					   bool new_carrier);
+ extern int		dev_hard_start_xmit(struct sk_buff *skb,
+ 					    struct net_device *dev,
+ 					    struct netdev_queue *txq);
+diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
+index 60f3b6b..c4edfe1 100644
+--- a/include/uapi/linux/if_link.h
++++ b/include/uapi/linux/if_link.h
+@@ -142,6 +142,7 @@ enum {
+ #define IFLA_PROMISCUITY IFLA_PROMISCUITY
+ 	IFLA_NUM_TX_QUEUES,
+ 	IFLA_NUM_RX_QUEUES,
++	IFLA_CARRIER,
+ 	__IFLA_MAX
+ };
+ 
+diff --git a/net/core/dev.c b/net/core/dev.c
+index f64e439..e0045ba 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -5027,6 +5027,25 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
+ }
+ EXPORT_SYMBOL(dev_set_mac_address);
+ 
++/**
++ *	dev_change_carrier - Change device carrier
++ *	@dev: device
++ *	@new_carries: new value
++ *
++ *	Change device carrier
++ */
++int dev_change_carrier(struct net_device *dev, bool new_carrier)
++{
++	const struct net_device_ops *ops = dev->netdev_ops;
++
++	if (!ops->ndo_change_carrier)
++		return -EOPNOTSUPP;
++	if (!netif_device_present(dev))
++		return -ENODEV;
++	return ops->ndo_change_carrier(dev, new_carrier);
++}
++EXPORT_SYMBOL(dev_change_carrier);
++
+ /*
+  *	Perform the SIOCxIFxxx calls, inside rcu_read_lock()
+  */
+diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
+index 1868625..2ef7a56 100644
+--- a/net/core/rtnetlink.c
++++ b/net/core/rtnetlink.c
+@@ -780,6 +780,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ 	       + nla_total_size(4) /* IFLA_MTU */
+ 	       + nla_total_size(4) /* IFLA_LINK */
+ 	       + nla_total_size(4) /* IFLA_MASTER */
++	       + nla_total_size(1) /* IFLA_CARRIER */
+ 	       + nla_total_size(4) /* IFLA_PROMISCUITY */
+ 	       + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */
+ 	       + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */
+@@ -909,6 +910,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
+ 	     nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
+ 	    (dev->master &&
+ 	     nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) ||
++	    nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
+ 	    (dev->qdisc &&
+ 	     nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
+ 	    (dev->ifalias &&
+@@ -1108,6 +1110,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
+ 	[IFLA_MTU]		= { .type = NLA_U32 },
+ 	[IFLA_LINK]		= { .type = NLA_U32 },
+ 	[IFLA_MASTER]		= { .type = NLA_U32 },
++	[IFLA_CARRIER]		= { .type = NLA_U8 },
+ 	[IFLA_TXQLEN]		= { .type = NLA_U32 },
+ 	[IFLA_WEIGHT]		= { .type = NLA_U32 },
+ 	[IFLA_OPERSTATE]	= { .type = NLA_U8 },
+@@ -1438,6 +1441,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
+ 		modified = 1;
+ 	}
+ 
++	if (tb[IFLA_CARRIER]) {
++		err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER]));
++		if (err)
++			goto errout;
++		modified = 1;
++	}
++
+ 	if (tb[IFLA_TXQLEN])
+ 		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+ 


More information about the scm-commits mailing list