xref: /linux/drivers/net/ethernet/intel/ice/ice_irq.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
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  * @num_static: number of preallocated interrupts
13  */
14 static void
ice_init_irq_tracker(struct ice_pf * pf,unsigned int max_vectors,unsigned int num_static)15 ice_init_irq_tracker(struct ice_pf *pf, unsigned int max_vectors,
16 		     unsigned int num_static)
17 {
18 	pf->irq_tracker.num_entries = max_vectors;
19 	pf->irq_tracker.num_static = num_static;
20 	xa_init_flags(&pf->irq_tracker.entries, XA_FLAGS_ALLOC);
21 }
22 
23 static int
ice_init_virt_irq_tracker(struct ice_pf * pf,u32 base,u32 num_entries)24 ice_init_virt_irq_tracker(struct ice_pf *pf, u32 base, u32 num_entries)
25 {
26 	pf->virt_irq_tracker.bm = bitmap_zalloc(num_entries, GFP_KERNEL);
27 	if (!pf->virt_irq_tracker.bm)
28 		return -ENOMEM;
29 
30 	pf->virt_irq_tracker.num_entries = num_entries;
31 	pf->virt_irq_tracker.base = base;
32 
33 	return 0;
34 }
35 
36 /**
37  * ice_deinit_irq_tracker - free xarray tracker
38  * @pf: board private structure
39  */
ice_deinit_irq_tracker(struct ice_pf * pf)40 static void ice_deinit_irq_tracker(struct ice_pf *pf)
41 {
42 	xa_destroy(&pf->irq_tracker.entries);
43 }
44 
ice_deinit_virt_irq_tracker(struct ice_pf * pf)45 static void ice_deinit_virt_irq_tracker(struct ice_pf *pf)
46 {
47 	bitmap_free(pf->virt_irq_tracker.bm);
48 }
49 
50 /**
51  * ice_free_irq_res - free a block of resources
52  * @pf: board private structure
53  * @index: starting index previously returned by ice_get_res
54  */
ice_free_irq_res(struct ice_pf * pf,u16 index)55 static void ice_free_irq_res(struct ice_pf *pf, u16 index)
56 {
57 	struct ice_irq_entry *entry;
58 
59 	entry = xa_erase(&pf->irq_tracker.entries, index);
60 	kfree(entry);
61 }
62 
63 /**
64  * ice_get_irq_res - get an interrupt resource
65  * @pf: board private structure
66  * @dyn_allowed: allow entry to be dynamically allocated
67  *
68  * Allocate new irq entry in the free slot of the tracker. Since xarray
69  * is used, always allocate new entry at the lowest possible index. Set
70  * proper allocation limit for maximum tracker entries.
71  *
72  * Returns allocated irq entry or NULL on failure.
73  */
ice_get_irq_res(struct ice_pf * pf,bool dyn_allowed)74 static struct ice_irq_entry *ice_get_irq_res(struct ice_pf *pf,
75 					     bool dyn_allowed)
76 {
77 	struct xa_limit limit = { .max = pf->irq_tracker.num_entries - 1,
78 				  .min = 0 };
79 	unsigned int num_static = pf->irq_tracker.num_static - 1;
80 	struct ice_irq_entry *entry;
81 	unsigned int index;
82 	int ret;
83 
84 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
85 	if (!entry)
86 		return NULL;
87 
88 	/* only already allocated if the caller says so */
89 	if (!dyn_allowed)
90 		limit.max = num_static;
91 
92 	ret = xa_alloc(&pf->irq_tracker.entries, &index, entry, limit,
93 		       GFP_KERNEL);
94 
95 	if (ret) {
96 		kfree(entry);
97 		entry = NULL;
98 	} else {
99 		entry->index = index;
100 		entry->dynamic = index > num_static;
101 	}
102 
103 	return entry;
104 }
105 
106 #define ICE_RDMA_AEQ_MSIX 1
ice_get_default_msix_amount(struct ice_pf * pf)107 static int ice_get_default_msix_amount(struct ice_pf *pf)
108 {
109 	return ICE_MIN_LAN_OICR_MSIX + num_online_cpus() +
110 	       (test_bit(ICE_FLAG_FD_ENA, pf->flags) ? ICE_FDIR_MSIX : 0) +
111 	       (ice_is_rdma_ena(pf) ? num_online_cpus() + ICE_RDMA_AEQ_MSIX : 0);
112 }
113 
114 /**
115  * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme
116  * @pf: board private structure
117  */
ice_clear_interrupt_scheme(struct ice_pf * pf)118 void ice_clear_interrupt_scheme(struct ice_pf *pf)
119 {
120 	pci_free_irq_vectors(pf->pdev);
121 	ice_deinit_irq_tracker(pf);
122 	ice_deinit_virt_irq_tracker(pf);
123 }
124 
125 /**
126  * ice_init_interrupt_scheme - Determine proper interrupt scheme
127  * @pf: board private structure to initialize
128  */
ice_init_interrupt_scheme(struct ice_pf * pf)129 int ice_init_interrupt_scheme(struct ice_pf *pf)
130 {
131 	int total_vectors = pf->hw.func_caps.common_cap.num_msix_vectors;
132 	int vectors;
133 
134 	/* load default PF MSI-X range */
135 	if (!pf->msix.min)
136 		pf->msix.min = ICE_MIN_MSIX;
137 
138 	if (!pf->msix.max)
139 		pf->msix.max = min(total_vectors,
140 				   ice_get_default_msix_amount(pf));
141 
142 	pf->msix.total = total_vectors;
143 	pf->msix.rest = total_vectors - pf->msix.max;
144 
145 	if (pci_msix_can_alloc_dyn(pf->pdev))
146 		vectors = pf->msix.min;
147 	else
148 		vectors = pf->msix.max;
149 
150 	vectors = pci_alloc_irq_vectors(pf->pdev, pf->msix.min, vectors,
151 					PCI_IRQ_MSIX);
152 	if (vectors < 0)
153 		return vectors;
154 
155 	ice_init_irq_tracker(pf, pf->msix.max, vectors);
156 
157 	return ice_init_virt_irq_tracker(pf, pf->msix.max, pf->msix.rest);
158 }
159 
160 /**
161  * ice_alloc_irq - Allocate new interrupt vector
162  * @pf: board private structure
163  * @dyn_allowed: allow dynamic allocation of the interrupt
164  *
165  * Allocate new interrupt vector for a given owner id.
166  * return struct msi_map with interrupt details and track
167  * allocated interrupt appropriately.
168  *
169  * This function reserves new irq entry from the irq_tracker.
170  * if according to the tracker information all interrupts that
171  * were allocated with ice_pci_alloc_irq_vectors are already used
172  * and dynamically allocated interrupts are supported then new
173  * interrupt will be allocated with pci_msix_alloc_irq_at.
174  *
175  * Some callers may only support dynamically allocated interrupts.
176  * This is indicated with dyn_allowed flag.
177  *
178  * On failure, return map with negative .index. The caller
179  * is expected to check returned map index.
180  *
181  */
ice_alloc_irq(struct ice_pf * pf,bool dyn_allowed)182 struct msi_map ice_alloc_irq(struct ice_pf *pf, bool dyn_allowed)
183 {
184 	struct msi_map map = { .index = -ENOENT };
185 	struct device *dev = ice_pf_to_dev(pf);
186 	struct ice_irq_entry *entry;
187 
188 	entry = ice_get_irq_res(pf, dyn_allowed);
189 	if (!entry)
190 		return map;
191 
192 	if (pci_msix_can_alloc_dyn(pf->pdev) && entry->dynamic) {
193 		map = pci_msix_alloc_irq_at(pf->pdev, entry->index, NULL);
194 		if (map.index < 0)
195 			goto exit_free_res;
196 		dev_dbg(dev, "allocated new irq at index %d\n", map.index);
197 	} else {
198 		map.index = entry->index;
199 		map.virq = pci_irq_vector(pf->pdev, map.index);
200 	}
201 
202 	return map;
203 
204 exit_free_res:
205 	dev_err(dev, "Could not allocate irq at idx %d\n", entry->index);
206 	ice_free_irq_res(pf, entry->index);
207 	return map;
208 }
209 
210 /**
211  * ice_free_irq - Free interrupt vector
212  * @pf: board private structure
213  * @map: map with interrupt details
214  *
215  * Remove allocated interrupt from the interrupt tracker. If interrupt was
216  * allocated dynamically, free respective interrupt vector.
217  */
ice_free_irq(struct ice_pf * pf,struct msi_map map)218 void ice_free_irq(struct ice_pf *pf, struct msi_map map)
219 {
220 	struct ice_irq_entry *entry;
221 
222 	entry = xa_load(&pf->irq_tracker.entries, map.index);
223 
224 	if (!entry) {
225 		dev_err(ice_pf_to_dev(pf), "Failed to get MSIX interrupt entry at index %d",
226 			map.index);
227 		return;
228 	}
229 
230 	dev_dbg(ice_pf_to_dev(pf), "Free irq at index %d\n", map.index);
231 
232 	if (entry->dynamic)
233 		pci_msix_free_irq(pf->pdev, map);
234 
235 	ice_free_irq_res(pf, map.index);
236 }
237 
238 /**
239  * ice_virt_get_irqs - get irqs for SR-IOV usacase
240  * @pf: pointer to PF structure
241  * @needed: number of irqs to get
242  *
243  * This returns the first MSI-X vector index in PF space that is used by this
244  * VF. This index is used when accessing PF relative registers such as
245  * GLINT_VECT2FUNC and GLINT_DYN_CTL.
246  * This will always be the OICR index in the AVF driver so any functionality
247  * using vf->first_vector_idx for queue configuration_id: id of VF which will
248  * use this irqs
249  */
ice_virt_get_irqs(struct ice_pf * pf,u32 needed)250 int ice_virt_get_irqs(struct ice_pf *pf, u32 needed)
251 {
252 	int res = bitmap_find_next_zero_area(pf->virt_irq_tracker.bm,
253 					     pf->virt_irq_tracker.num_entries,
254 					     0, needed, 0);
255 
256 	if (res >= pf->virt_irq_tracker.num_entries)
257 		return -ENOENT;
258 
259 	bitmap_set(pf->virt_irq_tracker.bm, res, needed);
260 
261 	/* conversion from number in bitmap to global irq index */
262 	return res + pf->virt_irq_tracker.base;
263 }
264 
265 /**
266  * ice_virt_free_irqs - free irqs used by the VF
267  * @pf: pointer to PF structure
268  * @index: first index to be free
269  * @irqs: number of irqs to free
270  */
ice_virt_free_irqs(struct ice_pf * pf,u32 index,u32 irqs)271 void ice_virt_free_irqs(struct ice_pf *pf, u32 index, u32 irqs)
272 {
273 	bitmap_clear(pf->virt_irq_tracker.bm, index - pf->virt_irq_tracker.base,
274 		     irqs);
275 }
276