Commit | Line | Data |
---|---|---|
fc50db98 EC |
1 | /* |
2 | * Copyright (c) 2014, Mellanox Technologies inc. 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/pci.h> | |
34 | #include <linux/mlx5/driver.h> | |
7ecf6d8f | 35 | #include <linux/mlx5/vport.h> |
fc50db98 | 36 | #include "mlx5_core.h" |
3b43190b | 37 | #include "mlx5_irq.h" |
81848731 | 38 | #include "eswitch.h" |
fc50db98 | 39 | |
2ee3db80 | 40 | static int sriov_restore_guids(struct mlx5_core_dev *dev, int vf, u16 func_id) |
7ecf6d8f BW |
41 | { |
42 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; | |
43 | struct mlx5_hca_vport_context *in; | |
44 | int err = 0; | |
45 | ||
46 | /* Restore sriov guid and policy settings */ | |
47 | if (sriov->vfs_ctx[vf].node_guid || | |
48 | sriov->vfs_ctx[vf].port_guid || | |
49 | sriov->vfs_ctx[vf].policy != MLX5_POLICY_INVALID) { | |
50 | in = kzalloc(sizeof(*in), GFP_KERNEL); | |
51 | if (!in) | |
52 | return -ENOMEM; | |
53 | ||
54 | in->node_guid = sriov->vfs_ctx[vf].node_guid; | |
55 | in->port_guid = sriov->vfs_ctx[vf].port_guid; | |
56 | in->policy = sriov->vfs_ctx[vf].policy; | |
57 | in->field_select = | |
58 | !!(in->port_guid) * MLX5_HCA_VPORT_SEL_PORT_GUID | | |
59 | !!(in->node_guid) * MLX5_HCA_VPORT_SEL_NODE_GUID | | |
60 | !!(in->policy) * MLX5_HCA_VPORT_SEL_STATE_POLICY; | |
61 | ||
2ee3db80 | 62 | err = mlx5_core_modify_hca_vport_context(dev, 1, 1, func_id, in); |
7ecf6d8f BW |
63 | if (err) |
64 | mlx5_core_warn(dev, "modify vport context failed, unable to restore VF %d settings\n", vf); | |
65 | ||
66 | kfree(in); | |
67 | } | |
68 | ||
69 | return err; | |
70 | } | |
71 | ||
6b6adee3 | 72 | static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) |
fc50db98 EC |
73 | { |
74 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; | |
604774ad | 75 | int err, vf, num_msix_count; |
2ee3db80 | 76 | int vport_num; |
fc50db98 | 77 | |
8e0aa4bc | 78 | err = mlx5_eswitch_enable(dev->priv.eswitch, num_vfs); |
6b6adee3 MHY |
79 | if (err) { |
80 | mlx5_core_warn(dev, | |
81 | "failed to enable eswitch SRIOV (%d)\n", err); | |
82 | return err; | |
83 | } | |
6b6adee3 | 84 | |
604774ad | 85 | num_msix_count = mlx5_get_default_msix_vec_count(dev, num_vfs); |
6b6adee3 | 86 | for (vf = 0; vf < num_vfs; vf++) { |
846e4373 YH |
87 | /* Notify the VF before its enablement to let it set |
88 | * some stuff. | |
89 | */ | |
90 | blocking_notifier_call_chain(&sriov->vfs_ctx[vf].notifier, | |
91 | MLX5_PF_NOTIFY_ENABLE_VF, dev); | |
6b6adee3 | 92 | err = mlx5_core_enable_hca(dev, vf + 1); |
fc50db98 | 93 | if (err) { |
6b6adee3 MHY |
94 | mlx5_core_warn(dev, "failed to enable VF %d (%d)\n", vf, err); |
95 | continue; | |
fc50db98 | 96 | } |
604774ad LR |
97 | |
98 | err = mlx5_set_msix_vec_count(dev, vf + 1, num_msix_count); | |
99 | if (err) { | |
100 | mlx5_core_warn(dev, | |
101 | "failed to set MSI-X vector counts VF %d, err %d\n", | |
102 | vf, err); | |
103 | continue; | |
104 | } | |
105 | ||
6b6adee3 | 106 | sriov->vfs_ctx[vf].enabled = 1; |
7ecf6d8f | 107 | if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) { |
2ee3db80 DJ |
108 | vport_num = mlx5_core_ec_sriov_enabled(dev) ? |
109 | mlx5_core_ec_vf_vport_base(dev) + vf | |
110 | : vf + 1; | |
111 | err = sriov_restore_guids(dev, vf, vport_num); | |
7ecf6d8f BW |
112 | if (err) { |
113 | mlx5_core_warn(dev, | |
114 | "failed to restore VF %d settings, err %d\n", | |
115 | vf, err); | |
353f59f4 | 116 | continue; |
7ecf6d8f BW |
117 | } |
118 | } | |
6b6adee3 | 119 | mlx5_core_dbg(dev, "successfully enabled VF* %d\n", vf); |
fc50db98 | 120 | } |
6b6adee3 MHY |
121 | |
122 | return 0; | |
fc50db98 EC |
123 | } |
124 | ||
a7cba0a4 | 125 | static void |
6d98f314 | 126 | mlx5_device_disable_sriov(struct mlx5_core_dev *dev, int num_vfs, bool clear_vf, bool num_vf_change) |
fc50db98 EC |
127 | { |
128 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; | |
6d98f314 DJ |
129 | bool wait_for_ec_vf_pages = true; |
130 | bool wait_for_vf_pages = true; | |
6b6adee3 | 131 | int err; |
fc50db98 EC |
132 | int vf; |
133 | ||
d886aba6 | 134 | for (vf = num_vfs - 1; vf >= 0; vf--) { |
6b6adee3 MHY |
135 | if (!sriov->vfs_ctx[vf].enabled) |
136 | continue; | |
846e4373 YH |
137 | /* Notify the VF before its disablement to let it clean |
138 | * some resources. | |
139 | */ | |
140 | blocking_notifier_call_chain(&sriov->vfs_ctx[vf].notifier, | |
141 | MLX5_PF_NOTIFY_DISABLE_VF, dev); | |
6b6adee3 MHY |
142 | err = mlx5_core_disable_hca(dev, vf + 1); |
143 | if (err) { | |
144 | mlx5_core_warn(dev, "failed to disable VF %d\n", vf); | |
145 | continue; | |
fc50db98 | 146 | } |
6b6adee3 | 147 | sriov->vfs_ctx[vf].enabled = 0; |
fc50db98 | 148 | } |
6b6adee3 | 149 | |
f019679e | 150 | mlx5_eswitch_disable_sriov(dev->priv.eswitch, clear_vf); |
6b6adee3 | 151 | |
6d98f314 DJ |
152 | /* There are a number of scenarios when SRIOV is being disabled: |
153 | * 1. VFs or ECVFs had been created, and now set back to 0 (num_vf_change == true). | |
154 | * - If EC SRIOV is enabled then this flow is happening on the | |
155 | * embedded platform, wait for only EC VF pages. | |
156 | * - If EC SRIOV is not enabled this flow is happening on non-embedded | |
157 | * platform, wait for the VF pages. | |
158 | * | |
159 | * 2. The driver is being unloaded. In this case wait for all pages. | |
160 | */ | |
161 | if (num_vf_change) { | |
162 | if (mlx5_core_ec_sriov_enabled(dev)) | |
163 | wait_for_vf_pages = false; | |
164 | else | |
165 | wait_for_ec_vf_pages = false; | |
166 | } | |
167 | ||
168 | if (wait_for_ec_vf_pages && mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_EC_VF])) | |
169 | mlx5_core_warn(dev, "timeout reclaiming EC VFs pages\n"); | |
170 | ||
e1ed30c8 MS |
171 | /* For ECPFs, skip waiting for host VF pages until ECPF is destroyed */ |
172 | if (mlx5_core_is_ecpf(dev)) | |
173 | return; | |
174 | ||
6d98f314 | 175 | if (wait_for_vf_pages && mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_VF])) |
6b6adee3 | 176 | mlx5_core_warn(dev, "timeout reclaiming VFs pages\n"); |
fc50db98 EC |
177 | } |
178 | ||
6b6adee3 | 179 | static int mlx5_sriov_enable(struct pci_dev *pdev, int num_vfs) |
fc50db98 EC |
180 | { |
181 | struct mlx5_core_dev *dev = pci_get_drvdata(pdev); | |
84a433a4 | 182 | struct devlink *devlink = priv_to_devlink(dev); |
88d73849 | 183 | int err; |
fc50db98 | 184 | |
84a433a4 | 185 | devl_lock(devlink); |
6b6adee3 | 186 | err = mlx5_device_enable_sriov(dev, num_vfs); |
35419025 | 187 | devl_unlock(devlink); |
6b6adee3 MHY |
188 | if (err) { |
189 | mlx5_core_warn(dev, "mlx5_device_enable_sriov failed : %d\n", err); | |
190 | return err; | |
191 | } | |
fc50db98 | 192 | |
88d73849 | 193 | err = pci_enable_sriov(pdev, num_vfs); |
fc50db98 | 194 | if (err) { |
88d73849 | 195 | mlx5_core_warn(dev, "pci_enable_sriov failed : %d\n", err); |
6d98f314 | 196 | mlx5_device_disable_sriov(dev, num_vfs, true, true); |
fc50db98 | 197 | } |
88d73849 | 198 | return err; |
fc50db98 EC |
199 | } |
200 | ||
6d98f314 | 201 | void mlx5_sriov_disable(struct pci_dev *pdev, bool num_vf_change) |
fc50db98 | 202 | { |
6b6adee3 | 203 | struct mlx5_core_dev *dev = pci_get_drvdata(pdev); |
84a433a4 | 204 | struct devlink *devlink = priv_to_devlink(dev); |
a7cba0a4 | 205 | int num_vfs = pci_num_vf(dev->pdev); |
fc50db98 | 206 | |
88d73849 | 207 | pci_disable_sriov(pdev); |
84a433a4 | 208 | devl_lock(devlink); |
6d98f314 | 209 | mlx5_device_disable_sriov(dev, num_vfs, true, num_vf_change); |
84a433a4 | 210 | devl_unlock(devlink); |
fc50db98 EC |
211 | } |
212 | ||
213 | int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) | |
214 | { | |
215 | struct mlx5_core_dev *dev = pci_get_drvdata(pdev); | |
88d73849 | 216 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; |
6b6adee3 | 217 | int err = 0; |
fc50db98 | 218 | |
c19ca6cb | 219 | mlx5_core_dbg(dev, "requested num_vfs %d\n", num_vfs); |
fc50db98 | 220 | |
eff849b2 | 221 | if (num_vfs) |
6b6adee3 | 222 | err = mlx5_sriov_enable(pdev, num_vfs); |
eff849b2 | 223 | else |
6d98f314 | 224 | mlx5_sriov_disable(pdev, true); |
fc50db98 | 225 | |
88d73849 PP |
226 | if (!err) |
227 | sriov->num_vfs = num_vfs; | |
6b6adee3 | 228 | return err ? err : num_vfs; |
fc50db98 EC |
229 | } |
230 | ||
e71b75f7 LR |
231 | int mlx5_core_sriov_set_msix_vec_count(struct pci_dev *vf, int msix_vec_count) |
232 | { | |
233 | struct pci_dev *pf = pci_physfn(vf); | |
234 | struct mlx5_core_sriov *sriov; | |
235 | struct mlx5_core_dev *dev; | |
236 | int num_vf_msix, id; | |
237 | ||
238 | dev = pci_get_drvdata(pf); | |
239 | num_vf_msix = MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix); | |
240 | if (!num_vf_msix) | |
241 | return -EOPNOTSUPP; | |
242 | ||
243 | if (!msix_vec_count) | |
244 | msix_vec_count = | |
245 | mlx5_get_default_msix_vec_count(dev, pci_num_vf(pf)); | |
246 | ||
247 | sriov = &dev->priv.sriov; | |
6ebd25b8 LR |
248 | id = pci_iov_vf_id(vf); |
249 | if (id < 0 || !sriov->vfs_ctx[id].enabled) | |
e71b75f7 LR |
250 | return -EINVAL; |
251 | ||
252 | return mlx5_set_msix_vec_count(dev, id + 1, msix_vec_count); | |
253 | } | |
254 | ||
acab721b MHY |
255 | int mlx5_sriov_attach(struct mlx5_core_dev *dev) |
256 | { | |
d886aba6 | 257 | if (!mlx5_core_is_pf(dev) || !pci_num_vf(dev->pdev)) |
acab721b MHY |
258 | return 0; |
259 | ||
260 | /* If sriov VFs exist in PCI level, enable them in device level */ | |
d886aba6 | 261 | return mlx5_device_enable_sriov(dev, pci_num_vf(dev->pdev)); |
acab721b MHY |
262 | } |
263 | ||
264 | void mlx5_sriov_detach(struct mlx5_core_dev *dev) | |
265 | { | |
266 | if (!mlx5_core_is_pf(dev)) | |
267 | return; | |
268 | ||
6d98f314 | 269 | mlx5_device_disable_sriov(dev, pci_num_vf(dev->pdev), false, false); |
acab721b MHY |
270 | } |
271 | ||
86eec50b BW |
272 | static u16 mlx5_get_max_vfs(struct mlx5_core_dev *dev) |
273 | { | |
86eec50b | 274 | u16 host_total_vfs; |
dd28087c | 275 | const u32 *out; |
86eec50b BW |
276 | |
277 | if (mlx5_core_is_ecpf_esw_manager(dev)) { | |
dd28087c | 278 | out = mlx5_esw_query_functions(dev); |
86eec50b BW |
279 | |
280 | /* Old FW doesn't support getting total_vfs from esw func | |
281 | * but supports getting it from pci_sriov. | |
282 | */ | |
dd28087c PP |
283 | if (IS_ERR(out)) |
284 | goto done; | |
285 | host_total_vfs = MLX5_GET(query_esw_functions_out, out, | |
286 | host_params_context.host_total_vfs); | |
287 | kvfree(out); | |
2dc2b392 | 288 | return host_total_vfs; |
86eec50b BW |
289 | } |
290 | ||
dd28087c | 291 | done: |
86eec50b BW |
292 | return pci_sriov_get_totalvfs(dev->pdev); |
293 | } | |
294 | ||
fc50db98 EC |
295 | int mlx5_sriov_init(struct mlx5_core_dev *dev) |
296 | { | |
297 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; | |
298 | struct pci_dev *pdev = dev->pdev; | |
846e4373 | 299 | int total_vfs, i; |
fc50db98 EC |
300 | |
301 | if (!mlx5_core_is_pf(dev)) | |
302 | return 0; | |
303 | ||
6b6adee3 | 304 | total_vfs = pci_sriov_get_totalvfs(pdev); |
86eec50b | 305 | sriov->max_vfs = mlx5_get_max_vfs(dev); |
6b6adee3 | 306 | sriov->num_vfs = pci_num_vf(pdev); |
7057fe56 | 307 | sriov->max_ec_vfs = mlx5_core_ec_sriov_enabled(dev) ? pci_sriov_get_totalvfs(dev->pdev) : 0; |
6b6adee3 | 308 | sriov->vfs_ctx = kcalloc(total_vfs, sizeof(*sriov->vfs_ctx), GFP_KERNEL); |
fc50db98 EC |
309 | if (!sriov->vfs_ctx) |
310 | return -ENOMEM; | |
311 | ||
846e4373 YH |
312 | for (i = 0; i < total_vfs; i++) |
313 | BLOCKING_INIT_NOTIFIER_HEAD(&sriov->vfs_ctx[i].notifier); | |
314 | ||
c2d6e31a | 315 | return 0; |
fc50db98 EC |
316 | } |
317 | ||
6b6adee3 | 318 | void mlx5_sriov_cleanup(struct mlx5_core_dev *dev) |
fc50db98 | 319 | { |
6b6adee3 | 320 | struct mlx5_core_sriov *sriov = &dev->priv.sriov; |
fc50db98 EC |
321 | |
322 | if (!mlx5_core_is_pf(dev)) | |
6b6adee3 | 323 | return; |
c2d6e31a | 324 | |
6b6adee3 | 325 | kfree(sriov->vfs_ctx); |
fc50db98 | 326 | } |
846e4373 YH |
327 | |
328 | /** | |
329 | * mlx5_sriov_blocking_notifier_unregister - Unregister a VF from | |
330 | * a notification block chain. | |
331 | * | |
332 | * @mdev: The mlx5 core device. | |
333 | * @vf_id: The VF id. | |
334 | * @nb: The notifier block to be unregistered. | |
335 | */ | |
336 | void mlx5_sriov_blocking_notifier_unregister(struct mlx5_core_dev *mdev, | |
337 | int vf_id, | |
338 | struct notifier_block *nb) | |
339 | { | |
340 | struct mlx5_vf_context *vfs_ctx; | |
341 | struct mlx5_core_sriov *sriov; | |
342 | ||
343 | sriov = &mdev->priv.sriov; | |
344 | if (WARN_ON(vf_id < 0 || vf_id >= sriov->num_vfs)) | |
345 | return; | |
346 | ||
347 | vfs_ctx = &sriov->vfs_ctx[vf_id]; | |
348 | blocking_notifier_chain_unregister(&vfs_ctx->notifier, nb); | |
349 | } | |
350 | EXPORT_SYMBOL(mlx5_sriov_blocking_notifier_unregister); | |
351 | ||
352 | /** | |
353 | * mlx5_sriov_blocking_notifier_register - Register a VF notification | |
354 | * block chain. | |
355 | * | |
356 | * @mdev: The mlx5 core device. | |
357 | * @vf_id: The VF id. | |
358 | * @nb: The notifier block to be called upon the VF events. | |
359 | * | |
360 | * Returns 0 on success or an error code. | |
361 | */ | |
362 | int mlx5_sriov_blocking_notifier_register(struct mlx5_core_dev *mdev, | |
363 | int vf_id, | |
364 | struct notifier_block *nb) | |
365 | { | |
366 | struct mlx5_vf_context *vfs_ctx; | |
367 | struct mlx5_core_sriov *sriov; | |
368 | ||
369 | sriov = &mdev->priv.sriov; | |
370 | if (vf_id < 0 || vf_id >= sriov->num_vfs) | |
371 | return -EINVAL; | |
372 | ||
373 | vfs_ctx = &sriov->vfs_ctx[vf_id]; | |
374 | return blocking_notifier_chain_register(&vfs_ctx->notifier, nb); | |
375 | } | |
376 | EXPORT_SYMBOL(mlx5_sriov_blocking_notifier_register); |