Commit 8f4ef499 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-Remove-switchdev_ops'

Florian Fainelli says:

====================
net: Remove switchdev_ops

This patch series completes the removal of the switchdev_ops by
converting switchdev_port_attr_set() to use either the blocking
(process) or non-blocking (atomic) notifier since we typically need to
deal with both depending on where in the bridge code we get called from.

This was tested with the forwarding selftests and DSA hardware.

Ido, hopefully this captures your comments done on v1, if not, can you
illustrate with some pseudo-code what you had in mind if that's okay?

Changes in v3:

- added Reviewed-by tags from Ido where relevant
- added missing notifier_to_errno() in net/bridge/br_switchdev.c when
  calling the atomic notifier for PRE_BRIDGE_FLAGS
- kept mlxsw_sp_switchdev_init() in mlxsw/

Changes in v2:

- do not check for SWITCHDEV_F_DEFER when calling the blocking notifier
  and instead directly call the atomic notifier from the single location
  where this is required
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1d997875 3d705f07
...@@ -3660,7 +3660,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, ...@@ -3660,7 +3660,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
} }
mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan; mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
mlxsw_sp_port_switchdev_init(mlxsw_sp_port);
mlxsw_sp->ports[local_port] = mlxsw_sp_port; mlxsw_sp->ports[local_port] = mlxsw_sp_port;
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
...@@ -3677,7 +3676,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, ...@@ -3677,7 +3676,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
err_register_netdev: err_register_netdev:
mlxsw_sp->ports[local_port] = NULL; mlxsw_sp->ports[local_port] = NULL;
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
err_port_vlan_create: err_port_vlan_create:
err_port_pvid_set: err_port_pvid_set:
...@@ -3720,7 +3718,6 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -3720,7 +3718,6 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp); mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp);
unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
mlxsw_sp->ports[local_port] = NULL; mlxsw_sp->ports[local_port] = NULL;
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
mlxsw_sp_port_vlan_flush(mlxsw_sp_port, true); mlxsw_sp_port_vlan_flush(mlxsw_sp_port, true);
mlxsw_sp_port_nve_fini(mlxsw_sp_port); mlxsw_sp_port_nve_fini(mlxsw_sp_port);
mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port); mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
......
...@@ -407,8 +407,6 @@ extern const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals; ...@@ -407,8 +407,6 @@ extern const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals;
/* spectrum_switchdev.c */ /* spectrum_switchdev.c */
int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port);
void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port);
int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
bool adding); bool adding);
void void
......
...@@ -1938,10 +1938,6 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, ...@@ -1938,10 +1938,6 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
return NULL; return NULL;
} }
static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
.switchdev_port_attr_set = mlxsw_sp_port_attr_set,
};
static int static int
mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device, mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
struct mlxsw_sp_bridge_port *bridge_port, struct mlxsw_sp_bridge_port *bridge_port,
...@@ -3123,6 +3119,13 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused, ...@@ -3123,6 +3119,13 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
struct net_device *br_dev; struct net_device *br_dev;
int err; int err;
if (event == SWITCHDEV_PORT_ATTR_SET) {
err = switchdev_handle_port_attr_set(dev, ptr,
mlxsw_sp_port_dev_check,
mlxsw_sp_port_attr_set);
return notifier_from_errno(err);
}
/* Tunnel devices are not our uppers, so check their master instead */ /* Tunnel devices are not our uppers, so check their master instead */
br_dev = netdev_master_upper_dev_get_rcu(dev); br_dev = netdev_master_upper_dev_get_rcu(dev);
if (!br_dev) if (!br_dev)
...@@ -3446,6 +3449,11 @@ static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused, ...@@ -3446,6 +3449,11 @@ static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
mlxsw_sp_port_dev_check, mlxsw_sp_port_dev_check,
mlxsw_sp_port_obj_del); mlxsw_sp_port_obj_del);
return notifier_from_errno(err); return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
err = switchdev_handle_port_attr_set(dev, ptr,
mlxsw_sp_port_dev_check,
mlxsw_sp_port_attr_set);
return notifier_from_errno(err);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -3533,11 +3541,3 @@ void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) ...@@ -3533,11 +3541,3 @@ void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp)
kfree(mlxsw_sp->bridge); kfree(mlxsw_sp->bridge);
} }
void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops;
}
void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{
}
...@@ -1324,10 +1324,6 @@ static int ocelot_port_obj_del(struct net_device *dev, ...@@ -1324,10 +1324,6 @@ static int ocelot_port_obj_del(struct net_device *dev,
return ret; return ret;
} }
static const struct switchdev_ops ocelot_port_switchdev_ops = {
.switchdev_port_attr_set = ocelot_port_attr_set,
};
static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port,
struct net_device *bridge) struct net_device *bridge)
{ {
...@@ -1582,6 +1578,28 @@ struct notifier_block ocelot_netdevice_nb __read_mostly = { ...@@ -1582,6 +1578,28 @@ struct notifier_block ocelot_netdevice_nb __read_mostly = {
}; };
EXPORT_SYMBOL(ocelot_netdevice_nb); EXPORT_SYMBOL(ocelot_netdevice_nb);
static int ocelot_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
int err;
switch (event) {
case SWITCHDEV_PORT_ATTR_SET:
err = switchdev_handle_port_attr_set(dev, ptr,
ocelot_netdevice_dev_check,
ocelot_port_attr_set);
return notifier_from_errno(err);
}
return NOTIFY_DONE;
}
struct notifier_block ocelot_switchdev_nb __read_mostly = {
.notifier_call = ocelot_switchdev_event,
};
EXPORT_SYMBOL(ocelot_switchdev_nb);
static int ocelot_switchdev_blocking_event(struct notifier_block *unused, static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
...@@ -1600,6 +1618,11 @@ static int ocelot_switchdev_blocking_event(struct notifier_block *unused, ...@@ -1600,6 +1618,11 @@ static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
ocelot_netdevice_dev_check, ocelot_netdevice_dev_check,
ocelot_port_obj_del); ocelot_port_obj_del);
return notifier_from_errno(err); return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
err = switchdev_handle_port_attr_set(dev, ptr,
ocelot_netdevice_dev_check,
ocelot_port_attr_set);
return notifier_from_errno(err);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -1633,7 +1656,6 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, ...@@ -1633,7 +1656,6 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
dev->netdev_ops = &ocelot_port_netdev_ops; dev->netdev_ops = &ocelot_port_netdev_ops;
dev->ethtool_ops = &ocelot_ethtool_ops; dev->ethtool_ops = &ocelot_ethtool_ops;
dev->switchdev_ops = &ocelot_port_switchdev_ops;
dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS; dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
......
...@@ -499,6 +499,7 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, ...@@ -499,6 +499,7 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
struct phy_device *phy); struct phy_device *phy);
extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb; extern struct notifier_block ocelot_switchdev_blocking_nb;
#endif #endif
...@@ -329,6 +329,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -329,6 +329,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
} }
register_netdevice_notifier(&ocelot_netdevice_nb); register_netdevice_notifier(&ocelot_netdevice_nb);
register_switchdev_notifier(&ocelot_switchdev_nb);
register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
dev_info(&pdev->dev, "Ocelot switch probed\n"); dev_info(&pdev->dev, "Ocelot switch probed\n");
...@@ -345,6 +346,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev) ...@@ -345,6 +346,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
ocelot_deinit(ocelot); ocelot_deinit(ocelot);
unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
unregister_switchdev_notifier(&ocelot_switchdev_nb);
unregister_netdevice_notifier(&ocelot_netdevice_nb); unregister_netdevice_notifier(&ocelot_netdevice_nb);
return 0; return 0;
......
...@@ -2142,10 +2142,6 @@ static int rocker_port_obj_del(struct net_device *dev, ...@@ -2142,10 +2142,6 @@ static int rocker_port_obj_del(struct net_device *dev,
return err; return err;
} }
static const struct switchdev_ops rocker_port_switchdev_ops = {
.switchdev_port_attr_set = rocker_port_attr_set,
};
struct rocker_fib_event_work { struct rocker_fib_event_work {
struct work_struct work; struct work_struct work;
union { union {
...@@ -2599,7 +2595,6 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) ...@@ -2599,7 +2595,6 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
rocker_port_dev_addr_init(rocker_port); rocker_port_dev_addr_init(rocker_port);
dev->netdev_ops = &rocker_port_netdev_ops; dev->netdev_ops = &rocker_port_netdev_ops;
dev->ethtool_ops = &rocker_port_ethtool_ops; dev->ethtool_ops = &rocker_port_ethtool_ops;
dev->switchdev_ops = &rocker_port_switchdev_ops;
netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx, netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
NAPI_POLL_WEIGHT); NAPI_POLL_WEIGHT);
netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx, netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx,
...@@ -2710,6 +2705,19 @@ static bool rocker_port_dev_check(const struct net_device *dev) ...@@ -2710,6 +2705,19 @@ static bool rocker_port_dev_check(const struct net_device *dev)
return dev->netdev_ops == &rocker_port_netdev_ops; return dev->netdev_ops == &rocker_port_netdev_ops;
} }
static int
rocker_switchdev_port_attr_set_event(struct net_device *netdev,
struct switchdev_notifier_port_attr_info *port_attr_info)
{
int err;
err = rocker_port_attr_set(netdev, port_attr_info->attr,
port_attr_info->trans);
port_attr_info->handled = true;
return notifier_from_errno(err);
}
struct rocker_switchdev_event_work { struct rocker_switchdev_event_work {
struct work_struct work; struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info; struct switchdev_notifier_fdb_info fdb_info;
...@@ -2779,6 +2787,9 @@ static int rocker_switchdev_event(struct notifier_block *unused, ...@@ -2779,6 +2787,9 @@ static int rocker_switchdev_event(struct notifier_block *unused,
if (!rocker_port_dev_check(dev)) if (!rocker_port_dev_check(dev))
return NOTIFY_DONE; return NOTIFY_DONE;
if (event == SWITCHDEV_PORT_ATTR_SET)
return rocker_switchdev_port_attr_set_event(dev, ptr);
rocker_port = netdev_priv(dev); rocker_port = netdev_priv(dev);
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (WARN_ON(!switchdev_work)) if (WARN_ON(!switchdev_work))
...@@ -2841,6 +2852,8 @@ static int rocker_switchdev_blocking_event(struct notifier_block *unused, ...@@ -2841,6 +2852,8 @@ static int rocker_switchdev_blocking_event(struct notifier_block *unused,
case SWITCHDEV_PORT_OBJ_ADD: case SWITCHDEV_PORT_OBJ_ADD:
case SWITCHDEV_PORT_OBJ_DEL: case SWITCHDEV_PORT_OBJ_DEL:
return rocker_switchdev_port_obj_event(event, dev, ptr); return rocker_switchdev_port_obj_event(event, dev, ptr);
case SWITCHDEV_PORT_ATTR_SET:
return rocker_switchdev_port_attr_set_event(dev, ptr);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
......
...@@ -925,9 +925,18 @@ static int swdev_port_obj_del(struct net_device *netdev, ...@@ -925,9 +925,18 @@ static int swdev_port_obj_del(struct net_device *netdev,
return err; return err;
} }
static const struct switchdev_ops ethsw_port_switchdev_ops = { static int
.switchdev_port_attr_set = swdev_port_attr_set, ethsw_switchdev_port_attr_set_event(struct net_device *netdev,
}; struct switchdev_notifier_port_attr_info *port_attr_info)
{
int err;
err = swdev_port_attr_set(netdev, port_attr_info->attr,
port_attr_info->trans);
port_attr_info->handled = true;
return notifier_from_errno(err);
}
/* For the moment, only flood setting needs to be updated */ /* For the moment, only flood setting needs to be updated */
static int port_bridge_join(struct net_device *netdev, static int port_bridge_join(struct net_device *netdev,
...@@ -1047,6 +1056,12 @@ static int port_switchdev_event(struct notifier_block *unused, ...@@ -1047,6 +1056,12 @@ static int port_switchdev_event(struct notifier_block *unused,
struct ethsw_switchdev_event_work *switchdev_work; struct ethsw_switchdev_event_work *switchdev_work;
struct switchdev_notifier_fdb_info *fdb_info = ptr; struct switchdev_notifier_fdb_info *fdb_info = ptr;
if (!ethsw_port_dev_check(dev))
return NOTIFY_DONE;
if (event == SWITCHDEV_PORT_ATTR_SET)
return ethsw_switchdev_port_attr_set_event(dev, ptr);
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (!switchdev_work) if (!switchdev_work)
return NOTIFY_BAD; return NOTIFY_BAD;
...@@ -1115,6 +1130,8 @@ static int port_switchdev_blocking_event(struct notifier_block *unused, ...@@ -1115,6 +1130,8 @@ static int port_switchdev_blocking_event(struct notifier_block *unused,
case SWITCHDEV_PORT_OBJ_ADD: /* fall through */ case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
case SWITCHDEV_PORT_OBJ_DEL: case SWITCHDEV_PORT_OBJ_DEL:
return ethsw_switchdev_port_obj_event(event, dev, ptr); return ethsw_switchdev_port_obj_event(event, dev, ptr);
case SWITCHDEV_PORT_ATTR_SET:
return ethsw_switchdev_port_attr_set_event(dev, ptr);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -1434,7 +1451,6 @@ static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx) ...@@ -1434,7 +1451,6 @@ static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
SET_NETDEV_DEV(port_netdev, dev); SET_NETDEV_DEV(port_netdev, dev);
port_netdev->netdev_ops = &ethsw_port_ops; port_netdev->netdev_ops = &ethsw_port_ops;
port_netdev->ethtool_ops = &ethsw_port_ethtool_ops; port_netdev->ethtool_ops = &ethsw_port_ethtool_ops;
port_netdev->switchdev_ops = &ethsw_port_switchdev_ops;
/* Set MTU limits */ /* Set MTU limits */
port_netdev->min_mtu = ETH_MIN_MTU; port_netdev->min_mtu = ETH_MIN_MTU;
......
...@@ -1843,9 +1843,6 @@ struct net_device { ...@@ -1843,9 +1843,6 @@ struct net_device {
#endif #endif
const struct net_device_ops *netdev_ops; const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops; const struct ethtool_ops *ethtool_ops;
#ifdef CONFIG_NET_SWITCHDEV
const struct switchdev_ops *switchdev_ops;
#endif
#ifdef CONFIG_NET_L3_MASTER_DEV #ifdef CONFIG_NET_L3_MASTER_DEV
const struct l3mdev_ops *l3mdev_ops; const struct l3mdev_ops *l3mdev_ops;
#endif #endif
......
...@@ -112,17 +112,6 @@ void *switchdev_trans_item_dequeue(struct switchdev_trans *trans); ...@@ -112,17 +112,6 @@ void *switchdev_trans_item_dequeue(struct switchdev_trans *trans);
typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj); typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
/**
* struct switchdev_ops - switchdev operations
*
* @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
*/
struct switchdev_ops {
int (*switchdev_port_attr_set)(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans);
};
enum switchdev_notifier_type { enum switchdev_notifier_type {
SWITCHDEV_FDB_ADD_TO_BRIDGE = 1, SWITCHDEV_FDB_ADD_TO_BRIDGE = 1,
SWITCHDEV_FDB_DEL_TO_BRIDGE, SWITCHDEV_FDB_DEL_TO_BRIDGE,
...@@ -132,6 +121,7 @@ enum switchdev_notifier_type { ...@@ -132,6 +121,7 @@ enum switchdev_notifier_type {
SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */ SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */ SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
SWITCHDEV_PORT_ATTR_SET, /* May be blocking . */
SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE, SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE, SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
...@@ -160,6 +150,13 @@ struct switchdev_notifier_port_obj_info { ...@@ -160,6 +150,13 @@ struct switchdev_notifier_port_obj_info {
bool handled; bool handled;
}; };
struct switchdev_notifier_port_attr_info {
struct switchdev_notifier_info info; /* must be first */
const struct switchdev_attr *attr;
struct switchdev_trans *trans;
bool handled;
};
static inline struct net_device * static inline struct net_device *
switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info) switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
{ {
...@@ -212,7 +209,12 @@ int switchdev_handle_port_obj_del(struct net_device *dev, ...@@ -212,7 +209,12 @@ int switchdev_handle_port_obj_del(struct net_device *dev,
int (*del_cb)(struct net_device *dev, int (*del_cb)(struct net_device *dev,
const struct switchdev_obj *obj)); const struct switchdev_obj *obj));
#define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops)) 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,
const struct switchdev_attr *attr,
struct switchdev_trans *trans));
#else #else
static inline void switchdev_deferred_process(void) static inline void switchdev_deferred_process(void)
...@@ -299,8 +301,16 @@ switchdev_handle_port_obj_del(struct net_device *dev, ...@@ -299,8 +301,16 @@ switchdev_handle_port_obj_del(struct net_device *dev,
return 0; return 0;
} }
#define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0) 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,
const struct switchdev_attr *attr,
struct switchdev_trans *trans))
{
return 0;
}
#endif #endif
#endif /* _LINUX_SWITCHDEV_H_ */ #endif /* _LINUX_SWITCHDEV_H_ */
...@@ -67,12 +67,18 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, ...@@ -67,12 +67,18 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS, .id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
.u.brport_flags = mask, .u.brport_flags = mask,
}; };
struct switchdev_notifier_port_attr_info info = {
.attr = &attr,
};
int err; int err;
if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD) if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
return 0; return 0;
err = switchdev_port_attr_set(p->dev, &attr); /* We run from atomic context here */
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
&info.info, NULL);
err = notifier_to_errno(err);
if (err == -EOPNOTSUPP) if (err == -EOPNOTSUPP)
return 0; return 0;
......
...@@ -1118,10 +1118,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -1118,10 +1118,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid,
}; };
static const struct switchdev_ops dsa_slave_switchdev_ops = {
.switchdev_port_attr_set = dsa_slave_port_attr_set,
};
static struct device_type dsa_type = { static struct device_type dsa_type = {
.name = "dsa", .name = "dsa",
}; };
...@@ -1382,7 +1378,6 @@ int dsa_slave_create(struct dsa_port *port) ...@@ -1382,7 +1378,6 @@ int dsa_slave_create(struct dsa_port *port)
eth_hw_addr_inherit(slave_dev, master); eth_hw_addr_inherit(slave_dev, master);
slave_dev->priv_flags |= IFF_NO_QUEUE; slave_dev->priv_flags |= IFF_NO_QUEUE;
slave_dev->netdev_ops = &dsa_slave_netdev_ops; slave_dev->netdev_ops = &dsa_slave_netdev_ops;
slave_dev->switchdev_ops = &dsa_slave_switchdev_ops;
slave_dev->min_mtu = 0; slave_dev->min_mtu = 0;
slave_dev->max_mtu = ETH_MAX_MTU; slave_dev->max_mtu = ETH_MAX_MTU;
SET_NETDEV_DEVTYPE(slave_dev, &dsa_type); SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
...@@ -1524,6 +1519,19 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, ...@@ -1524,6 +1519,19 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static int
dsa_slave_switchdev_port_attr_set_event(struct net_device *netdev,
struct switchdev_notifier_port_attr_info *port_attr_info)
{
int err;
err = dsa_slave_port_attr_set(netdev, port_attr_info->attr,
port_attr_info->trans);
port_attr_info->handled = true;
return notifier_from_errno(err);
}
struct dsa_switchdev_event_work { struct dsa_switchdev_event_work {
struct work_struct work; struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info; struct switchdev_notifier_fdb_info fdb_info;
...@@ -1602,6 +1610,9 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused, ...@@ -1602,6 +1610,9 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
if (!dsa_slave_dev_check(dev)) if (!dsa_slave_dev_check(dev))
return NOTIFY_DONE; return NOTIFY_DONE;
if (event == SWITCHDEV_PORT_ATTR_SET)
return dsa_slave_switchdev_port_attr_set_event(dev, ptr);
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
if (!switchdev_work) if (!switchdev_work)
return NOTIFY_BAD; return NOTIFY_BAD;
...@@ -1664,6 +1675,8 @@ static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused, ...@@ -1664,6 +1675,8 @@ static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
case SWITCHDEV_PORT_OBJ_ADD: /* fall through */ case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
case SWITCHDEV_PORT_OBJ_DEL: case SWITCHDEV_PORT_OBJ_DEL:
return dsa_slave_switchdev_port_obj_event(event, dev, ptr); return dsa_slave_switchdev_port_obj_event(event, dev, ptr);
case SWITCHDEV_PORT_ATTR_SET:
return dsa_slave_switchdev_port_attr_set_event(dev, ptr);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
......
...@@ -174,39 +174,32 @@ static int switchdev_deferred_enqueue(struct net_device *dev, ...@@ -174,39 +174,32 @@ static int switchdev_deferred_enqueue(struct net_device *dev,
return 0; return 0;
} }
static int __switchdev_port_attr_set(struct net_device *dev, static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
const struct switchdev_attr *attr, struct net_device *dev,
struct switchdev_trans *trans) const struct switchdev_attr *attr,
struct switchdev_trans *trans)
{ {
const struct switchdev_ops *ops = dev->switchdev_ops; int err;
struct net_device *lower_dev; int rc;
struct list_head *iter;
int err = -EOPNOTSUPP;
if (ops && ops->switchdev_port_attr_set) {
err = ops->switchdev_port_attr_set(dev, attr, trans);
goto done;
}
if (attr->flags & SWITCHDEV_F_NO_RECURSE)
goto done;
/* Switch device port(s) may be stacked under struct switchdev_notifier_port_attr_info attr_info = {
* bond/team/vlan dev, so recurse down to set attr on .attr = attr,
* each port. .trans = trans,
*/ .handled = false,
};
netdev_for_each_lower_dev(dev, lower_dev, iter) { rc = call_switchdev_blocking_notifiers(nt, dev,
err = __switchdev_port_attr_set(lower_dev, attr, trans); &attr_info.info, NULL);
if (err) err = notifier_to_errno(rc);
break; if (err) {
WARN_ON(!attr_info.handled);
return err;
} }
done: if (!attr_info.handled)
if (err == -EOPNOTSUPP && attr->flags & SWITCHDEV_F_SKIP_EOPNOTSUPP) return -EOPNOTSUPP;
err = 0;
return err; return 0;
} }
static int switchdev_port_attr_set_now(struct net_device *dev, static int switchdev_port_attr_set_now(struct net_device *dev,
...@@ -225,7 +218,8 @@ static int switchdev_port_attr_set_now(struct net_device *dev, ...@@ -225,7 +218,8 @@ static int switchdev_port_attr_set_now(struct net_device *dev,
*/ */
trans.ph_prepare = true; trans.ph_prepare = true;
err = __switchdev_port_attr_set(dev, attr, &trans); err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
&trans);
if (err) { if (err) {
/* Prepare phase failed: abort the transaction. Any /* Prepare phase failed: abort the transaction. Any
* resources reserved in the prepare phase are * resources reserved in the prepare phase are
...@@ -244,7 +238,8 @@ static int switchdev_port_attr_set_now(struct net_device *dev, ...@@ -244,7 +238,8 @@ static int switchdev_port_attr_set_now(struct net_device *dev,
*/ */
trans.ph_prepare = false; trans.ph_prepare = false;
err = __switchdev_port_attr_set(dev, attr, &trans); err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
&trans);
WARN(err, "%s: Commit of attribute (id=%d) failed.\n", WARN(err, "%s: Commit of attribute (id=%d) failed.\n",
dev->name, attr->id); dev->name, attr->id);
switchdev_trans_items_warn_destroy(dev, &trans); switchdev_trans_items_warn_destroy(dev, &trans);
...@@ -655,3 +650,54 @@ int switchdev_handle_port_obj_del(struct net_device *dev, ...@@ -655,3 +650,54 @@ int switchdev_handle_port_obj_del(struct net_device *dev,
return err; return err;
} }
EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del); 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,
const struct switchdev_attr *attr,
struct switchdev_trans *trans))
{
struct net_device *lower_dev;
struct list_head *iter;
int err = -EOPNOTSUPP;
if (check_cb(dev)) {
port_attr_info->handled = true;
return set_cb(dev, port_attr_info->attr,
port_attr_info->trans);
}
/* Switch ports might be stacked under e.g. a LAG. Ignore the
* unsupported devices, another driver might be able to handle them. But
* propagate to the callers any hard errors.
*
* If the driver does its own bookkeeping of stacked ports, it's not
* necessary to go through this helper.
*/
netdev_for_each_lower_dev(dev, lower_dev, iter) {
err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info,
check_cb, set_cb);
if (err && err != -EOPNOTSUPP)
return err;
}
return err;
}
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,
const struct switchdev_attr *attr,
struct switchdev_trans *trans))
{
int err;
err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb,
set_cb);
if (err == -EOPNOTSUPP)
err = 0;
return err;
}
EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment