]>
Commit | Line | Data |
---|---|---|
48935bbb SM |
1 | /* |
2 | * Copyright (c) 2017, Mellanox Technologies. All rights reserved. | |
3 | * | |
4 | * This software is available to you under a choice of one of two | |
5 | * licenses. You may choose to be licensed under the terms of the GNU | |
6 | * General Public License (GPL) Version 2, available from the file | |
7 | * COPYING in the main directory of this source tree, or the | |
8 | * OpenIB.org BSD license below: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or | |
11 | * without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistributions of source code must retain the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer. | |
17 | * | |
18 | * - Redistributions in binary form must reproduce the above | |
19 | * copyright notice, this list of conditions and the following | |
20 | * disclaimer in the documentation and/or other materials | |
21 | * provided with the distribution. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
30 | * SOFTWARE. | |
31 | */ | |
32 | ||
693dfd5a | 33 | #include <rdma/ib_verbs.h> |
48935bbb SM |
34 | #include <linux/mlx5/fs.h> |
35 | #include "en.h" | |
36 | #include "ipoib.h" | |
37 | ||
ec8fd927 | 38 | #define IB_DEFAULT_Q_KEY 0xb1b |
b6dc510f | 39 | #define MLX5I_PARAMS_DEFAULT_LOG_RQ_SIZE 9 |
ec8fd927 | 40 | |
603f4a45 SM |
41 | static int mlx5i_open(struct net_device *netdev); |
42 | static int mlx5i_close(struct net_device *netdev); | |
807c4415 | 43 | static int mlx5i_change_mtu(struct net_device *netdev, int new_mtu); |
603f4a45 SM |
44 | |
45 | static const struct net_device_ops mlx5i_netdev_ops = { | |
46 | .ndo_open = mlx5i_open, | |
47 | .ndo_stop = mlx5i_close, | |
48 | .ndo_init = mlx5i_dev_init, | |
49 | .ndo_uninit = mlx5i_dev_cleanup, | |
807c4415 | 50 | .ndo_change_mtu = mlx5i_change_mtu, |
1170fbd8 | 51 | .ndo_do_ioctl = mlx5i_ioctl, |
603f4a45 SM |
52 | }; |
53 | ||
48935bbb | 54 | /* IPoIB mlx5 netdev profile */ |
b6dc510f ES |
55 | static void mlx5i_build_nic_params(struct mlx5_core_dev *mdev, |
56 | struct mlx5e_params *params) | |
57 | { | |
58 | /* Override RQ params as IPoIB supports only LINKED LIST RQ for now */ | |
2ccb0a79 TT |
59 | MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ, false); |
60 | mlx5e_set_rq_type(mdev, params); | |
2a0f561b | 61 | mlx5e_init_rq_type_params(mdev, params); |
b6dc510f ES |
62 | |
63 | /* RQ size in ipoib by default is 512 */ | |
73281b78 | 64 | params->log_rq_mtu_frames = is_kdump_kernel() ? |
b6dc510f ES |
65 | MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE : |
66 | MLX5I_PARAMS_DEFAULT_LOG_RQ_SIZE; | |
67 | ||
68 | params->lro_en = false; | |
472a1e44 | 69 | params->hard_mtu = MLX5_IB_GRH_BYTES + MLX5_IPOIB_HARD_LEN; |
b6dc510f | 70 | } |
48935bbb SM |
71 | |
72 | /* Called directly after IPoIB netdevice was created to initialize SW structs */ | |
4c6c615e AV |
73 | void mlx5i_init(struct mlx5_core_dev *mdev, |
74 | struct net_device *netdev, | |
75 | const struct mlx5e_profile *profile, | |
76 | void *ppriv) | |
48935bbb SM |
77 | { |
78 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); | |
8e1d162d | 79 | u16 max_mtu; |
48935bbb | 80 | |
c139dbfd | 81 | /* priv init */ |
8f493ffd SM |
82 | priv->mdev = mdev; |
83 | priv->netdev = netdev; | |
84 | priv->profile = profile; | |
85 | priv->ppriv = ppriv; | |
c139dbfd | 86 | mutex_init(&priv->state_lock); |
8f493ffd | 87 | |
8e1d162d FD |
88 | mlx5_query_port_max_mtu(mdev, &max_mtu, 1); |
89 | netdev->mtu = max_mtu; | |
90 | ||
472a1e44 TT |
91 | mlx5e_build_nic_params(mdev, &priv->channels.params, |
92 | profile->max_nch(mdev), netdev->mtu); | |
b6dc510f | 93 | mlx5i_build_nic_params(mdev, &priv->channels.params); |
5360fd47 | 94 | |
237f258c FD |
95 | mlx5e_timestamp_init(priv); |
96 | ||
c139dbfd | 97 | /* netdev init */ |
603f4a45 SM |
98 | netdev->hw_features |= NETIF_F_SG; |
99 | netdev->hw_features |= NETIF_F_IP_CSUM; | |
100 | netdev->hw_features |= NETIF_F_IPV6_CSUM; | |
101 | netdev->hw_features |= NETIF_F_GRO; | |
102 | netdev->hw_features |= NETIF_F_TSO; | |
103 | netdev->hw_features |= NETIF_F_TSO6; | |
104 | netdev->hw_features |= NETIF_F_RXCSUM; | |
105 | netdev->hw_features |= NETIF_F_RXHASH; | |
106 | ||
107 | netdev->netdev_ops = &mlx5i_netdev_ops; | |
076b0936 | 108 | netdev->ethtool_ops = &mlx5i_ethtool_ops; |
48935bbb SM |
109 | } |
110 | ||
111 | /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ | |
112 | static void mlx5i_cleanup(struct mlx5e_priv *priv) | |
113 | { | |
114 | /* Do nothing .. */ | |
115 | } | |
116 | ||
af98cebc | 117 | int mlx5i_init_underlay_qp(struct mlx5e_priv *priv) |
c8249eda AV |
118 | { |
119 | struct mlx5_core_dev *mdev = priv->mdev; | |
120 | struct mlx5i_priv *ipriv = priv->ppriv; | |
121 | struct mlx5_core_qp *qp = &ipriv->qp; | |
122 | struct mlx5_qp_context *context; | |
123 | int ret; | |
124 | ||
125 | /* QP states */ | |
126 | context = kzalloc(sizeof(*context), GFP_KERNEL); | |
127 | if (!context) | |
128 | return -ENOMEM; | |
129 | ||
130 | context->flags = cpu_to_be32(MLX5_QP_PM_MIGRATED << 11); | |
131 | context->pri_path.port = 1; | |
da34f1a8 | 132 | context->pri_path.pkey_index = cpu_to_be16(ipriv->pkey_index); |
c8249eda AV |
133 | context->qkey = cpu_to_be32(IB_DEFAULT_Q_KEY); |
134 | ||
135 | ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, context, qp); | |
136 | if (ret) { | |
137 | mlx5_core_err(mdev, "Failed to modify qp RST2INIT, err: %d\n", ret); | |
138 | goto err_qp_modify_to_err; | |
139 | } | |
140 | memset(context, 0, sizeof(*context)); | |
141 | ||
142 | ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, context, qp); | |
143 | if (ret) { | |
144 | mlx5_core_err(mdev, "Failed to modify qp INIT2RTR, err: %d\n", ret); | |
145 | goto err_qp_modify_to_err; | |
146 | } | |
147 | ||
148 | ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, 0, context, qp); | |
149 | if (ret) { | |
150 | mlx5_core_err(mdev, "Failed to modify qp RTR2RTS, err: %d\n", ret); | |
151 | goto err_qp_modify_to_err; | |
152 | } | |
153 | ||
154 | kfree(context); | |
155 | return 0; | |
156 | ||
157 | err_qp_modify_to_err: | |
158 | mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2ERR_QP, 0, &context, qp); | |
159 | kfree(context); | |
160 | return ret; | |
161 | } | |
162 | ||
af98cebc | 163 | void mlx5i_uninit_underlay_qp(struct mlx5e_priv *priv) |
c8249eda AV |
164 | { |
165 | struct mlx5i_priv *ipriv = priv->ppriv; | |
166 | struct mlx5_core_dev *mdev = priv->mdev; | |
167 | struct mlx5_qp_context context; | |
168 | int err; | |
169 | ||
170 | err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2RST_QP, 0, &context, | |
171 | &ipriv->qp); | |
172 | if (err) | |
173 | mlx5_core_err(mdev, "Failed to modify qp 2RST, err: %d\n", err); | |
174 | } | |
175 | ||
ec8fd927 SM |
176 | #define MLX5_QP_ENHANCED_ULP_STATELESS_MODE 2 |
177 | ||
4c6c615e | 178 | int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp) |
ec8fd927 | 179 | { |
ec8fd927 SM |
180 | u32 *in = NULL; |
181 | void *addr_path; | |
182 | int ret = 0; | |
183 | int inlen; | |
184 | void *qpc; | |
185 | ||
186 | inlen = MLX5_ST_SZ_BYTES(create_qp_in); | |
1b9a07ee | 187 | in = kvzalloc(inlen, GFP_KERNEL); |
ec8fd927 SM |
188 | if (!in) |
189 | return -ENOMEM; | |
190 | ||
191 | qpc = MLX5_ADDR_OF(create_qp_in, in, qpc); | |
192 | MLX5_SET(qpc, qpc, st, MLX5_QP_ST_UD); | |
193 | MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED); | |
194 | MLX5_SET(qpc, qpc, ulp_stateless_offload_mode, | |
195 | MLX5_QP_ENHANCED_ULP_STATELESS_MODE); | |
196 | ||
197 | addr_path = MLX5_ADDR_OF(qpc, qpc, primary_address_path); | |
32f69e4b | 198 | MLX5_SET(ads, addr_path, vhca_port_num, 1); |
ec8fd927 SM |
199 | MLX5_SET(ads, addr_path, grh, 1); |
200 | ||
201 | ret = mlx5_core_create_qp(mdev, qp, in, inlen); | |
202 | if (ret) { | |
203 | mlx5_core_err(mdev, "Failed creating IPoIB QP err : %d\n", ret); | |
204 | goto out; | |
205 | } | |
206 | ||
ec8fd927 | 207 | out: |
ec8fd927 SM |
208 | kvfree(in); |
209 | return ret; | |
210 | } | |
211 | ||
4c6c615e | 212 | void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp) |
ec8fd927 SM |
213 | { |
214 | mlx5_core_destroy_qp(mdev, qp); | |
215 | } | |
216 | ||
48935bbb SM |
217 | static int mlx5i_init_tx(struct mlx5e_priv *priv) |
218 | { | |
5426a0b2 SM |
219 | struct mlx5i_priv *ipriv = priv->ppriv; |
220 | int err; | |
221 | ||
ec8fd927 SM |
222 | err = mlx5i_create_underlay_qp(priv->mdev, &ipriv->qp); |
223 | if (err) { | |
224 | mlx5_core_warn(priv->mdev, "create underlay QP failed, %d\n", err); | |
225 | return err; | |
226 | } | |
5426a0b2 SM |
227 | |
228 | err = mlx5e_create_tis(priv->mdev, 0 /* tc */, ipriv->qp.qpn, &priv->tisn[0]); | |
229 | if (err) { | |
230 | mlx5_core_warn(priv->mdev, "create tis failed, %d\n", err); | |
c8249eda | 231 | goto err_destroy_underlay_qp; |
5426a0b2 SM |
232 | } |
233 | ||
48935bbb | 234 | return 0; |
c8249eda AV |
235 | |
236 | err_destroy_underlay_qp: | |
237 | mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp); | |
238 | return err; | |
48935bbb SM |
239 | } |
240 | ||
a7082ef0 | 241 | static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) |
48935bbb | 242 | { |
ec8fd927 SM |
243 | struct mlx5i_priv *ipriv = priv->ppriv; |
244 | ||
5426a0b2 | 245 | mlx5e_destroy_tis(priv->mdev, priv->tisn[0]); |
ec8fd927 | 246 | mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp); |
48935bbb SM |
247 | } |
248 | ||
bc81b9d3 SM |
249 | static int mlx5i_create_flow_steering(struct mlx5e_priv *priv) |
250 | { | |
1ae1df3a OG |
251 | struct ttc_params ttc_params = {}; |
252 | int tt, err; | |
bc81b9d3 SM |
253 | |
254 | priv->fs.ns = mlx5_get_flow_namespace(priv->mdev, | |
255 | MLX5_FLOW_NAMESPACE_KERNEL); | |
256 | ||
257 | if (!priv->fs.ns) | |
258 | return -EINVAL; | |
259 | ||
260 | err = mlx5e_arfs_create_tables(priv); | |
261 | if (err) { | |
262 | netdev_err(priv->netdev, "Failed to create arfs tables, err=%d\n", | |
263 | err); | |
264 | priv->netdev->hw_features &= ~NETIF_F_NTUPLE; | |
265 | } | |
266 | ||
1ae1df3a OG |
267 | mlx5e_set_ttc_basic_params(priv, &ttc_params); |
268 | mlx5e_set_inner_ttc_ft_params(&ttc_params); | |
269 | for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) | |
270 | ttc_params.indir_tirn[tt] = priv->inner_indir_tir[tt].tirn; | |
271 | ||
272 | err = mlx5e_create_inner_ttc_table(priv, &ttc_params, &priv->fs.inner_ttc); | |
458821c7 FD |
273 | if (err) { |
274 | netdev_err(priv->netdev, "Failed to create inner ttc table, err=%d\n", | |
275 | err); | |
276 | goto err_destroy_arfs_tables; | |
277 | } | |
278 | ||
1ae1df3a OG |
279 | mlx5e_set_ttc_ft_params(&ttc_params); |
280 | for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) | |
468330e8 | 281 | ttc_params.indir_tirn[tt] = priv->indir_tir[tt].tirn; |
1ae1df3a OG |
282 | |
283 | err = mlx5e_create_ttc_table(priv, &ttc_params, &priv->fs.ttc); | |
bc81b9d3 SM |
284 | if (err) { |
285 | netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", | |
286 | err); | |
458821c7 | 287 | goto err_destroy_inner_ttc_table; |
bc81b9d3 SM |
288 | } |
289 | ||
290 | return 0; | |
291 | ||
458821c7 | 292 | err_destroy_inner_ttc_table: |
1ae1df3a | 293 | mlx5e_destroy_inner_ttc_table(priv, &priv->fs.inner_ttc); |
bc81b9d3 SM |
294 | err_destroy_arfs_tables: |
295 | mlx5e_arfs_destroy_tables(priv); | |
296 | ||
297 | return err; | |
298 | } | |
299 | ||
300 | static void mlx5i_destroy_flow_steering(struct mlx5e_priv *priv) | |
301 | { | |
1ae1df3a OG |
302 | mlx5e_destroy_ttc_table(priv, &priv->fs.ttc); |
303 | mlx5e_destroy_inner_ttc_table(priv, &priv->fs.inner_ttc); | |
bc81b9d3 SM |
304 | mlx5e_arfs_destroy_tables(priv); |
305 | } | |
306 | ||
48935bbb SM |
307 | static int mlx5i_init_rx(struct mlx5e_priv *priv) |
308 | { | |
8f493ffd SM |
309 | int err; |
310 | ||
311 | err = mlx5e_create_indirect_rqt(priv); | |
312 | if (err) | |
313 | return err; | |
314 | ||
315 | err = mlx5e_create_direct_rqts(priv); | |
316 | if (err) | |
317 | goto err_destroy_indirect_rqts; | |
318 | ||
319 | err = mlx5e_create_indirect_tirs(priv); | |
320 | if (err) | |
321 | goto err_destroy_direct_rqts; | |
322 | ||
323 | err = mlx5e_create_direct_tirs(priv); | |
324 | if (err) | |
325 | goto err_destroy_indirect_tirs; | |
326 | ||
58569ef8 AV |
327 | err = mlx5i_create_flow_steering(priv); |
328 | if (err) | |
dae37456 | 329 | goto err_destroy_direct_tirs; |
58569ef8 | 330 | |
48935bbb | 331 | return 0; |
8f493ffd | 332 | |
bc81b9d3 SM |
333 | err_destroy_direct_tirs: |
334 | mlx5e_destroy_direct_tirs(priv); | |
8f493ffd SM |
335 | err_destroy_indirect_tirs: |
336 | mlx5e_destroy_indirect_tirs(priv); | |
337 | err_destroy_direct_rqts: | |
338 | mlx5e_destroy_direct_rqts(priv); | |
339 | err_destroy_indirect_rqts: | |
340 | mlx5e_destroy_rqt(priv, &priv->indir_rqt); | |
341 | return err; | |
48935bbb SM |
342 | } |
343 | ||
344 | static void mlx5i_cleanup_rx(struct mlx5e_priv *priv) | |
345 | { | |
bc81b9d3 | 346 | mlx5i_destroy_flow_steering(priv); |
8f493ffd SM |
347 | mlx5e_destroy_direct_tirs(priv); |
348 | mlx5e_destroy_indirect_tirs(priv); | |
349 | mlx5e_destroy_direct_rqts(priv); | |
350 | mlx5e_destroy_rqt(priv, &priv->indir_rqt); | |
48935bbb SM |
351 | } |
352 | ||
353 | static const struct mlx5e_profile mlx5i_nic_profile = { | |
354 | .init = mlx5i_init, | |
355 | .cleanup = mlx5i_cleanup, | |
356 | .init_tx = mlx5i_init_tx, | |
357 | .cleanup_tx = mlx5i_cleanup_tx, | |
358 | .init_rx = mlx5i_init_rx, | |
359 | .cleanup_rx = mlx5i_cleanup_rx, | |
360 | .enable = NULL, /* mlx5i_enable */ | |
361 | .disable = NULL, /* mlx5i_disable */ | |
362 | .update_stats = NULL, /* mlx5i_update_stats */ | |
363 | .max_nch = mlx5e_get_max_num_channels, | |
7ca42c80 | 364 | .update_carrier = NULL, /* no HW update in IB link */ |
9d6bd752 SM |
365 | .rx_handlers.handle_rx_cqe = mlx5i_handle_rx_cqe, |
366 | .rx_handlers.handle_rx_cqe_mpwqe = NULL, /* Not supported */ | |
48935bbb SM |
367 | .max_tc = MLX5I_MAX_NUM_TC, |
368 | }; | |
369 | ||
603f4a45 SM |
370 | /* mlx5i netdev NDos */ |
371 | ||
807c4415 ES |
372 | static int mlx5i_change_mtu(struct net_device *netdev, int new_mtu) |
373 | { | |
374 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); | |
375 | struct mlx5e_channels new_channels = {}; | |
472a1e44 | 376 | struct mlx5e_params *params; |
807c4415 ES |
377 | int err = 0; |
378 | ||
379 | mutex_lock(&priv->state_lock); | |
380 | ||
472a1e44 | 381 | params = &priv->channels.params; |
807c4415 | 382 | |
472a1e44 TT |
383 | if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { |
384 | params->sw_mtu = new_mtu; | |
385 | netdev->mtu = params->sw_mtu; | |
807c4415 | 386 | goto out; |
472a1e44 | 387 | } |
807c4415 | 388 | |
472a1e44 TT |
389 | new_channels.params = *params; |
390 | new_channels.params.sw_mtu = new_mtu; | |
807c4415 | 391 | err = mlx5e_open_channels(priv, &new_channels); |
472a1e44 | 392 | if (err) |
807c4415 | 393 | goto out; |
807c4415 ES |
394 | |
395 | mlx5e_switch_priv_channels(priv, &new_channels, NULL); | |
472a1e44 | 396 | netdev->mtu = new_channels.params.sw_mtu; |
807c4415 ES |
397 | |
398 | out: | |
399 | mutex_unlock(&priv->state_lock); | |
400 | return err; | |
401 | } | |
402 | ||
af98cebc | 403 | int mlx5i_dev_init(struct net_device *dev) |
603f4a45 SM |
404 | { |
405 | struct mlx5e_priv *priv = mlx5i_epriv(dev); | |
406 | struct mlx5i_priv *ipriv = priv->ppriv; | |
407 | ||
408 | /* Set dev address using underlay QP */ | |
409 | dev->dev_addr[1] = (ipriv->qp.qpn >> 16) & 0xff; | |
410 | dev->dev_addr[2] = (ipriv->qp.qpn >> 8) & 0xff; | |
411 | dev->dev_addr[3] = (ipriv->qp.qpn) & 0xff; | |
412 | ||
7e7f4780 AV |
413 | /* Add QPN to net-device mapping to HT */ |
414 | mlx5i_pkey_add_qpn(dev ,ipriv->qp.qpn); | |
415 | ||
603f4a45 SM |
416 | return 0; |
417 | } | |
418 | ||
08437c57 | 419 | int mlx5i_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
1170fbd8 FD |
420 | { |
421 | struct mlx5e_priv *priv = mlx5i_epriv(dev); | |
422 | ||
423 | switch (cmd) { | |
424 | case SIOCSHWTSTAMP: | |
425 | return mlx5e_hwstamp_set(priv, ifr); | |
426 | case SIOCGHWTSTAMP: | |
427 | return mlx5e_hwstamp_get(priv, ifr); | |
428 | default: | |
429 | return -EOPNOTSUPP; | |
430 | } | |
431 | } | |
432 | ||
af98cebc | 433 | void mlx5i_dev_cleanup(struct net_device *dev) |
603f4a45 | 434 | { |
ec8fd927 | 435 | struct mlx5e_priv *priv = mlx5i_epriv(dev); |
7e7f4780 | 436 | struct mlx5i_priv *ipriv = priv->ppriv; |
ec8fd927 | 437 | |
c8249eda | 438 | mlx5i_uninit_underlay_qp(priv); |
7e7f4780 AV |
439 | |
440 | /* Delete QPN to net-device mapping from HT */ | |
441 | mlx5i_pkey_del_qpn(dev, ipriv->qp.qpn); | |
603f4a45 SM |
442 | } |
443 | ||
444 | static int mlx5i_open(struct net_device *netdev) | |
445 | { | |
dae37456 AV |
446 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); |
447 | struct mlx5i_priv *ipriv = epriv->ppriv; | |
448 | struct mlx5_core_dev *mdev = epriv->mdev; | |
603f4a45 SM |
449 | int err; |
450 | ||
dae37456 | 451 | mutex_lock(&epriv->state_lock); |
603f4a45 | 452 | |
dae37456 | 453 | set_bit(MLX5E_STATE_OPENED, &epriv->state); |
603f4a45 | 454 | |
dae37456 AV |
455 | err = mlx5i_init_underlay_qp(epriv); |
456 | if (err) { | |
457 | mlx5_core_warn(mdev, "prepare underlay qp state failed, %d\n", err); | |
603f4a45 | 458 | goto err_clear_state_opened_flag; |
dae37456 AV |
459 | } |
460 | ||
461 | err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qp.qpn); | |
462 | if (err) { | |
463 | mlx5_core_warn(mdev, "attach underlay qp to ft failed, %d\n", err); | |
464 | goto err_reset_qp; | |
465 | } | |
603f4a45 | 466 | |
dae37456 AV |
467 | err = mlx5e_open_channels(epriv, &epriv->channels); |
468 | if (err) | |
469 | goto err_remove_fs_underlay_qp; | |
7ca42c80 | 470 | |
dae37456 AV |
471 | mlx5e_refresh_tirs(epriv, false); |
472 | mlx5e_activate_priv_channels(epriv); | |
dae37456 AV |
473 | |
474 | mutex_unlock(&epriv->state_lock); | |
603f4a45 SM |
475 | return 0; |
476 | ||
dae37456 AV |
477 | err_remove_fs_underlay_qp: |
478 | mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); | |
479 | err_reset_qp: | |
480 | mlx5i_uninit_underlay_qp(epriv); | |
603f4a45 | 481 | err_clear_state_opened_flag: |
dae37456 AV |
482 | clear_bit(MLX5E_STATE_OPENED, &epriv->state); |
483 | mutex_unlock(&epriv->state_lock); | |
603f4a45 SM |
484 | return err; |
485 | } | |
486 | ||
487 | static int mlx5i_close(struct net_device *netdev) | |
488 | { | |
dae37456 AV |
489 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); |
490 | struct mlx5i_priv *ipriv = epriv->ppriv; | |
491 | struct mlx5_core_dev *mdev = epriv->mdev; | |
603f4a45 SM |
492 | |
493 | /* May already be CLOSED in case a previous configuration operation | |
494 | * (e.g RX/TX queue size change) that involves close&open failed. | |
495 | */ | |
dae37456 | 496 | mutex_lock(&epriv->state_lock); |
603f4a45 | 497 | |
dae37456 | 498 | if (!test_bit(MLX5E_STATE_OPENED, &epriv->state)) |
603f4a45 SM |
499 | goto unlock; |
500 | ||
dae37456 | 501 | clear_bit(MLX5E_STATE_OPENED, &epriv->state); |
603f4a45 | 502 | |
dae37456 AV |
503 | netif_carrier_off(epriv->netdev); |
504 | mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); | |
505 | mlx5i_uninit_underlay_qp(epriv); | |
506 | mlx5e_deactivate_priv_channels(epriv); | |
4c5386ba | 507 | mlx5e_close_channels(&epriv->channels); |
603f4a45 | 508 | unlock: |
dae37456 | 509 | mutex_unlock(&epriv->state_lock); |
603f4a45 SM |
510 | return 0; |
511 | } | |
512 | ||
48935bbb | 513 | /* IPoIB RDMA netdev callbacks */ |
a7082ef0 | 514 | static int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, |
693dfd5a ES |
515 | union ib_gid *gid, u16 lid, int set_qkey, |
516 | u32 qkey) | |
ec8fd927 SM |
517 | { |
518 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); | |
519 | struct mlx5_core_dev *mdev = epriv->mdev; | |
520 | struct mlx5i_priv *ipriv = epriv->ppriv; | |
521 | int err; | |
522 | ||
523 | mlx5_core_dbg(mdev, "attaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); | |
524 | err = mlx5_core_attach_mcg(mdev, gid, ipriv->qp.qpn); | |
525 | if (err) | |
526 | mlx5_core_warn(mdev, "failed attaching QPN 0x%x, MGID %pI6\n", | |
527 | ipriv->qp.qpn, gid->raw); | |
528 | ||
693dfd5a ES |
529 | if (set_qkey) { |
530 | mlx5_core_dbg(mdev, "%s setting qkey 0x%x\n", | |
531 | netdev->name, qkey); | |
532 | ipriv->qkey = qkey; | |
533 | } | |
534 | ||
ec8fd927 SM |
535 | return err; |
536 | } | |
537 | ||
a7082ef0 SH |
538 | static int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, |
539 | union ib_gid *gid, u16 lid) | |
ec8fd927 SM |
540 | { |
541 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); | |
542 | struct mlx5_core_dev *mdev = epriv->mdev; | |
543 | struct mlx5i_priv *ipriv = epriv->ppriv; | |
544 | int err; | |
545 | ||
546 | mlx5_core_dbg(mdev, "detaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); | |
547 | ||
548 | err = mlx5_core_detach_mcg(mdev, gid, ipriv->qp.qpn); | |
549 | if (err) | |
53378898 | 550 | mlx5_core_dbg(mdev, "failed detaching QPN 0x%x, MGID %pI6\n", |
ec8fd927 SM |
551 | ipriv->qp.qpn, gid->raw); |
552 | ||
553 | return err; | |
554 | } | |
48935bbb | 555 | |
a7082ef0 | 556 | static int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, |
693dfd5a | 557 | struct ib_ah *address, u32 dqpn) |
25854544 SM |
558 | { |
559 | struct mlx5e_priv *epriv = mlx5i_epriv(dev); | |
560 | struct mlx5e_txqsq *sq = epriv->txq2sq[skb_get_queue_mapping(skb)]; | |
561 | struct mlx5_ib_ah *mah = to_mah(address); | |
693dfd5a | 562 | struct mlx5i_priv *ipriv = epriv->ppriv; |
25854544 | 563 | |
693dfd5a | 564 | return mlx5i_sq_xmit(sq, skb, &mah->av, dqpn, ipriv->qkey); |
25854544 SM |
565 | } |
566 | ||
da34f1a8 AV |
567 | static void mlx5i_set_pkey_index(struct net_device *netdev, int id) |
568 | { | |
569 | struct mlx5i_priv *ipriv = netdev_priv(netdev); | |
570 | ||
571 | ipriv->pkey_index = (u16)id; | |
572 | } | |
573 | ||
48935bbb SM |
574 | static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) |
575 | { | |
576 | if (MLX5_CAP_GEN(mdev, port_type) != MLX5_CAP_PORT_TYPE_IB) | |
577 | return -EOPNOTSUPP; | |
578 | ||
579 | if (!MLX5_CAP_GEN(mdev, ipoib_enhanced_offloads)) { | |
580 | mlx5_core_warn(mdev, "IPoIB enhanced offloads are not supported\n"); | |
693dfd5a | 581 | return -EOPNOTSUPP; |
48935bbb SM |
582 | } |
583 | ||
584 | return 0; | |
585 | } | |
586 | ||
693dfd5a ES |
587 | struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, |
588 | struct ib_device *ibdev, | |
589 | const char *name, | |
590 | void (*setup)(struct net_device *)) | |
48935bbb | 591 | { |
b5ae5777 | 592 | const struct mlx5e_profile *profile; |
48935bbb SM |
593 | struct net_device *netdev; |
594 | struct mlx5i_priv *ipriv; | |
595 | struct mlx5e_priv *epriv; | |
693dfd5a | 596 | struct rdma_netdev *rn; |
b5ae5777 AV |
597 | bool sub_interface; |
598 | int nch; | |
48935bbb SM |
599 | int err; |
600 | ||
601 | if (mlx5i_check_required_hca_cap(mdev)) { | |
602 | mlx5_core_warn(mdev, "Accelerated mode is not supported\n"); | |
603 | return ERR_PTR(-EOPNOTSUPP); | |
604 | } | |
605 | ||
b5ae5777 AV |
606 | /* TODO: Need to find a better way to check if child device*/ |
607 | sub_interface = (mdev->mlx5e_res.pdn != 0); | |
608 | ||
609 | if (sub_interface) | |
610 | profile = mlx5i_pkey_get_profile(); | |
611 | else | |
612 | profile = &mlx5i_nic_profile; | |
613 | ||
614 | nch = profile->max_nch(mdev); | |
48935bbb SM |
615 | |
616 | netdev = alloc_netdev_mqs(sizeof(struct mlx5i_priv) + sizeof(struct mlx5e_priv), | |
617 | name, NET_NAME_UNKNOWN, | |
618 | setup, | |
619 | nch * MLX5E_MAX_NUM_TC, | |
620 | nch); | |
621 | if (!netdev) { | |
622 | mlx5_core_warn(mdev, "alloc_netdev_mqs failed\n"); | |
b5ae5777 | 623 | return NULL; |
48935bbb SM |
624 | } |
625 | ||
626 | ipriv = netdev_priv(netdev); | |
627 | epriv = mlx5i_epriv(netdev); | |
628 | ||
629 | epriv->wq = create_singlethread_workqueue("mlx5i"); | |
630 | if (!epriv->wq) | |
631 | goto err_free_netdev; | |
632 | ||
b5ae5777 AV |
633 | ipriv->sub_interface = sub_interface; |
634 | if (!ipriv->sub_interface) { | |
635 | err = mlx5i_pkey_qpn_ht_init(netdev); | |
636 | if (err) { | |
637 | mlx5_core_warn(mdev, "allocate qpn_to_netdev ht failed\n"); | |
638 | goto destroy_wq; | |
639 | } | |
640 | ||
641 | /* This should only be called once per mdev */ | |
642 | err = mlx5e_create_mdev_resources(mdev); | |
643 | if (err) | |
644 | goto destroy_ht; | |
7e7f4780 AV |
645 | } |
646 | ||
48935bbb SM |
647 | profile->init(mdev, netdev, profile, ipriv); |
648 | ||
649 | mlx5e_attach_netdev(epriv); | |
650 | netif_carrier_off(netdev); | |
651 | ||
693dfd5a ES |
652 | /* set rdma_netdev func pointers */ |
653 | rn = &ipriv->rn; | |
654 | rn->hca = ibdev; | |
655 | rn->send = mlx5i_xmit; | |
656 | rn->attach_mcast = mlx5i_attach_mcast; | |
657 | rn->detach_mcast = mlx5i_detach_mcast; | |
da34f1a8 | 658 | rn->set_id = mlx5i_set_pkey_index; |
693dfd5a | 659 | |
48935bbb SM |
660 | return netdev; |
661 | ||
b5ae5777 AV |
662 | destroy_ht: |
663 | mlx5i_pkey_qpn_ht_cleanup(netdev); | |
7e7f4780 AV |
664 | destroy_wq: |
665 | destroy_workqueue(epriv->wq); | |
48935bbb SM |
666 | err_free_netdev: |
667 | free_netdev(netdev); | |
6905e5a5 | 668 | |
48935bbb SM |
669 | return NULL; |
670 | } | |
671 | EXPORT_SYMBOL(mlx5_rdma_netdev_alloc); | |
672 | ||
693dfd5a | 673 | void mlx5_rdma_netdev_free(struct net_device *netdev) |
48935bbb | 674 | { |
b5ae5777 AV |
675 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); |
676 | struct mlx5i_priv *ipriv = priv->ppriv; | |
48935bbb SM |
677 | const struct mlx5e_profile *profile = priv->profile; |
678 | ||
679 | mlx5e_detach_netdev(priv); | |
680 | profile->cleanup(priv); | |
681 | destroy_workqueue(priv->wq); | |
48935bbb | 682 | |
b5ae5777 AV |
683 | if (!ipriv->sub_interface) { |
684 | mlx5i_pkey_qpn_ht_cleanup(netdev); | |
685 | mlx5e_destroy_mdev_resources(priv->mdev); | |
686 | } | |
687 | free_netdev(netdev); | |
48935bbb SM |
688 | } |
689 | EXPORT_SYMBOL(mlx5_rdma_netdev_free); |