ice: track interrupt vectors with xarray
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / intel / ice / ice_irq.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023, Intel Corporation. */
3
4 #include "ice.h"
5 #include "ice_lib.h"
6 #include "ice_irq.h"
7
8 /**
9  * ice_init_irq_tracker - initialize interrupt tracker
10  * @pf: board private structure
11  * @max_vectors: maximum number of vectors that tracker can hold
12  */
13 static void
14 ice_init_irq_tracker(struct ice_pf *pf, unsigned int max_vectors)
15 {
16         pf->irq_tracker.num_entries = max_vectors;
17         xa_init_flags(&pf->irq_tracker.entries, XA_FLAGS_ALLOC);
18 }
19
20 /**
21  * ice_deinit_irq_tracker - free xarray tracker
22  * @pf: board private structure
23  */
24 static void ice_deinit_irq_tracker(struct ice_pf *pf)
25 {
26         xa_destroy(&pf->irq_tracker.entries);
27 }
28
29 /**
30  * ice_free_irq_res - free a block of resources
31  * @pf: board private structure
32  * @index: starting index previously returned by ice_get_res
33  */
34 static void ice_free_irq_res(struct ice_pf *pf, u16 index)
35 {
36         struct ice_irq_entry *entry;
37
38         entry = xa_erase(&pf->irq_tracker.entries, index);
39         kfree(entry);
40 }
41
42 /**
43  * ice_get_irq_res - get an interrupt resource
44  * @pf: board private structure
45  *
46  * Allocate new irq entry in the free slot of the tracker. Since xarray
47  * is used, always allocate new entry at the lowest possible index. Set
48  * proper allocation limit for maximum tracker entries.
49  *
50  * Returns allocated irq entry or NULL on failure.
51  */
52 static struct ice_irq_entry *ice_get_irq_res(struct ice_pf *pf)
53 {
54         struct xa_limit limit = { .max = pf->irq_tracker.num_entries,
55                                   .min = 0 };
56         struct ice_irq_entry *entry;
57         unsigned int index;
58         int ret;
59
60         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
61         if (!entry)
62                 return NULL;
63
64         ret = xa_alloc(&pf->irq_tracker.entries, &index, entry, limit,
65                        GFP_KERNEL);
66
67         if (ret) {
68                 kfree(entry);
69                 entry = NULL;
70         } else {
71                 entry->index = index;
72         }
73
74         return entry;
75 }
76
77 /**
78  * ice_reduce_msix_usage - Reduce usage of MSI-X vectors
79  * @pf: board private structure
80  * @v_remain: number of remaining MSI-X vectors to be distributed
81  *
82  * Reduce the usage of MSI-X vectors when entire request cannot be fulfilled.
83  * pf->num_lan_msix and pf->num_rdma_msix values are set based on number of
84  * remaining vectors.
85  */
86 static void ice_reduce_msix_usage(struct ice_pf *pf, int v_remain)
87 {
88         int v_rdma;
89
90         if (!ice_is_rdma_ena(pf)) {
91                 pf->num_lan_msix = v_remain;
92                 return;
93         }
94
95         /* RDMA needs at least 1 interrupt in addition to AEQ MSIX */
96         v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1;
97
98         if (v_remain < ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_RDMA_MSIX) {
99                 dev_warn(ice_pf_to_dev(pf), "Not enough MSI-X vectors to support RDMA.\n");
100                 clear_bit(ICE_FLAG_RDMA_ENA, pf->flags);
101
102                 pf->num_rdma_msix = 0;
103                 pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX;
104         } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) ||
105                    (v_remain - v_rdma < v_rdma)) {
106                 /* Support minimum RDMA and give remaining vectors to LAN MSIX
107                  */
108                 pf->num_rdma_msix = ICE_MIN_RDMA_MSIX;
109                 pf->num_lan_msix = v_remain - ICE_MIN_RDMA_MSIX;
110         } else {
111                 /* Split remaining MSIX with RDMA after accounting for AEQ MSIX
112                  */
113                 pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 +
114                                     ICE_RDMA_NUM_AEQ_MSIX;
115                 pf->num_lan_msix = v_remain - pf->num_rdma_msix;
116         }
117 }
118
119 /**
120  * ice_ena_msix_range - Request a range of MSIX vectors from the OS
121  * @pf: board private structure
122  *
123  * Compute the number of MSIX vectors wanted and request from the OS. Adjust
124  * device usage if there are not enough vectors. Return the number of vectors
125  * reserved or negative on failure.
126  */
127 static int ice_ena_msix_range(struct ice_pf *pf)
128 {
129         int num_cpus, hw_num_msix, v_other, v_wanted, v_actual;
130         struct device *dev = ice_pf_to_dev(pf);
131         int err;
132
133         hw_num_msix = pf->hw.func_caps.common_cap.num_msix_vectors;
134         num_cpus = num_online_cpus();
135
136         /* LAN miscellaneous handler */
137         v_other = ICE_MIN_LAN_OICR_MSIX;
138
139         /* Flow Director */
140         if (test_bit(ICE_FLAG_FD_ENA, pf->flags))
141                 v_other += ICE_FDIR_MSIX;
142
143         /* switchdev */
144         v_other += ICE_ESWITCH_MSIX;
145
146         v_wanted = v_other;
147
148         /* LAN traffic */
149         pf->num_lan_msix = num_cpus;
150         v_wanted += pf->num_lan_msix;
151
152         /* RDMA auxiliary driver */
153         if (ice_is_rdma_ena(pf)) {
154                 pf->num_rdma_msix = num_cpus + ICE_RDMA_NUM_AEQ_MSIX;
155                 v_wanted += pf->num_rdma_msix;
156         }
157
158         if (v_wanted > hw_num_msix) {
159                 int v_remain;
160
161                 dev_warn(dev, "not enough device MSI-X vectors. wanted = %d, available = %d\n",
162                          v_wanted, hw_num_msix);
163
164                 if (hw_num_msix < ICE_MIN_MSIX) {
165                         err = -ERANGE;
166                         goto exit_err;
167                 }
168
169                 v_remain = hw_num_msix - v_other;
170                 if (v_remain < ICE_MIN_LAN_TXRX_MSIX) {
171                         v_other = ICE_MIN_MSIX - ICE_MIN_LAN_TXRX_MSIX;
172                         v_remain = ICE_MIN_LAN_TXRX_MSIX;
173                 }
174
175                 ice_reduce_msix_usage(pf, v_remain);
176                 v_wanted = pf->num_lan_msix + pf->num_rdma_msix + v_other;
177
178                 dev_notice(dev, "Reducing request to %d MSI-X vectors for LAN traffic.\n",
179                            pf->num_lan_msix);
180                 if (ice_is_rdma_ena(pf))
181                         dev_notice(dev, "Reducing request to %d MSI-X vectors for RDMA.\n",
182                                    pf->num_rdma_msix);
183         }
184
185         /* actually reserve the vectors */
186         v_actual = pci_alloc_irq_vectors(pf->pdev, ICE_MIN_MSIX, v_wanted,
187                                          PCI_IRQ_MSIX);
188         if (v_actual < 0) {
189                 dev_err(dev, "unable to reserve MSI-X vectors\n");
190                 err = v_actual;
191                 goto exit_err;
192         }
193
194         if (v_actual < v_wanted) {
195                 dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n",
196                          v_wanted, v_actual);
197
198                 if (v_actual < ICE_MIN_MSIX) {
199                         /* error if we can't get minimum vectors */
200                         pci_free_irq_vectors(pf->pdev);
201                         err = -ERANGE;
202                         goto exit_err;
203                 } else {
204                         int v_remain = v_actual - v_other;
205
206                         if (v_remain < ICE_MIN_LAN_TXRX_MSIX)
207                                 v_remain = ICE_MIN_LAN_TXRX_MSIX;
208
209                         ice_reduce_msix_usage(pf, v_remain);
210
211                         dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n",
212                                    pf->num_lan_msix);
213
214                         if (ice_is_rdma_ena(pf))
215                                 dev_notice(dev, "Enabled %d MSI-X vectors for RDMA.\n",
216                                            pf->num_rdma_msix);
217                 }
218         }
219
220         return v_actual;
221
222 exit_err:
223         pf->num_rdma_msix = 0;
224         pf->num_lan_msix = 0;
225         return err;
226 }
227
228 /**
229  * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme
230  * @pf: board private structure
231  */
232 void ice_clear_interrupt_scheme(struct ice_pf *pf)
233 {
234         pci_free_irq_vectors(pf->pdev);
235         ice_deinit_irq_tracker(pf);
236 }
237
238 /**
239  * ice_init_interrupt_scheme - Determine proper interrupt scheme
240  * @pf: board private structure to initialize
241  */
242 int ice_init_interrupt_scheme(struct ice_pf *pf)
243 {
244         int vectors;
245
246         vectors = ice_ena_msix_range(pf);
247
248         if (vectors < 0)
249                 return vectors;
250
251         ice_init_irq_tracker(pf, vectors);
252
253         return 0;
254 }
255
256 /**
257  * ice_alloc_irq - Allocate new interrupt vector
258  * @pf: board private structure
259  *
260  * Allocate new interrupt vector for a given owner id.
261  * return struct msi_map with interrupt details and track
262  * allocated interrupt appropriately.
263  *
264  * This function mimics individual interrupt allocation,
265  * even interrupts are actually already allocated with
266  * pci_alloc_irq_vectors. Individual allocation helps
267  * to track interrupts and simplifies interrupt related
268  * handling.
269  *
270  * On failure, return map with negative .index. The caller
271  * is expected to check returned map index.
272  *
273  */
274 struct msi_map ice_alloc_irq(struct ice_pf *pf)
275 {
276         struct msi_map map = { .index = -ENOENT };
277         struct ice_irq_entry *entry;
278
279         entry = ice_get_irq_res(pf);
280         if (!entry)
281                 return map;
282
283         map.index = entry->index;
284         map.virq = pci_irq_vector(pf->pdev, map.index);
285
286         return map;
287 }
288
289 /**
290  * ice_free_irq - Free interrupt vector
291  * @pf: board private structure
292  * @map: map with interrupt details
293  *
294  * Remove allocated interrupt from the interrupt tracker.
295  */
296 void ice_free_irq(struct ice_pf *pf, struct msi_map map)
297 {
298         ice_free_irq_res(pf, map.index);
299 }