1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3 
4 #include <linux/netdevice.h>
5 #include <net/nexthop.h>
6 #include "lag/lag.h"
7 #include "eswitch.h"
8 #include "esw/acl/ofld.h"
9 #include "lib/events.h"
10 
mlx5_mpesw_metadata_cleanup(struct mlx5_lag * ldev)11 static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)
12 {
13 	struct mlx5_core_dev *dev;
14 	struct mlx5_eswitch *esw;
15 	u32 pf_metadata;
16 	int i;
17 
18 	for (i = 0; i < ldev->ports; i++) {
19 		dev = ldev->pf[i].dev;
20 		esw = dev->priv.eswitch;
21 		pf_metadata = ldev->lag_mpesw.pf_metadata[i];
22 		if (!pf_metadata)
23 			continue;
24 		mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK, 0);
25 		mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW,
26 					 (void *)0);
27 		mlx5_esw_match_metadata_free(esw, pf_metadata);
28 		ldev->lag_mpesw.pf_metadata[i] = 0;
29 	}
30 }
31 
mlx5_mpesw_metadata_set(struct mlx5_lag * ldev)32 static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
33 {
34 	struct mlx5_core_dev *dev;
35 	struct mlx5_eswitch *esw;
36 	u32 pf_metadata;
37 	int i, err;
38 
39 	for (i = 0; i < ldev->ports; i++) {
40 		dev = ldev->pf[i].dev;
41 		esw = dev->priv.eswitch;
42 		pf_metadata = mlx5_esw_match_metadata_alloc(esw);
43 		if (!pf_metadata) {
44 			err = -ENOSPC;
45 			goto err_metadata;
46 		}
47 
48 		ldev->lag_mpesw.pf_metadata[i] = pf_metadata;
49 		err = mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK,
50 								 pf_metadata);
51 		if (err)
52 			goto err_metadata;
53 	}
54 
55 	for (i = 0; i < ldev->ports; i++) {
56 		dev = ldev->pf[i].dev;
57 		mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW,
58 					 (void *)0);
59 	}
60 
61 	return 0;
62 
63 err_metadata:
64 	mlx5_mpesw_metadata_cleanup(ldev);
65 	return err;
66 }
67 
68 #define MLX5_LAG_MPESW_OFFLOADS_SUPPORTED_PORTS 4
enable_mpesw(struct mlx5_lag * ldev)69 static int enable_mpesw(struct mlx5_lag *ldev)
70 {
71 	struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev;
72 	int err;
73 	int i;
74 
75 	if (ldev->mode != MLX5_LAG_MODE_NONE)
76 		return -EINVAL;
77 
78 	if (ldev->ports > MLX5_LAG_MPESW_OFFLOADS_SUPPORTED_PORTS)
79 		return -EOPNOTSUPP;
80 
81 	if (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||
82 	    !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) ||
83 	    !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) ||
84 	    !mlx5_lag_check_prereq(ldev))
85 		return -EOPNOTSUPP;
86 
87 	err = mlx5_mpesw_metadata_set(ldev);
88 	if (err)
89 		return err;
90 
91 	mlx5_lag_remove_devices(ldev);
92 
93 	err = mlx5_activate_lag(ldev, NULL, MLX5_LAG_MODE_MPESW, true);
94 	if (err) {
95 		mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err);
96 		goto err_add_devices;
97 	}
98 
99 	dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
100 	mlx5_rescan_drivers_locked(dev0);
101 	for (i = 0; i < ldev->ports; i++) {
102 		err = mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch);
103 		if (err)
104 			goto err_rescan_drivers;
105 	}
106 
107 	return 0;
108 
109 err_rescan_drivers:
110 	dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
111 	mlx5_rescan_drivers_locked(dev0);
112 	mlx5_deactivate_lag(ldev);
113 err_add_devices:
114 	mlx5_lag_add_devices(ldev);
115 	for (i = 0; i < ldev->ports; i++)
116 		mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch);
117 	mlx5_mpesw_metadata_cleanup(ldev);
118 	return err;
119 }
120 
disable_mpesw(struct mlx5_lag * ldev)121 static void disable_mpesw(struct mlx5_lag *ldev)
122 {
123 	if (ldev->mode == MLX5_LAG_MODE_MPESW) {
124 		mlx5_mpesw_metadata_cleanup(ldev);
125 		mlx5_disable_lag(ldev);
126 	}
127 }
128 
mlx5_mpesw_work(struct work_struct * work)129 static void mlx5_mpesw_work(struct work_struct *work)
130 {
131 	struct mlx5_mpesw_work_st *mpesww = container_of(work, struct mlx5_mpesw_work_st, work);
132 	struct mlx5_lag *ldev = mpesww->lag;
133 
134 	mlx5_dev_list_lock();
135 	mutex_lock(&ldev->lock);
136 	if (ldev->mode_changes_in_progress) {
137 		mpesww->result = -EAGAIN;
138 		goto unlock;
139 	}
140 
141 	if (mpesww->op == MLX5_MPESW_OP_ENABLE)
142 		mpesww->result = enable_mpesw(ldev);
143 	else if (mpesww->op == MLX5_MPESW_OP_DISABLE)
144 		disable_mpesw(ldev);
145 unlock:
146 	mutex_unlock(&ldev->lock);
147 	mlx5_dev_list_unlock();
148 	complete(&mpesww->comp);
149 }
150 
mlx5_lag_mpesw_queue_work(struct mlx5_core_dev * dev,enum mpesw_op op)151 static int mlx5_lag_mpesw_queue_work(struct mlx5_core_dev *dev,
152 				     enum mpesw_op op)
153 {
154 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
155 	struct mlx5_mpesw_work_st *work;
156 	int err = 0;
157 
158 	if (!ldev)
159 		return 0;
160 
161 	work = kzalloc(sizeof(*work), GFP_KERNEL);
162 	if (!work)
163 		return -ENOMEM;
164 
165 	INIT_WORK(&work->work, mlx5_mpesw_work);
166 	init_completion(&work->comp);
167 	work->op = op;
168 	work->lag = ldev;
169 
170 	if (!queue_work(ldev->wq, &work->work)) {
171 		mlx5_core_warn(dev, "failed to queue mpesw work\n");
172 		err = -EINVAL;
173 		goto out;
174 	}
175 	wait_for_completion(&work->comp);
176 	err = work->result;
177 out:
178 	kfree(work);
179 	return err;
180 }
181 
mlx5_lag_mpesw_disable(struct mlx5_core_dev * dev)182 void mlx5_lag_mpesw_disable(struct mlx5_core_dev *dev)
183 {
184 	mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_DISABLE);
185 }
186 
mlx5_lag_mpesw_enable(struct mlx5_core_dev * dev)187 int mlx5_lag_mpesw_enable(struct mlx5_core_dev *dev)
188 {
189 	return mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_ENABLE);
190 }
191 
mlx5_lag_mpesw_do_mirred(struct mlx5_core_dev * mdev,struct net_device * out_dev,struct netlink_ext_ack * extack)192 int mlx5_lag_mpesw_do_mirred(struct mlx5_core_dev *mdev,
193 			     struct net_device *out_dev,
194 			     struct netlink_ext_ack *extack)
195 {
196 	struct mlx5_lag *ldev = mlx5_lag_dev(mdev);
197 
198 	if (!netif_is_bond_master(out_dev) || !ldev)
199 		return 0;
200 
201 	if (ldev->mode != MLX5_LAG_MODE_MPESW)
202 		return 0;
203 
204 	NL_SET_ERR_MSG_MOD(extack, "can't forward to bond in mpesw mode");
205 	return -EOPNOTSUPP;
206 }
207 
mlx5_lag_is_mpesw(struct mlx5_core_dev * dev)208 bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev)
209 {
210 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
211 
212 	return ldev && ldev->mode == MLX5_LAG_MODE_MPESW;
213 }
214 EXPORT_SYMBOL(mlx5_lag_is_mpesw);
215