xref: /linux/drivers/dca/dca-core.c (revision d39d0ed196aa1685bb24771e92f78633c66ac9cb)
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 
32 #define DCA_VERSION "1.12.1"
33 
34 MODULE_VERSION(DCA_VERSION);
35 MODULE_LICENSE("GPL");
36 MODULE_AUTHOR("Intel Corporation");
37 
38 static DEFINE_SPINLOCK(dca_lock);
39 
40 static LIST_HEAD(dca_domains);
41 
42 static struct pci_bus *dca_pci_rc_from_dev(struct device *dev)
43 {
44 	struct pci_dev *pdev = to_pci_dev(dev);
45 	struct pci_bus *bus = pdev->bus;
46 
47 	while (bus->parent)
48 		bus = bus->parent;
49 
50 	return bus;
51 }
52 
53 static struct dca_domain *dca_allocate_domain(struct pci_bus *rc)
54 {
55 	struct dca_domain *domain;
56 
57 	domain = kzalloc(sizeof(*domain), GFP_NOWAIT);
58 	if (!domain)
59 		return NULL;
60 
61 	INIT_LIST_HEAD(&domain->dca_providers);
62 	domain->pci_rc = rc;
63 
64 	return domain;
65 }
66 
67 static void dca_free_domain(struct dca_domain *domain)
68 {
69 	list_del(&domain->node);
70 	kfree(domain);
71 }
72 
73 static struct dca_domain *dca_find_domain(struct pci_bus *rc)
74 {
75 	struct dca_domain *domain;
76 
77 	list_for_each_entry(domain, &dca_domains, node)
78 		if (domain->pci_rc == rc)
79 			return domain;
80 
81 	return NULL;
82 }
83 
84 static struct dca_domain *dca_get_domain(struct device *dev)
85 {
86 	struct pci_bus *rc;
87 	struct dca_domain *domain;
88 
89 	rc = dca_pci_rc_from_dev(dev);
90 	domain = dca_find_domain(rc);
91 
92 	if (!domain) {
93 		domain = dca_allocate_domain(rc);
94 		if (domain)
95 			list_add(&domain->node, &dca_domains);
96 	}
97 
98 	return domain;
99 }
100 
101 static struct dca_provider *dca_find_provider_by_dev(struct device *dev)
102 {
103 	struct dca_provider *dca;
104 	struct pci_bus *rc;
105 	struct dca_domain *domain;
106 
107 	if (dev) {
108 		rc = dca_pci_rc_from_dev(dev);
109 		domain = dca_find_domain(rc);
110 		if (!domain)
111 			return NULL;
112 	} else {
113 		if (!list_empty(&dca_domains))
114 			domain = list_first_entry(&dca_domains,
115 						  struct dca_domain,
116 						  node);
117 		else
118 			return NULL;
119 	}
120 
121 	list_for_each_entry(dca, &domain->dca_providers, node)
122 		if ((!dev) || (dca->ops->dev_managed(dca, dev)))
123 			return dca;
124 
125 	return NULL;
126 }
127 
128 /**
129  * dca_add_requester - add a dca client to the list
130  * @dev - the device that wants dca service
131  */
132 int dca_add_requester(struct device *dev)
133 {
134 	struct dca_provider *dca;
135 	int err, slot = -ENODEV;
136 	unsigned long flags;
137 	struct pci_bus *pci_rc;
138 	struct dca_domain *domain;
139 
140 	if (!dev)
141 		return -EFAULT;
142 
143 	spin_lock_irqsave(&dca_lock, flags);
144 
145 	/* check if the requester has not been added already */
146 	dca = dca_find_provider_by_dev(dev);
147 	if (dca) {
148 		spin_unlock_irqrestore(&dca_lock, flags);
149 		return -EEXIST;
150 	}
151 
152 	pci_rc = dca_pci_rc_from_dev(dev);
153 	domain = dca_find_domain(pci_rc);
154 	if (!domain) {
155 		spin_unlock_irqrestore(&dca_lock, flags);
156 		return -ENODEV;
157 	}
158 
159 	list_for_each_entry(dca, &domain->dca_providers, node) {
160 		slot = dca->ops->add_requester(dca, dev);
161 		if (slot >= 0)
162 			break;
163 	}
164 
165 	spin_unlock_irqrestore(&dca_lock, flags);
166 
167 	if (slot < 0)
168 		return slot;
169 
170 	err = dca_sysfs_add_req(dca, dev, slot);
171 	if (err) {
172 		spin_lock_irqsave(&dca_lock, flags);
173 		if (dca == dca_find_provider_by_dev(dev))
174 			dca->ops->remove_requester(dca, dev);
175 		spin_unlock_irqrestore(&dca_lock, flags);
176 		return err;
177 	}
178 
179 	return 0;
180 }
181 EXPORT_SYMBOL_GPL(dca_add_requester);
182 
183 /**
184  * dca_remove_requester - remove a dca client from the list
185  * @dev - the device that wants dca service
186  */
187 int dca_remove_requester(struct device *dev)
188 {
189 	struct dca_provider *dca;
190 	int slot;
191 	unsigned long flags;
192 
193 	if (!dev)
194 		return -EFAULT;
195 
196 	spin_lock_irqsave(&dca_lock, flags);
197 	dca = dca_find_provider_by_dev(dev);
198 	if (!dca) {
199 		spin_unlock_irqrestore(&dca_lock, flags);
200 		return -ENODEV;
201 	}
202 	slot = dca->ops->remove_requester(dca, dev);
203 	spin_unlock_irqrestore(&dca_lock, flags);
204 
205 	if (slot < 0)
206 		return slot;
207 
208 	dca_sysfs_remove_req(dca, slot);
209 
210 	return 0;
211 }
212 EXPORT_SYMBOL_GPL(dca_remove_requester);
213 
214 /**
215  * dca_common_get_tag - return the dca tag (serves both new and old api)
216  * @dev - the device that wants dca service
217  * @cpu - the cpuid as returned by get_cpu()
218  */
219 u8 dca_common_get_tag(struct device *dev, int cpu)
220 {
221 	struct dca_provider *dca;
222 	u8 tag;
223 	unsigned long flags;
224 
225 	spin_lock_irqsave(&dca_lock, flags);
226 
227 	dca = dca_find_provider_by_dev(dev);
228 	if (!dca) {
229 		spin_unlock_irqrestore(&dca_lock, flags);
230 		return -ENODEV;
231 	}
232 	tag = dca->ops->get_tag(dca, dev, cpu);
233 
234 	spin_unlock_irqrestore(&dca_lock, flags);
235 	return tag;
236 }
237 
238 /**
239  * dca3_get_tag - return the dca tag to the requester device
240  *                for the given cpu (new api)
241  * @dev - the device that wants dca service
242  * @cpu - the cpuid as returned by get_cpu()
243  */
244 u8 dca3_get_tag(struct device *dev, int cpu)
245 {
246 	if (!dev)
247 		return -EFAULT;
248 
249 	return dca_common_get_tag(dev, cpu);
250 }
251 EXPORT_SYMBOL_GPL(dca3_get_tag);
252 
253 /**
254  * dca_get_tag - return the dca tag for the given cpu (old api)
255  * @cpu - the cpuid as returned by get_cpu()
256  */
257 u8 dca_get_tag(int cpu)
258 {
259 	struct device *dev = NULL;
260 
261 	return dca_common_get_tag(dev, cpu);
262 }
263 EXPORT_SYMBOL_GPL(dca_get_tag);
264 
265 /**
266  * alloc_dca_provider - get data struct for describing a dca provider
267  * @ops - pointer to struct of dca operation function pointers
268  * @priv_size - size of extra mem to be added for provider's needs
269  */
270 struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size)
271 {
272 	struct dca_provider *dca;
273 	int alloc_size;
274 
275 	alloc_size = (sizeof(*dca) + priv_size);
276 	dca = kzalloc(alloc_size, GFP_KERNEL);
277 	if (!dca)
278 		return NULL;
279 	dca->ops = ops;
280 
281 	return dca;
282 }
283 EXPORT_SYMBOL_GPL(alloc_dca_provider);
284 
285 /**
286  * free_dca_provider - release the dca provider data struct
287  * @ops - pointer to struct of dca operation function pointers
288  * @priv_size - size of extra mem to be added for provider's needs
289  */
290 void free_dca_provider(struct dca_provider *dca)
291 {
292 	kfree(dca);
293 }
294 EXPORT_SYMBOL_GPL(free_dca_provider);
295 
296 static BLOCKING_NOTIFIER_HEAD(dca_provider_chain);
297 
298 /**
299  * register_dca_provider - register a dca provider
300  * @dca - struct created by alloc_dca_provider()
301  * @dev - device providing dca services
302  */
303 int register_dca_provider(struct dca_provider *dca, struct device *dev)
304 {
305 	int err;
306 	unsigned long flags;
307 	struct dca_domain *domain;
308 
309 	err = dca_sysfs_add_provider(dca, dev);
310 	if (err)
311 		return err;
312 
313 	spin_lock_irqsave(&dca_lock, flags);
314 	domain = dca_get_domain(dev);
315 	if (!domain) {
316 		spin_unlock_irqrestore(&dca_lock, flags);
317 		return -ENODEV;
318 	}
319 	list_add(&dca->node, &domain->dca_providers);
320 	spin_unlock_irqrestore(&dca_lock, flags);
321 
322 	blocking_notifier_call_chain(&dca_provider_chain,
323 				     DCA_PROVIDER_ADD, NULL);
324 	return 0;
325 }
326 EXPORT_SYMBOL_GPL(register_dca_provider);
327 
328 /**
329  * unregister_dca_provider - remove a dca provider
330  * @dca - struct created by alloc_dca_provider()
331  */
332 void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
333 {
334 	unsigned long flags;
335 	struct pci_bus *pci_rc;
336 	struct dca_domain *domain;
337 
338 	blocking_notifier_call_chain(&dca_provider_chain,
339 				     DCA_PROVIDER_REMOVE, NULL);
340 
341 	spin_lock_irqsave(&dca_lock, flags);
342 
343 	list_del(&dca->node);
344 
345 	pci_rc = dca_pci_rc_from_dev(dev);
346 	domain = dca_find_domain(pci_rc);
347 	if (list_empty(&domain->dca_providers))
348 		dca_free_domain(domain);
349 
350 	spin_unlock_irqrestore(&dca_lock, flags);
351 
352 	dca_sysfs_remove_provider(dca);
353 }
354 EXPORT_SYMBOL_GPL(unregister_dca_provider);
355 
356 /**
357  * dca_register_notify - register a client's notifier callback
358  */
359 void dca_register_notify(struct notifier_block *nb)
360 {
361 	blocking_notifier_chain_register(&dca_provider_chain, nb);
362 }
363 EXPORT_SYMBOL_GPL(dca_register_notify);
364 
365 /**
366  * dca_unregister_notify - remove a client's notifier callback
367  */
368 void dca_unregister_notify(struct notifier_block *nb)
369 {
370 	blocking_notifier_chain_unregister(&dca_provider_chain, nb);
371 }
372 EXPORT_SYMBOL_GPL(dca_unregister_notify);
373 
374 static int __init dca_init(void)
375 {
376 	pr_info("dca service started, version %s\n", DCA_VERSION);
377 	return dca_sysfs_init();
378 }
379 
380 static void __exit dca_exit(void)
381 {
382 	dca_sysfs_exit();
383 }
384 
385 arch_initcall(dca_init);
386 module_exit(dca_exit);
387 
388