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