18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * This driver supports an interface for DCA clients and providers to meet. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/notifier.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/dca.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define DCA_VERSION "1.12.1" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciMODULE_VERSION(DCA_VERSION); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(dca_lock); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic LIST_HEAD(dca_domains); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(dca_provider_chain); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int dca_providers_blocked; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic struct pci_bus *dca_pci_rc_from_dev(struct device *dev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 348c2ecf20Sopenharmony_ci struct pci_bus *bus = pdev->bus; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci while (bus->parent) 378c2ecf20Sopenharmony_ci bus = bus->parent; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return bus; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct dca_domain *dca_allocate_domain(struct pci_bus *rc) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct dca_domain *domain; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_NOWAIT); 478c2ecf20Sopenharmony_ci if (!domain) 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&domain->dca_providers); 518c2ecf20Sopenharmony_ci domain->pci_rc = rc; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return domain; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void dca_free_domain(struct dca_domain *domain) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci list_del(&domain->node); 598c2ecf20Sopenharmony_ci kfree(domain); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int dca_provider_ioat_ver_3_0(struct device *dev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return ((pdev->vendor == PCI_VENDOR_ID_INTEL) && 678c2ecf20Sopenharmony_ci ((pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG0) || 688c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG1) || 698c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG2) || 708c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG3) || 718c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG4) || 728c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG5) || 738c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG6) || 748c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG7))); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void unregister_dca_providers(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct dca_provider *dca, *_dca; 808c2ecf20Sopenharmony_ci struct list_head unregistered_providers; 818c2ecf20Sopenharmony_ci struct dca_domain *domain; 828c2ecf20Sopenharmony_ci unsigned long flags; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&dca_provider_chain, 858c2ecf20Sopenharmony_ci DCA_PROVIDER_REMOVE, NULL); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&unregistered_providers); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (list_empty(&dca_domains)) { 928c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* at this point only one domain in the list is expected */ 978c2ecf20Sopenharmony_ci domain = list_first_entry(&dca_domains, struct dca_domain, node); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node) 1008c2ecf20Sopenharmony_ci list_move(&dca->node, &unregistered_providers); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dca_free_domain(domain); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci list_for_each_entry_safe(dca, _dca, &unregistered_providers, node) { 1078c2ecf20Sopenharmony_ci dca_sysfs_remove_provider(dca); 1088c2ecf20Sopenharmony_ci list_del(&dca->node); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct dca_domain *dca_find_domain(struct pci_bus *rc) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct dca_domain *domain; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci list_for_each_entry(domain, &dca_domains, node) 1178c2ecf20Sopenharmony_ci if (domain->pci_rc == rc) 1188c2ecf20Sopenharmony_ci return domain; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return NULL; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct dca_domain *dca_get_domain(struct device *dev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct pci_bus *rc; 1268c2ecf20Sopenharmony_ci struct dca_domain *domain; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci rc = dca_pci_rc_from_dev(dev); 1298c2ecf20Sopenharmony_ci domain = dca_find_domain(rc); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!domain) { 1328c2ecf20Sopenharmony_ci if (dca_provider_ioat_ver_3_0(dev) && !list_empty(&dca_domains)) 1338c2ecf20Sopenharmony_ci dca_providers_blocked = 1; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return domain; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct dca_provider *dca_find_provider_by_dev(struct device *dev) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct dca_provider *dca; 1428c2ecf20Sopenharmony_ci struct pci_bus *rc; 1438c2ecf20Sopenharmony_ci struct dca_domain *domain; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (dev) { 1468c2ecf20Sopenharmony_ci rc = dca_pci_rc_from_dev(dev); 1478c2ecf20Sopenharmony_ci domain = dca_find_domain(rc); 1488c2ecf20Sopenharmony_ci if (!domain) 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci if (!list_empty(&dca_domains)) 1528c2ecf20Sopenharmony_ci domain = list_first_entry(&dca_domains, 1538c2ecf20Sopenharmony_ci struct dca_domain, 1548c2ecf20Sopenharmony_ci node); 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci list_for_each_entry(dca, &domain->dca_providers, node) 1608c2ecf20Sopenharmony_ci if ((!dev) || (dca->ops->dev_managed(dca, dev))) 1618c2ecf20Sopenharmony_ci return dca; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return NULL; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/** 1678c2ecf20Sopenharmony_ci * dca_add_requester - add a dca client to the list 1688c2ecf20Sopenharmony_ci * @dev - the device that wants dca service 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciint dca_add_requester(struct device *dev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct dca_provider *dca; 1738c2ecf20Sopenharmony_ci int err, slot = -ENODEV; 1748c2ecf20Sopenharmony_ci unsigned long flags; 1758c2ecf20Sopenharmony_ci struct pci_bus *pci_rc; 1768c2ecf20Sopenharmony_ci struct dca_domain *domain; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!dev) 1798c2ecf20Sopenharmony_ci return -EFAULT; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* check if the requester has not been added already */ 1848c2ecf20Sopenharmony_ci dca = dca_find_provider_by_dev(dev); 1858c2ecf20Sopenharmony_ci if (dca) { 1868c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 1878c2ecf20Sopenharmony_ci return -EEXIST; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci pci_rc = dca_pci_rc_from_dev(dev); 1918c2ecf20Sopenharmony_ci domain = dca_find_domain(pci_rc); 1928c2ecf20Sopenharmony_ci if (!domain) { 1938c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 1948c2ecf20Sopenharmony_ci return -ENODEV; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci list_for_each_entry(dca, &domain->dca_providers, node) { 1988c2ecf20Sopenharmony_ci slot = dca->ops->add_requester(dca, dev); 1998c2ecf20Sopenharmony_ci if (slot >= 0) 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (slot < 0) 2068c2ecf20Sopenharmony_ci return slot; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci err = dca_sysfs_add_req(dca, dev, slot); 2098c2ecf20Sopenharmony_ci if (err) { 2108c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 2118c2ecf20Sopenharmony_ci if (dca == dca_find_provider_by_dev(dev)) 2128c2ecf20Sopenharmony_ci dca->ops->remove_requester(dca, dev); 2138c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2148c2ecf20Sopenharmony_ci return err; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca_add_requester); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * dca_remove_requester - remove a dca client from the list 2238c2ecf20Sopenharmony_ci * @dev - the device that wants dca service 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ciint dca_remove_requester(struct device *dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct dca_provider *dca; 2288c2ecf20Sopenharmony_ci int slot; 2298c2ecf20Sopenharmony_ci unsigned long flags; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!dev) 2328c2ecf20Sopenharmony_ci return -EFAULT; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 2358c2ecf20Sopenharmony_ci dca = dca_find_provider_by_dev(dev); 2368c2ecf20Sopenharmony_ci if (!dca) { 2378c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2388c2ecf20Sopenharmony_ci return -ENODEV; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci slot = dca->ops->remove_requester(dca, dev); 2418c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (slot < 0) 2448c2ecf20Sopenharmony_ci return slot; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci dca_sysfs_remove_req(dca, slot); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca_remove_requester); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/** 2538c2ecf20Sopenharmony_ci * dca_common_get_tag - return the dca tag (serves both new and old api) 2548c2ecf20Sopenharmony_ci * @dev - the device that wants dca service 2558c2ecf20Sopenharmony_ci * @cpu - the cpuid as returned by get_cpu() 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic u8 dca_common_get_tag(struct device *dev, int cpu) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct dca_provider *dca; 2608c2ecf20Sopenharmony_ci u8 tag; 2618c2ecf20Sopenharmony_ci unsigned long flags; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci dca = dca_find_provider_by_dev(dev); 2668c2ecf20Sopenharmony_ci if (!dca) { 2678c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2688c2ecf20Sopenharmony_ci return -ENODEV; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci tag = dca->ops->get_tag(dca, dev, cpu); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 2738c2ecf20Sopenharmony_ci return tag; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/** 2778c2ecf20Sopenharmony_ci * dca3_get_tag - return the dca tag to the requester device 2788c2ecf20Sopenharmony_ci * for the given cpu (new api) 2798c2ecf20Sopenharmony_ci * @dev - the device that wants dca service 2808c2ecf20Sopenharmony_ci * @cpu - the cpuid as returned by get_cpu() 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ciu8 dca3_get_tag(struct device *dev, int cpu) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (!dev) 2858c2ecf20Sopenharmony_ci return -EFAULT; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return dca_common_get_tag(dev, cpu); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca3_get_tag); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/** 2928c2ecf20Sopenharmony_ci * dca_get_tag - return the dca tag for the given cpu (old api) 2938c2ecf20Sopenharmony_ci * @cpu - the cpuid as returned by get_cpu() 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ciu8 dca_get_tag(int cpu) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct device *dev = NULL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return dca_common_get_tag(dev, cpu); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca_get_tag); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * alloc_dca_provider - get data struct for describing a dca provider 3058c2ecf20Sopenharmony_ci * @ops - pointer to struct of dca operation function pointers 3068c2ecf20Sopenharmony_ci * @priv_size - size of extra mem to be added for provider's needs 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistruct dca_provider *alloc_dca_provider(const struct dca_ops *ops, 3098c2ecf20Sopenharmony_ci int priv_size) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct dca_provider *dca; 3128c2ecf20Sopenharmony_ci int alloc_size; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci alloc_size = (sizeof(*dca) + priv_size); 3158c2ecf20Sopenharmony_ci dca = kzalloc(alloc_size, GFP_KERNEL); 3168c2ecf20Sopenharmony_ci if (!dca) 3178c2ecf20Sopenharmony_ci return NULL; 3188c2ecf20Sopenharmony_ci dca->ops = ops; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return dca; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(alloc_dca_provider); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/** 3258c2ecf20Sopenharmony_ci * free_dca_provider - release the dca provider data struct 3268c2ecf20Sopenharmony_ci * @ops - pointer to struct of dca operation function pointers 3278c2ecf20Sopenharmony_ci * @priv_size - size of extra mem to be added for provider's needs 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_civoid free_dca_provider(struct dca_provider *dca) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci kfree(dca); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(free_dca_provider); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/** 3368c2ecf20Sopenharmony_ci * register_dca_provider - register a dca provider 3378c2ecf20Sopenharmony_ci * @dca - struct created by alloc_dca_provider() 3388c2ecf20Sopenharmony_ci * @dev - device providing dca services 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ciint register_dca_provider(struct dca_provider *dca, struct device *dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci int err; 3438c2ecf20Sopenharmony_ci unsigned long flags; 3448c2ecf20Sopenharmony_ci struct dca_domain *domain, *newdomain = NULL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 3478c2ecf20Sopenharmony_ci if (dca_providers_blocked) { 3488c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 3498c2ecf20Sopenharmony_ci return -ENODEV; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci err = dca_sysfs_add_provider(dca, dev); 3548c2ecf20Sopenharmony_ci if (err) 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 3588c2ecf20Sopenharmony_ci domain = dca_get_domain(dev); 3598c2ecf20Sopenharmony_ci if (!domain) { 3608c2ecf20Sopenharmony_ci struct pci_bus *rc; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (dca_providers_blocked) { 3638c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 3648c2ecf20Sopenharmony_ci dca_sysfs_remove_provider(dca); 3658c2ecf20Sopenharmony_ci unregister_dca_providers(); 3668c2ecf20Sopenharmony_ci return -ENODEV; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 3708c2ecf20Sopenharmony_ci rc = dca_pci_rc_from_dev(dev); 3718c2ecf20Sopenharmony_ci newdomain = dca_allocate_domain(rc); 3728c2ecf20Sopenharmony_ci if (!newdomain) 3738c2ecf20Sopenharmony_ci return -ENODEV; 3748c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 3758c2ecf20Sopenharmony_ci /* Recheck, we might have raced after dropping the lock */ 3768c2ecf20Sopenharmony_ci domain = dca_get_domain(dev); 3778c2ecf20Sopenharmony_ci if (!domain) { 3788c2ecf20Sopenharmony_ci domain = newdomain; 3798c2ecf20Sopenharmony_ci newdomain = NULL; 3808c2ecf20Sopenharmony_ci list_add(&domain->node, &dca_domains); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci list_add(&dca->node, &domain->dca_providers); 3848c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&dca_provider_chain, 3878c2ecf20Sopenharmony_ci DCA_PROVIDER_ADD, NULL); 3888c2ecf20Sopenharmony_ci kfree(newdomain); 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_dca_provider); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * unregister_dca_provider - remove a dca provider 3958c2ecf20Sopenharmony_ci * @dca - struct created by alloc_dca_provider() 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_civoid unregister_dca_provider(struct dca_provider *dca, struct device *dev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci unsigned long flags; 4008c2ecf20Sopenharmony_ci struct pci_bus *pci_rc; 4018c2ecf20Sopenharmony_ci struct dca_domain *domain; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&dca_provider_chain, 4048c2ecf20Sopenharmony_ci DCA_PROVIDER_REMOVE, NULL); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&dca_lock, flags); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (list_empty(&dca_domains)) { 4098c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci list_del(&dca->node); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pci_rc = dca_pci_rc_from_dev(dev); 4168c2ecf20Sopenharmony_ci domain = dca_find_domain(pci_rc); 4178c2ecf20Sopenharmony_ci if (list_empty(&domain->dca_providers)) 4188c2ecf20Sopenharmony_ci dca_free_domain(domain); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&dca_lock, flags); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci dca_sysfs_remove_provider(dca); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_dca_provider); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/** 4278c2ecf20Sopenharmony_ci * dca_register_notify - register a client's notifier callback 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_civoid dca_register_notify(struct notifier_block *nb) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci blocking_notifier_chain_register(&dca_provider_chain, nb); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca_register_notify); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * dca_unregister_notify - remove a client's notifier callback 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_civoid dca_unregister_notify(struct notifier_block *nb) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci blocking_notifier_chain_unregister(&dca_provider_chain, nb); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dca_unregister_notify); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int __init dca_init(void) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci pr_info("dca service started, version %s\n", DCA_VERSION); 4478c2ecf20Sopenharmony_ci return dca_sysfs_init(); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void __exit dca_exit(void) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci dca_sysfs_exit(); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ciarch_initcall(dca_init); 4568c2ecf20Sopenharmony_cimodule_exit(dca_exit); 4578c2ecf20Sopenharmony_ci 458