From patchwork Sun Jul 22 08:17:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Aleksandrov X-Patchwork-Id: 947460 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=cumulusnetworks.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="DfL9yfCc"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41YHZ01k3Gz9s55 for ; Sun, 22 Jul 2018 18:18:16 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728204AbeGVJOF (ORCPT ); Sun, 22 Jul 2018 05:14:05 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:51461 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727865AbeGVJOE (ORCPT ); Sun, 22 Jul 2018 05:14:04 -0400 Received: by mail-wm0-f66.google.com with SMTP id h3-v6so12597454wmb.1 for ; Sun, 22 Jul 2018 01:18:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=oBM6zg5fXFcRQ5HFaX1H1DkgSWeWoN1HkkiyemVfP/E=; b=DfL9yfCcZU5iWHeyFNQ4CjXZSYWb8BXhAOMzk3IOZeCSeJPFI6TLEeDw8Wl9H+/HGG OP3VEk37iZ1aCkSJSjSsKBzSdM5j63ReakwE/IOx3ZF8naSqXigDRSS0O6eF7S0HbosG qrXbJy00BSP5wjjtzTGcc5WLQClnvO2kgUiMU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=oBM6zg5fXFcRQ5HFaX1H1DkgSWeWoN1HkkiyemVfP/E=; b=RqIENSWpHyJRQx7q8AVaNKtMmE7sPBuVDKCQuFkD/nre4v3FRW3MnwbtWvtQVne3W4 R7fGOb9l8bpHpIMXX5e1wb7vwERW8s0ykU9LvezQjKr3oW9uaqn/vzP9cXAcySvuHGHM HTzPKlM6/0gomGht96didH5tWDUEzGNNrXyzv7oJE53Nt0EDBulKEoy7rdjmum61Xe5Q tolfiXwYrtyS6Uxmp4BhuBDIscS0is5tmsITRUTgXoP/i+sMqd/CZt2GO79VHwuVyW6b 7INuRvKjd8Y3RYRGSlvdoKvFKQTfqKzu+0xda3OVNwOMiMl1jIdppmVaVOvFllO+DQ1N j/Qw== X-Gm-Message-State: AOUpUlHknhpkETVD+zH5ig/tM8oenAgtQelyZ2ZNus9rrFjZd9N0H3Fs UJg7n6d02Y11qfug8yNeyZj7yjovtMs= X-Google-Smtp-Source: AAOMgpceDuoYDgWwrV1NMVMons2iC31oNL3xIu6DjpltyTK3lipXT777mjgbYOmpEmy/qJCRnloZVA== X-Received: by 2002:a1c:20cb:: with SMTP id g194-v6mr5203653wmg.102.1532247490957; Sun, 22 Jul 2018 01:18:10 -0700 (PDT) Received: from localhost.localdomain (95-42-17-99.ip.btc-net.bg. [95.42.17.99]) by smtp.gmail.com with ESMTPSA id i15-v6sm635697wrw.75.2018.07.22.01.18.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Jul 2018 01:18:10 -0700 (PDT) From: Nikolay Aleksandrov To: netdev@vger.kernel.org Cc: roopa@cumulusnetworks.com, davem@davemloft.net, anuradhak@cumulusnetworks.com, wkok@cumulusnetworks.com, stephen@networkplumber.org, bridge@lists.linux-foundation.org, Nikolay Aleksandrov Subject: [PATCH net-next v2 1/2] net: bridge: add support for raw sysfs port options Date: Sun, 22 Jul 2018 11:17:44 +0300 Message-Id: <20180722081745.23272-2-nikolay@cumulusnetworks.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180722081745.23272-1-nikolay@cumulusnetworks.com> References: <20180722081745.23272-1-nikolay@cumulusnetworks.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds a new alternative store callback for port sysfs options which takes a raw value (buf) and can use it directly. It is needed for the backup port sysfs support since we have to pass the device by its name. Signed-off-by: Nikolay Aleksandrov --- There are a few checkpatch warnings here because of the old code, I've noted them in my todo and will send separate patch for simple_strtoul and the function prototypes. v2: Use kstrndup/kfree instead of casting the const buf. In order to avoid using GFP_ATOMIC or always allocating I kept the spinlock inside each branch. net/bridge/br_sysfs_if.c | 56 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index f99c5bf5c906..c4f5c83c92d2 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -25,6 +25,15 @@ struct brport_attribute { struct attribute attr; ssize_t (*show)(struct net_bridge_port *, char *); int (*store)(struct net_bridge_port *, unsigned long); + int (*store_raw)(struct net_bridge_port *, char *); +}; + +#define BRPORT_ATTR_RAW(_name, _mode, _show, _store) \ +const struct brport_attribute brport_attr_##_name = { \ + .attr = {.name = __stringify(_name), \ + .mode = _mode }, \ + .show = _show, \ + .store_raw = _store, \ }; #define BRPORT_ATTR(_name, _mode, _show, _store) \ @@ -270,27 +279,46 @@ static ssize_t brport_store(struct kobject *kobj, struct brport_attribute *brport_attr = to_brport_attr(attr); struct net_bridge_port *p = to_brport(kobj); ssize_t ret = -EINVAL; - char *endp; unsigned long val; + char *endp; if (!ns_capable(dev_net(p->dev)->user_ns, CAP_NET_ADMIN)) return -EPERM; - val = simple_strtoul(buf, &endp, 0); - if (endp != buf) { - if (!rtnl_trylock()) - return restart_syscall(); - if (p->dev && p->br && brport_attr->store) { - spin_lock_bh(&p->br->lock); - ret = brport_attr->store(p, val); - spin_unlock_bh(&p->br->lock); - if (!ret) { - br_ifinfo_notify(RTM_NEWLINK, NULL, p); - ret = count; - } + if (!rtnl_trylock()) + return restart_syscall(); + + if (!p->dev || !p->br) + goto out_unlock; + + if (brport_attr->store_raw) { + char *buf_copy; + + buf_copy = kstrndup(buf, count, GFP_KERNEL); + if (!buf_copy) { + ret = -ENOMEM; + goto out_unlock; } - rtnl_unlock(); + spin_lock_bh(&p->br->lock); + ret = brport_attr->store_raw(p, buf_copy); + spin_unlock_bh(&p->br->lock); + kfree(buf_copy); + } else if (brport_attr->store) { + val = simple_strtoul(buf, &endp, 0); + if (endp == buf) + goto out_unlock; + spin_lock_bh(&p->br->lock); + ret = brport_attr->store(p, val); + spin_unlock_bh(&p->br->lock); } + + if (!ret) { + br_ifinfo_notify(RTM_NEWLINK, NULL, p); + ret = count; + } +out_unlock: + rtnl_unlock(); + return ret; } From patchwork Sun Jul 22 08:17:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Aleksandrov X-Patchwork-Id: 947462 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=cumulusnetworks.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="GE0HuXUN"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41YHZ14D1Mz9s5H for ; Sun, 22 Jul 2018 18:18:17 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728301AbeGVJOH (ORCPT ); Sun, 22 Jul 2018 05:14:07 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:45245 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728078AbeGVJOG (ORCPT ); Sun, 22 Jul 2018 05:14:06 -0400 Received: by mail-wr1-f66.google.com with SMTP id t13-v6so5501577wrv.12 for ; Sun, 22 Jul 2018 01:18:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=S7TLZrLdiIuIMZufssb50xJ94H9seDeeKVcGggaKdkQ=; b=GE0HuXUNg/+LGrM3qCbPgHZJbKix/Ww2AJ0WR+m/v+SgrU8rBrT2g2Nvo3qN8joP/k 5FCMHweT2iIFYr9mo9ox9OUGGJemvkH36fK3cdE/xxyX2n+cQulCdYMAorSAhysNlyKc a2S87soUKdG3F3/uTVQDG3gjCyje1UEUguX8A= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=S7TLZrLdiIuIMZufssb50xJ94H9seDeeKVcGggaKdkQ=; b=BA9BOnzz3a+++dQtYzyB3Qd+woFKCBW6kkjjQn1QETHgR2ec9/WIfEEzXxXrjbyfd3 Mo2l87wL3frmaaEFW71RAy7nceeVFn307A/s2lBctNcFyt0iGb0CfmKWrnptdf+Yh5Fg ARJgf1N9QHWLHw+wd7f1Fa0OrrKS+fqcyCh0AhS6P9STdxxU34kDGLjBdqAwoTpmcu59 QsBOrvD10PM7D0EJ75R0WnTrEpH3LI56LkmsGI+qCPREFGXVc/7gxXLEc/+3o3WKtup5 s21dWIOunzBc45TPlQvc9ZgeE54BCYUhem9pxACwYEJuIB69LZtuzLfnRQZGhr9LbG4+ tcjg== X-Gm-Message-State: AOUpUlFB+7KDX8NcwleX5xQ7qQ3cYsBuxLfVJr53XdoW5WDNXMqzxHOn v4gW/kyosLxqNVC2uqRAzmAYX7md9x8= X-Google-Smtp-Source: AAOMgpdGUMjDGhoixjkPKNCuBj6WopPGr8c/OfmkLxfYhB+hL//iAE7IJTk1k8SmJBiLT1bDhsxg5Q== X-Received: by 2002:adf:8362:: with SMTP id 89-v6mr5713773wrd.147.1532247492397; Sun, 22 Jul 2018 01:18:12 -0700 (PDT) Received: from localhost.localdomain (95-42-17-99.ip.btc-net.bg. [95.42.17.99]) by smtp.gmail.com with ESMTPSA id i15-v6sm635697wrw.75.2018.07.22.01.18.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Jul 2018 01:18:11 -0700 (PDT) From: Nikolay Aleksandrov To: netdev@vger.kernel.org Cc: roopa@cumulusnetworks.com, davem@davemloft.net, anuradhak@cumulusnetworks.com, wkok@cumulusnetworks.com, stephen@networkplumber.org, bridge@lists.linux-foundation.org, Nikolay Aleksandrov Subject: [PATCH net-next v2 2/2] net: bridge: add support for backup port Date: Sun, 22 Jul 2018 11:17:45 +0300 Message-Id: <20180722081745.23272-3-nikolay@cumulusnetworks.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180722081745.23272-1-nikolay@cumulusnetworks.com> References: <20180722081745.23272-1-nikolay@cumulusnetworks.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds a new port attribute - IFLA_BRPORT_BACKUP_PORT, which allows to set a backup port to be used for known unicast traffic if the port has gone carrier down. The backup pointer is rcu protected and set only under RTNL, a counter is maintained so when deleting a port we know how many other ports reference it as a backup and we remove it from all. Also the pointer is in the first cache line which is hot at the time of the check and thus in the common case we only add one more test. The backup port will be used only for the non-flooding case since it's a part of the bridge and the flooded packets will be forwarded to it anyway. To remove the forwarding just send a 0/non-existing backup port. This is used to avoid numerous scalability problems when using MLAG most notably if we have thousands of fdbs one would need to change all of them on port carrier going down which takes too long and causes a storm of fdb notifications (and again when the port comes back up). In a Multi-chassis Link Aggregation setup usually hosts are connected to two different switches which act as a single logical switch. Those switches usually have a control and backup link between them called peerlink which might be used for communication in case a host loses connectivity to one of them. We need a fast way to failover in case a host port goes down and currently none of the solutions (like bond) cannot fulfill the requirements because the participating ports are actually the "master" devices and must have the same peerlink as their backup interface and at the same time all of them must participate in the bridge device. As Roopa noted it's normal practice in routing called fast re-route where a precalculated backup path is used when the main one is down. Another use case of this is with EVPN, having a single vxlan device which is backup of every port. Due to the nature of master devices it's not currently possible to use one device as a backup for many and still have all of them participate in the bridge (which is master itself). More detailed information about MLAG is available at the link below. https://docs.cumulusnetworks.com/display/DOCS/Multi-Chassis+Link+Aggregation+-+MLAG Signed-off-by: Nikolay Aleksandrov --- include/uapi/linux/if_link.h | 1 + net/bridge/br_forward.c | 16 ++++++++++++- net/bridge/br_if.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_netlink.c | 30 ++++++++++++++++++++++++- net/bridge/br_private.h | 3 +++ net/bridge/br_sysfs_if.c | 33 +++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 8759cfb8aa2e..01b5069a73a5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -334,6 +334,7 @@ enum { IFLA_BRPORT_GROUP_FWD_MASK, IFLA_BRPORT_NEIGH_SUPPRESS, IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 9019f326fe81..5372e2042adf 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -142,7 +142,20 @@ static int deliver_clone(const struct net_bridge_port *prev, void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, bool local_rcv, bool local_orig) { - if (to && should_deliver(to, skb)) { + if (unlikely(!to)) + goto out; + + /* redirect to backup link if the destination port is down */ + if (rcu_access_pointer(to->backup_port) && !netif_carrier_ok(to->dev)) { + struct net_bridge_port *backup_port; + + backup_port = rcu_dereference(to->backup_port); + if (unlikely(!backup_port)) + goto out; + to = backup_port; + } + + if (should_deliver(to, skb)) { if (local_rcv) deliver_clone(to, skb, local_orig); else @@ -150,6 +163,7 @@ void br_forward(const struct net_bridge_port *to, return; } +out: if (!local_rcv) kfree_skb(skb); } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 05e42d86882d..365759ac7c73 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -169,6 +169,58 @@ void br_manage_promisc(struct net_bridge *br) } } +int nbp_backup_change(struct net_bridge_port *p, + struct net_device *backup_dev) +{ + struct net_bridge_port *old_backup = rtnl_dereference(p->backup_port); + struct net_bridge_port *backup_p = NULL; + + ASSERT_RTNL(); + + if (backup_dev) { + if (!br_port_exists(backup_dev)) + return -ENOENT; + + backup_p = br_port_get_rtnl(backup_dev); + if (backup_p->br != p->br) + return -EINVAL; + } + + if (p == backup_p) + return -EINVAL; + + if (old_backup == backup_p) + return 0; + + /* if the backup link is already set, clear it */ + if (old_backup) + old_backup->backup_redirected_cnt--; + + if (backup_p) + backup_p->backup_redirected_cnt++; + rcu_assign_pointer(p->backup_port, backup_p); + + return 0; +} + +static void nbp_backup_clear(struct net_bridge_port *p) +{ + nbp_backup_change(p, NULL); + if (p->backup_redirected_cnt) { + struct net_bridge_port *cur_p; + + list_for_each_entry(cur_p, &p->br->port_list, list) { + struct net_bridge_port *backup_p; + + backup_p = rtnl_dereference(cur_p->backup_port); + if (backup_p == p) + nbp_backup_change(cur_p, NULL); + } + } + + WARN_ON(rcu_access_pointer(p->backup_port) || p->backup_redirected_cnt); +} + static void nbp_update_port_count(struct net_bridge *br) { struct net_bridge_port *p; @@ -286,6 +338,7 @@ static void del_nbp(struct net_bridge_port *p) nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 0, 1); switchdev_deferred_process(); + nbp_backup_clear(p); nbp_update_port_count(br); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 9f5eb05b0373..ec2b58a09f76 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -169,13 +169,15 @@ static inline size_t br_nlmsg_size(struct net_device *dev, u32 filter_mask) + nla_total_size(1) /* IFLA_OPERSTATE */ + nla_total_size(br_port_info_size()) /* IFLA_PROTINFO */ + nla_total_size(br_get_link_af_size_filtered(dev, - filter_mask)); /* IFLA_AF_SPEC */ + filter_mask)) /* IFLA_AF_SPEC */ + + nla_total_size(4); /* IFLA_BRPORT_BACKUP_PORT */ } static int br_port_fill_attrs(struct sk_buff *skb, const struct net_bridge_port *p) { u8 mode = !!(p->flags & BR_HAIRPIN_MODE); + struct net_bridge_port *backup_p; u64 timerval; if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) || @@ -237,6 +239,14 @@ static int br_port_fill_attrs(struct sk_buff *skb, return -EMSGSIZE; #endif + /* we might be called only with br->lock */ + rcu_read_lock(); + backup_p = rcu_dereference(p->backup_port); + if (backup_p) + nla_put_u32(skb, IFLA_BRPORT_BACKUP_PORT, + backup_p->dev->ifindex); + rcu_read_unlock(); + return 0; } @@ -663,6 +673,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NLA_U16 }, [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 }, [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, + [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, }; /* Change the state of the port and notify spanning tree */ @@ -817,6 +828,23 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) if (err) return err; + if (tb[IFLA_BRPORT_BACKUP_PORT]) { + struct net_device *backup_dev = NULL; + u32 backup_ifindex; + + backup_ifindex = nla_get_u32(tb[IFLA_BRPORT_BACKUP_PORT]); + if (backup_ifindex) { + backup_dev = __dev_get_by_index(dev_net(p->dev), + backup_ifindex); + if (!backup_dev) + return -ENOENT; + } + + err = nbp_backup_change(p, backup_dev); + if (err) + return err; + } + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 5216a524b537..ab7db2466c43 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -237,6 +237,7 @@ struct net_bridge_port { #ifdef CONFIG_BRIDGE_VLAN_FILTERING struct net_bridge_vlan_group __rcu *vlgrp; #endif + struct net_bridge_port __rcu *backup_port; /* STP */ u8 priority; @@ -281,6 +282,7 @@ struct net_bridge_port { int offload_fwd_mark; #endif u16 group_fwd_mask; + u16 backup_redirected_cnt; }; #define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) @@ -595,6 +597,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); void br_manage_promisc(struct net_bridge *br); +int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); /* br_input.c */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index c4f5c83c92d2..91b88df20019 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -191,6 +191,38 @@ static int store_group_fwd_mask(struct net_bridge_port *p, static BRPORT_ATTR(group_fwd_mask, 0644, show_group_fwd_mask, store_group_fwd_mask); +static ssize_t show_backup_port(struct net_bridge_port *p, char *buf) +{ + struct net_bridge_port *backup_p; + int ret = 0; + + rcu_read_lock(); + backup_p = rcu_dereference(p->backup_port); + if (backup_p) + ret = sprintf(buf, "%s\n", backup_p->dev->name); + rcu_read_unlock(); + + return ret; +} + +static int store_backup_port(struct net_bridge_port *p, char *buf) +{ + struct net_device *backup_dev = NULL; + char *nl = strchr(buf, '\n'); + + if (nl) + *nl = '\0'; + + if (strlen(buf) > 0) { + backup_dev = __dev_get_by_name(dev_net(p->dev), buf); + if (!backup_dev) + return -ENOENT; + } + + return nbp_backup_change(p, backup_dev); +} +static BRPORT_ATTR_RAW(backup_port, 0644, show_backup_port, store_backup_port); + BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); @@ -254,6 +286,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_group_fwd_mask, &brport_attr_neigh_suppress, &brport_attr_isolated, + &brport_attr_backup_port, NULL };