at v2.6.35-rc5 387 lines 8.8 kB view raw
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 34MODULE_VERSION(DCA_VERSION); 35MODULE_LICENSE("GPL"); 36MODULE_AUTHOR("Intel Corporation"); 37 38static DEFINE_SPINLOCK(dca_lock); 39 40static LIST_HEAD(dca_domains); 41 42static 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 53static 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 67static void dca_free_domain(struct dca_domain *domain) 68{ 69 list_del(&domain->node); 70 kfree(domain); 71} 72 73static 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 84static 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 101static 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 */ 132int 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} 181EXPORT_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 */ 187int 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} 212EXPORT_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 */ 219u8 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 */ 244u8 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} 251EXPORT_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 */ 257u8 dca_get_tag(int cpu) 258{ 259 struct device *dev = NULL; 260 261 return dca_common_get_tag(dev, cpu); 262} 263EXPORT_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 */ 270struct 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} 283EXPORT_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 */ 290void free_dca_provider(struct dca_provider *dca) 291{ 292 kfree(dca); 293} 294EXPORT_SYMBOL_GPL(free_dca_provider); 295 296static 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 */ 303int 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} 326EXPORT_SYMBOL_GPL(register_dca_provider); 327 328/** 329 * unregister_dca_provider - remove a dca provider 330 * @dca - struct created by alloc_dca_provider() 331 */ 332void 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} 354EXPORT_SYMBOL_GPL(unregister_dca_provider); 355 356/** 357 * dca_register_notify - register a client's notifier callback 358 */ 359void dca_register_notify(struct notifier_block *nb) 360{ 361 blocking_notifier_chain_register(&dca_provider_chain, nb); 362} 363EXPORT_SYMBOL_GPL(dca_register_notify); 364 365/** 366 * dca_unregister_notify - remove a client's notifier callback 367 */ 368void dca_unregister_notify(struct notifier_block *nb) 369{ 370 blocking_notifier_chain_unregister(&dca_provider_chain, nb); 371} 372EXPORT_SYMBOL_GPL(dca_unregister_notify); 373 374static int __init dca_init(void) 375{ 376 pr_info("dca service started, version %s\n", DCA_VERSION); 377 return dca_sysfs_init(); 378} 379 380static void __exit dca_exit(void) 381{ 382 dca_sysfs_exit(); 383} 384 385arch_initcall(dca_init); 386module_exit(dca_exit); 387