Merge tag 'wireless-drivers-next-2021-04-13' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / drivers / net / ethernet / mellanox / mlx5 / core / pci_irq.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3
4 #include <linux/interrupt.h>
5 #include <linux/notifier.h>
6 #include <linux/module.h>
7 #include <linux/mlx5/driver.h>
8 #include "mlx5_core.h"
9 #ifdef CONFIG_RFS_ACCEL
10 #include <linux/cpu_rmap.h>
11 #endif
12
13 #define MLX5_MAX_IRQ_NAME (32)
14
15 struct mlx5_irq {
16         struct atomic_notifier_head nh;
17         cpumask_var_t mask;
18         char name[MLX5_MAX_IRQ_NAME];
19 };
20
21 struct mlx5_irq_table {
22         struct mlx5_irq *irq;
23         int nvec;
24 #ifdef CONFIG_RFS_ACCEL
25         struct cpu_rmap *rmap;
26 #endif
27 };
28
29 int mlx5_irq_table_init(struct mlx5_core_dev *dev)
30 {
31         struct mlx5_irq_table *irq_table;
32
33         if (mlx5_core_is_sf(dev))
34                 return 0;
35
36         irq_table = kvzalloc(sizeof(*irq_table), GFP_KERNEL);
37         if (!irq_table)
38                 return -ENOMEM;
39
40         dev->priv.irq_table = irq_table;
41         return 0;
42 }
43
44 void mlx5_irq_table_cleanup(struct mlx5_core_dev *dev)
45 {
46         if (mlx5_core_is_sf(dev))
47                 return;
48
49         kvfree(dev->priv.irq_table);
50 }
51
52 int mlx5_irq_get_num_comp(struct mlx5_irq_table *table)
53 {
54         return table->nvec - MLX5_IRQ_VEC_COMP_BASE;
55 }
56
57 static struct mlx5_irq *mlx5_irq_get(struct mlx5_core_dev *dev, int vecidx)
58 {
59         struct mlx5_irq_table *irq_table = dev->priv.irq_table;
60
61         return &irq_table->irq[vecidx];
62 }
63
64 /**
65  * mlx5_get_default_msix_vec_count - Get the default number of MSI-X vectors
66  *                                   to be ssigned to each VF.
67  * @dev: PF to work on
68  * @num_vfs: Number of enabled VFs
69  */
70 int mlx5_get_default_msix_vec_count(struct mlx5_core_dev *dev, int num_vfs)
71 {
72         int num_vf_msix, min_msix, max_msix;
73
74         num_vf_msix = MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix);
75         if (!num_vf_msix)
76                 return 0;
77
78         min_msix = MLX5_CAP_GEN(dev, min_dynamic_vf_msix_table_size);
79         max_msix = MLX5_CAP_GEN(dev, max_dynamic_vf_msix_table_size);
80
81         /* Limit maximum number of MSI-X vectors so the default configuration
82          * has some available in the pool. This will allow the user to increase
83          * the number of vectors in a VF without having to first size-down other
84          * VFs.
85          */
86         return max(min(num_vf_msix / num_vfs, max_msix / 2), min_msix);
87 }
88
89 /**
90  * mlx5_set_msix_vec_count - Set dynamically allocated MSI-X on the VF
91  * @dev: PF to work on
92  * @function_id: Internal PCI VF function IDd
93  * @msix_vec_count: Number of MSI-X vectors to set
94  */
95 int mlx5_set_msix_vec_count(struct mlx5_core_dev *dev, int function_id,
96                             int msix_vec_count)
97 {
98         int sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
99         int num_vf_msix, min_msix, max_msix;
100         void *hca_cap, *cap;
101         int ret;
102
103         num_vf_msix = MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix);
104         if (!num_vf_msix)
105                 return 0;
106
107         if (!MLX5_CAP_GEN(dev, vport_group_manager) || !mlx5_core_is_pf(dev))
108                 return -EOPNOTSUPP;
109
110         min_msix = MLX5_CAP_GEN(dev, min_dynamic_vf_msix_table_size);
111         max_msix = MLX5_CAP_GEN(dev, max_dynamic_vf_msix_table_size);
112
113         if (msix_vec_count < min_msix)
114                 return -EINVAL;
115
116         if (msix_vec_count > max_msix)
117                 return -EOVERFLOW;
118
119         hca_cap = kzalloc(sz, GFP_KERNEL);
120         if (!hca_cap)
121                 return -ENOMEM;
122
123         cap = MLX5_ADDR_OF(set_hca_cap_in, hca_cap, capability);
124         MLX5_SET(cmd_hca_cap, cap, dynamic_msix_table_size, msix_vec_count);
125
126         MLX5_SET(set_hca_cap_in, hca_cap, opcode, MLX5_CMD_OP_SET_HCA_CAP);
127         MLX5_SET(set_hca_cap_in, hca_cap, other_function, 1);
128         MLX5_SET(set_hca_cap_in, hca_cap, function_id, function_id);
129
130         MLX5_SET(set_hca_cap_in, hca_cap, op_mod,
131                  MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1);
132         ret = mlx5_cmd_exec_in(dev, set_hca_cap, hca_cap);
133         kfree(hca_cap);
134         return ret;
135 }
136
137 int mlx5_irq_attach_nb(struct mlx5_irq_table *irq_table, int vecidx,
138                        struct notifier_block *nb)
139 {
140         struct mlx5_irq *irq;
141
142         irq = &irq_table->irq[vecidx];
143         return atomic_notifier_chain_register(&irq->nh, nb);
144 }
145
146 int mlx5_irq_detach_nb(struct mlx5_irq_table *irq_table, int vecidx,
147                        struct notifier_block *nb)
148 {
149         struct mlx5_irq *irq;
150
151         irq = &irq_table->irq[vecidx];
152         return atomic_notifier_chain_unregister(&irq->nh, nb);
153 }
154
155 static irqreturn_t mlx5_irq_int_handler(int irq, void *nh)
156 {
157         atomic_notifier_call_chain(nh, 0, NULL);
158         return IRQ_HANDLED;
159 }
160
161 static void irq_set_name(char *name, int vecidx)
162 {
163         if (vecidx == 0) {
164                 snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_async");
165                 return;
166         }
167
168         snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_comp%d",
169                  vecidx - MLX5_IRQ_VEC_COMP_BASE);
170         return;
171 }
172
173 static int request_irqs(struct mlx5_core_dev *dev, int nvec)
174 {
175         char name[MLX5_MAX_IRQ_NAME];
176         int err;
177         int i;
178
179         for (i = 0; i < nvec; i++) {
180                 struct mlx5_irq *irq = mlx5_irq_get(dev, i);
181                 int irqn = pci_irq_vector(dev->pdev, i);
182
183                 irq_set_name(name, i);
184                 ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh);
185                 snprintf(irq->name, MLX5_MAX_IRQ_NAME,
186                          "%s@pci:%s", name, pci_name(dev->pdev));
187                 err = request_irq(irqn, mlx5_irq_int_handler, 0, irq->name,
188                                   &irq->nh);
189                 if (err) {
190                         mlx5_core_err(dev, "Failed to request irq\n");
191                         goto err_request_irq;
192                 }
193         }
194         return 0;
195
196 err_request_irq:
197         while (i--) {
198                 struct mlx5_irq *irq = mlx5_irq_get(dev, i);
199                 int irqn = pci_irq_vector(dev->pdev, i);
200
201                 free_irq(irqn, &irq->nh);
202         }
203         return  err;
204 }
205
206 static void irq_clear_rmap(struct mlx5_core_dev *dev)
207 {
208 #ifdef CONFIG_RFS_ACCEL
209         struct mlx5_irq_table *irq_table = dev->priv.irq_table;
210
211         free_irq_cpu_rmap(irq_table->rmap);
212 #endif
213 }
214
215 static int irq_set_rmap(struct mlx5_core_dev *mdev)
216 {
217         int err = 0;
218 #ifdef CONFIG_RFS_ACCEL
219         struct mlx5_irq_table *irq_table = mdev->priv.irq_table;
220         int num_affinity_vec;
221         int vecidx;
222
223         num_affinity_vec = mlx5_irq_get_num_comp(irq_table);
224         irq_table->rmap = alloc_irq_cpu_rmap(num_affinity_vec);
225         if (!irq_table->rmap) {
226                 err = -ENOMEM;
227                 mlx5_core_err(mdev, "Failed to allocate cpu_rmap. err %d", err);
228                 goto err_out;
229         }
230
231         vecidx = MLX5_IRQ_VEC_COMP_BASE;
232         for (; vecidx < irq_table->nvec; vecidx++) {
233                 err = irq_cpu_rmap_add(irq_table->rmap,
234                                        pci_irq_vector(mdev->pdev, vecidx));
235                 if (err) {
236                         mlx5_core_err(mdev, "irq_cpu_rmap_add failed. err %d",
237                                       err);
238                         goto err_irq_cpu_rmap_add;
239                 }
240         }
241         return 0;
242
243 err_irq_cpu_rmap_add:
244         irq_clear_rmap(mdev);
245 err_out:
246 #endif
247         return err;
248 }
249
250 /* Completion IRQ vectors */
251
252 static int set_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
253 {
254         int vecidx = MLX5_IRQ_VEC_COMP_BASE + i;
255         struct mlx5_irq *irq;
256         int irqn;
257
258         irq = mlx5_irq_get(mdev, vecidx);
259         irqn = pci_irq_vector(mdev->pdev, vecidx);
260         if (!zalloc_cpumask_var(&irq->mask, GFP_KERNEL)) {
261                 mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
262                 return -ENOMEM;
263         }
264
265         cpumask_set_cpu(cpumask_local_spread(i, mdev->priv.numa_node),
266                         irq->mask);
267         if (IS_ENABLED(CONFIG_SMP) &&
268             irq_set_affinity_hint(irqn, irq->mask))
269                 mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x",
270                                irqn);
271
272         return 0;
273 }
274
275 static void clear_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
276 {
277         int vecidx = MLX5_IRQ_VEC_COMP_BASE + i;
278         struct mlx5_irq *irq;
279         int irqn;
280
281         irq = mlx5_irq_get(mdev, vecidx);
282         irqn = pci_irq_vector(mdev->pdev, vecidx);
283         irq_set_affinity_hint(irqn, NULL);
284         free_cpumask_var(irq->mask);
285 }
286
287 static int set_comp_irq_affinity_hints(struct mlx5_core_dev *mdev)
288 {
289         int nvec = mlx5_irq_get_num_comp(mdev->priv.irq_table);
290         int err;
291         int i;
292
293         for (i = 0; i < nvec; i++) {
294                 err = set_comp_irq_affinity_hint(mdev, i);
295                 if (err)
296                         goto err_out;
297         }
298
299         return 0;
300
301 err_out:
302         for (i--; i >= 0; i--)
303                 clear_comp_irq_affinity_hint(mdev, i);
304
305         return err;
306 }
307
308 static void clear_comp_irqs_affinity_hints(struct mlx5_core_dev *mdev)
309 {
310         int nvec = mlx5_irq_get_num_comp(mdev->priv.irq_table);
311         int i;
312
313         for (i = 0; i < nvec; i++)
314                 clear_comp_irq_affinity_hint(mdev, i);
315 }
316
317 struct cpumask *
318 mlx5_irq_get_affinity_mask(struct mlx5_irq_table *irq_table, int vecidx)
319 {
320         return irq_table->irq[vecidx].mask;
321 }
322
323 #ifdef CONFIG_RFS_ACCEL
324 struct cpu_rmap *mlx5_irq_get_rmap(struct mlx5_irq_table *irq_table)
325 {
326         return irq_table->rmap;
327 }
328 #endif
329
330 static void unrequest_irqs(struct mlx5_core_dev *dev)
331 {
332         struct mlx5_irq_table *table = dev->priv.irq_table;
333         int i;
334
335         for (i = 0; i < table->nvec; i++)
336                 free_irq(pci_irq_vector(dev->pdev, i),
337                          &mlx5_irq_get(dev, i)->nh);
338 }
339
340 int mlx5_irq_table_create(struct mlx5_core_dev *dev)
341 {
342         struct mlx5_priv *priv = &dev->priv;
343         struct mlx5_irq_table *table = priv->irq_table;
344         int num_eqs = MLX5_CAP_GEN(dev, max_num_eqs) ?
345                       MLX5_CAP_GEN(dev, max_num_eqs) :
346                       1 << MLX5_CAP_GEN(dev, log_max_eq);
347         int nvec;
348         int err;
349
350         if (mlx5_core_is_sf(dev))
351                 return 0;
352
353         nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() +
354                MLX5_IRQ_VEC_COMP_BASE;
355         nvec = min_t(int, nvec, num_eqs);
356         if (nvec <= MLX5_IRQ_VEC_COMP_BASE)
357                 return -ENOMEM;
358
359         table->irq = kcalloc(nvec, sizeof(*table->irq), GFP_KERNEL);
360         if (!table->irq)
361                 return -ENOMEM;
362
363         nvec = pci_alloc_irq_vectors(dev->pdev, MLX5_IRQ_VEC_COMP_BASE + 1,
364                                      nvec, PCI_IRQ_MSIX);
365         if (nvec < 0) {
366                 err = nvec;
367                 goto err_free_irq;
368         }
369
370         table->nvec = nvec;
371
372         err = irq_set_rmap(dev);
373         if (err)
374                 goto err_set_rmap;
375
376         err = request_irqs(dev, nvec);
377         if (err)
378                 goto err_request_irqs;
379
380         err = set_comp_irq_affinity_hints(dev);
381         if (err) {
382                 mlx5_core_err(dev, "Failed to alloc affinity hint cpumask\n");
383                 goto err_set_affinity;
384         }
385
386         return 0;
387
388 err_set_affinity:
389         unrequest_irqs(dev);
390 err_request_irqs:
391         irq_clear_rmap(dev);
392 err_set_rmap:
393         pci_free_irq_vectors(dev->pdev);
394 err_free_irq:
395         kfree(table->irq);
396         return err;
397 }
398
399 void mlx5_irq_table_destroy(struct mlx5_core_dev *dev)
400 {
401         struct mlx5_irq_table *table = dev->priv.irq_table;
402         int i;
403
404         if (mlx5_core_is_sf(dev))
405                 return;
406
407         /* free_irq requires that affinity and rmap will be cleared
408          * before calling it. This is why there is asymmetry with set_rmap
409          * which should be called after alloc_irq but before request_irq.
410          */
411         irq_clear_rmap(dev);
412         clear_comp_irqs_affinity_hints(dev);
413         for (i = 0; i < table->nvec; i++)
414                 free_irq(pci_irq_vector(dev->pdev, i),
415                          &mlx5_irq_get(dev, i)->nh);
416         pci_free_irq_vectors(dev->pdev);
417         kfree(table->irq);
418 }
419
420 struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev)
421 {
422 #ifdef CONFIG_MLX5_SF
423         if (mlx5_core_is_sf(dev))
424                 return dev->priv.parent_mdev->priv.irq_table;
425 #endif
426         return dev->priv.irq_table;
427 }