1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
4 #include <linux/interrupt.h>
5 #include <linux/notifier.h>
6 #include <linux/module.h>
7 #include <linux/mlx5/driver.h>
9 #ifdef CONFIG_RFS_ACCEL
10 #include <linux/cpu_rmap.h>
13 #define MLX5_MAX_IRQ_NAME (32)
16 struct atomic_notifier_head nh;
18 char name[MLX5_MAX_IRQ_NAME];
21 struct mlx5_irq_table {
24 #ifdef CONFIG_RFS_ACCEL
25 struct cpu_rmap *rmap;
29 int mlx5_irq_table_init(struct mlx5_core_dev *dev)
31 struct mlx5_irq_table *irq_table;
33 if (mlx5_core_is_sf(dev))
36 irq_table = kvzalloc(sizeof(*irq_table), GFP_KERNEL);
40 dev->priv.irq_table = irq_table;
44 void mlx5_irq_table_cleanup(struct mlx5_core_dev *dev)
46 if (mlx5_core_is_sf(dev))
49 kvfree(dev->priv.irq_table);
52 int mlx5_irq_get_num_comp(struct mlx5_irq_table *table)
54 return table->nvec - MLX5_IRQ_VEC_COMP_BASE;
57 static struct mlx5_irq *mlx5_irq_get(struct mlx5_core_dev *dev, int vecidx)
59 struct mlx5_irq_table *irq_table = dev->priv.irq_table;
61 return &irq_table->irq[vecidx];
64 int mlx5_irq_attach_nb(struct mlx5_irq_table *irq_table, int vecidx,
65 struct notifier_block *nb)
69 irq = &irq_table->irq[vecidx];
70 return atomic_notifier_chain_register(&irq->nh, nb);
73 int mlx5_irq_detach_nb(struct mlx5_irq_table *irq_table, int vecidx,
74 struct notifier_block *nb)
78 irq = &irq_table->irq[vecidx];
79 return atomic_notifier_chain_unregister(&irq->nh, nb);
82 static irqreturn_t mlx5_irq_int_handler(int irq, void *nh)
84 atomic_notifier_call_chain(nh, 0, NULL);
88 static void irq_set_name(char *name, int vecidx)
91 snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_async");
95 snprintf(name, MLX5_MAX_IRQ_NAME, "mlx5_comp%d",
96 vecidx - MLX5_IRQ_VEC_COMP_BASE);
100 static int request_irqs(struct mlx5_core_dev *dev, int nvec)
102 char name[MLX5_MAX_IRQ_NAME];
106 for (i = 0; i < nvec; i++) {
107 struct mlx5_irq *irq = mlx5_irq_get(dev, i);
108 int irqn = pci_irq_vector(dev->pdev, i);
110 irq_set_name(name, i);
111 ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh);
112 snprintf(irq->name, MLX5_MAX_IRQ_NAME,
113 "%s@pci:%s", name, pci_name(dev->pdev));
114 err = request_irq(irqn, mlx5_irq_int_handler, 0, irq->name,
117 mlx5_core_err(dev, "Failed to request irq\n");
118 goto err_request_irq;
125 struct mlx5_irq *irq = mlx5_irq_get(dev, i);
126 int irqn = pci_irq_vector(dev->pdev, i);
128 free_irq(irqn, &irq->nh);
133 static void irq_clear_rmap(struct mlx5_core_dev *dev)
135 #ifdef CONFIG_RFS_ACCEL
136 struct mlx5_irq_table *irq_table = dev->priv.irq_table;
138 free_irq_cpu_rmap(irq_table->rmap);
142 static int irq_set_rmap(struct mlx5_core_dev *mdev)
145 #ifdef CONFIG_RFS_ACCEL
146 struct mlx5_irq_table *irq_table = mdev->priv.irq_table;
147 int num_affinity_vec;
150 num_affinity_vec = mlx5_irq_get_num_comp(irq_table);
151 irq_table->rmap = alloc_irq_cpu_rmap(num_affinity_vec);
152 if (!irq_table->rmap) {
154 mlx5_core_err(mdev, "Failed to allocate cpu_rmap. err %d", err);
158 vecidx = MLX5_IRQ_VEC_COMP_BASE;
159 for (; vecidx < irq_table->nvec; vecidx++) {
160 err = irq_cpu_rmap_add(irq_table->rmap,
161 pci_irq_vector(mdev->pdev, vecidx));
163 mlx5_core_err(mdev, "irq_cpu_rmap_add failed. err %d",
165 goto err_irq_cpu_rmap_add;
170 err_irq_cpu_rmap_add:
171 irq_clear_rmap(mdev);
177 /* Completion IRQ vectors */
179 static int set_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
181 int vecidx = MLX5_IRQ_VEC_COMP_BASE + i;
182 struct mlx5_irq *irq;
185 irq = mlx5_irq_get(mdev, vecidx);
186 irqn = pci_irq_vector(mdev->pdev, vecidx);
187 if (!zalloc_cpumask_var(&irq->mask, GFP_KERNEL)) {
188 mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
192 cpumask_set_cpu(cpumask_local_spread(i, mdev->priv.numa_node),
194 if (IS_ENABLED(CONFIG_SMP) &&
195 irq_set_affinity_hint(irqn, irq->mask))
196 mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x",
202 static void clear_comp_irq_affinity_hint(struct mlx5_core_dev *mdev, int i)
204 int vecidx = MLX5_IRQ_VEC_COMP_BASE + i;
205 struct mlx5_irq *irq;
208 irq = mlx5_irq_get(mdev, vecidx);
209 irqn = pci_irq_vector(mdev->pdev, vecidx);
210 irq_set_affinity_hint(irqn, NULL);
211 free_cpumask_var(irq->mask);
214 static int set_comp_irq_affinity_hints(struct mlx5_core_dev *mdev)
216 int nvec = mlx5_irq_get_num_comp(mdev->priv.irq_table);
220 for (i = 0; i < nvec; i++) {
221 err = set_comp_irq_affinity_hint(mdev, i);
229 for (i--; i >= 0; i--)
230 clear_comp_irq_affinity_hint(mdev, i);
235 static void clear_comp_irqs_affinity_hints(struct mlx5_core_dev *mdev)
237 int nvec = mlx5_irq_get_num_comp(mdev->priv.irq_table);
240 for (i = 0; i < nvec; i++)
241 clear_comp_irq_affinity_hint(mdev, i);
245 mlx5_irq_get_affinity_mask(struct mlx5_irq_table *irq_table, int vecidx)
247 return irq_table->irq[vecidx].mask;
250 #ifdef CONFIG_RFS_ACCEL
251 struct cpu_rmap *mlx5_irq_get_rmap(struct mlx5_irq_table *irq_table)
253 return irq_table->rmap;
257 static void unrequest_irqs(struct mlx5_core_dev *dev)
259 struct mlx5_irq_table *table = dev->priv.irq_table;
262 for (i = 0; i < table->nvec; i++)
263 free_irq(pci_irq_vector(dev->pdev, i),
264 &mlx5_irq_get(dev, i)->nh);
267 int mlx5_irq_table_create(struct mlx5_core_dev *dev)
269 struct mlx5_priv *priv = &dev->priv;
270 struct mlx5_irq_table *table = priv->irq_table;
271 int num_eqs = MLX5_CAP_GEN(dev, max_num_eqs) ?
272 MLX5_CAP_GEN(dev, max_num_eqs) :
273 1 << MLX5_CAP_GEN(dev, log_max_eq);
277 if (mlx5_core_is_sf(dev))
280 nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() +
281 MLX5_IRQ_VEC_COMP_BASE;
282 nvec = min_t(int, nvec, num_eqs);
283 if (nvec <= MLX5_IRQ_VEC_COMP_BASE)
286 table->irq = kcalloc(nvec, sizeof(*table->irq), GFP_KERNEL);
290 nvec = pci_alloc_irq_vectors(dev->pdev, MLX5_IRQ_VEC_COMP_BASE + 1,
299 err = irq_set_rmap(dev);
303 err = request_irqs(dev, nvec);
305 goto err_request_irqs;
307 err = set_comp_irq_affinity_hints(dev);
309 mlx5_core_err(dev, "Failed to alloc affinity hint cpumask\n");
310 goto err_set_affinity;
320 pci_free_irq_vectors(dev->pdev);
326 void mlx5_irq_table_destroy(struct mlx5_core_dev *dev)
328 struct mlx5_irq_table *table = dev->priv.irq_table;
331 if (mlx5_core_is_sf(dev))
334 /* free_irq requires that affinity and rmap will be cleared
335 * before calling it. This is why there is asymmetry with set_rmap
336 * which should be called after alloc_irq but before request_irq.
339 clear_comp_irqs_affinity_hints(dev);
340 for (i = 0; i < table->nvec; i++)
341 free_irq(pci_irq_vector(dev->pdev, i),
342 &mlx5_irq_get(dev, i)->nh);
343 pci_free_irq_vectors(dev->pdev);
347 struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev)
349 #ifdef CONFIG_MLX5_SF
350 if (mlx5_core_is_sf(dev))
351 return dev->priv.parent_mdev->priv.irq_table;
353 return dev->priv.irq_table;