Commit fe3ef616 authored by David S. Miller's avatar David S. Miller

Merge branch 'switchdev_offload_flags'

Roopa Prabhu says:

====================
switchdev offload flags

This patch series introduces new offload flags for switchdev.
Kernel network subsystems can use this flag to accelerate
network functions by offloading to hw.

I expect that there will be need for subsystem specific feature
flag in the future.

This patch series currently only addresses bridge driver link
attribute offloads to hardware.

Looking at the current state of bridge l2 offload in the kernel,
    - flag 'self' is the way to directly manage the bridge device in hw via
      the ndo_bridge_setlink/ndo_bridge_getlink calls

    - flag 'master' is always used to manage the in kernel bridge devices
      via the same ndo_bridge_setlink/ndo_bridge_getlink calls

Today these are used separately. The nic offloads use hwmode "vepa/veb" to go
directly to hw with the "self" flag.

At this point i am trying not to introduce any new user facing flags/attributes.
In the model where we want the kernel bridging to be accelerated with
hardware, we very much want the bridge driver to be involved.

In this proposal,
- The offload flag/bit helps switch asic drivers to indicate that they
  accelerate the kernel networking objects/functions
- The user does not have to specify a new flag to do so. A bridge created with
  switch asic ports will be accelerated if the switch driver supports it.
- The user can continue to directly manage l2 in nics (ixgbe) using the
  existing hwmode/self flags
- It also does not stop users from using the 'self' flag to talk to the
  switch asic driver directly
- Involving the bridge driver makes sure the add/del notifications to user
  space go out after both kernel and hardware are programmed

(To selectively offload bridge port attributes,
example learning in hw only etc, we can introduce offload bits for
per bridge port flag attribute as in my previous patch
https://patchwork.ozlabs.org/patch/413211/. I have not included that in this
series)

v2
   - try a different name for the offload flag/bit
   - tries to solve the stacked netdev case by traversing the lowerdev
     list to reach the switch port

v3 -
    - Tested with bond as bridge port for the stacked device case.
      Includes a bond_fix_features change to not ignore the
      NETIF_F_HW_NETFUNC_OFFLOAD flag
    - Some checkpatch fixes

v4 -
    - rename flag to NETIF_F_HW_SWITCH_OFFLOAD
    - add ndo_bridge_setlink/dellink handlers in bond and team drivers as
      suggested by jiri.
    - introduce default ndo_dflt_netdev_switch_port_bridge_setlink/dellink
    handlers that masters can use to call offload api on lowerdevs.
====================
Signed-off-by: default avatarRoopa Prabhu <roopa@cumulusnetworks.com>
parents b2dec116 a16a8ee7
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <net/flow_keys.h> #include <net/flow_keys.h>
#include <net/switchdev.h>
#include <net/bonding.h> #include <net/bonding.h>
#include <net/bond_3ad.h> #include <net/bond_3ad.h>
#include <net/bond_alb.h> #include <net/bond_alb.h>
...@@ -979,7 +980,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev, ...@@ -979,7 +980,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
netdev_features_t mask; netdev_features_t mask;
struct slave *slave; struct slave *slave;
mask = features; /* If any slave has the offload feature flag set,
* set the offload flag on the bond.
*/
mask = features | NETIF_F_HW_SWITCH_OFFLOAD;
features &= ~NETIF_F_ONE_FOR_ALL; features &= ~NETIF_F_ONE_FOR_ALL;
features |= NETIF_F_ALL_FOR_ALL; features |= NETIF_F_ALL_FOR_ALL;
...@@ -3952,6 +3957,8 @@ static const struct net_device_ops bond_netdev_ops = { ...@@ -3952,6 +3957,8 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_add_slave = bond_enslave, .ndo_add_slave = bond_enslave,
.ndo_del_slave = bond_release, .ndo_del_slave = bond_release,
.ndo_fix_features = bond_fix_features, .ndo_fix_features = bond_fix_features,
.ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink,
.ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink,
}; };
static const struct device_type bond_type = { static const struct device_type bond_type = {
......
...@@ -4327,7 +4327,8 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file) ...@@ -4327,7 +4327,8 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file)
return status; return status;
} }
static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh) static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
u16 flags)
{ {
struct be_adapter *adapter = netdev_priv(dev); struct be_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec; struct nlattr *attr, *br_spec;
......
...@@ -7786,7 +7786,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -7786,7 +7786,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
} }
static int ixgbe_ndo_bridge_setlink(struct net_device *dev, static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh) struct nlmsghdr *nlh, u16 flags)
{ {
struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec; struct nlattr *attr, *br_spec;
......
...@@ -3722,7 +3722,7 @@ static int rocker_port_fdb_dump(struct sk_buff *skb, ...@@ -3722,7 +3722,7 @@ static int rocker_port_fdb_dump(struct sk_buff *skb,
} }
static int rocker_port_bridge_setlink(struct net_device *dev, static int rocker_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh) struct nlmsghdr *nlh, u16 flags)
{ {
struct rocker_port *rocker_port = netdev_priv(dev); struct rocker_port *rocker_port = netdev_priv(dev);
struct nlattr *protinfo; struct nlattr *protinfo;
...@@ -4030,7 +4030,8 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) ...@@ -4030,7 +4030,8 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
NAPI_POLL_WEIGHT); NAPI_POLL_WEIGHT);
rocker_carrier_init(rocker_port); rocker_carrier_init(rocker_port);
dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_SWITCH_OFFLOAD;
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/sch_generic.h> #include <net/sch_generic.h>
#include <net/switchdev.h>
#include <generated/utsrelease.h> #include <generated/utsrelease.h>
#include <linux/if_team.h> #include <linux/if_team.h>
...@@ -1925,7 +1926,7 @@ static netdev_features_t team_fix_features(struct net_device *dev, ...@@ -1925,7 +1926,7 @@ static netdev_features_t team_fix_features(struct net_device *dev,
struct team *team = netdev_priv(dev); struct team *team = netdev_priv(dev);
netdev_features_t mask; netdev_features_t mask;
mask = features; mask = features | NETIF_F_HW_SWITCH_OFFLOAD;
features &= ~NETIF_F_ONE_FOR_ALL; features &= ~NETIF_F_ONE_FOR_ALL;
features |= NETIF_F_ALL_FOR_ALL; features |= NETIF_F_ALL_FOR_ALL;
...@@ -1975,6 +1976,8 @@ static const struct net_device_ops team_netdev_ops = { ...@@ -1975,6 +1976,8 @@ static const struct net_device_ops team_netdev_ops = {
.ndo_del_slave = team_del_slave, .ndo_del_slave = team_del_slave,
.ndo_fix_features = team_fix_features, .ndo_fix_features = team_fix_features,
.ndo_change_carrier = team_change_carrier, .ndo_change_carrier = team_change_carrier,
.ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink,
.ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink,
}; };
/*********************** /***********************
......
...@@ -66,6 +66,7 @@ enum { ...@@ -66,6 +66,7 @@ enum {
NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */ NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
NETIF_F_HW_L2FW_DOFFLOAD_BIT, /* Allow L2 Forwarding in Hardware */ NETIF_F_HW_L2FW_DOFFLOAD_BIT, /* Allow L2 Forwarding in Hardware */
NETIF_F_BUSY_POLL_BIT, /* Busy poll */ NETIF_F_BUSY_POLL_BIT, /* Busy poll */
NETIF_F_HW_SWITCH_OFFLOAD_BIT, /* HW switch offload */
/* /*
* Add your fresh new feature above and remember to update * Add your fresh new feature above and remember to update
...@@ -124,6 +125,7 @@ enum { ...@@ -124,6 +125,7 @@ enum {
#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
#define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD) #define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD)
#define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL) #define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL)
#define NETIF_F_HW_SWITCH_OFFLOAD __NETIF_F(HW_SWITCH_OFFLOAD)
/* Features valid for ethtool to change */ /* Features valid for ethtool to change */
/* = all defined minus driver/device-class-related */ /* = all defined minus driver/device-class-related */
...@@ -159,7 +161,9 @@ enum { ...@@ -159,7 +161,9 @@ enum {
*/ */
#define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \ #define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \
NETIF_F_SG | NETIF_F_HIGHDMA | \ NETIF_F_SG | NETIF_F_HIGHDMA | \
NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED) NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED | \
NETIF_F_HW_SWITCH_OFFLOAD)
/* /*
* If one device doesn't support one of these features, then disable it * If one device doesn't support one of these features, then disable it
* for all in netdev_increment_features. * for all in netdev_increment_features.
......
...@@ -1154,13 +1154,15 @@ struct net_device_ops { ...@@ -1154,13 +1154,15 @@ struct net_device_ops {
int idx); int idx);
int (*ndo_bridge_setlink)(struct net_device *dev, int (*ndo_bridge_setlink)(struct net_device *dev,
struct nlmsghdr *nlh); struct nlmsghdr *nlh,
u16 flags);
int (*ndo_bridge_getlink)(struct sk_buff *skb, int (*ndo_bridge_getlink)(struct sk_buff *skb,
u32 pid, u32 seq, u32 pid, u32 seq,
struct net_device *dev, struct net_device *dev,
u32 filter_mask); u32 filter_mask);
int (*ndo_bridge_dellink)(struct net_device *dev, int (*ndo_bridge_dellink)(struct net_device *dev,
struct nlmsghdr *nlh); struct nlmsghdr *nlh,
u16 flags);
int (*ndo_change_carrier)(struct net_device *dev, int (*ndo_change_carrier)(struct net_device *dev,
bool new_carrier); bool new_carrier);
int (*ndo_get_phys_port_id)(struct net_device *dev, int (*ndo_get_phys_port_id)(struct net_device *dev,
......
...@@ -43,7 +43,14 @@ int register_netdev_switch_notifier(struct notifier_block *nb); ...@@ -43,7 +43,14 @@ int register_netdev_switch_notifier(struct notifier_block *nb);
int unregister_netdev_switch_notifier(struct notifier_block *nb); int unregister_netdev_switch_notifier(struct notifier_block *nb);
int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev, int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
struct netdev_switch_notifier_info *info); struct netdev_switch_notifier_info *info);
int netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
int netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags);
#else #else
static inline int netdev_switch_parent_id_get(struct net_device *dev, static inline int netdev_switch_parent_id_get(struct net_device *dev,
...@@ -74,6 +81,34 @@ static inline int call_netdev_switch_notifiers(unsigned long val, struct net_dev ...@@ -74,6 +81,34 @@ static inline int call_netdev_switch_notifiers(unsigned long val, struct net_dev
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static inline int netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return -EOPNOTSUPP;
}
static inline int netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return -EOPNOTSUPP;
}
static inline int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return 0;
}
static inline int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
{
return 0;
}
#endif #endif
#endif /* _LINUX_SWITCHDEV_H_ */ #endif /* _LINUX_SWITCHDEV_H_ */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/switchdev.h>
#include <uapi/linux/if_bridge.h> #include <uapi/linux/if_bridge.h>
#include "br_private.h" #include "br_private.h"
...@@ -494,13 +495,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) ...@@ -494,13 +495,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
} }
/* Change state and parameters on port. */ /* Change state and parameters on port. */
int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
{ {
struct nlattr *protinfo; struct nlattr *protinfo;
struct nlattr *afspec; struct nlattr *afspec;
struct net_bridge_port *p; struct net_bridge_port *p;
struct nlattr *tb[IFLA_BRPORT_MAX + 1]; struct nlattr *tb[IFLA_BRPORT_MAX + 1];
int err = 0; int err = 0, ret_offload = 0;
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO); protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
...@@ -542,19 +543,28 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) ...@@ -542,19 +543,28 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
afspec, RTM_SETLINK); afspec, RTM_SETLINK);
} }
if (!(flags & BRIDGE_FLAGS_SELF)) {
/* set bridge attributes in hardware if supported
*/
ret_offload = netdev_switch_port_bridge_setlink(dev, nlh,
flags);
if (ret_offload && ret_offload != -EOPNOTSUPP)
br_warn(p->br, "error setting attrs on port %u(%s)\n",
(unsigned int)p->port_no, p->dev->name);
}
if (err == 0) if (err == 0)
br_ifinfo_notify(RTM_NEWLINK, p); br_ifinfo_notify(RTM_NEWLINK, p);
out: out:
return err; return err;
} }
/* Delete port information */ /* Delete port information */
int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
{ {
struct nlattr *afspec; struct nlattr *afspec;
struct net_bridge_port *p; struct net_bridge_port *p;
int err; int err = 0, ret_offload = 0;
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
if (!afspec) if (!afspec)
...@@ -573,6 +583,16 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) ...@@ -573,6 +583,16 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
*/ */
br_ifinfo_notify(RTM_NEWLINK, p); br_ifinfo_notify(RTM_NEWLINK, p);
if (!(flags & BRIDGE_FLAGS_SELF)) {
/* del bridge attributes in hardware
*/
ret_offload = netdev_switch_port_bridge_dellink(dev, nlh,
flags);
if (ret_offload && ret_offload != -EOPNOTSUPP)
br_warn(p->br, "error deleting attrs on port %u (%s)\n",
(unsigned int)p->port_no, p->dev->name);
}
return err; return err;
} }
static int br_validate(struct nlattr *tb[], struct nlattr *data[]) static int br_validate(struct nlattr *tb[], struct nlattr *data[])
......
...@@ -819,8 +819,8 @@ extern struct rtnl_link_ops br_link_ops; ...@@ -819,8 +819,8 @@ extern struct rtnl_link_ops br_link_ops;
int br_netlink_init(void); int br_netlink_init(void);
void br_netlink_fini(void); void br_netlink_fini(void);
void br_ifinfo_notify(int event, struct net_bridge_port *port); void br_ifinfo_notify(int event, struct net_bridge_port *port);
int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
u32 filter_mask); u32 filter_mask);
......
...@@ -2991,7 +2991,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -2991,7 +2991,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
goto out; goto out;
} }
err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh); err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags);
if (err) if (err)
goto out; goto out;
...@@ -3002,7 +3002,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -3002,7 +3002,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!dev->netdev_ops->ndo_bridge_setlink) if (!dev->netdev_ops->ndo_bridge_setlink)
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
else else
err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh,
flags);
if (!err) { if (!err) {
flags &= ~BRIDGE_FLAGS_SELF; flags &= ~BRIDGE_FLAGS_SELF;
...@@ -3064,7 +3065,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -3064,7 +3065,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
goto out; goto out;
} }
err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh); err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh, flags);
if (err) if (err)
goto out; goto out;
...@@ -3075,7 +3076,8 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -3075,7 +3076,8 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!dev->netdev_ops->ndo_bridge_dellink) if (!dev->netdev_ops->ndo_bridge_dellink)
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
else else
err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh); err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh,
flags);
if (!err) { if (!err) {
flags &= ~BRIDGE_FLAGS_SELF; flags &= ~BRIDGE_FLAGS_SELF;
......
...@@ -115,3 +115,113 @@ int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev, ...@@ -115,3 +115,113 @@ int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
return err; return err;
} }
EXPORT_SYMBOL(call_netdev_switch_notifiers); EXPORT_SYMBOL(call_netdev_switch_notifiers);
/**
* netdev_switch_port_bridge_setlink - Notify switch device port of bridge
* port attributes
*
* @dev: port device
* @nlh: netlink msg with bridge port attributes
* @flags: bridge setlink flags
*
* Notify switch device port of bridge port attributes
*/
int netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
return 0;
if (!ops->ndo_bridge_setlink)
return -EOPNOTSUPP;
return ops->ndo_bridge_setlink(dev, nlh, flags);
}
EXPORT_SYMBOL(netdev_switch_port_bridge_setlink);
/**
* netdev_switch_port_bridge_dellink - Notify switch device port of bridge
* port attribute delete
*
* @dev: port device
* @nlh: netlink msg with bridge port attributes
* @flags: bridge setlink flags
*
* Notify switch device port of bridge port attribute delete
*/
int netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
return 0;
if (!ops->ndo_bridge_dellink)
return -EOPNOTSUPP;
return ops->ndo_bridge_dellink(dev, nlh, flags);
}
EXPORT_SYMBOL(netdev_switch_port_bridge_dellink);
/**
* ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
* op for master devices
*
* @dev: port device
* @nlh: netlink msg with bridge port attributes
* @flags: bridge setlink flags
*
* Notify master device slaves of bridge port attributes
*/
int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
struct net_device *lower_dev;
struct list_head *iter;
int ret = 0, err = 0;
if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
return ret;
netdev_for_each_lower_dev(dev, lower_dev, iter) {
err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags);
if (err && err != -EOPNOTSUPP)
ret = err;
}
return ret;
}
EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink);
/**
* ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
* op for master devices
*
* @dev: port device
* @nlh: netlink msg with bridge port attributes
* @flags: bridge dellink flags
*
* Notify master device slaves of bridge port attribute deletes
*/
int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
struct net_device *lower_dev;
struct list_head *iter;
int ret = 0, err = 0;
if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
return ret;
netdev_for_each_lower_dev(dev, lower_dev, iter) {
err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags);
if (err && err != -EOPNOTSUPP)
ret = err;
}
return ret;
}
EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment