switchdev: add new switchdev bridge setlink
Add new switchdev_port_bridge_setlink that can be used by drivers implementing .ndo_bridge_setlink to set switchdev bridge attributes. Basically turn the raw rtnl_bridge_setlink netlink into switchdev attr sets. Proper netlink attr policy checking is done on the protinfo part of the netlink msg. Currently, for protinfo, only bridge port attrs BR_LEARNING and BR_LEARNING_SYNC are parsed and passed to port driver. For afspec, VLAN objs are passed so switchdev driver can set VLANs assigned to SELF. To illustrate with iproute2 cmd, we have: bridge vlan add vid 10 dev sw1p1 self master To add VLAN 10 to port sw1p1 for both the bridge (master) and the device (self). Signed-off-by: Scott Feldman <sfeldma@gmail.com> Acked-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
6004c86718
commit
47f8328bb1
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/if_bridge.h>
|
||||||
#include <net/ip_fib.h>
|
#include <net/ip_fib.h>
|
||||||
#include <net/switchdev.h>
|
#include <net/switchdev.h>
|
||||||
|
|
||||||
|
@ -357,28 +358,156 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
|
EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
|
||||||
|
|
||||||
|
static int switchdev_port_br_setflag(struct net_device *dev,
|
||||||
|
struct nlattr *nlattr,
|
||||||
|
unsigned long brport_flag)
|
||||||
|
{
|
||||||
|
struct switchdev_attr attr = {
|
||||||
|
.id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS,
|
||||||
|
};
|
||||||
|
u8 flag = nla_get_u8(nlattr);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = switchdev_port_attr_get(dev, &attr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (flag)
|
||||||
|
attr.brport_flags |= brport_flag;
|
||||||
|
else
|
||||||
|
attr.brport_flags &= ~brport_flag;
|
||||||
|
|
||||||
|
return switchdev_port_attr_set(dev, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nla_policy
|
||||||
|
switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = {
|
||||||
|
[IFLA_BRPORT_STATE] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_COST] = { .type = NLA_U32 },
|
||||||
|
[IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
|
||||||
|
[IFLA_BRPORT_MODE] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_LEARNING] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 },
|
||||||
|
[IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
|
||||||
|
struct nlattr *protinfo)
|
||||||
|
{
|
||||||
|
struct nlattr *attr;
|
||||||
|
int rem;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX,
|
||||||
|
switchdev_port_bridge_policy);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
nla_for_each_nested(attr, protinfo, rem) {
|
||||||
|
switch (nla_type(attr)) {
|
||||||
|
case IFLA_BRPORT_LEARNING:
|
||||||
|
err = switchdev_port_br_setflag(dev, attr,
|
||||||
|
BR_LEARNING);
|
||||||
|
break;
|
||||||
|
case IFLA_BRPORT_LEARNING_SYNC:
|
||||||
|
err = switchdev_port_br_setflag(dev, attr,
|
||||||
|
BR_LEARNING_SYNC);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int switchdev_port_br_afspec(struct net_device *dev,
|
||||||
|
struct nlattr *afspec,
|
||||||
|
int (*f)(struct net_device *dev,
|
||||||
|
struct switchdev_obj *obj))
|
||||||
|
{
|
||||||
|
struct nlattr *attr;
|
||||||
|
struct bridge_vlan_info *vinfo;
|
||||||
|
struct switchdev_obj obj = {
|
||||||
|
.id = SWITCHDEV_OBJ_PORT_VLAN,
|
||||||
|
};
|
||||||
|
int rem;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nla_for_each_nested(attr, afspec, rem) {
|
||||||
|
if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
|
||||||
|
continue;
|
||||||
|
if (nla_len(attr) != sizeof(struct bridge_vlan_info))
|
||||||
|
return -EINVAL;
|
||||||
|
vinfo = nla_data(attr);
|
||||||
|
obj.vlan.flags = vinfo->flags;
|
||||||
|
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
|
||||||
|
if (obj.vlan.vid_start)
|
||||||
|
return -EINVAL;
|
||||||
|
obj.vlan.vid_start = vinfo->vid;
|
||||||
|
} else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
|
||||||
|
if (!obj.vlan.vid_start)
|
||||||
|
return -EINVAL;
|
||||||
|
obj.vlan.vid_end = vinfo->vid;
|
||||||
|
if (obj.vlan.vid_end <= obj.vlan.vid_start)
|
||||||
|
return -EINVAL;
|
||||||
|
err = f(dev, &obj);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
memset(&obj.vlan, 0, sizeof(obj.vlan));
|
||||||
|
} else {
|
||||||
|
if (obj.vlan.vid_start)
|
||||||
|
return -EINVAL;
|
||||||
|
obj.vlan.vid_start = vinfo->vid;
|
||||||
|
obj.vlan.vid_end = vinfo->vid;
|
||||||
|
err = f(dev, &obj);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
memset(&obj.vlan, 0, sizeof(obj.vlan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* switchdev_port_bridge_setlink - Notify switch device port of bridge
|
* switchdev_port_bridge_setlink - Set bridge port attributes
|
||||||
* port attributes
|
|
||||||
*
|
*
|
||||||
* @dev: port device
|
* @dev: port device
|
||||||
* @nlh: netlink msg with bridge port attributes
|
* @nlh: netlink header
|
||||||
* @flags: bridge setlink flags
|
* @flags: netlink flags
|
||||||
*
|
*
|
||||||
* Notify switch device port of bridge port attributes
|
* Called for SELF on rtnl_bridge_setlink to set bridge port
|
||||||
|
* attributes.
|
||||||
*/
|
*/
|
||||||
int switchdev_port_bridge_setlink(struct net_device *dev,
|
int switchdev_port_bridge_setlink(struct net_device *dev,
|
||||||
struct nlmsghdr *nlh, u16 flags)
|
struct nlmsghdr *nlh, u16 flags)
|
||||||
{
|
{
|
||||||
const struct net_device_ops *ops = dev->netdev_ops;
|
struct nlattr *protinfo;
|
||||||
|
struct nlattr *afspec;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
|
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
||||||
return 0;
|
IFLA_PROTINFO);
|
||||||
|
if (protinfo) {
|
||||||
|
err = switchdev_port_br_setlink_protinfo(dev, protinfo);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ops->ndo_bridge_setlink)
|
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
||||||
return -EOPNOTSUPP;
|
IFLA_AF_SPEC);
|
||||||
|
if (afspec)
|
||||||
|
err = switchdev_port_br_afspec(dev, afspec,
|
||||||
|
switchdev_port_obj_add);
|
||||||
|
|
||||||
return ops->ndo_bridge_setlink(dev, nlh, flags);
|
return err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
|
EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue