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 | |
33 | #include <linux/hash.h> |
34 | #include "ipoib.h" |
35 | |
36 | #define MLX5I_MAX_LOG_PKEY_SUP 7 |
37 | |
38 | struct qpn_to_netdev { |
39 | struct net_device *netdev; |
40 | struct hlist_node hlist; |
41 | u32 underlay_qpn; |
42 | }; |
43 | |
44 | struct mlx5i_pkey_qpn_ht { |
45 | struct hlist_head buckets[1 << MLX5I_MAX_LOG_PKEY_SUP]; |
46 | spinlock_t ht_lock; /* Synchronise with NAPI */ |
47 | }; |
48 | |
49 | int mlx5i_pkey_qpn_ht_init(struct net_device *netdev) |
50 | { |
51 | struct mlx5i_priv *ipriv = netdev_priv(dev: netdev); |
52 | struct mlx5i_pkey_qpn_ht *qpn_htbl; |
53 | |
54 | qpn_htbl = kzalloc(size: sizeof(*qpn_htbl), GFP_KERNEL); |
55 | if (!qpn_htbl) |
56 | return -ENOMEM; |
57 | |
58 | ipriv->qpn_htbl = qpn_htbl; |
59 | spin_lock_init(&qpn_htbl->ht_lock); |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | void mlx5i_pkey_qpn_ht_cleanup(struct net_device *netdev) |
65 | { |
66 | struct mlx5i_priv *ipriv = netdev_priv(dev: netdev); |
67 | |
68 | kfree(objp: ipriv->qpn_htbl); |
69 | } |
70 | |
71 | static struct qpn_to_netdev *mlx5i_find_qpn_to_netdev_node(struct hlist_head *buckets, |
72 | u32 qpn) |
73 | { |
74 | struct hlist_head *h = &buckets[hash_32(val: qpn, MLX5I_MAX_LOG_PKEY_SUP)]; |
75 | struct qpn_to_netdev *node; |
76 | |
77 | hlist_for_each_entry(node, h, hlist) { |
78 | if (node->underlay_qpn == qpn) |
79 | return node; |
80 | } |
81 | |
82 | return NULL; |
83 | } |
84 | |
85 | int mlx5i_pkey_add_qpn(struct net_device *netdev, u32 qpn) |
86 | { |
87 | struct mlx5i_priv *ipriv = netdev_priv(dev: netdev); |
88 | struct mlx5i_pkey_qpn_ht *ht = ipriv->qpn_htbl; |
89 | u8 key = hash_32(val: qpn, MLX5I_MAX_LOG_PKEY_SUP); |
90 | struct qpn_to_netdev *new_node; |
91 | |
92 | new_node = kzalloc(size: sizeof(*new_node), GFP_KERNEL); |
93 | if (!new_node) |
94 | return -ENOMEM; |
95 | |
96 | new_node->netdev = netdev; |
97 | new_node->underlay_qpn = qpn; |
98 | spin_lock_bh(lock: &ht->ht_lock); |
99 | hlist_add_head(n: &new_node->hlist, h: &ht->buckets[key]); |
100 | spin_unlock_bh(lock: &ht->ht_lock); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | int mlx5i_pkey_del_qpn(struct net_device *netdev, u32 qpn) |
106 | { |
107 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); |
108 | struct mlx5i_priv *ipriv = epriv->ppriv; |
109 | struct mlx5i_pkey_qpn_ht *ht = ipriv->qpn_htbl; |
110 | struct qpn_to_netdev *node; |
111 | |
112 | node = mlx5i_find_qpn_to_netdev_node(buckets: ht->buckets, qpn); |
113 | if (!node) { |
114 | mlx5_core_warn(epriv->mdev, "QPN to netdev delete from HT failed\n" ); |
115 | return -EINVAL; |
116 | } |
117 | |
118 | spin_lock_bh(lock: &ht->ht_lock); |
119 | hlist_del_init(n: &node->hlist); |
120 | spin_unlock_bh(lock: &ht->ht_lock); |
121 | kfree(objp: node); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | struct net_device *mlx5i_pkey_get_netdev(struct net_device *netdev, u32 qpn) |
127 | { |
128 | struct mlx5i_priv *ipriv = netdev_priv(dev: netdev); |
129 | struct qpn_to_netdev *node; |
130 | |
131 | node = mlx5i_find_qpn_to_netdev_node(buckets: ipriv->qpn_htbl->buckets, qpn); |
132 | if (!node) |
133 | return NULL; |
134 | |
135 | return node->netdev; |
136 | } |
137 | |
138 | static int mlx5i_pkey_open(struct net_device *netdev); |
139 | static int mlx5i_pkey_close(struct net_device *netdev); |
140 | static int mlx5i_pkey_dev_init(struct net_device *dev); |
141 | static void mlx5i_pkey_dev_cleanup(struct net_device *netdev); |
142 | static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu); |
143 | static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); |
144 | |
145 | static const struct net_device_ops mlx5i_pkey_netdev_ops = { |
146 | .ndo_open = mlx5i_pkey_open, |
147 | .ndo_stop = mlx5i_pkey_close, |
148 | .ndo_init = mlx5i_pkey_dev_init, |
149 | .ndo_get_stats64 = mlx5i_get_stats, |
150 | .ndo_uninit = mlx5i_pkey_dev_cleanup, |
151 | .ndo_change_mtu = mlx5i_pkey_change_mtu, |
152 | .ndo_eth_ioctl = mlx5i_pkey_ioctl, |
153 | }; |
154 | |
155 | /* Child NDOs */ |
156 | static int mlx5i_pkey_dev_init(struct net_device *dev) |
157 | { |
158 | struct mlx5e_priv *priv = mlx5i_epriv(dev); |
159 | struct mlx5i_priv *ipriv, *parent_ipriv; |
160 | struct net_device *parent_dev; |
161 | |
162 | ipriv = priv->ppriv; |
163 | |
164 | /* Link to parent */ |
165 | parent_dev = mlx5i_parent_get(netdev: dev); |
166 | if (!parent_dev) { |
167 | mlx5_core_warn(priv->mdev, "failed to get parent device\n" ); |
168 | return -EINVAL; |
169 | } |
170 | |
171 | if (dev->num_rx_queues < parent_dev->real_num_rx_queues) { |
172 | mlx5_core_warn(priv->mdev, |
173 | "failed to create child device with rx queues [%d] less than parent's [%d]\n" , |
174 | dev->num_rx_queues, |
175 | parent_dev->real_num_rx_queues); |
176 | mlx5i_parent_put(netdev: dev); |
177 | return -EINVAL; |
178 | } |
179 | |
180 | /* Get QPN to netdevice hash table from parent */ |
181 | parent_ipriv = netdev_priv(dev: parent_dev); |
182 | ipriv->qpn_htbl = parent_ipriv->qpn_htbl; |
183 | |
184 | return mlx5i_dev_init(dev); |
185 | } |
186 | |
187 | static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
188 | { |
189 | return mlx5i_ioctl(dev, ifr, cmd); |
190 | } |
191 | |
192 | static void mlx5i_pkey_dev_cleanup(struct net_device *netdev) |
193 | { |
194 | mlx5i_parent_put(netdev); |
195 | return mlx5i_dev_cleanup(dev: netdev); |
196 | } |
197 | |
198 | static int mlx5i_pkey_open(struct net_device *netdev) |
199 | { |
200 | struct mlx5e_priv *epriv = mlx5i_epriv(netdev); |
201 | struct mlx5i_priv *ipriv = epriv->ppriv; |
202 | struct mlx5_core_dev *mdev = epriv->mdev; |
203 | int err; |
204 | |
205 | mutex_lock(&epriv->state_lock); |
206 | |
207 | set_bit(nr: MLX5E_STATE_OPENED, addr: &epriv->state); |
208 | |
209 | err = mlx5i_init_underlay_qp(priv: epriv); |
210 | if (err) { |
211 | mlx5_core_warn(mdev, "prepare child underlay qp state failed, %d\n" , err); |
212 | goto err_release_lock; |
213 | } |
214 | |
215 | err = mlx5_fs_add_rx_underlay_qpn(dev: mdev, underlay_qpn: ipriv->qpn); |
216 | if (err) { |
217 | mlx5_core_warn(mdev, "attach child underlay qp to ft failed, %d\n" , err); |
218 | goto err_unint_underlay_qp; |
219 | } |
220 | |
221 | err = mlx5i_create_tis(mdev, underlay_qpn: ipriv->qpn, tisn: &ipriv->tisn); |
222 | if (err) { |
223 | mlx5_core_warn(mdev, "create child tis failed, %d\n" , err); |
224 | goto err_remove_rx_uderlay_qp; |
225 | } |
226 | |
227 | err = mlx5e_open_channels(priv: epriv, chs: &epriv->channels); |
228 | if (err) { |
229 | mlx5_core_warn(mdev, "opening child channels failed, %d\n" , err); |
230 | goto err_clear_state_opened_flag; |
231 | } |
232 | err = epriv->profile->update_rx(epriv); |
233 | if (err) |
234 | goto err_close_channels; |
235 | mlx5e_activate_priv_channels(priv: epriv); |
236 | mutex_unlock(lock: &epriv->state_lock); |
237 | |
238 | return 0; |
239 | |
240 | err_close_channels: |
241 | mlx5e_close_channels(chs: &epriv->channels); |
242 | err_clear_state_opened_flag: |
243 | mlx5e_destroy_tis(mdev, tisn: ipriv->tisn); |
244 | err_remove_rx_uderlay_qp: |
245 | mlx5_fs_remove_rx_underlay_qpn(dev: mdev, underlay_qpn: ipriv->qpn); |
246 | err_unint_underlay_qp: |
247 | mlx5i_uninit_underlay_qp(priv: epriv); |
248 | err_release_lock: |
249 | clear_bit(nr: MLX5E_STATE_OPENED, addr: &epriv->state); |
250 | mutex_unlock(lock: &epriv->state_lock); |
251 | return err; |
252 | } |
253 | |
254 | static int mlx5i_pkey_close(struct net_device *netdev) |
255 | { |
256 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); |
257 | struct mlx5i_priv *ipriv = priv->ppriv; |
258 | struct mlx5_core_dev *mdev = priv->mdev; |
259 | |
260 | mutex_lock(&priv->state_lock); |
261 | |
262 | if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) |
263 | goto unlock; |
264 | |
265 | clear_bit(nr: MLX5E_STATE_OPENED, addr: &priv->state); |
266 | |
267 | netif_carrier_off(dev: priv->netdev); |
268 | mlx5_fs_remove_rx_underlay_qpn(dev: mdev, underlay_qpn: ipriv->qpn); |
269 | mlx5i_uninit_underlay_qp(priv); |
270 | mlx5e_deactivate_priv_channels(priv); |
271 | mlx5e_close_channels(chs: &priv->channels); |
272 | mlx5e_destroy_tis(mdev, tisn: ipriv->tisn); |
273 | unlock: |
274 | mutex_unlock(lock: &priv->state_lock); |
275 | return 0; |
276 | } |
277 | |
278 | static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu) |
279 | { |
280 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); |
281 | |
282 | mutex_lock(&priv->state_lock); |
283 | netdev->mtu = new_mtu; |
284 | mutex_unlock(lock: &priv->state_lock); |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | /* Called directly after IPoIB netdevice was created to initialize SW structs */ |
290 | static int mlx5i_pkey_init(struct mlx5_core_dev *mdev, |
291 | struct net_device *netdev) |
292 | { |
293 | struct mlx5e_priv *priv = mlx5i_epriv(netdev); |
294 | int err; |
295 | |
296 | err = mlx5i_init(mdev, netdev); |
297 | if (err) |
298 | return err; |
299 | |
300 | /* Override parent ndo */ |
301 | netdev->netdev_ops = &mlx5i_pkey_netdev_ops; |
302 | |
303 | /* Set child limited ethtool support */ |
304 | netdev->ethtool_ops = &mlx5i_pkey_ethtool_ops; |
305 | |
306 | /* Use dummy rqs */ |
307 | priv->channels.params.log_rq_mtu_frames = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; |
308 | |
309 | return 0; |
310 | } |
311 | |
312 | /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ |
313 | static void mlx5i_pkey_cleanup(struct mlx5e_priv *priv) |
314 | { |
315 | mlx5i_cleanup(priv); |
316 | } |
317 | |
318 | static int mlx5i_pkey_init_tx(struct mlx5e_priv *priv) |
319 | { |
320 | int err; |
321 | |
322 | err = mlx5i_create_underlay_qp(priv); |
323 | if (err) |
324 | mlx5_core_warn(priv->mdev, "create child underlay QP failed, %d\n" , err); |
325 | |
326 | return err; |
327 | } |
328 | |
329 | static void mlx5i_pkey_cleanup_tx(struct mlx5e_priv *priv) |
330 | { |
331 | struct mlx5i_priv *ipriv = priv->ppriv; |
332 | |
333 | mlx5i_destroy_underlay_qp(mdev: priv->mdev, qpn: ipriv->qpn); |
334 | } |
335 | |
336 | static int mlx5i_pkey_init_rx(struct mlx5e_priv *priv) |
337 | { |
338 | /* Since the rx resources are shared between child and parent, the |
339 | * parent interface is taking care of rx resource allocation and init |
340 | */ |
341 | return 0; |
342 | } |
343 | |
344 | static void mlx5i_pkey_cleanup_rx(struct mlx5e_priv *priv) |
345 | { |
346 | /* Since the rx resources are shared between child and parent, the |
347 | * parent interface is taking care of rx resource free and de-init |
348 | */ |
349 | } |
350 | |
351 | static const struct mlx5e_profile mlx5i_pkey_nic_profile = { |
352 | .init = mlx5i_pkey_init, |
353 | .cleanup = mlx5i_pkey_cleanup, |
354 | .init_tx = mlx5i_pkey_init_tx, |
355 | .cleanup_tx = mlx5i_pkey_cleanup_tx, |
356 | .init_rx = mlx5i_pkey_init_rx, |
357 | .cleanup_rx = mlx5i_pkey_cleanup_rx, |
358 | .enable = NULL, |
359 | .disable = NULL, |
360 | .update_rx = mlx5i_update_nic_rx, |
361 | .update_stats = NULL, |
362 | .rx_handlers = &mlx5i_rx_handlers, |
363 | .max_tc = MLX5I_MAX_NUM_TC, |
364 | .get_tisn = mlx5i_get_tisn, |
365 | }; |
366 | |
367 | const struct mlx5e_profile *mlx5i_pkey_get_profile(void) |
368 | { |
369 | return &mlx5i_pkey_nic_profile; |
370 | } |
371 | |