Merge branch 'Pass-extack-to-SWITCHDEV_PORT_OBJ_ADD'

Petr Machata says:

====================
Pass extack to SWITCHDEV_PORT_OBJ_ADD

Drivers may need to do validation as a result of port object addition.
An example is mlxsw, which needs to check the configuration of a VXLAN
device attached to an offloaded bridge. Without a mapped VLAN, the
invalidity of the device is not important, but as soon as a pvid,
untagged VLAN is configured for the device, it has to be validated and
offloaded. Should the validation fail, there's currently no way to
communicate details of the failure to the user, beyond an error number.

Because currently, extack is not available at all in that area of code,
this patch starts down at the RTNL level and progresses up towards the
driver(s).

In patch #1, ndo_bridge_setlink is updated to include extack, and
callbacks of all clients are updated as well (ignoring the argument).

In patch #2, the bridge driver is updated to propagate the extack
through to the switchdev border, br_switchdev_port_vlan_add().

Patches #3, #4 and #5 then gradually extend switchdev to pass the extack
argument through to the switchdev blocking notifier chain.

Patches #6 and #7 then update mlxsw to pass the extack argument from
VXLAN events resp. port events on to mlxsw_sp_bridge_8021q_vxlan_join().

Finally in patches #8 and #9, the code paths from the previous two
patches are verified to yield an error message.

v2:
- Patch #1:
    - In ndo_bridge_setlink(), keep the whole extack declaration on the
      same line.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-12-12 16:34:22 -08:00
commit 02e1dbe402
19 changed files with 229 additions and 102 deletions

View File

@ -9618,7 +9618,7 @@ static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
}
static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
u16 flags)
u16 flags, struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct nlattr *attr, *br_spec;

View File

@ -4955,7 +4955,7 @@ fw_exit:
}
static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
u16 flags)
u16 flags, struct netlink_ext_ack *extack)
{
struct be_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec;

View File

@ -11685,6 +11685,7 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
* @dev: the netdev being configured
* @nlh: RTNL message
* @flags: bridge flags
* @extack: netlink extended ack
*
* Inserts a new hardware bridge if not already created and
* enables the bridging mode requested (VEB or VEPA). If the
@ -11697,7 +11698,8 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
**/
static int i40e_ndo_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags)
u16 flags,
struct netlink_ext_ack *extack)
{
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;

View File

@ -3624,6 +3624,7 @@ static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
* @dev: the netdev being configured
* @nlh: RTNL message
* @flags: bridge setlink flags
* @extack: netlink extended ack
*
* Sets the bridge mode (VEB/VEPA) of the switch to which the netdev (VSI) is
* hooked up to. Iterates through the PF VSI list and sets the loopback mode (if
@ -3632,7 +3633,7 @@ static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
*/
static int
ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
u16 __always_unused flags)
u16 __always_unused flags, struct netlink_ext_ack *extack)
{
struct ice_netdev_priv *np = netdev_priv(dev);
struct ice_pf *pf = np->vsi->back;

View File

@ -9979,7 +9979,8 @@ static int ixgbe_configure_bridge_mode(struct ixgbe_adapter *adapter,
}
static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
struct nlmsghdr *nlh, u16 flags,
struct netlink_ext_ack *extack)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec;

View File

@ -89,7 +89,7 @@ struct mlxsw_sp_bridge_ops {
struct netlink_ext_ack *extack);
struct mlxsw_sp_fid *
(*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid);
u16 vid, struct netlink_ext_ack *extack);
struct mlxsw_sp_fid *
(*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid);
@ -933,7 +933,8 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
static int
mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
struct mlxsw_sp_bridge_port *bridge_port)
struct mlxsw_sp_bridge_port *bridge_port,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
struct mlxsw_sp_bridge_device *bridge_device;
@ -943,7 +944,7 @@ mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
int err;
bridge_device = bridge_port->bridge_device;
fid = bridge_device->ops->fid_get(bridge_device, vid);
fid = bridge_device->ops->fid_get(bridge_device, vid, extack);
if (IS_ERR(fid))
return PTR_ERR(fid);
@ -1011,7 +1012,8 @@ mlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port,
static int
mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
struct mlxsw_sp_bridge_port *bridge_port)
struct mlxsw_sp_bridge_port *bridge_port,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
struct mlxsw_sp_bridge_vlan *bridge_vlan;
@ -1024,7 +1026,8 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
return 0;
}
err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port);
err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port,
extack);
if (err)
return err;
@ -1101,7 +1104,8 @@ mlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
static int
mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_bridge_port *bridge_port,
u16 vid, bool is_untagged, bool is_pvid)
u16 vid, bool is_untagged, bool is_pvid,
struct netlink_ext_ack *extack)
{
u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
@ -1121,7 +1125,8 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
if (err)
goto err_port_pvid_set;
err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port);
err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
extack);
if (err)
goto err_port_vlan_bridge_join;
@ -1171,7 +1176,8 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
@ -1208,7 +1214,7 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
vid, flag_untagged,
flag_pvid);
flag_pvid, extack);
if (err)
return err;
}
@ -1777,7 +1783,8 @@ static void mlxsw_sp_span_respin_schedule(struct mlxsw_sp *mlxsw_sp)
static int mlxsw_sp_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans)
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
const struct switchdev_obj_port_vlan *vlan;
@ -1786,7 +1793,8 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans);
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans,
extack);
if (switchdev_trans_ph_prepare(trans)) {
/* The event is emitted before the changes are actually
@ -2086,7 +2094,7 @@ mlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid)
static struct mlxsw_sp_fid *
mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid)
u16 vid, struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct net_device *vxlan_dev;
@ -2112,7 +2120,7 @@ mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
return fid;
err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
NULL);
extack);
if (err)
goto err_vxlan_join;
@ -2189,7 +2197,8 @@ mlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
if (mlxsw_sp_port_vlan->fid)
mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port);
return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port,
extack);
}
static void
@ -2252,7 +2261,7 @@ err_vni_exists:
static struct mlxsw_sp_fid *
mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
u16 vid)
u16 vid, struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct net_device *vxlan_dev;
@ -2274,7 +2283,7 @@ mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
return fid;
err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, 0,
NULL);
extack);
if (err)
goto err_vxlan_join;
@ -3204,7 +3213,8 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_bridge_device *bridge_device,
const struct net_device *vxlan_dev, u16 vid,
bool flag_untagged, bool flag_pvid,
struct switchdev_trans *trans)
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
__be32 vni = vxlan->cfg.vni;
@ -3236,7 +3246,7 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
if (!flag_untagged || !flag_pvid)
return 0;
return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device,
vxlan_dev, vid, NULL);
vxlan_dev, vid, extack);
}
/* Second case: FID is associated with the VNI and the VLAN associated
@ -3276,7 +3286,7 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
return 0;
err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
NULL);
extack);
if (err)
goto err_vxlan_join;
@ -3325,10 +3335,12 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
struct switchdev_trans *trans = port_obj_info->trans;
struct mlxsw_sp_bridge_device *bridge_device;
struct netlink_ext_ack *extack;
struct mlxsw_sp *mlxsw_sp;
struct net_device *br_dev;
u16 vid;
extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
br_dev = netdev_master_upper_dev_get(vxlan_dev);
if (!br_dev)
return 0;
@ -3352,7 +3364,8 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
vxlan_dev, vid,
flag_untagged,
flag_pvid, trans);
flag_pvid, trans,
extack);
if (err)
return err;
}

View File

@ -1293,7 +1293,8 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
static int ocelot_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans)
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
int ret = 0;

View File

@ -364,6 +364,7 @@ static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
struct switchdev_notifier_vxlan_fdb_info *fdb_info)
{
fdb_info->info.dev = vxlan->dev;
fdb_info->info.extack = NULL;
fdb_info->remote_ip = rd->remote_ip;
fdb_info->remote_port = rd->remote_port;
fdb_info->remote_vni = rd->remote_vni;

View File

@ -1165,7 +1165,7 @@ struct dev_ifalias {
* entries to skb and update idx with the number of entries.
*
* int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh,
* u16 flags)
* u16 flags, struct netlink_ext_ack *extack)
* int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
* struct net_device *dev, u32 filter_mask,
* int nlflags)
@ -1390,7 +1390,8 @@ struct net_device_ops {
int (*ndo_bridge_setlink)(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags);
u16 flags,
struct netlink_ext_ack *extack);
int (*ndo_bridge_getlink)(struct sk_buff *skb,
u32 pid, u32 seq,
struct net_device *dev,

View File

@ -149,6 +149,7 @@ enum switchdev_notifier_type {
struct switchdev_notifier_info {
struct net_device *dev;
struct netlink_ext_ack *extack;
};
struct switchdev_notifier_fdb_info {
@ -172,6 +173,12 @@ switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
return info->dev;
}
static inline struct netlink_ext_ack *
switchdev_notifier_info_to_extack(const struct switchdev_notifier_info *info)
{
return info->extack;
}
#ifdef CONFIG_NET_SWITCHDEV
void switchdev_deferred_process(void);
@ -180,7 +187,8 @@ int switchdev_port_attr_get(struct net_device *dev,
int switchdev_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr);
int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj);
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack);
int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj);
@ -192,7 +200,8 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
int register_switchdev_blocking_notifier(struct notifier_block *nb);
int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info);
struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack);
void switchdev_port_fwd_mark_set(struct net_device *dev,
struct net_device *group_dev,
@ -206,7 +215,8 @@ int switchdev_handle_port_obj_add(struct net_device *dev,
bool (*check_cb)(const struct net_device *dev),
int (*add_cb)(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans));
struct switchdev_trans *trans,
struct netlink_ext_ack *extack));
int switchdev_handle_port_obj_del(struct net_device *dev,
struct switchdev_notifier_port_obj_info *port_obj_info,
bool (*check_cb)(const struct net_device *dev),
@ -233,7 +243,8 @@ static inline int switchdev_port_attr_set(struct net_device *dev,
}
static inline int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj)
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
@ -276,7 +287,8 @@ unregister_switchdev_blocking_notifier(struct notifier_block *nb)
static inline int
call_switchdev_blocking_notifiers(unsigned long val,
struct net_device *dev,
struct switchdev_notifier_info *info)
struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack)
{
return NOTIFY_DONE;
}
@ -293,7 +305,8 @@ switchdev_handle_port_obj_add(struct net_device *dev,
bool (*check_cb)(const struct net_device *dev),
int (*add_cb)(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans))
struct switchdev_trans *trans,
struct netlink_ext_ack *extack))
{
return 0;
}

View File

@ -650,7 +650,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
err = nbp_vlan_init(p);
err = nbp_vlan_init(p, extack);
if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
goto err7;

View File

@ -331,7 +331,7 @@ static void br_mdb_switchdev_host_port(struct net_device *dev,
mdb.obj.orig_dev = dev;
switch (type) {
case RTM_NEWMDB:
switchdev_port_obj_add(lower_dev, &mdb.obj);
switchdev_port_obj_add(lower_dev, &mdb.obj, NULL);
break;
case RTM_DELMDB:
switchdev_port_obj_del(lower_dev, &mdb.obj);
@ -381,7 +381,7 @@ static void __br_mdb_notify(struct net_device *dev, struct net_bridge_port *p,
__mdb_entry_to_br_ip(entry, &complete_info->ip);
mdb.obj.complete_priv = complete_info;
mdb.obj.complete = br_mdb_complete;
if (switchdev_port_obj_add(port_dev, &mdb.obj))
if (switchdev_port_obj_add(port_dev, &mdb.obj, NULL))
kfree(complete_info);
}
} else if (p && port_dev && type == RTM_DELMDB) {

View File

@ -525,7 +525,8 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
}
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
int cmd, struct bridge_vlan_info *vinfo, bool *changed)
int cmd, struct bridge_vlan_info *vinfo, bool *changed,
struct netlink_ext_ack *extack)
{
bool curr_change;
int err = 0;
@ -537,11 +538,11 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
* per-VLAN entry as well
*/
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags,
&curr_change);
&curr_change, extack);
} else {
vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
err = br_vlan_add(br, vinfo->vid, vinfo->flags,
&curr_change);
&curr_change, extack);
}
if (curr_change)
*changed = true;
@ -568,7 +569,8 @@ static int br_process_vlan_info(struct net_bridge *br,
struct net_bridge_port *p, int cmd,
struct bridge_vlan_info *vinfo_curr,
struct bridge_vlan_info **vinfo_last,
bool *changed)
bool *changed,
struct netlink_ext_ack *extack)
{
if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK)
return -EINVAL;
@ -598,7 +600,8 @@ static int br_process_vlan_info(struct net_bridge *br,
sizeof(struct bridge_vlan_info));
for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
tmp_vinfo.vid = v;
err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed);
err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed,
extack);
if (err)
break;
}
@ -607,13 +610,14 @@ static int br_process_vlan_info(struct net_bridge *br,
return err;
}
return br_vlan_info(br, p, cmd, vinfo_curr, changed);
return br_vlan_info(br, p, cmd, vinfo_curr, changed, extack);
}
static int br_afspec(struct net_bridge *br,
struct net_bridge_port *p,
struct nlattr *af_spec,
int cmd, bool *changed)
int cmd, bool *changed,
struct netlink_ext_ack *extack)
{
struct bridge_vlan_info *vinfo_curr = NULL;
struct bridge_vlan_info *vinfo_last = NULL;
@ -643,7 +647,8 @@ static int br_afspec(struct net_bridge *br,
return -EINVAL;
vinfo_curr = nla_data(attr);
err = br_process_vlan_info(br, p, cmd, vinfo_curr,
&vinfo_last, changed);
&vinfo_last, changed,
extack);
if (err)
return err;
break;
@ -850,7 +855,8 @@ 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, u16 flags)
int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
struct netlink_ext_ack *extack)
{
struct net_bridge *br = (struct net_bridge *)netdev_priv(dev);
struct nlattr *tb[IFLA_BRPORT_MAX + 1];
@ -897,7 +903,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
}
if (afspec)
err = br_afspec(br, p, afspec, RTM_SETLINK, &changed);
err = br_afspec(br, p, afspec, RTM_SETLINK, &changed, extack);
if (changed)
br_ifinfo_notify(RTM_NEWLINK, br, p);
@ -923,7 +929,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
if (!p && !(dev->priv_flags & IFF_EBRIDGE))
return -EINVAL;
err = br_afspec(br, p, afspec, RTM_DELLINK, &changed);
err = br_afspec(br, p, afspec, RTM_DELLINK, &changed, NULL);
if (changed)
/* Send RTM_NEWLINK because userspace
* expects RTM_NEWLINK for vlan dels
@ -1105,7 +1111,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
__u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
err = __br_vlan_set_default_pvid(br, defpvid);
err = __br_vlan_set_default_pvid(br, defpvid, extack);
if (err)
return err;
}

View File

@ -868,7 +868,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
struct net_bridge_vlan_group *vg,
struct sk_buff *skb);
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
bool *changed);
bool *changed, struct netlink_ext_ack *extack);
int br_vlan_delete(struct net_bridge *br, u16 vid);
void br_vlan_flush(struct net_bridge *br);
struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid);
@ -881,12 +881,13 @@ int br_vlan_set_stats(struct net_bridge *br, unsigned long val);
int br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val);
int br_vlan_init(struct net_bridge *br);
int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val);
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid);
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
struct netlink_ext_ack *extack);
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed);
bool *changed, struct netlink_ext_ack *extack);
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
void nbp_vlan_flush(struct net_bridge_port *port);
int nbp_vlan_init(struct net_bridge_port *port);
int nbp_vlan_init(struct net_bridge_port *port, struct netlink_ext_ack *extack);
int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
void br_vlan_get_stats(const struct net_bridge_vlan *v,
struct br_vlan_stats *stats);
@ -971,7 +972,7 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
}
static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
bool *changed)
bool *changed, struct netlink_ext_ack *extack)
{
*changed = false;
return -EOPNOTSUPP;
@ -996,7 +997,7 @@ static inline int br_vlan_init(struct net_bridge *br)
}
static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed)
bool *changed, struct netlink_ext_ack *extack)
{
*changed = false;
return -EOPNOTSUPP;
@ -1017,7 +1018,8 @@ static inline struct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group
return NULL;
}
static inline int nbp_vlan_init(struct net_bridge_port *port)
static inline int nbp_vlan_init(struct net_bridge_port *port,
struct netlink_ext_ack *extack)
{
return 0;
}
@ -1138,7 +1140,8 @@ int br_netlink_init(void);
void br_netlink_fini(void);
void br_ifinfo_notify(int event, const struct net_bridge *br,
const struct net_bridge_port *port);
int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags,
struct netlink_ext_ack *extack);
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, int nlflags);
@ -1173,7 +1176,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long mask);
void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
int type);
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags);
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
struct netlink_ext_ack *extack);
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid);
static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
@ -1205,7 +1209,8 @@ static inline int br_switchdev_set_port_flag(struct net_bridge_port *p,
}
static inline int br_switchdev_port_vlan_add(struct net_device *dev,
u16 vid, u16 flags)
u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}

View File

@ -140,7 +140,8 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
}
}
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags)
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
@ -150,7 +151,7 @@ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags)
.vid_end = vid,
};
return switchdev_port_obj_add(dev, &v.obj);
return switchdev_port_obj_add(dev, &v.obj, extack);
}
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)

View File

@ -80,14 +80,14 @@ static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
}
static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
u16 vid, u16 flags)
u16 vid, u16 flags, struct netlink_ext_ack *extack)
{
int err;
/* Try switchdev op first. In case it is not supported, fallback to
* 8021q add.
*/
err = br_switchdev_port_vlan_add(dev, vid, flags);
err = br_switchdev_port_vlan_add(dev, vid, flags, extack);
if (err == -EOPNOTSUPP)
return vlan_vid_add(dev, br->vlan_proto, vid);
return err;
@ -139,7 +139,9 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
/* Returns a master vlan, if it didn't exist it gets created. In all cases a
* a reference is taken to the master vlan before returning.
*/
static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid)
static struct net_bridge_vlan *
br_vlan_get_master(struct net_bridge *br, u16 vid,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *masterv;
@ -150,7 +152,7 @@ static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid
bool changed;
/* missing global ctx, create it now */
if (br_vlan_add(br, vid, 0, &changed))
if (br_vlan_add(br, vid, 0, &changed, extack))
return NULL;
masterv = br_vlan_find(vg, vid);
if (WARN_ON(!masterv))
@ -214,7 +216,8 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu)
* 4. same as 3 but with both master and brentry flags set so the entry
* will be used for filtering in both the port and the bridge
*/
static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan *masterv = NULL;
struct net_bridge_port *p = NULL;
@ -239,7 +242,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
* This ensures tagged traffic enters the bridge when
* promiscuous mode is disabled by br_manage_promisc().
*/
err = __vlan_vid_add(dev, br, v->vid, flags);
err = __vlan_vid_add(dev, br, v->vid, flags, extack);
if (err)
goto out;
@ -249,12 +252,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
err = br_vlan_add(br, v->vid,
flags | BRIDGE_VLAN_INFO_BRENTRY,
&changed);
&changed, extack);
if (err)
goto out_filt;
}
masterv = br_vlan_get_master(br, v->vid);
masterv = br_vlan_get_master(br, v->vid, extack);
if (!masterv)
goto out_filt;
v->brvlan = masterv;
@ -269,7 +272,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
v->stats = masterv->stats;
}
} else {
err = br_switchdev_port_vlan_add(dev, v->vid, flags);
err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack);
if (err && err != -EOPNOTSUPP)
goto out;
}
@ -591,11 +594,12 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
static int br_vlan_add_existing(struct net_bridge *br,
struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan,
u16 flags, bool *changed)
u16 flags, bool *changed,
struct netlink_ext_ack *extack)
{
int err;
err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags);
err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags, extack);
if (err && err != -EOPNOTSUPP)
return err;
@ -634,7 +638,8 @@ err_flags:
* Must be called with vid in range from 1 to 4094 inclusive.
* changed must be true only if the vlan was created or updated
*/
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *vlan;
@ -646,7 +651,8 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
vg = br_vlan_group(br);
vlan = br_vlan_find(vg, vid);
if (vlan)
return br_vlan_add_existing(br, vg, vlan, flags, changed);
return br_vlan_add_existing(br, vg, vlan, flags, changed,
extack);
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
if (!vlan)
@ -663,7 +669,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed)
vlan->br = br;
if (flags & BRIDGE_VLAN_INFO_BRENTRY)
refcount_set(&vlan->refcnt, 1);
ret = __vlan_add(vlan, flags);
ret = __vlan_add(vlan, flags, extack);
if (ret) {
free_percpu(vlan->stats);
kfree(vlan);
@ -914,7 +920,8 @@ static void br_vlan_disable_default_pvid(struct net_bridge *br)
br->default_pvid = 0;
}
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
struct netlink_ext_ack *extack)
{
const struct net_bridge_vlan *pvent;
struct net_bridge_vlan_group *vg;
@ -946,7 +953,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY,
&vlchange);
&vlchange, extack);
if (err)
goto out;
br_vlan_delete(br, old_pvid);
@ -966,7 +973,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid)
err = nbp_vlan_add(p, pvid,
BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED,
&vlchange);
&vlchange, extack);
if (err)
goto err_port;
nbp_vlan_delete(p, old_pvid);
@ -988,7 +995,7 @@ err_port:
nbp_vlan_add(p, old_pvid,
BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED,
&vlchange);
&vlchange, NULL);
nbp_vlan_delete(p, pvid);
}
@ -998,7 +1005,7 @@ err_port:
BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY,
&vlchange);
&vlchange, NULL);
br_vlan_delete(br, pvid);
}
goto out;
@ -1021,7 +1028,7 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
err = -EPERM;
goto out;
}
err = __br_vlan_set_default_pvid(br, pvid);
err = __br_vlan_set_default_pvid(br, pvid, NULL);
out:
return err;
}
@ -1047,7 +1054,7 @@ int br_vlan_init(struct net_bridge *br)
rcu_assign_pointer(br->vlgrp, vg);
ret = br_vlan_add(br, 1,
BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED |
BRIDGE_VLAN_INFO_BRENTRY, &changed);
BRIDGE_VLAN_INFO_BRENTRY, &changed, NULL);
if (ret)
goto err_vlan_add;
@ -1064,7 +1071,7 @@ err_rhtbl:
goto out;
}
int nbp_vlan_init(struct net_bridge_port *p)
int nbp_vlan_init(struct net_bridge_port *p, struct netlink_ext_ack *extack)
{
struct switchdev_attr attr = {
.orig_dev = p->br->dev,
@ -1097,7 +1104,7 @@ int nbp_vlan_init(struct net_bridge_port *p)
ret = nbp_vlan_add(p, p->br->default_pvid,
BRIDGE_VLAN_INFO_PVID |
BRIDGE_VLAN_INFO_UNTAGGED,
&changed);
&changed, extack);
if (ret)
goto err_vlan_add;
}
@ -1122,7 +1129,7 @@ err_vlan_enabled:
* changed must be true only if the vlan was created or updated
*/
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
bool *changed)
bool *changed, struct netlink_ext_ack *extack)
{
struct net_bridge_vlan *vlan;
int ret;
@ -1133,7 +1140,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
vlan = br_vlan_find(nbp_vlan_group(port), vid);
if (vlan) {
/* Pass the flags to the hardware bridge */
ret = br_switchdev_port_vlan_add(port->dev, vid, flags);
ret = br_switchdev_port_vlan_add(port->dev, vid, flags, extack);
if (ret && ret != -EOPNOTSUPP)
return ret;
*changed = __vlan_add_flags(vlan, flags);
@ -1147,7 +1154,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
vlan->vid = vid;
vlan->port = port;
ret = __vlan_add(vlan, flags);
ret = __vlan_add(vlan, flags, extack);
if (ret)
kfree(vlan);
else

View File

@ -4332,7 +4332,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
}
err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags);
err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags,
extack);
if (err)
goto out;
@ -4344,7 +4345,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
err = -EOPNOTSUPP;
else
err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh,
flags);
flags,
extack);
if (!err) {
flags &= ~BRIDGE_FLAGS_SELF;

View File

@ -356,7 +356,8 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans)
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
int rc;
int err;
@ -367,7 +368,7 @@ static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
.handled = false,
};
rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info);
rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack);
err = notifier_to_errno(rc);
if (err) {
WARN_ON(!obj_info.handled);
@ -379,7 +380,8 @@ static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
}
static int switchdev_port_obj_add_now(struct net_device *dev,
const struct switchdev_obj *obj)
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack)
{
struct switchdev_trans trans;
int err;
@ -397,7 +399,7 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
trans.ph_prepare = true;
err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
dev, obj, &trans);
dev, obj, &trans, extack);
if (err) {
/* Prepare phase failed: abort the transaction. Any
* resources reserved in the prepare phase are
@ -417,7 +419,7 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
trans.ph_prepare = false;
err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
dev, obj, &trans);
dev, obj, &trans, extack);
WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
switchdev_trans_items_warn_destroy(dev, &trans);
@ -430,7 +432,7 @@ static void switchdev_port_obj_add_deferred(struct net_device *dev,
const struct switchdev_obj *obj = data;
int err;
err = switchdev_port_obj_add_now(dev, obj);
err = switchdev_port_obj_add_now(dev, obj, NULL);
if (err && err != -EOPNOTSUPP)
netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
err, obj->id);
@ -460,12 +462,13 @@ static int switchdev_port_obj_add_defer(struct net_device *dev,
* in case SWITCHDEV_F_DEFER flag is not set.
*/
int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj)
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack)
{
if (obj->flags & SWITCHDEV_F_DEFER)
return switchdev_port_obj_add_defer(dev, obj);
ASSERT_RTNL();
return switchdev_port_obj_add_now(dev, obj);
return switchdev_port_obj_add_now(dev, obj, extack);
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
@ -473,7 +476,7 @@ static int switchdev_port_obj_del_now(struct net_device *dev,
const struct switchdev_obj *obj)
{
return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
dev, obj, NULL);
dev, obj, NULL, NULL);
}
static void switchdev_port_obj_del_deferred(struct net_device *dev,
@ -556,6 +559,7 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info)
{
info->dev = dev;
info->extack = NULL;
return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
}
EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
@ -577,9 +581,11 @@ int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info)
struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack)
{
info->dev = dev;
info->extack = extack;
return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
val, info);
}
@ -610,16 +616,21 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
bool (*check_cb)(const struct net_device *dev),
int (*add_cb)(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans))
struct switchdev_trans *trans,
struct netlink_ext_ack *extack))
{
struct netlink_ext_ack *extack;
struct net_device *lower_dev;
struct list_head *iter;
int err = -EOPNOTSUPP;
extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
if (check_cb(dev)) {
/* This flag is only checked if the return value is success. */
port_obj_info->handled = true;
return add_cb(dev, port_obj_info->obj, port_obj_info->trans);
return add_cb(dev, port_obj_info->obj, port_obj_info->trans,
extack);
}
/* Switch ports might be stacked under e.g. a LAG. Ignore the
@ -644,7 +655,8 @@ int switchdev_handle_port_obj_add(struct net_device *dev,
bool (*check_cb)(const struct net_device *dev),
int (*add_cb)(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans))
struct switchdev_trans *trans,
struct netlink_ext_ack *extack))
{
int err;

View File

@ -7,6 +7,8 @@ lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
netdev_pre_up_test
vxlan_vlan_add_test
port_vlan_add_test
"
NUM_NETIFS=2
source $lib_dir/lib.sh
@ -74,6 +76,65 @@ netdev_pre_up_test()
ip link del dev br1
}
vxlan_vlan_add_test()
{
RET=0
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
# Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
ip link add name vx1 up type vxlan id 1000 \
local 192.0.2.17 remote 192.0.2.18 \
dstport 4789 tos inherit ttl 100
ip link set dev vx1 master br1
check_err $?
bridge vlan add dev vx1 vid 1
check_err $?
ip link set dev $swp1 master br1
check_err $?
bridge vlan add dev vx1 vid 1 pvid untagged 2>&1 >/dev/null \
| grep -q mlxsw_spectrum
check_err $?
log_test "extack - map VLAN at VXLAN device"
ip link del dev vx1
ip link del dev br1
}
port_vlan_add_test()
{
RET=0
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
# Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
ip link add name vx1 up type vxlan id 1000 \
local 192.0.2.17 remote 192.0.2.18 \
dstport 4789 tos inherit ttl 100
ip link set dev $swp1 master br1
check_err $?
bridge vlan del dev $swp1 vid 1
ip link set dev vx1 master br1
check_err $?
bridge vlan add dev $swp1 vid 1 pvid untagged 2>&1 >/dev/null \
| grep -q mlxsw_spectrum
check_err $?
log_test "extack - map VLAN at port"
ip link del dev vx1
ip link del dev br1
}
trap cleanup EXIT
setup_prepare