forked from Minki/linux
Merge branch 'bridge-replay-helpers'
Vladimir Oltean says: ==================== Cleanup for the bridge replay helpers This patch series brings some improvements to the logic added to the bridge and DSA to handle LAG interfaces sandwiched between a bridge and a DSA switch port. br0 / \ / \ bond0 swp2 / \ / \ swp0 swp1 In particular, it ensures that the switchdev object additions and deletions are well balanced per physical port. This is important for future work in the area of offloading local bridge FDB entries to hardware in the context of DSA requesting a replay of those entries at bridge join time (this will be submitted in a future patch series). Due to some difficulty ensuring that the deletion of local FDB entries pointing towards the bridge device itself is notified to switchdev in time (before the switchdev port disconnects from the bridge), this is potentially still not the final form in which the replay helpers will exist. I'm thinking about moving from the pull mode (in which DSA requests the replay) to a push mode (in which the bridge initiates the replay). Nonetheless, these preliminary changes are needed either way. The patch series also addresses some feedback from Nikolai which is long overdue by now (sorry). Switchdev driver maintainers were deliberately omitted due to the trivial nature of the driver changes (just a function prototype). Changes in v2: - fix build issue in patch 4 (function prototype mismatch) - move switchdev object unsync to the NETDEV_PRECHANGEUPPER code path ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
3095f512e3
@ -1625,7 +1625,7 @@ static int dpaa2_switch_port_bridge_flags(struct net_device *netdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpaa2_switch_port_attr_set(struct net_device *netdev,
|
||||
static int dpaa2_switch_port_attr_set(struct net_device *netdev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
|
@ -708,7 +708,7 @@ err_port_stp_set:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int prestera_port_obj_attr_set(struct net_device *dev,
|
||||
static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -1040,7 +1040,7 @@ static int prestera_port_vlans_add(struct prestera_port *port,
|
||||
flag_pvid, extack);
|
||||
}
|
||||
|
||||
static int prestera_port_obj_add(struct net_device *dev,
|
||||
static int prestera_port_obj_add(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -1078,7 +1078,7 @@ static int prestera_port_vlans_del(struct prestera_port *port,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prestera_port_obj_del(struct net_device *dev,
|
||||
static int prestera_port_obj_del(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct prestera_port *port = netdev_priv(dev);
|
||||
|
@ -76,6 +76,7 @@ static int mlx5_esw_bridge_switchdev_port_event(struct notifier_block *nb,
|
||||
}
|
||||
|
||||
static int mlx5_esw_bridge_port_obj_add(struct net_device *dev,
|
||||
const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -107,6 +108,7 @@ static int mlx5_esw_bridge_port_obj_add(struct net_device *dev,
|
||||
}
|
||||
|
||||
static int mlx5_esw_bridge_port_obj_del(struct net_device *dev,
|
||||
const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
const struct switchdev_obj_port_vlan *vlan;
|
||||
@ -136,6 +138,7 @@ static int mlx5_esw_bridge_port_obj_del(struct net_device *dev,
|
||||
}
|
||||
|
||||
static int mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
|
||||
const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
|
@ -898,7 +898,7 @@ mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_attr_set(struct net_device *dev,
|
||||
static int mlxsw_sp_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -1766,7 +1766,7 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_obj_add(struct net_device *dev,
|
||||
static int mlxsw_sp_port_obj_add(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -1916,7 +1916,7 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_obj_del(struct net_device *dev,
|
||||
static int mlxsw_sp_port_obj_del(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
||||
|
@ -65,7 +65,7 @@ static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
|
||||
sparx5_set_ageing(port->sparx5, ageing_time);
|
||||
}
|
||||
|
||||
static int sparx5_port_attr_set(struct net_device *dev,
|
||||
static int sparx5_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
|
@ -939,7 +939,7 @@ static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
|
||||
ANA_PORT_CPU_FWD_CFG, port);
|
||||
}
|
||||
|
||||
static int ocelot_port_attr_set(struct net_device *dev,
|
||||
static int ocelot_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -948,6 +948,9 @@ static int ocelot_port_attr_set(struct net_device *dev,
|
||||
int port = priv->chip_port;
|
||||
int err = 0;
|
||||
|
||||
if (ctx && ctx != priv)
|
||||
return 0;
|
||||
|
||||
switch (attr->id) {
|
||||
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
|
||||
ocelot_port_attr_stp_state_set(ocelot, port, attr->u.stp_state);
|
||||
@ -1058,12 +1061,16 @@ ocelot_port_obj_mrp_del_ring_role(struct net_device *dev,
|
||||
return ocelot_mrp_del_ring_role(ocelot, port, mrp);
|
||||
}
|
||||
|
||||
static int ocelot_port_obj_add(struct net_device *dev,
|
||||
static int ocelot_port_obj_add(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ocelot_port_private *priv = netdev_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (ctx && ctx != priv)
|
||||
return 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
ret = ocelot_port_obj_add_vlan(dev,
|
||||
@ -1086,11 +1093,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocelot_port_obj_del(struct net_device *dev,
|
||||
static int ocelot_port_obj_del(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct ocelot_port_private *priv = netdev_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (ctx && ctx != priv)
|
||||
return 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
ret = ocelot_vlan_vid_del(dev,
|
||||
@ -1143,10 +1154,14 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port,
|
||||
struct net_device *bridge_dev,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||||
struct ocelot_port_private *priv;
|
||||
clock_t ageing_time;
|
||||
u8 stp_state;
|
||||
int err;
|
||||
|
||||
priv = container_of(ocelot_port, struct ocelot_port_private, port);
|
||||
|
||||
ocelot_inherit_brport_flags(ocelot, port, brport_dev);
|
||||
|
||||
stp_state = br_port_get_stp_state(brport_dev);
|
||||
@ -1160,16 +1175,12 @@ static int ocelot_switchdev_sync(struct ocelot *ocelot, int port,
|
||||
ageing_time = br_get_ageing_time(bridge_dev);
|
||||
ocelot_port_attr_ageing_set(ocelot, port, ageing_time);
|
||||
|
||||
err = br_mdb_replay(bridge_dev, brport_dev,
|
||||
err = br_mdb_replay(bridge_dev, brport_dev, priv, true,
|
||||
&ocelot_switchdev_blocking_nb, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_fdb_replay(bridge_dev, brport_dev, &ocelot_switchdev_nb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_vlan_replay(bridge_dev, brport_dev,
|
||||
err = br_vlan_replay(bridge_dev, brport_dev, priv, true,
|
||||
&ocelot_switchdev_blocking_nb, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
@ -84,7 +84,7 @@ static int am65_cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int am65_cpsw_port_attr_set(struct net_device *ndev,
|
||||
static int am65_cpsw_port_attr_set(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -302,7 +302,7 @@ static int am65_cpsw_port_mdb_del(struct am65_cpsw_port *port,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int am65_cpsw_port_obj_add(struct net_device *ndev,
|
||||
static int am65_cpsw_port_obj_add(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -329,7 +329,7 @@ static int am65_cpsw_port_obj_add(struct net_device *ndev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int am65_cpsw_port_obj_del(struct net_device *ndev,
|
||||
static int am65_cpsw_port_obj_del(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
|
||||
|
@ -86,7 +86,7 @@ static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpsw_port_attr_set(struct net_device *ndev,
|
||||
static int cpsw_port_attr_set(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -310,7 +310,7 @@ static int cpsw_port_mdb_del(struct cpsw_priv *priv,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cpsw_port_obj_add(struct net_device *ndev,
|
||||
static int cpsw_port_obj_add(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@ -338,7 +338,7 @@ static int cpsw_port_obj_add(struct net_device *ndev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cpsw_port_obj_del(struct net_device *ndev,
|
||||
static int cpsw_port_obj_del(struct net_device *ndev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
|
||||
|
@ -71,7 +71,8 @@ bool br_multicast_has_router_adjacent(struct net_device *dev, int proto);
|
||||
bool br_multicast_enabled(const struct net_device *dev);
|
||||
bool br_multicast_router(const struct net_device *dev);
|
||||
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb, struct netlink_ext_ack *extack);
|
||||
const void *ctx, bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack);
|
||||
#else
|
||||
static inline int br_multicast_list_adjacent(struct net_device *dev,
|
||||
struct list_head *br_ip_list)
|
||||
@ -103,9 +104,9 @@ static inline bool br_multicast_router(const struct net_device *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline int br_mdb_replay(struct net_device *br_dev,
|
||||
struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
static inline int br_mdb_replay(const struct net_device *br_dev,
|
||||
const struct net_device *dev, const void *ctx,
|
||||
bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
@ -120,7 +121,8 @@ int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto);
|
||||
int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
||||
struct bridge_vlan_info *p_vinfo);
|
||||
int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb, struct netlink_ext_ack *extack);
|
||||
const void *ctx, bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack);
|
||||
#else
|
||||
static inline bool br_vlan_enabled(const struct net_device *dev)
|
||||
{
|
||||
@ -149,8 +151,8 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
||||
}
|
||||
|
||||
static inline int br_vlan_replay(struct net_device *br_dev,
|
||||
struct net_device *dev,
|
||||
struct notifier_block *nb,
|
||||
struct net_device *dev, const void *ctx,
|
||||
bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
@ -164,9 +166,9 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev,
|
||||
void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
|
||||
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
|
||||
u8 br_port_get_stp_state(const struct net_device *dev);
|
||||
clock_t br_get_ageing_time(struct net_device *br_dev);
|
||||
int br_fdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb);
|
||||
clock_t br_get_ageing_time(const struct net_device *br_dev);
|
||||
int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
|
||||
const void *ctx, bool adding, struct notifier_block *nb);
|
||||
#else
|
||||
static inline struct net_device *
|
||||
br_fdb_find_port(const struct net_device *br_dev,
|
||||
@ -191,14 +193,14 @@ static inline u8 br_port_get_stp_state(const struct net_device *dev)
|
||||
return BR_STATE_DISABLED;
|
||||
}
|
||||
|
||||
static inline clock_t br_get_ageing_time(struct net_device *br_dev)
|
||||
static inline clock_t br_get_ageing_time(const struct net_device *br_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int br_fdb_replay(struct net_device *br_dev,
|
||||
struct net_device *dev,
|
||||
struct notifier_block *nb)
|
||||
static inline int br_fdb_replay(const struct net_device *br_dev,
|
||||
const struct net_device *dev, const void *ctx,
|
||||
bool adding, struct notifier_block *nb)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -202,6 +202,7 @@ enum switchdev_notifier_type {
|
||||
struct switchdev_notifier_info {
|
||||
struct net_device *dev;
|
||||
struct netlink_ext_ack *extack;
|
||||
const void *ctx;
|
||||
};
|
||||
|
||||
struct switchdev_notifier_fdb_info {
|
||||
@ -268,19 +269,19 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
|
||||
int switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
int (*add_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
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),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
int (*del_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj));
|
||||
|
||||
int switchdev_handle_port_attr_set(struct net_device *dev,
|
||||
struct switchdev_notifier_port_attr_info *port_attr_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*set_cb)(struct net_device *dev,
|
||||
int (*set_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack));
|
||||
#else
|
||||
@ -352,7 +353,7 @@ static inline int
|
||||
switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
int (*add_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
@ -363,7 +364,7 @@ static inline 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),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
int (*del_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
return 0;
|
||||
@ -373,7 +374,7 @@ static inline int
|
||||
switchdev_handle_port_attr_set(struct net_device *dev,
|
||||
struct switchdev_notifier_port_attr_info *port_attr_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*set_cb)(struct net_device *dev,
|
||||
int (*set_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
|
@ -727,8 +727,9 @@ static inline size_t fdb_nlmsg_size(void)
|
||||
}
|
||||
|
||||
static int br_fdb_replay_one(struct notifier_block *nb,
|
||||
struct net_bridge_fdb_entry *fdb,
|
||||
struct net_device *dev)
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
struct net_device *dev, unsigned long action,
|
||||
const void *ctx)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info item;
|
||||
int err;
|
||||
@ -737,17 +738,20 @@ static int br_fdb_replay_one(struct notifier_block *nb,
|
||||
item.vid = fdb->key.vlan_id;
|
||||
item.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
|
||||
item.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags);
|
||||
item.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags);
|
||||
item.info.dev = dev;
|
||||
item.info.ctx = ctx;
|
||||
|
||||
err = nb->notifier_call(nb, SWITCHDEV_FDB_ADD_TO_DEVICE, &item);
|
||||
err = nb->notifier_call(nb, action, &item);
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
int br_fdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb)
|
||||
int br_fdb_replay(const struct net_device *br_dev, const struct net_device *dev,
|
||||
const void *ctx, bool adding, struct notifier_block *nb)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
struct net_bridge *br;
|
||||
unsigned long action;
|
||||
int err = 0;
|
||||
|
||||
if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev))
|
||||
@ -755,17 +759,22 @@ int br_fdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
|
||||
br = netdev_priv(br_dev);
|
||||
|
||||
if (adding)
|
||||
action = SWITCHDEV_FDB_ADD_TO_DEVICE;
|
||||
else
|
||||
action = SWITCHDEV_FDB_DEL_TO_DEVICE;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) {
|
||||
struct net_bridge_port *dst = READ_ONCE(fdb->dst);
|
||||
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
|
||||
struct net_device *dst_dev;
|
||||
|
||||
dst_dev = dst ? dst->dev : br->dev;
|
||||
if (dst_dev != br_dev && dst_dev != dev)
|
||||
continue;
|
||||
|
||||
err = br_fdb_replay_one(nb, fdb, dst_dev);
|
||||
err = br_fdb_replay_one(nb, fdb, dst_dev, action, ctx);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
@ -567,19 +567,21 @@ static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
|
||||
}
|
||||
|
||||
static int br_mdb_replay_one(struct notifier_block *nb, struct net_device *dev,
|
||||
struct switchdev_obj_port_mdb *mdb,
|
||||
const struct switchdev_obj_port_mdb *mdb,
|
||||
unsigned long action, const void *ctx,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct switchdev_notifier_port_obj_info obj_info = {
|
||||
.info = {
|
||||
.dev = dev,
|
||||
.extack = extack,
|
||||
.ctx = ctx,
|
||||
},
|
||||
.obj = &mdb->obj,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nb->notifier_call(nb, SWITCHDEV_PORT_OBJ_ADD, &obj_info);
|
||||
err = nb->notifier_call(nb, action, &obj_info);
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
@ -603,11 +605,13 @@ static int br_mdb_queue_one(struct list_head *mdb_list,
|
||||
}
|
||||
|
||||
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb, struct netlink_ext_ack *extack)
|
||||
const void *ctx, bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net_bridge_mdb_entry *mp;
|
||||
const struct net_bridge_mdb_entry *mp;
|
||||
struct switchdev_obj *obj, *tmp;
|
||||
struct net_bridge *br;
|
||||
unsigned long action;
|
||||
LIST_HEAD(mdb_list);
|
||||
int err = 0;
|
||||
|
||||
@ -632,8 +636,8 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
rcu_read_lock();
|
||||
|
||||
hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
|
||||
struct net_bridge_port_group __rcu **pp;
|
||||
struct net_bridge_port_group *p;
|
||||
struct net_bridge_port_group __rcu * const *pp;
|
||||
const struct net_bridge_port_group *p;
|
||||
|
||||
if (mp->host_joined) {
|
||||
err = br_mdb_queue_one(&mdb_list,
|
||||
@ -662,9 +666,14 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (adding)
|
||||
action = SWITCHDEV_PORT_OBJ_ADD;
|
||||
else
|
||||
action = SWITCHDEV_PORT_OBJ_DEL;
|
||||
|
||||
list_for_each_entry(obj, &mdb_list, list) {
|
||||
err = br_mdb_replay_one(nb, dev, SWITCHDEV_OBJ_PORT_MDB(obj),
|
||||
extack);
|
||||
action, ctx, extack);
|
||||
if (err)
|
||||
goto out_free_mdb;
|
||||
}
|
||||
|
@ -639,9 +639,9 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time)
|
||||
return 0;
|
||||
}
|
||||
|
||||
clock_t br_get_ageing_time(struct net_device *br_dev)
|
||||
clock_t br_get_ageing_time(const struct net_device *br_dev)
|
||||
{
|
||||
struct net_bridge *br;
|
||||
const struct net_bridge *br;
|
||||
|
||||
if (!netif_is_bridge_master(br_dev))
|
||||
return 0;
|
||||
|
@ -1807,28 +1807,32 @@ out_kfree:
|
||||
static int br_vlan_replay_one(struct notifier_block *nb,
|
||||
struct net_device *dev,
|
||||
struct switchdev_obj_port_vlan *vlan,
|
||||
const void *ctx, unsigned long action,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct switchdev_notifier_port_obj_info obj_info = {
|
||||
.info = {
|
||||
.dev = dev,
|
||||
.extack = extack,
|
||||
.ctx = ctx,
|
||||
},
|
||||
.obj = &vlan->obj,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = nb->notifier_call(nb, SWITCHDEV_PORT_OBJ_ADD, &obj_info);
|
||||
err = nb->notifier_call(nb, action, &obj_info);
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
struct notifier_block *nb, struct netlink_ext_ack *extack)
|
||||
const void *ctx, bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_vlan *v;
|
||||
struct net_bridge_port *p;
|
||||
struct net_bridge *br;
|
||||
unsigned long action;
|
||||
int err = 0;
|
||||
u16 pvid;
|
||||
|
||||
@ -1855,6 +1859,11 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
if (!vg)
|
||||
return 0;
|
||||
|
||||
if (adding)
|
||||
action = SWITCHDEV_PORT_OBJ_ADD;
|
||||
else
|
||||
action = SWITCHDEV_PORT_OBJ_DEL;
|
||||
|
||||
pvid = br_get_pvid(vg);
|
||||
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist) {
|
||||
@ -1868,7 +1877,7 @@ int br_vlan_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
if (!br_vlan_should_use(v))
|
||||
continue;
|
||||
|
||||
err = br_vlan_replay_one(nb, dev, &vlan, extack);
|
||||
err = br_vlan_replay_one(nb, dev, &vlan, ctx, action, extack);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -188,12 +188,16 @@ void dsa_port_disable_rt(struct dsa_port *dp);
|
||||
void dsa_port_disable(struct dsa_port *dp);
|
||||
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
|
||||
struct netlink_ext_ack *extack);
|
||||
int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br,
|
||||
struct netlink_ext_ack *extack);
|
||||
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br);
|
||||
int dsa_port_lag_change(struct dsa_port *dp,
|
||||
struct netdev_lag_lower_state_info *linfo);
|
||||
int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
|
||||
struct netdev_lag_upper_info *uinfo,
|
||||
struct netlink_ext_ack *extack);
|
||||
int dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev,
|
||||
struct netlink_ext_ack *extack);
|
||||
void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev);
|
||||
int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
@ -194,26 +194,51 @@ static int dsa_port_switchdev_sync(struct dsa_port *dp,
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_mdb_replay(br, brport_dev,
|
||||
&dsa_slave_switchdev_blocking_notifier,
|
||||
extack);
|
||||
err = br_mdb_replay(br, brport_dev, dp, true,
|
||||
&dsa_slave_switchdev_blocking_notifier, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_fdb_replay(br, brport_dev, &dsa_slave_switchdev_notifier);
|
||||
err = br_fdb_replay(br, brport_dev, dp, true,
|
||||
&dsa_slave_switchdev_notifier);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_vlan_replay(br, brport_dev,
|
||||
&dsa_slave_switchdev_blocking_notifier,
|
||||
extack);
|
||||
err = br_vlan_replay(br, brport_dev, dp, true,
|
||||
&dsa_slave_switchdev_blocking_notifier, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsa_port_switchdev_unsync(struct dsa_port *dp)
|
||||
static int dsa_port_switchdev_unsync_objs(struct dsa_port *dp,
|
||||
struct net_device *br,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
|
||||
int err;
|
||||
|
||||
/* Delete the switchdev objects left on this port */
|
||||
err = br_mdb_replay(br, brport_dev, dp, false,
|
||||
&dsa_slave_switchdev_blocking_notifier, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_fdb_replay(br, brport_dev, dp, false,
|
||||
&dsa_slave_switchdev_notifier);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
err = br_vlan_replay(br, brport_dev, dp, false,
|
||||
&dsa_slave_switchdev_blocking_notifier, extack);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
|
||||
{
|
||||
/* Configure the port for standalone mode (no address learning,
|
||||
* flood everything).
|
||||
@ -279,6 +304,12 @@ out_rollback:
|
||||
return err;
|
||||
}
|
||||
|
||||
int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return dsa_port_switchdev_unsync_objs(dp, br, extack);
|
||||
}
|
||||
|
||||
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
|
||||
{
|
||||
struct dsa_notifier_bridge_info info = {
|
||||
@ -298,7 +329,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
|
||||
if (err)
|
||||
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
|
||||
|
||||
dsa_port_switchdev_unsync(dp);
|
||||
dsa_port_switchdev_unsync_attrs(dp);
|
||||
}
|
||||
|
||||
int dsa_port_lag_change(struct dsa_port *dp,
|
||||
@ -366,6 +397,15 @@ err_lag_join:
|
||||
return err;
|
||||
}
|
||||
|
||||
int dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (dp->bridge_dev)
|
||||
return dsa_port_pre_bridge_leave(dp, dp->bridge_dev, extack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
|
||||
{
|
||||
struct dsa_notifier_lag_info info = {
|
||||
|
110
net/dsa/slave.c
110
net/dsa/slave.c
@ -271,13 +271,16 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
return phylink_mii_ioctl(p->dp->pl, ifr, cmd);
|
||||
}
|
||||
|
||||
static int dsa_slave_port_attr_set(struct net_device *dev,
|
||||
static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
int ret;
|
||||
|
||||
if (ctx && ctx != dp)
|
||||
return 0;
|
||||
|
||||
switch (attr->id) {
|
||||
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
|
||||
if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
|
||||
@ -394,13 +397,16 @@ static int dsa_slave_vlan_add(struct net_device *dev,
|
||||
return vlan_vid_add(master, htons(ETH_P_8021Q), vlan.vid);
|
||||
}
|
||||
|
||||
static int dsa_slave_port_obj_add(struct net_device *dev,
|
||||
static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
int err;
|
||||
|
||||
if (ctx && ctx != dp)
|
||||
return 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
||||
if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
|
||||
@ -469,12 +475,15 @@ static int dsa_slave_vlan_del(struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_slave_port_obj_del(struct net_device *dev,
|
||||
static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
int err;
|
||||
|
||||
if (ctx && ctx != dp)
|
||||
return 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
||||
if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
|
||||
@ -2068,6 +2077,26 @@ static int dsa_slave_changeupper(struct net_device *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dsa_slave_prechangeupper(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
struct netlink_ext_ack *extack;
|
||||
int err = 0;
|
||||
|
||||
extack = netdev_notifier_info_to_extack(&info->info);
|
||||
|
||||
if (netif_is_bridge_master(info->upper_dev) && !info->linking)
|
||||
err = dsa_port_pre_bridge_leave(dp, info->upper_dev, extack);
|
||||
else if (netif_is_lag_master(info->upper_dev) && !info->linking)
|
||||
err = dsa_port_pre_lag_leave(dp, info->upper_dev, extack);
|
||||
/* dsa_port_pre_hsr_leave is not yet necessary since hsr cannot be
|
||||
* meaningfully enslaved to a bridge yet
|
||||
*/
|
||||
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
static int
|
||||
dsa_slave_lag_changeupper(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
@ -2094,6 +2123,35 @@ dsa_slave_lag_changeupper(struct net_device *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Same as dsa_slave_lag_changeupper() except that it calls
|
||||
* dsa_slave_prechangeupper()
|
||||
*/
|
||||
static int
|
||||
dsa_slave_lag_prechangeupper(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
{
|
||||
struct net_device *lower;
|
||||
struct list_head *iter;
|
||||
int err = NOTIFY_DONE;
|
||||
struct dsa_port *dp;
|
||||
|
||||
netdev_for_each_lower_dev(dev, lower, iter) {
|
||||
if (!dsa_slave_dev_check(lower))
|
||||
continue;
|
||||
|
||||
dp = dsa_slave_to_port(lower);
|
||||
if (!dp->lag_dev)
|
||||
/* Software LAG */
|
||||
continue;
|
||||
|
||||
err = dsa_slave_prechangeupper(lower, info);
|
||||
if (notifier_to_errno(err))
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
dsa_prevent_bridging_8021q_upper(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
@ -2157,6 +2215,32 @@ dsa_slave_check_8021q_upper(struct net_device *dev,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int
|
||||
dsa_slave_prechangeupper_sanity_check(struct net_device *dev,
|
||||
struct netdev_notifier_changeupper_info *info)
|
||||
{
|
||||
struct dsa_switch *ds;
|
||||
struct dsa_port *dp;
|
||||
int err;
|
||||
|
||||
if (!dsa_slave_dev_check(dev))
|
||||
return dsa_prevent_bridging_8021q_upper(dev, info);
|
||||
|
||||
dp = dsa_slave_to_port(dev);
|
||||
ds = dp->ds;
|
||||
|
||||
if (ds->ops->port_prechangeupper) {
|
||||
err = ds->ops->port_prechangeupper(ds, dp->index, info);
|
||||
if (err)
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
if (is_vlan_dev(info->upper_dev))
|
||||
return dsa_slave_check_8021q_upper(dev, info);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@ -2165,24 +2249,18 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||||
switch (event) {
|
||||
case NETDEV_PRECHANGEUPPER: {
|
||||
struct netdev_notifier_changeupper_info *info = ptr;
|
||||
struct dsa_switch *ds;
|
||||
struct dsa_port *dp;
|
||||
int err;
|
||||
|
||||
if (!dsa_slave_dev_check(dev))
|
||||
return dsa_prevent_bridging_8021q_upper(dev, ptr);
|
||||
err = dsa_slave_prechangeupper_sanity_check(dev, info);
|
||||
if (err != NOTIFY_DONE)
|
||||
return err;
|
||||
|
||||
dp = dsa_slave_to_port(dev);
|
||||
ds = dp->ds;
|
||||
if (dsa_slave_dev_check(dev))
|
||||
return dsa_slave_prechangeupper(dev, ptr);
|
||||
|
||||
if (ds->ops->port_prechangeupper) {
|
||||
err = ds->ops->port_prechangeupper(ds, dp->index, info);
|
||||
if (err)
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
if (netif_is_lag_master(dev))
|
||||
return dsa_slave_lag_prechangeupper(dev, ptr);
|
||||
|
||||
if (is_vlan_dev(info->upper_dev))
|
||||
return dsa_slave_check_8021q_upper(dev, ptr);
|
||||
break;
|
||||
}
|
||||
case NETDEV_CHANGEUPPER:
|
||||
|
@ -381,19 +381,20 @@ EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
|
||||
static int __switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
int (*add_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
struct switchdev_notifier_info *info = &port_obj_info->info;
|
||||
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);
|
||||
extack = switchdev_notifier_info_to_extack(info);
|
||||
|
||||
if (check_cb(dev)) {
|
||||
err = add_cb(dev, port_obj_info->obj, extack);
|
||||
err = add_cb(dev, info->ctx, port_obj_info->obj, extack);
|
||||
if (err != -EOPNOTSUPP)
|
||||
port_obj_info->handled = true;
|
||||
return err;
|
||||
@ -422,7 +423,7 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
int switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
int (*add_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
@ -439,15 +440,16 @@ EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
|
||||
static 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),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
int (*del_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
struct switchdev_notifier_info *info = &port_obj_info->info;
|
||||
struct net_device *lower_dev;
|
||||
struct list_head *iter;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (check_cb(dev)) {
|
||||
err = del_cb(dev, port_obj_info->obj);
|
||||
err = del_cb(dev, info->ctx, port_obj_info->obj);
|
||||
if (err != -EOPNOTSUPP)
|
||||
port_obj_info->handled = true;
|
||||
return err;
|
||||
@ -476,7 +478,7 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev,
|
||||
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),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
int (*del_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
int err;
|
||||
@ -492,19 +494,20 @@ EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
|
||||
static int __switchdev_handle_port_attr_set(struct net_device *dev,
|
||||
struct switchdev_notifier_port_attr_info *port_attr_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*set_cb)(struct net_device *dev,
|
||||
int (*set_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
struct switchdev_notifier_info *info = &port_attr_info->info;
|
||||
struct netlink_ext_ack *extack;
|
||||
struct net_device *lower_dev;
|
||||
struct list_head *iter;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
extack = switchdev_notifier_info_to_extack(&port_attr_info->info);
|
||||
extack = switchdev_notifier_info_to_extack(info);
|
||||
|
||||
if (check_cb(dev)) {
|
||||
err = set_cb(dev, port_attr_info->attr, extack);
|
||||
err = set_cb(dev, info->ctx, port_attr_info->attr, extack);
|
||||
if (err != -EOPNOTSUPP)
|
||||
port_attr_info->handled = true;
|
||||
return err;
|
||||
@ -533,7 +536,7 @@ static int __switchdev_handle_port_attr_set(struct net_device *dev,
|
||||
int switchdev_handle_port_attr_set(struct net_device *dev,
|
||||
struct switchdev_notifier_port_attr_info *port_attr_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*set_cb)(struct net_device *dev,
|
||||
int (*set_cb)(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user