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 !mlx5_lag_shared_fdb_supported(ldev))
86 return -EOPNOTSUPP;
87
88 err = mlx5_mpesw_metadata_set(ldev);
89 if (err)
90 return err;
91
92 mlx5_lag_remove_devices(ldev);
93
94 err = mlx5_activate_lag(ldev, NULL, MLX5_LAG_MODE_MPESW, true);
95 if (err) {
96 mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err);
97 goto err_add_devices;
98 }
99
100 dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
101 mlx5_rescan_drivers_locked(dev0);
102 for (i = 0; i < ldev->ports; i++) {
103 err = mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch);
104 if (err)
105 goto err_rescan_drivers;
106 }
107
108 return 0;
109
110 err_rescan_drivers:
111 dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
112 mlx5_rescan_drivers_locked(dev0);
113 mlx5_deactivate_lag(ldev);
114 err_add_devices:
115 mlx5_lag_add_devices(ldev);
116 for (i = 0; i < ldev->ports; i++)
117 mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch);
118 mlx5_mpesw_metadata_cleanup(ldev);
119 return err;
120 }
121
disable_mpesw(struct mlx5_lag * ldev)122 static void disable_mpesw(struct mlx5_lag *ldev)
123 {
124 if (ldev->mode == MLX5_LAG_MODE_MPESW) {
125 mlx5_mpesw_metadata_cleanup(ldev);
126 mlx5_disable_lag(ldev);
127 }
128 }
129
mlx5_mpesw_work(struct work_struct * work)130 static void mlx5_mpesw_work(struct work_struct *work)
131 {
132 struct mlx5_mpesw_work_st *mpesww = container_of(work, struct mlx5_mpesw_work_st, work);
133 struct mlx5_lag *ldev = mpesww->lag;
134
135 mlx5_dev_list_lock();
136 mutex_lock(&ldev->lock);
137 if (ldev->mode_changes_in_progress) {
138 mpesww->result = -EAGAIN;
139 goto unlock;
140 }
141
142 if (mpesww->op == MLX5_MPESW_OP_ENABLE)
143 mpesww->result = enable_mpesw(ldev);
144 else if (mpesww->op == MLX5_MPESW_OP_DISABLE)
145 disable_mpesw(ldev);
146 unlock:
147 mutex_unlock(&ldev->lock);
148 mlx5_dev_list_unlock();
149 complete(&mpesww->comp);
150 }
151
mlx5_lag_mpesw_queue_work(struct mlx5_core_dev * dev,enum mpesw_op op)152 static int mlx5_lag_mpesw_queue_work(struct mlx5_core_dev *dev,
153 enum mpesw_op op)
154 {
155 struct mlx5_lag *ldev = mlx5_lag_dev(dev);
156 struct mlx5_mpesw_work_st *work;
157 int err = 0;
158
159 if (!ldev)
160 return 0;
161
162 work = kzalloc(sizeof(*work), GFP_KERNEL);
163 if (!work)
164 return -ENOMEM;
165
166 INIT_WORK(&work->work, mlx5_mpesw_work);
167 init_completion(&work->comp);
168 work->op = op;
169 work->lag = ldev;
170
171 if (!queue_work(ldev->wq, &work->work)) {
172 mlx5_core_warn(dev, "failed to queue mpesw work\n");
173 err = -EINVAL;
174 goto out;
175 }
176 wait_for_completion(&work->comp);
177 err = work->result;
178 out:
179 kfree(work);
180 return err;
181 }
182
mlx5_lag_mpesw_disable(struct mlx5_core_dev * dev)183 void mlx5_lag_mpesw_disable(struct mlx5_core_dev *dev)
184 {
185 mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_DISABLE);
186 }
187
mlx5_lag_mpesw_enable(struct mlx5_core_dev * dev)188 int mlx5_lag_mpesw_enable(struct mlx5_core_dev *dev)
189 {
190 return mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_ENABLE);
191 }
192
mlx5_lag_mpesw_do_mirred(struct mlx5_core_dev * mdev,struct net_device * out_dev,struct netlink_ext_ack * extack)193 int mlx5_lag_mpesw_do_mirred(struct mlx5_core_dev *mdev,
194 struct net_device *out_dev,
195 struct netlink_ext_ack *extack)
196 {
197 struct mlx5_lag *ldev = mlx5_lag_dev(mdev);
198
199 if (!netif_is_bond_master(out_dev) || !ldev)
200 return 0;
201
202 if (ldev->mode != MLX5_LAG_MODE_MPESW)
203 return 0;
204
205 NL_SET_ERR_MSG_MOD(extack, "can't forward to bond in mpesw mode");
206 return -EOPNOTSUPP;
207 }
208
mlx5_lag_is_mpesw(struct mlx5_core_dev * dev)209 bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev)
210 {
211 struct mlx5_lag *ldev = mlx5_lag_dev(dev);
212
213 return ldev && ldev->mode == MLX5_LAG_MODE_MPESW;
214 }
215 EXPORT_SYMBOL(mlx5_lag_is_mpesw);
216