Commit f796fcd6 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bridge-port-offload'

Vladimir Oltean says:

====================
Let switchdev drivers offload and unoffload bridge ports at their own convenience

This series introduces an explicit API through which switchdev drivers
mark a bridge port as offloaded or not:
- switchdev_bridge_port_offload()
- switchdev_bridge_port_unoffload()

Currently, the bridge assumes that a port is offloaded if
dev_get_port_parent_id(dev, &ppid, recurse=true) returns something, but
that is just an assumption that breaks some use cases (like a
non-offloaded LAG interface on top of a switchdev port, bridged with
other switchdev ports).

Along with some consolidation of the bridge logic to assign a "switchdev
offloading mark" to a port (now better called a "hardware domain"), this
series allows the bridge driver side to no longer impose restrictions on
that configuration.

Right now, all switchdev drivers must be modified to use the explicit
API, but more and more logic can then be placed centrally in the bridge
and therefore ease the job of a switchdev driver writer in the future.

For example, the first thing we can hook into the explicit switchdev
offloading API calls are the switchdev object and FDB replay helpers.
So far, these have only been used by DSA in "pull" mode (where the
driver must ask for them). Adding the replay helpers to other drivers
involves a lot of repetition. But by moving the helpers inside the
bridge port offload/unoffload hook points, we can move the entire replay
process to "push" mode (where the bridge provides them automatically).

The explicit switchdev offloading API will see further extensions in the
future.

The patches were split from a larger series for easier review:
https://patchwork.kernel.org/project/netdevbpf/cover/20210718214434.3938850-1-vladimir.oltean@nxp.com/



Changes in v6:
- Make the switchdev replay helpers opt-in
- Opt out of the replay helpers for mlxsw, rocker, prestera, sparx5,
  cpsw, am65-cpsw
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c2255ff4 4e51bf44
Loading
Loading
Loading
Loading
+53 −14
Original line number Diff line number Diff line
@@ -1889,8 +1889,12 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
	return notifier_from_errno(err);
}

static struct notifier_block dpaa2_switch_port_switchdev_nb;
static struct notifier_block dpaa2_switch_port_switchdev_blocking_nb;

static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
					 struct net_device *upper_dev)
					 struct net_device *upper_dev,
					 struct netlink_ext_ack *extack)
{
	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
	struct ethsw_core *ethsw = port_priv->ethsw_data;
@@ -1906,8 +1910,8 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,

		other_port_priv = netdev_priv(other_dev);
		if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
			netdev_err(netdev,
				   "Interface from a different DPSW is in the bridge already!\n");
			NL_SET_ERR_MSG_MOD(extack,
					   "Interface from a different DPSW is in the bridge already");
			return -EINVAL;
		}
	}
@@ -1929,8 +1933,16 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
	if (err)
		goto err_egress_flood;

	err = switchdev_bridge_port_offload(netdev, netdev, NULL,
					    &dpaa2_switch_port_switchdev_nb,
					    &dpaa2_switch_port_switchdev_blocking_nb,
					    extack);
	if (err)
		goto err_switchdev_offload;

	return 0;

err_switchdev_offload:
err_egress_flood:
	dpaa2_switch_port_set_fdb(port_priv, NULL);
	return err;
@@ -1956,6 +1968,13 @@ static int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, vo
	return dpaa2_switch_port_vlan_add(arg, vlan_proto, vid);
}

static void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev)
{
	switchdev_bridge_port_unoffload(netdev, NULL,
					&dpaa2_switch_port_switchdev_nb,
					&dpaa2_switch_port_switchdev_blocking_nb);
}

static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
{
	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
@@ -2029,6 +2048,28 @@ static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *net
	return 0;
}

static int
dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
					  struct net_device *upper_dev,
					  struct netlink_ext_ack *extack)
{
	int err;

	if (!br_vlan_enabled(upper_dev)) {
		NL_SET_ERR_MSG_MOD(extack, "Cannot join a VLAN-unaware bridge");
		return -EOPNOTSUPP;
	}

	err = dpaa2_switch_prevent_bridging_with_8021q_upper(netdev);
	if (err) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Cannot join a bridge while VLAN uppers are present");
		return 0;
	}

	return 0;
}

static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
					     unsigned long event, void *ptr)
{
@@ -2049,25 +2090,23 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
		if (!netif_is_bridge_master(upper_dev))
			break;

		if (!br_vlan_enabled(upper_dev)) {
			NL_SET_ERR_MSG_MOD(extack, "Cannot join a VLAN-unaware bridge");
			err = -EOPNOTSUPP;
		err = dpaa2_switch_prechangeupper_sanity_checks(netdev,
								upper_dev,
								extack);
		if (err)
			goto out;
		}

		err = dpaa2_switch_prevent_bridging_with_8021q_upper(netdev);
		if (err) {
			NL_SET_ERR_MSG_MOD(extack,
					   "Cannot join a bridge while VLAN uppers are present");
			goto out;
		}
		if (!info->linking)
			dpaa2_switch_port_pre_bridge_leave(netdev);

		break;
	case NETDEV_CHANGEUPPER:
		upper_dev = info->upper_dev;
		if (netif_is_bridge_master(upper_dev)) {
			if (info->linking)
				err = dpaa2_switch_port_bridge_join(netdev, upper_dev);
				err = dpaa2_switch_port_bridge_join(netdev,
								    upper_dev,
								    extack);
			else
				err = dpaa2_switch_port_bridge_leave(netdev);
		}
+2 −1
Original line number Diff line number Diff line
@@ -746,7 +746,8 @@ static int prestera_netdev_port_event(struct net_device *lower,
	case NETDEV_CHANGEUPPER:
		if (netif_is_bridge_master(upper)) {
			if (info->linking)
				return prestera_bridge_port_join(upper, port);
				return prestera_bridge_port_join(upper, port,
								 extack);
			else
				prestera_bridge_port_leave(upper, port);
		} else if (netif_is_lag_master(upper)) {
+11 −1
Original line number Diff line number Diff line
@@ -480,7 +480,8 @@ prestera_bridge_1d_port_join(struct prestera_bridge_port *br_port)
}

int prestera_bridge_port_join(struct net_device *br_dev,
			      struct prestera_port *port)
			      struct prestera_port *port,
			      struct netlink_ext_ack *extack)
{
	struct prestera_switchdev *swdev = port->sw->swdev;
	struct prestera_bridge_port *br_port;
@@ -500,6 +501,11 @@ int prestera_bridge_port_join(struct net_device *br_dev,
		goto err_brport_create;
	}

	err = switchdev_bridge_port_offload(br_port->dev, port->dev, NULL,
					    NULL, NULL, extack);
	if (err)
		goto err_switchdev_offload;

	if (bridge->vlan_enabled)
		return 0;

@@ -510,6 +516,8 @@ int prestera_bridge_port_join(struct net_device *br_dev,
	return 0;

err_port_join:
	switchdev_bridge_port_unoffload(br_port->dev, NULL, NULL, NULL);
err_switchdev_offload:
	prestera_bridge_port_put(br_port);
err_brport_create:
	prestera_bridge_put(bridge);
@@ -584,6 +592,8 @@ void prestera_bridge_port_leave(struct net_device *br_dev,
	else
		prestera_bridge_1d_port_leave(br_port);

	switchdev_bridge_port_unoffload(br_port->dev, NULL, NULL, NULL);

	prestera_hw_port_learning_set(port, false);
	prestera_hw_port_flood_set(port, BR_FLOOD | BR_MCAST_FLOOD, 0);
	prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING);
+2 −1
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@ int prestera_switchdev_init(struct prestera_switch *sw);
void prestera_switchdev_fini(struct prestera_switch *sw);

int prestera_bridge_port_join(struct net_device *br_dev,
			      struct prestera_port *port);
			      struct prestera_port *port,
			      struct netlink_ext_ack *extack);

void prestera_bridge_port_leave(struct net_device *br_dev,
				struct prestera_port *port);
+19 −5
Original line number Diff line number Diff line
@@ -335,14 +335,16 @@ mlxsw_sp_bridge_port_find(struct mlxsw_sp_bridge *bridge,

static struct mlxsw_sp_bridge_port *
mlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device,
			    struct net_device *brport_dev)
			    struct net_device *brport_dev,
			    struct netlink_ext_ack *extack)
{
	struct mlxsw_sp_bridge_port *bridge_port;
	struct mlxsw_sp_port *mlxsw_sp_port;
	int err;

	bridge_port = kzalloc(sizeof(*bridge_port), GFP_KERNEL);
	if (!bridge_port)
		return NULL;
		return ERR_PTR(-ENOMEM);

	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(brport_dev);
	bridge_port->lagged = mlxsw_sp_port->lagged;
@@ -359,12 +361,23 @@ mlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device,
	list_add(&bridge_port->list, &bridge_device->ports_list);
	bridge_port->ref_count = 1;

	err = switchdev_bridge_port_offload(brport_dev, mlxsw_sp_port->dev,
					    NULL, NULL, NULL, extack);
	if (err)
		goto err_switchdev_offload;

	return bridge_port;

err_switchdev_offload:
	list_del(&bridge_port->list);
	kfree(bridge_port);
	return ERR_PTR(err);
}

static void
mlxsw_sp_bridge_port_destroy(struct mlxsw_sp_bridge_port *bridge_port)
{
	switchdev_bridge_port_unoffload(bridge_port->dev, NULL, NULL, NULL);
	list_del(&bridge_port->list);
	WARN_ON(!list_empty(&bridge_port->vlans_list));
	kfree(bridge_port);
@@ -390,9 +403,10 @@ mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge,
	if (IS_ERR(bridge_device))
		return ERR_CAST(bridge_device);

	bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev);
	if (!bridge_port) {
		err = -ENOMEM;
	bridge_port = mlxsw_sp_bridge_port_create(bridge_device, brport_dev,
						  extack);
	if (IS_ERR(bridge_port)) {
		err = PTR_ERR(bridge_port);
		goto err_bridge_port_create;
	}

Loading