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:
Родитель
6004c86718
Коммит
47f8328bb1
|
@ -15,6 +15,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <net/ip_fib.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);
|
||||
|
||||
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
|
||||
* port attributes
|
||||
* switchdev_port_bridge_setlink - Set bridge port attributes
|
||||
*
|
||||
* @dev: port device
|
||||
* @nlh: netlink msg with bridge port attributes
|
||||
* @flags: bridge setlink flags
|
||||
* @nlh: netlink header
|
||||
* @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,
|
||||
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))
|
||||
return 0;
|
||||
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
||||
IFLA_PROTINFO);
|
||||
if (protinfo) {
|
||||
err = switchdev_port_br_setlink_protinfo(dev, protinfo);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!ops->ndo_bridge_setlink)
|
||||
return -EOPNOTSUPP;
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
|
||||
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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче