xref: /openbmc/linux/drivers/dca/dca-core.c (revision 3ac39d20)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
4  */
5 
6 /*
7  * This driver supports an interface for DCA clients and providers to meet.
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/notifier.h>
12 #include <linux/device.h>
13 #include <linux/dca.h>
14 #include <linux/slab.h>
15 #include <linux/module.h>
16 
17 #define DCA_VERSION "1.12.1"
18 
19 MODULE_VERSION(DCA_VERSION);
20 MODULE_LICENSE("GPL");
21 MODULE_AUTHOR("Intel Corporation");
22 
23 static DEFINE_RAW_SPINLOCK(dca_lock);
24 
25 static LIST_HEAD(dca_domains);
26 
27 static BLOCKING_NOTIFIER_HEAD(dca_provider_chain);
28 
29 static int dca_providers_blocked;
30 
dca_pci_rc_from_dev(struct device * dev)31 static struct pci_bus *dca_pci_rc_from_dev(struct device *dev)
32 {
33 	struct pci_dev *pdev = to_pci_dev(dev);
34 	struct pci_bus *bus = pdev->bus;
35 
36 	while (bus->parent)
37 		bus = bus->parent;
38 
39 	return bus;
40 }
41 
dca_allocate_domain(struct pci_bus * rc)42 static struct dca_domain *dca_allocate_domain(struct pci_bus *rc)
43 {
44 	struct dca_domain *domain;
45 
46 	domain = kzalloc(sizeof(*domain), GFP_NOWAIT);
47 	if (!domain)
48 		return NULL;
49 
50 	INIT_LIST_HEAD(&domain->dca_providers);
51 	domain->pci_rc = rc;
52 
53 	return domain;
54 }
55 
dca_free_domain(struct dca_domain * domain)56 static void dca_free_domain(struct dca_domain *domain)
57 {
58 	list_del(&domain->node);
59 	kfree(domain);
60 }
61 
dca_provider_ioat_ver_3_0(struct device * dev)62 static int dca_provider_ioat_ver_3_0(struct device *dev)
63 {
64 	struct pci_dev *pdev = to_pci_dev(dev);
65 
66 	return ((pdev->vendor == PCI_VENDOR_ID_INTEL) &&
67 		((pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG0) ||
68 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG1) ||
69 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG2) ||
70 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG3) ||
71 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG4) ||
72 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG5) ||
73 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG6) ||
74 		(pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG7)));
75 }
76 
unregister_dca_providers(void)77 static void unregister_dca_providers(void)
78 {
79 	struct dca_provider *dca, *_dca;
80 	struct list_head unregistered_providers;
81 	struct dca_domain *domain;
82 	unsigned long flags;
83 
84 	blocking_notifier_call_chain(&dca_provider_chain,
85 				     DCA_PROVIDER_REMOVE, NULL);
86 
87 	INIT_LIST_HEAD(&unregistered_providers);
88 
89 	raw_spin_lock_irqsave(&dca_lock, flags);
90 
91 	if (list_empty(&dca_domains)) {
92 		raw_spin_unlock_irqrestore(&dca_lock, flags);
93 		return;
94 	}
95 
96 	/* at this point only one domain in the list is expected */
97 	domain = list_first_entry(&dca_domains, struct dca_domain, node);
98 
99 	list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node)
100 		list_move(&dca->node, &unregistered_providers);
101 
102 	dca_free_domain(domain);
103 
104 	raw_spin_unlock_irqrestore(&dca_lock, flags);
105 
106 	list_for_each_entry_safe(dca, _dca, &unregistered_providers, node) {
107 		dca_sysfs_remove_provider(dca);
108 		list_del(&dca->node);
109 	}
110 }
111 
dca_find_domain(struct pci_bus * rc)112 static struct dca_domain *dca_find_domain(struct pci_bus *rc)
113 {
114 	struct dca_domain *domain;
115 
116 	list_for_each_entry(domain, &dca_domains, node)
117 		if (domain->pci_rc == rc)
118 			return domain;
119 
120 	return NULL;
121 }
122 
dca_get_domain(struct device * dev)123 static struct dca_domain *dca_get_domain(struct device *dev)
124 {
125 	struct pci_bus *rc;
126 	struct dca_domain *domain;
127 
128 	rc = dca_pci_rc_from_dev(dev);
129 	domain = dca_find_domain(rc);
130 
131 	if (!domain) {
132 		if (dca_provider_ioat_ver_3_0(dev) && !list_empty(&dca_domains))
133 			dca_providers_blocked = 1;
134 	}
135 
136 	return domain;
137 }
138 
dca_find_provider_by_dev(struct device * dev)139 static struct dca_provider *dca_find_provider_by_dev(struct device *dev)
140 {
141 	struct dca_provider *dca;
142 	struct pci_bus *rc;
143 	struct dca_domain *domain;
144 
145 	if (dev) {
146 		rc = dca_pci_rc_from_dev(dev);
147 		domain = dca_find_domain(rc);
148 		if (!domain)
149 			return NULL;
150 	} else {
151 		if (!list_empty(&dca_domains))
152 			domain = list_first_entry(&dca_domains,
153 						  struct dca_domain,
154 						  node);
155 		else
156 			return NULL;
157 	}
158 
159 	list_for_each_entry(dca, &domain->dca_providers, node)
160 		if ((!dev) || (dca->ops->dev_managed(dca, dev)))
161 			return dca;
162 
163 	return NULL;
164 }
165 
166 /**
167  * dca_add_requester - add a dca client to the list
168  * @dev - the device that wants dca service
169  */
dca_add_requester(struct device * dev)170 int dca_add_requester(struct device *dev)
171 {
172 	struct dca_provider *dca;
173 	int err, slot = -ENODEV;
174 	unsigned long flags;
175 	struct pci_bus *pci_rc;
176 	struct dca_domain *domain;
177 
178 	if (!dev)
179 		return -EFAULT;
180 
181 	raw_spin_lock_irqsave(&dca_lock, flags);
182 
183 	/* check if the requester has not been added already */
184 	dca = dca_find_provider_by_dev(dev);
185 	if (dca) {
186 		raw_spin_unlock_irqrestore(&dca_lock, flags);
187 		return -EEXIST;
188 	}
189 
190 	pci_rc = dca_pci_rc_from_dev(dev);
191 	domain = dca_find_domain(pci_rc);
192 	if (!domain) {
193 		raw_spin_unlock_irqrestore(&dca_lock, flags);
194 		return -ENODEV;
195 	}
196 
197 	list_for_each_entry(dca, &domain->dca_providers, node) {
198 		slot = dca->ops->add_requester(dca, dev);
199 		if (slot >= 0)
200 			break;
201 	}
202 
203 	raw_spin_unlock_irqrestore(&dca_lock, flags);
204 
205 	if (slot < 0)
206 		return slot;
207 
208 	err = dca_sysfs_add_req(dca, dev, slot);
209 	if (err) {
210 		raw_spin_lock_irqsave(&dca_lock, flags);
211 		if (dca == dca_find_provider_by_dev(dev))
212 			dca->ops->remove_requester(dca, dev);
213 		raw_spin_unlock_irqrestore(&dca_lock, flags);
214 		return err;
215 	}
216 
217 	return 0;
218 }
219 EXPORT_SYMBOL_GPL(dca_add_requester);
220 
221 /**
222  * dca_remove_requester - remove a dca client from the list
223  * @dev - the device that wants dca service
224  */
dca_remove_requester(struct device * dev)225 int dca_remove_requester(struct device *dev)
226 {
227 	struct dca_provider *dca;
228 	int slot;
229 	unsigned long flags;
230 
231 	if (!dev)
232 		return -EFAULT;
233 
234 	raw_spin_lock_irqsave(&dca_lock, flags);
235 	dca = dca_find_provider_by_dev(dev);
236 	if (!dca) {
237 		raw_spin_unlock_irqrestore(&dca_lock, flags);
238 		return -ENODEV;
239 	}
240 	slot = dca->ops->remove_requester(dca, dev);
241 	raw_spin_unlock_irqrestore(&dca_lock, flags);
242 
243 	if (slot < 0)
244 		return slot;
245 
246 	dca_sysfs_remove_req(dca, slot);
247 
248 	return 0;
249 }
250 EXPORT_SYMBOL_GPL(dca_remove_requester);
251 
252 /**
253  * dca_common_get_tag - return the dca tag (serves both new and old api)
254  * @dev - the device that wants dca service
255  * @cpu - the cpuid as returned by get_cpu()
256  */
dca_common_get_tag(struct device * dev,int cpu)257 static u8 dca_common_get_tag(struct device *dev, int cpu)
258 {
259 	struct dca_provider *dca;
260 	u8 tag;
261 	unsigned long flags;
262 
263 	raw_spin_lock_irqsave(&dca_lock, flags);
264 
265 	dca = dca_find_provider_by_dev(dev);
266 	if (!dca) {
267 		raw_spin_unlock_irqrestore(&dca_lock, flags);
268 		return -ENODEV;
269 	}
270 	tag = dca->ops->get_tag(dca, dev, cpu);
271 
272 	raw_spin_unlock_irqrestore(&dca_lock, flags);
273 	return tag;
274 }
275 
276 /**
277  * dca3_get_tag - return the dca tag to the requester device
278  *                for the given cpu (new api)
279  * @dev - the device that wants dca service
280  * @cpu - the cpuid as returned by get_cpu()
281  */
dca3_get_tag(struct device * dev,int cpu)282 u8 dca3_get_tag(struct device *dev, int cpu)
283 {
284 	if (!dev)
285 		return -EFAULT;
286 
287 	return dca_common_get_tag(dev, cpu);
288 }
289 EXPORT_SYMBOL_GPL(dca3_get_tag);
290 
291 /**
292  * dca_get_tag - return the dca tag for the given cpu (old api)
293  * @cpu - the cpuid as returned by get_cpu()
294  */
dca_get_tag(int cpu)295 u8 dca_get_tag(int cpu)
296 {
297 	return dca_common_get_tag(NULL, cpu);
298 }
299 EXPORT_SYMBOL_GPL(dca_get_tag);
300 
301 /**
302  * alloc_dca_provider - get data struct for describing a dca provider
303  * @ops - pointer to struct of dca operation function pointers
304  * @priv_size - size of extra mem to be added for provider's needs
305  */
alloc_dca_provider(const struct dca_ops * ops,int priv_size)306 struct dca_provider *alloc_dca_provider(const struct dca_ops *ops,
307 					int priv_size)
308 {
309 	struct dca_provider *dca;
310 	int alloc_size;
311 
312 	alloc_size = (sizeof(*dca) + priv_size);
313 	dca = kzalloc(alloc_size, GFP_KERNEL);
314 	if (!dca)
315 		return NULL;
316 	dca->ops = ops;
317 
318 	return dca;
319 }
320 EXPORT_SYMBOL_GPL(alloc_dca_provider);
321 
322 /**
323  * free_dca_provider - release the dca provider data struct
324  * @ops - pointer to struct of dca operation function pointers
325  * @priv_size - size of extra mem to be added for provider's needs
326  */
free_dca_provider(struct dca_provider * dca)327 void free_dca_provider(struct dca_provider *dca)
328 {
329 	kfree(dca);
330 }
331 EXPORT_SYMBOL_GPL(free_dca_provider);
332 
333 /**
334  * register_dca_provider - register a dca provider
335  * @dca - struct created by alloc_dca_provider()
336  * @dev - device providing dca services
337  */
register_dca_provider(struct dca_provider * dca,struct device * dev)338 int register_dca_provider(struct dca_provider *dca, struct device *dev)
339 {
340 	int err;
341 	unsigned long flags;
342 	struct dca_domain *domain, *newdomain = NULL;
343 
344 	raw_spin_lock_irqsave(&dca_lock, flags);
345 	if (dca_providers_blocked) {
346 		raw_spin_unlock_irqrestore(&dca_lock, flags);
347 		return -ENODEV;
348 	}
349 	raw_spin_unlock_irqrestore(&dca_lock, flags);
350 
351 	err = dca_sysfs_add_provider(dca, dev);
352 	if (err)
353 		return err;
354 
355 	raw_spin_lock_irqsave(&dca_lock, flags);
356 	domain = dca_get_domain(dev);
357 	if (!domain) {
358 		struct pci_bus *rc;
359 
360 		if (dca_providers_blocked) {
361 			raw_spin_unlock_irqrestore(&dca_lock, flags);
362 			dca_sysfs_remove_provider(dca);
363 			unregister_dca_providers();
364 			return -ENODEV;
365 		}
366 
367 		raw_spin_unlock_irqrestore(&dca_lock, flags);
368 		rc = dca_pci_rc_from_dev(dev);
369 		newdomain = dca_allocate_domain(rc);
370 		if (!newdomain)
371 			return -ENODEV;
372 		raw_spin_lock_irqsave(&dca_lock, flags);
373 		/* Recheck, we might have raced after dropping the lock */
374 		domain = dca_get_domain(dev);
375 		if (!domain) {
376 			domain = newdomain;
377 			newdomain = NULL;
378 			list_add(&domain->node, &dca_domains);
379 		}
380 	}
381 	list_add(&dca->node, &domain->dca_providers);
382 	raw_spin_unlock_irqrestore(&dca_lock, flags);
383 
384 	blocking_notifier_call_chain(&dca_provider_chain,
385 				     DCA_PROVIDER_ADD, NULL);
386 	kfree(newdomain);
387 	return 0;
388 }
389 EXPORT_SYMBOL_GPL(register_dca_provider);
390 
391 /**
392  * unregister_dca_provider - remove a dca provider
393  * @dca - struct created by alloc_dca_provider()
394  */
unregister_dca_provider(struct dca_provider * dca,struct device * dev)395 void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
396 {
397 	unsigned long flags;
398 	struct pci_bus *pci_rc;
399 	struct dca_domain *domain;
400 
401 	blocking_notifier_call_chain(&dca_provider_chain,
402 				     DCA_PROVIDER_REMOVE, NULL);
403 
404 	raw_spin_lock_irqsave(&dca_lock, flags);
405 
406 	if (list_empty(&dca_domains)) {
407 		raw_spin_unlock_irqrestore(&dca_lock, flags);
408 		return;
409 	}
410 
411 	list_del(&dca->node);
412 
413 	pci_rc = dca_pci_rc_from_dev(dev);
414 	domain = dca_find_domain(pci_rc);
415 	if (list_empty(&domain->dca_providers))
416 		dca_free_domain(domain);
417 
418 	raw_spin_unlock_irqrestore(&dca_lock, flags);
419 
420 	dca_sysfs_remove_provider(dca);
421 }
422 EXPORT_SYMBOL_GPL(unregister_dca_provider);
423 
424 /**
425  * dca_register_notify - register a client's notifier callback
426  */
dca_register_notify(struct notifier_block * nb)427 void dca_register_notify(struct notifier_block *nb)
428 {
429 	blocking_notifier_chain_register(&dca_provider_chain, nb);
430 }
431 EXPORT_SYMBOL_GPL(dca_register_notify);
432 
433 /**
434  * dca_unregister_notify - remove a client's notifier callback
435  */
dca_unregister_notify(struct notifier_block * nb)436 void dca_unregister_notify(struct notifier_block *nb)
437 {
438 	blocking_notifier_chain_unregister(&dca_provider_chain, nb);
439 }
440 EXPORT_SYMBOL_GPL(dca_unregister_notify);
441 
dca_init(void)442 static int __init dca_init(void)
443 {
444 	pr_info("dca service started, version %s\n", DCA_VERSION);
445 	return dca_sysfs_init();
446 }
447 
dca_exit(void)448 static void __exit dca_exit(void)
449 {
450 	dca_sysfs_exit();
451 }
452 
453 arch_initcall(dca_init);
454 module_exit(dca_exit);
455 
456