Commit 28cfea98 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge tag 'mlx5-updates-2023-05-31' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux

Saeed Mahameed says:

====================
mlx5-updates-2023-05-31

net/mlx5: Support 4 ports VF LAG, part 1/2

This series continues the series[1] "Support 4 ports HCAs LAG mode"
by Mark Bloch. This series adds support for 4 ports VF LAG (single FDB
E-Switch).

This series of patches focuses on refactoring different sections of the
code that make assumptions about VF LAG supporting only two ports. For
instance, it assumes that each device can only have one peer.

Patches 1-5:
- Refactor ETH handling of TC rules of eswitches with peers.
Patch 6:
- Refactors peer miss group table.
Patches 7-9:
- Refactor single FDB E-Switch creation.
Patch 10:
- Refactor the DR layer.
Patches 11-14:
- Refactors devcom layer.

Next series will refactor LAG layer and enable 4 ports VF LAG.
This series specifically allows HCAs with 4 ports to create a VF LAG
with only 4 ports. It is not possible to create a VF LAG with 2 or 3
ports using HCAs that have 4 ports.

Currently, the Merged E-Switch feature only supports HCAs with 2 ports.
However, upcoming patches will introduce support for HCAs with 4 ports.

In order to activate VF LAG a user can execute:

devlink dev eswitch set pci/0000:08:00.0 mode switchdev
devlink dev eswitch set pci/0000:08:00.1 mode switchdev
devlink dev eswitch set pci/0000:08:00.2 mode switchdev
devlink dev eswitch set pci/0000:08:00.3 mode switchdev
ip link add name bond0 type bond
ip link set dev bond0 type bond mode 802.3ad
ip link set dev eth2 master bond0
ip link set dev eth3 master bond0
ip link set dev eth4 master bond0
ip link set dev eth5 master bond0

Where eth2, eth3, eth4 and eth5 are net-interfaces of pci/0000:08:00.0
pci/0000:08:00.1 pci/0000:08:00.2 pci/0000:08:00.3 respectively.

User can verify LAG state and type via debugfs:
/sys/kernel/debug/mlx5/0000\:08\:00.0/lag/state
/sys/kernel/debug/mlx5/0000\:08\:00.0/lag/type

[1]
https://lore.kernel.org/netdev/20220510055743.118828-1-saeedm@nvidia.com/

* tag 'mlx5-updates-2023-05-31' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux:
  net/mlx5: Devcom, extend mlx5_devcom_send_event to work with more than two devices
  net/mlx5: Devcom, introduce devcom_for_each_peer_entry
  net/mlx5: E-switch, mark devcom as not ready when all eswitches are unpaired
  net/mlx5: Devcom, Rename paired to ready
  net/mlx5: DR, handle more than one peer domain
  net/mlx5: E-switch, generalize shared FDB creation
  net/mlx5: E-switch, Handle multiple master egress rules
  net/mlx5: E-switch, refactor FDB miss rule add/remove
  net/mlx5: E-switch, enlarge peer miss group table
  net/mlx5e: Handle offloads flows per peer
  net/mlx5e: en_tc, re-factor query route port
  net/mlx5e: rep, store send to vport rules per peer
  net/mlx5e: tc, Refactor peer add/del flow
  net/mlx5e: en_tc, Extend peer flows to a list
====================

Link: https://lore.kernel.org/r/20230602191301.47004-1-saeed@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents c422ac94 e2a82bf8
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -94,13 +94,13 @@ struct mlx5e_tc_flow {
	 * destinations.
	 * destinations.
	 */
	 */
	struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
	struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
	struct mlx5e_tc_flow *peer_flow;
	struct mlx5e_hairpin_entry *hpe; /* attached hairpin instance */
	struct mlx5e_hairpin_entry *hpe; /* attached hairpin instance */
	struct list_head hairpin; /* flows sharing the same hairpin */
	struct list_head hairpin; /* flows sharing the same hairpin */
	struct list_head peer;    /* flows with peer flow */
	struct list_head peer[MLX5_MAX_PORTS];    /* flows with peer flow */
	struct list_head unready; /* flows not ready to be offloaded (e.g
	struct list_head unready; /* flows not ready to be offloaded (e.g
				   * due to missing route)
				   * due to missing route)
				   */
				   */
	struct list_head peer_flows; /* flows on peer */
	struct net_device *orig_dev; /* netdev adding flow first */
	struct net_device *orig_dev; /* netdev adding flow first */
	int tmp_entry_index;
	int tmp_entry_index;
	struct list_head tmp_list; /* temporary flow list used by neigh update */
	struct list_head tmp_list; /* temporary flow list used by neigh update */
+110 −27
Original line number Original line Diff line number Diff line
@@ -374,7 +374,9 @@ static void mlx5e_sqs2vport_stop(struct mlx5_eswitch *esw,
				 struct mlx5_eswitch_rep *rep)
				 struct mlx5_eswitch_rep *rep)
{
{
	struct mlx5e_rep_sq *rep_sq, *tmp;
	struct mlx5e_rep_sq *rep_sq, *tmp;
	struct mlx5e_rep_sq_peer *sq_peer;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_priv *rpriv;
	unsigned long i;


	if (esw->mode != MLX5_ESWITCH_OFFLOADS)
	if (esw->mode != MLX5_ESWITCH_OFFLOADS)
		return;
		return;
@@ -382,31 +384,78 @@ static void mlx5e_sqs2vport_stop(struct mlx5_eswitch *esw,
	rpriv = mlx5e_rep_to_rep_priv(rep);
	rpriv = mlx5e_rep_to_rep_priv(rep);
	list_for_each_entry_safe(rep_sq, tmp, &rpriv->vport_sqs_list, list) {
	list_for_each_entry_safe(rep_sq, tmp, &rpriv->vport_sqs_list, list) {
		mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule);
		mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule);
		if (rep_sq->send_to_vport_rule_peer)
		xa_for_each(&rep_sq->sq_peer, i, sq_peer) {
			mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule_peer);
			if (sq_peer->rule)
				mlx5_eswitch_del_send_to_vport_rule(sq_peer->rule);

			xa_erase(&rep_sq->sq_peer, i);
			kfree(sq_peer);
		}

		xa_destroy(&rep_sq->sq_peer);
		list_del(&rep_sq->list);
		list_del(&rep_sq->list);
		kfree(rep_sq);
		kfree(rep_sq);
	}
	}
}
}


static int mlx5e_sqs2vport_add_peers_rules(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep,
					   struct mlx5_devcom *devcom,
					   struct mlx5e_rep_sq *rep_sq, int i)
{
	struct mlx5_eswitch *peer_esw = NULL;
	struct mlx5_flow_handle *flow_rule;
	int tmp;

	mlx5_devcom_for_each_peer_entry(devcom, MLX5_DEVCOM_ESW_OFFLOADS,
					peer_esw, tmp) {
		int peer_rule_idx = mlx5_get_dev_index(peer_esw->dev);
		struct mlx5e_rep_sq_peer *sq_peer;
		int err;

		sq_peer = kzalloc(sizeof(*sq_peer), GFP_KERNEL);
		if (!sq_peer)
			return -ENOMEM;

		flow_rule = mlx5_eswitch_add_send_to_vport_rule(peer_esw, esw,
								rep, rep_sq->sqn);
		if (IS_ERR(flow_rule)) {
			kfree(sq_peer);
			return PTR_ERR(flow_rule);
		}

		sq_peer->rule = flow_rule;
		sq_peer->peer = peer_esw;
		err = xa_insert(&rep_sq->sq_peer, peer_rule_idx, sq_peer, GFP_KERNEL);
		if (err) {
			kfree(sq_peer);
			mlx5_eswitch_del_send_to_vport_rule(flow_rule);
			return err;
		}
	}

	return 0;
}

static int mlx5e_sqs2vport_start(struct mlx5_eswitch *esw,
static int mlx5e_sqs2vport_start(struct mlx5_eswitch *esw,
				 struct mlx5_eswitch_rep *rep,
				 struct mlx5_eswitch_rep *rep,
				 u32 *sqns_array, int sqns_num)
				 u32 *sqns_array, int sqns_num)
{
{
	struct mlx5_eswitch *peer_esw = NULL;
	struct mlx5_flow_handle *flow_rule;
	struct mlx5_flow_handle *flow_rule;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_sq *rep_sq;
	struct mlx5e_rep_sq *rep_sq;
	struct mlx5_devcom *devcom;
	bool devcom_locked = false;
	int err;
	int err;
	int i;
	int i;


	if (esw->mode != MLX5_ESWITCH_OFFLOADS)
	if (esw->mode != MLX5_ESWITCH_OFFLOADS)
		return 0;
		return 0;


	devcom = esw->dev->priv.devcom;
	rpriv = mlx5e_rep_to_rep_priv(rep);
	rpriv = mlx5e_rep_to_rep_priv(rep);
	if (mlx5_devcom_is_paired(esw->dev->priv.devcom, MLX5_DEVCOM_ESW_OFFLOADS))
	if (mlx5_devcom_comp_is_ready(devcom, MLX5_DEVCOM_ESW_OFFLOADS) &&
		peer_esw = mlx5_devcom_get_peer_data(esw->dev->priv.devcom,
	    mlx5_devcom_for_each_peer_begin(devcom, MLX5_DEVCOM_ESW_OFFLOADS))
						     MLX5_DEVCOM_ESW_OFFLOADS);
		devcom_locked = true;


	for (i = 0; i < sqns_num; i++) {
	for (i = 0; i < sqns_num; i++) {
		rep_sq = kzalloc(sizeof(*rep_sq), GFP_KERNEL);
		rep_sq = kzalloc(sizeof(*rep_sq), GFP_KERNEL);
@@ -426,31 +475,30 @@ static int mlx5e_sqs2vport_start(struct mlx5_eswitch *esw,
		rep_sq->send_to_vport_rule = flow_rule;
		rep_sq->send_to_vport_rule = flow_rule;
		rep_sq->sqn = sqns_array[i];
		rep_sq->sqn = sqns_array[i];


		if (peer_esw) {
		xa_init(&rep_sq->sq_peer);
			flow_rule = mlx5_eswitch_add_send_to_vport_rule(peer_esw, esw,
		if (devcom_locked) {
									rep, sqns_array[i]);
			err = mlx5e_sqs2vport_add_peers_rules(esw, rep, devcom, rep_sq, i);
			if (IS_ERR(flow_rule)) {
			if (err) {
				err = PTR_ERR(flow_rule);
				mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule);
				mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule);
				xa_destroy(&rep_sq->sq_peer);
				kfree(rep_sq);
				kfree(rep_sq);
				goto out_err;
				goto out_err;
			}
			}
			rep_sq->send_to_vport_rule_peer = flow_rule;
		}
		}


		list_add(&rep_sq->list, &rpriv->vport_sqs_list);
		list_add(&rep_sq->list, &rpriv->vport_sqs_list);
	}
	}


	if (peer_esw)
	if (devcom_locked)
		mlx5_devcom_release_peer_data(esw->dev->priv.devcom, MLX5_DEVCOM_ESW_OFFLOADS);
		mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);


	return 0;
	return 0;


out_err:
out_err:
	mlx5e_sqs2vport_stop(esw, rep);
	mlx5e_sqs2vport_stop(esw, rep);


	if (peer_esw)
	if (devcom_locked)
		mlx5_devcom_release_peer_data(esw->dev->priv.devcom, MLX5_DEVCOM_ESW_OFFLOADS);
		mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);


	return err;
	return err;
}
}
@@ -1530,17 +1578,24 @@ static void *mlx5e_vport_rep_get_proto_dev(struct mlx5_eswitch_rep *rep)
	return rpriv->netdev;
	return rpriv->netdev;
}
}


static void mlx5e_vport_rep_event_unpair(struct mlx5_eswitch_rep *rep)
static void mlx5e_vport_rep_event_unpair(struct mlx5_eswitch_rep *rep,
					 struct mlx5_eswitch *peer_esw)
{
{
	int i = mlx5_get_dev_index(peer_esw->dev);
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_sq *rep_sq;
	struct mlx5e_rep_sq *rep_sq;


	WARN_ON_ONCE(!peer_esw);
	rpriv = mlx5e_rep_to_rep_priv(rep);
	rpriv = mlx5e_rep_to_rep_priv(rep);
	list_for_each_entry(rep_sq, &rpriv->vport_sqs_list, list) {
	list_for_each_entry(rep_sq, &rpriv->vport_sqs_list, list) {
		if (!rep_sq->send_to_vport_rule_peer)
		struct mlx5e_rep_sq_peer *sq_peer = xa_load(&rep_sq->sq_peer, i);

		if (!sq_peer || sq_peer->peer != peer_esw)
			continue;
			continue;
		mlx5_eswitch_del_send_to_vport_rule(rep_sq->send_to_vport_rule_peer);

		rep_sq->send_to_vport_rule_peer = NULL;
		mlx5_eswitch_del_send_to_vport_rule(sq_peer->rule);
		xa_erase(&rep_sq->sq_peer, i);
		kfree(sq_peer);
	}
	}
}
}


@@ -1548,24 +1603,52 @@ static int mlx5e_vport_rep_event_pair(struct mlx5_eswitch *esw,
				      struct mlx5_eswitch_rep *rep,
				      struct mlx5_eswitch_rep *rep,
				      struct mlx5_eswitch *peer_esw)
				      struct mlx5_eswitch *peer_esw)
{
{
	int i = mlx5_get_dev_index(peer_esw->dev);
	struct mlx5_flow_handle *flow_rule;
	struct mlx5_flow_handle *flow_rule;
	struct mlx5e_rep_sq_peer *sq_peer;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_priv *rpriv;
	struct mlx5e_rep_sq *rep_sq;
	struct mlx5e_rep_sq *rep_sq;
	int err;


	rpriv = mlx5e_rep_to_rep_priv(rep);
	rpriv = mlx5e_rep_to_rep_priv(rep);
	list_for_each_entry(rep_sq, &rpriv->vport_sqs_list, list) {
	list_for_each_entry(rep_sq, &rpriv->vport_sqs_list, list) {
		if (rep_sq->send_to_vport_rule_peer)
		sq_peer = xa_load(&rep_sq->sq_peer, i);

		if (sq_peer && sq_peer->peer)
			continue;
			continue;
		flow_rule = mlx5_eswitch_add_send_to_vport_rule(peer_esw, esw, rep, rep_sq->sqn);

		if (IS_ERR(flow_rule))
		flow_rule = mlx5_eswitch_add_send_to_vport_rule(peer_esw, esw, rep,
								rep_sq->sqn);
		if (IS_ERR(flow_rule)) {
			err = PTR_ERR(flow_rule);
			goto err_out;
			goto err_out;
		rep_sq->send_to_vport_rule_peer = flow_rule;
		}

		if (sq_peer) {
			sq_peer->rule = flow_rule;
			sq_peer->peer = peer_esw;
			continue;
		}
		sq_peer = kzalloc(sizeof(*sq_peer), GFP_KERNEL);
		if (!sq_peer) {
			err = -ENOMEM;
			goto err_sq_alloc;
		}
		err = xa_insert(&rep_sq->sq_peer, i, sq_peer, GFP_KERNEL);
		if (err)
			goto err_xa;
		sq_peer->rule = flow_rule;
		sq_peer->peer = peer_esw;
	}
	}


	return 0;
	return 0;
err_xa:
	kfree(sq_peer);
err_sq_alloc:
	mlx5_eswitch_del_send_to_vport_rule(flow_rule);
err_out:
err_out:
	mlx5e_vport_rep_event_unpair(rep);
	mlx5e_vport_rep_event_unpair(rep, peer_esw);
	return PTR_ERR(flow_rule);
	return err;
}
}


static int mlx5e_vport_rep_event(struct mlx5_eswitch *esw,
static int mlx5e_vport_rep_event(struct mlx5_eswitch *esw,
@@ -1578,7 +1661,7 @@ static int mlx5e_vport_rep_event(struct mlx5_eswitch *esw,
	if (event == MLX5_SWITCHDEV_EVENT_PAIR)
	if (event == MLX5_SWITCHDEV_EVENT_PAIR)
		err = mlx5e_vport_rep_event_pair(esw, rep, data);
		err = mlx5e_vport_rep_event_pair(esw, rep, data);
	else if (event == MLX5_SWITCHDEV_EVENT_UNPAIR)
	else if (event == MLX5_SWITCHDEV_EVENT_UNPAIR)
		mlx5e_vport_rep_event_unpair(rep);
		mlx5e_vport_rep_event_unpair(rep, data);


	return err;
	return err;
}
}
+6 −1
Original line number Original line Diff line number Diff line
@@ -225,9 +225,14 @@ struct mlx5e_encap_entry {
	struct rcu_head rcu;
	struct rcu_head rcu;
};
};


struct mlx5e_rep_sq_peer {
	struct mlx5_flow_handle *rule;
	void *peer;
};

struct mlx5e_rep_sq {
struct mlx5e_rep_sq {
	struct mlx5_flow_handle	*send_to_vport_rule;
	struct mlx5_flow_handle	*send_to_vport_rule;
	struct mlx5_flow_handle *send_to_vport_rule_peer;
	struct xarray sq_peer;
	u32 sqn;
	u32 sqn;
	struct list_head	 list;
	struct list_head	 list;
};
};
+110 −73
Original line number Original line Diff line number Diff line
@@ -1666,8 +1666,11 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro
{
{
	struct mlx5e_priv *out_priv, *route_priv;
	struct mlx5e_priv *out_priv, *route_priv;
	struct mlx5_core_dev *route_mdev;
	struct mlx5_core_dev *route_mdev;
	struct mlx5_devcom *devcom;
	struct mlx5_eswitch *esw;
	struct mlx5_eswitch *esw;
	u16 vhca_id;
	u16 vhca_id;
	int err;
	int i;


	out_priv = netdev_priv(out_dev);
	out_priv = netdev_priv(out_dev);
	esw = out_priv->mdev->priv.eswitch;
	esw = out_priv->mdev->priv.eswitch;
@@ -1675,30 +1678,27 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro
	route_mdev = route_priv->mdev;
	route_mdev = route_priv->mdev;


	vhca_id = MLX5_CAP_GEN(route_mdev, vhca_id);
	vhca_id = MLX5_CAP_GEN(route_mdev, vhca_id);
	if (mlx5_lag_is_active(out_priv->mdev)) {
		struct mlx5_devcom *devcom;
		int err;

		/* In lag case we may get devices from different eswitch instances.
		 * If we failed to get vport num, it means, mostly, that we on the wrong
		 * eswitch.
		 */
	err = mlx5_eswitch_vhca_id_to_vport(esw, vhca_id, vport);
	err = mlx5_eswitch_vhca_id_to_vport(esw, vhca_id, vport);
		if (err != -ENOENT)
	if (!err)
		return err;

	if (!mlx5_lag_is_active(out_priv->mdev))
		return err;
		return err;


	rcu_read_lock();
	rcu_read_lock();
	devcom = out_priv->mdev->priv.devcom;
	devcom = out_priv->mdev->priv.devcom;
		esw = mlx5_devcom_get_peer_data_rcu(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	err = -ENODEV;
		err = esw ? mlx5_eswitch_vhca_id_to_vport(esw, vhca_id, vport) : -ENODEV;
	mlx5_devcom_for_each_peer_entry_rcu(devcom, MLX5_DEVCOM_ESW_OFFLOADS,
					    esw, i) {
		err = mlx5_eswitch_vhca_id_to_vport(esw, vhca_id, vport);
		if (!err)
			break;
	}
	rcu_read_unlock();
	rcu_read_unlock();


	return err;
	return err;
}
}


	return mlx5_eswitch_vhca_id_to_vport(esw, vhca_id, vport);
}

static int
static int
verify_attr_actions(u32 actions, struct netlink_ext_ack *extack)
verify_attr_actions(u32 actions, struct netlink_ext_ack *extack)
{
{
@@ -1986,47 +1986,59 @@ void mlx5e_put_flow_list(struct mlx5e_priv *priv, struct list_head *flow_list)
		mlx5e_flow_put(priv, flow);
		mlx5e_flow_put(priv, flow);
}
}


static void __mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow,
				       int peer_index)
{
{
	struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
	struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
	struct mlx5e_tc_flow *peer_flow;
	struct mlx5e_tc_flow *tmp;


	if (!flow_flag_test(flow, ESWITCH) ||
	if (!flow_flag_test(flow, ESWITCH) ||
	    !flow_flag_test(flow, DUP))
	    !flow_flag_test(flow, DUP))
		return;
		return;


	mutex_lock(&esw->offloads.peer_mutex);
	mutex_lock(&esw->offloads.peer_mutex);
	list_del(&flow->peer);
	list_del(&flow->peer[peer_index]);
	mutex_unlock(&esw->offloads.peer_mutex);
	mutex_unlock(&esw->offloads.peer_mutex);


	flow_flag_clear(flow, DUP);
	list_for_each_entry_safe(peer_flow, tmp, &flow->peer_flows, peer_flows) {

		if (peer_index != mlx5_get_dev_index(peer_flow->priv->mdev))
	if (refcount_dec_and_test(&flow->peer_flow->refcnt)) {
			continue;
		mlx5e_tc_del_fdb_flow(flow->peer_flow->priv, flow->peer_flow);
		if (refcount_dec_and_test(&peer_flow->refcnt)) {
		kfree(flow->peer_flow);
			mlx5e_tc_del_fdb_flow(peer_flow->priv, peer_flow);
			list_del(&peer_flow->peer_flows);
			kfree(peer_flow);
		}
	}
	}


	flow->peer_flow = NULL;
	if (list_empty(&flow->peer_flows))
		flow_flag_clear(flow, DUP);
}
}


static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
static void mlx5e_tc_del_fdb_peers_flow(struct mlx5e_tc_flow *flow)
{
{
	struct mlx5_core_dev *dev = flow->priv->mdev;
	int i;
	struct mlx5_devcom *devcom = dev->priv.devcom;
	struct mlx5_eswitch *peer_esw;

	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	if (!peer_esw)
		return;


	__mlx5e_tc_del_fdb_peer_flow(flow);
	for (i = 0; i < MLX5_MAX_PORTS; i++) {
	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
		if (i == mlx5_get_dev_index(flow->priv->mdev))
			continue;
		mlx5e_tc_del_fdb_peer_flow(flow, i);
	}
}
}


static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
			      struct mlx5e_tc_flow *flow)
			      struct mlx5e_tc_flow *flow)
{
{
	if (mlx5e_is_eswitch_flow(flow)) {
	if (mlx5e_is_eswitch_flow(flow)) {
		mlx5e_tc_del_fdb_peer_flow(flow);
		struct mlx5_devcom *devcom = flow->priv->mdev->priv.devcom;

		if (!mlx5_devcom_for_each_peer_begin(devcom, MLX5_DEVCOM_ESW_OFFLOADS)) {
			mlx5e_tc_del_fdb_flow(priv, flow);
			return;
		}

		mlx5e_tc_del_fdb_peers_flow(flow);
		mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
		mlx5e_tc_del_fdb_flow(priv, flow);
		mlx5e_tc_del_fdb_flow(priv, flow);
	} else {
	} else {
		mlx5e_tc_del_nic_flow(priv, flow);
		mlx5e_tc_del_nic_flow(priv, flow);
@@ -4203,7 +4215,7 @@ static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow)
		flow_flag_test(flow, INGRESS);
		flow_flag_test(flow, INGRESS);
	bool act_is_encap = !!(attr->action &
	bool act_is_encap = !!(attr->action &
			       MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT);
			       MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT);
	bool esw_paired = mlx5_devcom_is_paired(esw_attr->in_mdev->priv.devcom,
	bool esw_paired = mlx5_devcom_comp_is_ready(esw_attr->in_mdev->priv.devcom,
						    MLX5_DEVCOM_ESW_OFFLOADS);
						    MLX5_DEVCOM_ESW_OFFLOADS);


	if (!esw_paired)
	if (!esw_paired)
@@ -4295,6 +4307,7 @@ mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
	INIT_LIST_HEAD(&flow->hairpin);
	INIT_LIST_HEAD(&flow->hairpin);
	INIT_LIST_HEAD(&flow->l3_to_l2_reformat);
	INIT_LIST_HEAD(&flow->l3_to_l2_reformat);
	INIT_LIST_HEAD(&flow->attrs);
	INIT_LIST_HEAD(&flow->attrs);
	INIT_LIST_HEAD(&flow->peer_flows);
	refcount_set(&flow->refcnt, 1);
	refcount_set(&flow->refcnt, 1);
	init_completion(&flow->init_done);
	init_completion(&flow->init_done);
	init_completion(&flow->del_hw_done);
	init_completion(&flow->del_hw_done);
@@ -4403,22 +4416,19 @@ __mlx5e_add_fdb_flow(struct mlx5e_priv *priv,


static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
				      struct mlx5e_tc_flow *flow,
				      struct mlx5e_tc_flow *flow,
				      unsigned long flow_flags)
				      unsigned long flow_flags,
				      struct mlx5_eswitch *peer_esw)
{
{
	struct mlx5e_priv *priv = flow->priv, *peer_priv;
	struct mlx5e_priv *priv = flow->priv, *peer_priv;
	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch, *peer_esw;
	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
	struct mlx5_esw_flow_attr *attr = flow->attr->esw_attr;
	struct mlx5_esw_flow_attr *attr = flow->attr->esw_attr;
	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
	struct mlx5e_tc_flow_parse_attr *parse_attr;
	struct mlx5e_tc_flow_parse_attr *parse_attr;
	int i = mlx5_get_dev_index(peer_esw->dev);
	struct mlx5e_rep_priv *peer_urpriv;
	struct mlx5e_rep_priv *peer_urpriv;
	struct mlx5e_tc_flow *peer_flow;
	struct mlx5e_tc_flow *peer_flow;
	struct mlx5_core_dev *in_mdev;
	struct mlx5_core_dev *in_mdev;
	int err = 0;
	int err = 0;


	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	if (!peer_esw)
		return -ENODEV;

	peer_urpriv = mlx5_eswitch_get_uplink_priv(peer_esw, REP_ETH);
	peer_urpriv = mlx5_eswitch_get_uplink_priv(peer_esw, REP_ETH);
	peer_priv = netdev_priv(peer_urpriv->netdev);
	peer_priv = netdev_priv(peer_urpriv->netdev);


@@ -4443,14 +4453,13 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
		goto out;
		goto out;
	}
	}


	flow->peer_flow = peer_flow;
	list_add_tail(&peer_flow->peer_flows, &flow->peer_flows);
	flow_flag_set(flow, DUP);
	flow_flag_set(flow, DUP);
	mutex_lock(&esw->offloads.peer_mutex);
	mutex_lock(&esw->offloads.peer_mutex);
	list_add_tail(&flow->peer, &esw->offloads.peer_flows);
	list_add_tail(&flow->peer[i], &esw->offloads.peer_flows[i]);
	mutex_unlock(&esw->offloads.peer_mutex);
	mutex_unlock(&esw->offloads.peer_mutex);


out:
out:
	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	return err;
	return err;
}
}


@@ -4461,30 +4470,48 @@ mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
		   struct net_device *filter_dev,
		   struct net_device *filter_dev,
		   struct mlx5e_tc_flow **__flow)
		   struct mlx5e_tc_flow **__flow)
{
{
	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
	struct mlx5e_rep_priv *rpriv = priv->ppriv;
	struct mlx5e_rep_priv *rpriv = priv->ppriv;
	struct mlx5_eswitch_rep *in_rep = rpriv->rep;
	struct mlx5_eswitch_rep *in_rep = rpriv->rep;
	struct mlx5_core_dev *in_mdev = priv->mdev;
	struct mlx5_core_dev *in_mdev = priv->mdev;
	struct mlx5_eswitch *peer_esw;
	struct mlx5e_tc_flow *flow;
	struct mlx5e_tc_flow *flow;
	int err;
	int err;
	int i;


	flow = __mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, in_rep,
	flow = __mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, in_rep,
				    in_mdev);
				    in_mdev);
	if (IS_ERR(flow))
	if (IS_ERR(flow))
		return PTR_ERR(flow);
		return PTR_ERR(flow);


	if (is_peer_flow_needed(flow)) {
	if (!is_peer_flow_needed(flow)) {
		err = mlx5e_tc_add_fdb_peer_flow(f, flow, flow_flags);
		*__flow = flow;
		if (err) {
		return 0;
			mlx5e_tc_del_fdb_flow(priv, flow);
	}
			goto out;

	if (!mlx5_devcom_for_each_peer_begin(devcom, MLX5_DEVCOM_ESW_OFFLOADS)) {
		err = -ENODEV;
		goto clean_flow;
	}
	}

	mlx5_devcom_for_each_peer_entry(devcom,
					MLX5_DEVCOM_ESW_OFFLOADS,
					peer_esw, i) {
		err = mlx5e_tc_add_fdb_peer_flow(f, flow, flow_flags, peer_esw);
		if (err)
			goto peer_clean;
	}
	}


	*__flow = flow;
	mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);


	*__flow = flow;
	return 0;
	return 0;


out:
peer_clean:
	mlx5e_tc_del_fdb_peers_flow(flow);
	mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
clean_flow:
	mlx5e_tc_del_fdb_flow(priv, flow);
	return err;
	return err;
}
}


@@ -4702,7 +4729,6 @@ int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
{
{
	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
	struct mlx5_eswitch *peer_esw;
	struct mlx5e_tc_flow *flow;
	struct mlx5e_tc_flow *flow;
	struct mlx5_fc *counter;
	struct mlx5_fc *counter;
	u64 lastuse = 0;
	u64 lastuse = 0;
@@ -4737,23 +4763,29 @@ int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
	/* Under multipath it's possible for one rule to be currently
	/* Under multipath it's possible for one rule to be currently
	 * un-offloaded while the other rule is offloaded.
	 * un-offloaded while the other rule is offloaded.
	 */
	 */
	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	if (!mlx5_devcom_for_each_peer_begin(devcom, MLX5_DEVCOM_ESW_OFFLOADS))
	if (!peer_esw)
		goto out;
		goto out;


	if (flow_flag_test(flow, DUP) &&
	if (flow_flag_test(flow, DUP)) {
	    flow_flag_test(flow->peer_flow, OFFLOADED)) {
		struct mlx5e_tc_flow *peer_flow;
		u64 bytes2;

		list_for_each_entry(peer_flow, &flow->peer_flows, peer_flows) {
			u64 packets2;
			u64 packets2;
			u64 lastuse2;
			u64 lastuse2;
			u64 bytes2;


			if (!flow_flag_test(peer_flow, OFFLOADED))
				continue;
			if (flow_flag_test(flow, USE_ACT_STATS)) {
			if (flow_flag_test(flow, USE_ACT_STATS)) {
				f->use_act_stats = true;
				f->use_act_stats = true;
		} else {
				break;
			counter = mlx5e_tc_get_counter(flow->peer_flow);
			}

			counter = mlx5e_tc_get_counter(peer_flow);
			if (!counter)
			if (!counter)
				goto no_peer_counter;
				goto no_peer_counter;
			mlx5_fc_query_cached(counter, &bytes2, &packets2, &lastuse2);
			mlx5_fc_query_cached(counter, &bytes2, &packets2,
					     &lastuse2);


			bytes += bytes2;
			bytes += bytes2;
			packets += packets2;
			packets += packets2;
@@ -4762,7 +4794,7 @@ int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
	}
	}


no_peer_counter:
no_peer_counter:
	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
	mlx5_devcom_for_each_peer_end(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
out:
out:
	flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
	flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
			  FLOW_ACTION_HW_STATS_DELAYED);
			  FLOW_ACTION_HW_STATS_DELAYED);
@@ -5280,9 +5312,14 @@ int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
{
{
	struct mlx5e_tc_flow *flow, *tmp;
	struct mlx5e_tc_flow *flow, *tmp;
	int i;


	list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows, peer)
	for (i = 0; i < MLX5_MAX_PORTS; i++) {
		__mlx5e_tc_del_fdb_peer_flow(flow);
		if (i == mlx5_get_dev_index(esw->dev))
			continue;
		list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
			mlx5e_tc_del_fdb_peers_flow(flow);
	}
}
}


void mlx5e_tc_reoffload_flows_work(struct work_struct *work)
void mlx5e_tc_reoffload_flows_work(struct work_struct *work)
+20 −5
Original line number Original line Diff line number Diff line
@@ -15,13 +15,27 @@ static void esw_acl_egress_ofld_fwd2vport_destroy(struct mlx5_vport *vport)
	vport->egress.offloads.fwd_rule = NULL;
	vport->egress.offloads.fwd_rule = NULL;
}
}


static void esw_acl_egress_ofld_bounce_rule_destroy(struct mlx5_vport *vport)
void esw_acl_egress_ofld_bounce_rule_destroy(struct mlx5_vport *vport, int rule_index)
{
{
	if (!vport->egress.offloads.bounce_rule)
	struct mlx5_flow_handle *bounce_rule =
		xa_load(&vport->egress.offloads.bounce_rules, rule_index);

	if (!bounce_rule)
		return;
		return;


	mlx5_del_flow_rules(vport->egress.offloads.bounce_rule);
	mlx5_del_flow_rules(bounce_rule);
	vport->egress.offloads.bounce_rule = NULL;
	xa_erase(&vport->egress.offloads.bounce_rules, rule_index);
}

static void esw_acl_egress_ofld_bounce_rules_destroy(struct mlx5_vport *vport)
{
	struct mlx5_flow_handle *bounce_rule;
	unsigned long i;

	xa_for_each(&vport->egress.offloads.bounce_rules, i, bounce_rule) {
		mlx5_del_flow_rules(bounce_rule);
		xa_erase(&vport->egress.offloads.bounce_rules, i);
	}
}
}


static int esw_acl_egress_ofld_fwd2vport_create(struct mlx5_eswitch *esw,
static int esw_acl_egress_ofld_fwd2vport_create(struct mlx5_eswitch *esw,
@@ -96,7 +110,7 @@ static void esw_acl_egress_ofld_rules_destroy(struct mlx5_vport *vport)
{
{
	esw_acl_egress_vlan_destroy(vport);
	esw_acl_egress_vlan_destroy(vport);
	esw_acl_egress_ofld_fwd2vport_destroy(vport);
	esw_acl_egress_ofld_fwd2vport_destroy(vport);
	esw_acl_egress_ofld_bounce_rule_destroy(vport);
	esw_acl_egress_ofld_bounce_rules_destroy(vport);
}
}


static int esw_acl_egress_ofld_groups_create(struct mlx5_eswitch *esw,
static int esw_acl_egress_ofld_groups_create(struct mlx5_eswitch *esw,
@@ -194,6 +208,7 @@ int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport
		vport->egress.acl = NULL;
		vport->egress.acl = NULL;
		return err;
		return err;
	}
	}
	vport->egress.type = VPORT_EGRESS_ACL_TYPE_DEFAULT;


	err = esw_acl_egress_ofld_groups_create(esw, vport);
	err = esw_acl_egress_ofld_groups_create(esw, vport);
	if (err)
	if (err)
Loading