[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