diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_netlink.c | 30 | ||||
-rw-r--r-- | net/bridge/br_private.h | 4 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 10 | ||||
-rw-r--r-- | net/switchdev/switchdev.c | 110 |
4 files changed, 143 insertions, 11 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e08b260f33fe..b93f42c515da 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -16,6 +16,7 @@ #include <net/rtnetlink.h> #include <net/net_namespace.h> #include <net/sock.h> +#include <net/switchdev.h> #include <uapi/linux/if_bridge.h> #include "br_private.h" @@ -494,13 +495,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) } /* 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 *afspec; struct net_bridge_port *p; 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); 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) 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) br_ifinfo_notify(RTM_NEWLINK, p); - out: return err; } /* 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 net_bridge_port *p; - int err; + int err = 0, ret_offload = 0; afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); if (!afspec) @@ -573,6 +583,16 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) */ 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; } static int br_validate(struct nlattr *tb[], struct nlattr *data[]) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e8e3f3681680..de0919975a25 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -819,8 +819,8 @@ extern struct rtnl_link_ops br_link_ops; int br_netlink_init(void); void br_netlink_fini(void); void br_ifinfo_notify(int event, struct net_bridge_port *port); -int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); -int br_dellink(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, u16 flags); int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, u32 filter_mask); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index fedd7ab4085a..673cb4c6f391 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2991,7 +2991,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) 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) goto out; @@ -3002,7 +3002,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) if (!dev->netdev_ops->ndo_bridge_setlink) err = -EOPNOTSUPP; else - err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); + err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh, + flags); if (!err) { flags &= ~BRIDGE_FLAGS_SELF; @@ -3064,7 +3065,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) 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) goto out; @@ -3075,7 +3076,8 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) if (!dev->netdev_ops->ndo_bridge_dellink) err = -EOPNOTSUPP; else - err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh); + err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh, + flags); if (!err) { flags &= ~BRIDGE_FLAGS_SELF; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 22e02f4edd99..8c1e558db118 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -115,3 +115,113 @@ int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev, return err; } 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); |