1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /*
3 * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
4 */
5
6 #include <linux/mlx5/vport.h>
7 #include "ib_rep.h"
8 #include "srq.h"
9
10 static int
mlx5_ib_set_vport_rep(struct mlx5_core_dev * dev,struct mlx5_eswitch_rep * rep,int vport_index)11 mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev,
12 struct mlx5_eswitch_rep *rep,
13 int vport_index)
14 {
15 struct mlx5_ib_dev *ibdev;
16
17 ibdev = mlx5_eswitch_uplink_get_proto_dev(dev->priv.eswitch, REP_IB);
18 if (!ibdev)
19 return -EINVAL;
20
21 ibdev->port[vport_index].rep = rep;
22 rep->rep_data[REP_IB].priv = ibdev;
23 write_lock(&ibdev->port[vport_index].roce.netdev_lock);
24 ibdev->port[vport_index].roce.netdev =
25 mlx5_ib_get_rep_netdev(rep->esw, rep->vport);
26 write_unlock(&ibdev->port[vport_index].roce.netdev_lock);
27
28 return 0;
29 }
30
31 static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev);
32
33 static int
mlx5_ib_vport_rep_load(struct mlx5_core_dev * dev,struct mlx5_eswitch_rep * rep)34 mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
35 {
36 u32 num_ports = mlx5_eswitch_get_total_vports(dev);
37 const struct mlx5_ib_profile *profile;
38 struct mlx5_core_dev *peer_dev;
39 struct mlx5_ib_dev *ibdev;
40 int second_uplink = false;
41 u32 peer_num_ports;
42 int vport_index;
43 int ret;
44
45 vport_index = rep->vport_index;
46
47 if (mlx5_lag_is_shared_fdb(dev)) {
48 peer_dev = mlx5_lag_get_peer_mdev(dev);
49 peer_num_ports = mlx5_eswitch_get_total_vports(peer_dev);
50 if (mlx5_lag_is_master(dev)) {
51 if (mlx5_lag_is_mpesw(dev))
52 num_ports += peer_num_ports;
53 else
54 num_ports += peer_num_ports - 1;
55
56 } else {
57 if (rep->vport == MLX5_VPORT_UPLINK) {
58 if (!mlx5_lag_is_mpesw(dev))
59 return 0;
60 second_uplink = true;
61 }
62
63 vport_index += peer_num_ports;
64 dev = peer_dev;
65 }
66 }
67
68 if (rep->vport == MLX5_VPORT_UPLINK && !second_uplink)
69 profile = &raw_eth_profile;
70 else
71 return mlx5_ib_set_vport_rep(dev, rep, vport_index);
72
73 ibdev = ib_alloc_device(mlx5_ib_dev, ib_dev);
74 if (!ibdev)
75 return -ENOMEM;
76
77 ibdev->port = kcalloc(num_ports, sizeof(*ibdev->port),
78 GFP_KERNEL);
79 if (!ibdev->port) {
80 ret = -ENOMEM;
81 goto fail_port;
82 }
83
84 ibdev->is_rep = true;
85 vport_index = rep->vport_index;
86 ibdev->port[vport_index].rep = rep;
87 ibdev->port[vport_index].roce.netdev =
88 mlx5_ib_get_rep_netdev(dev->priv.eswitch, rep->vport);
89 ibdev->mdev = dev;
90 ibdev->num_ports = num_ports;
91
92 ret = __mlx5_ib_add(ibdev, profile);
93 if (ret)
94 goto fail_add;
95
96 rep->rep_data[REP_IB].priv = ibdev;
97 if (mlx5_lag_is_shared_fdb(dev))
98 mlx5_ib_register_peer_vport_reps(dev);
99
100 return 0;
101
102 fail_add:
103 kfree(ibdev->port);
104 fail_port:
105 ib_dealloc_device(&ibdev->ib_dev);
106 return ret;
107 }
108
mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep * rep)109 static void *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
110 {
111 return rep->rep_data[REP_IB].priv;
112 }
113
114 static void
mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep * rep)115 mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
116 {
117 struct mlx5_core_dev *mdev = mlx5_eswitch_get_core_dev(rep->esw);
118 struct mlx5_ib_dev *dev = mlx5_ib_rep_to_dev(rep);
119 int vport_index = rep->vport_index;
120 struct mlx5_ib_port *port;
121
122 if (WARN_ON(!mdev))
123 return;
124
125 if (mlx5_lag_is_shared_fdb(mdev) &&
126 !mlx5_lag_is_master(mdev)) {
127 struct mlx5_core_dev *peer_mdev;
128
129 if (rep->vport == MLX5_VPORT_UPLINK)
130 return;
131 peer_mdev = mlx5_lag_get_peer_mdev(mdev);
132 vport_index += mlx5_eswitch_get_total_vports(peer_mdev);
133 }
134
135 if (!dev)
136 return;
137
138 port = &dev->port[vport_index];
139 write_lock(&port->roce.netdev_lock);
140 port->roce.netdev = NULL;
141 write_unlock(&port->roce.netdev_lock);
142 rep->rep_data[REP_IB].priv = NULL;
143 port->rep = NULL;
144
145 if (rep->vport == MLX5_VPORT_UPLINK) {
146 struct mlx5_core_dev *peer_mdev;
147 struct mlx5_eswitch *esw;
148
149 if (mlx5_lag_is_shared_fdb(mdev)) {
150 peer_mdev = mlx5_lag_get_peer_mdev(mdev);
151 esw = peer_mdev->priv.eswitch;
152 mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
153 }
154 __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
155 }
156 }
157
158 static const struct mlx5_eswitch_rep_ops rep_ops = {
159 .load = mlx5_ib_vport_rep_load,
160 .unload = mlx5_ib_vport_rep_unload,
161 .get_proto_dev = mlx5_ib_rep_to_dev,
162 };
163
mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev * mdev)164 static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev)
165 {
166 struct mlx5_core_dev *peer_mdev = mlx5_lag_get_peer_mdev(mdev);
167 struct mlx5_eswitch *esw;
168
169 if (!peer_mdev)
170 return;
171
172 esw = peer_mdev->priv.eswitch;
173 mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
174 }
175
mlx5_ib_get_rep_netdev(struct mlx5_eswitch * esw,u16 vport_num)176 struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
177 u16 vport_num)
178 {
179 return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_ETH);
180 }
181
create_flow_rule_vport_sq(struct mlx5_ib_dev * dev,struct mlx5_ib_sq * sq,u32 port)182 struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
183 struct mlx5_ib_sq *sq,
184 u32 port)
185 {
186 struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
187 struct mlx5_eswitch_rep *rep;
188
189 if (!dev->is_rep || !port)
190 return NULL;
191
192 if (!dev->port[port - 1].rep)
193 return ERR_PTR(-EINVAL);
194
195 rep = dev->port[port - 1].rep;
196
197 return mlx5_eswitch_add_send_to_vport_rule(esw, esw, rep, sq->base.mqp.qpn);
198 }
199
mlx5r_rep_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)200 static int mlx5r_rep_probe(struct auxiliary_device *adev,
201 const struct auxiliary_device_id *id)
202 {
203 struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
204 struct mlx5_core_dev *mdev = idev->mdev;
205 struct mlx5_eswitch *esw;
206
207 esw = mdev->priv.eswitch;
208 mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
209 return 0;
210 }
211
mlx5r_rep_remove(struct auxiliary_device * adev)212 static void mlx5r_rep_remove(struct auxiliary_device *adev)
213 {
214 struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
215 struct mlx5_core_dev *mdev = idev->mdev;
216 struct mlx5_eswitch *esw;
217
218 esw = mdev->priv.eswitch;
219 mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
220 }
221
222 static const struct auxiliary_device_id mlx5r_rep_id_table[] = {
223 { .name = MLX5_ADEV_NAME ".rdma-rep", },
224 {},
225 };
226
227 MODULE_DEVICE_TABLE(auxiliary, mlx5r_rep_id_table);
228
229 static struct auxiliary_driver mlx5r_rep_driver = {
230 .name = "rep",
231 .probe = mlx5r_rep_probe,
232 .remove = mlx5r_rep_remove,
233 .id_table = mlx5r_rep_id_table,
234 };
235
mlx5r_rep_init(void)236 int mlx5r_rep_init(void)
237 {
238 return auxiliary_driver_register(&mlx5r_rep_driver);
239 }
240
mlx5r_rep_cleanup(void)241 void mlx5r_rep_cleanup(void)
242 {
243 auxiliary_driver_unregister(&mlx5r_rep_driver);
244 }
245