1e62d9491SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27589670fSShannon Nelson /*
3211a22ceSMaciej Sosnowski * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
47589670fSShannon Nelson */
57589670fSShannon Nelson
67589670fSShannon Nelson /*
77589670fSShannon Nelson * This driver supports an interface for DCA clients and providers to meet.
87589670fSShannon Nelson */
97589670fSShannon Nelson
107589670fSShannon Nelson #include <linux/kernel.h>
117589670fSShannon Nelson #include <linux/notifier.h>
127589670fSShannon Nelson #include <linux/device.h>
137589670fSShannon Nelson #include <linux/dca.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
15d229807fSPaul Gortmaker #include <linux/module.h>
167589670fSShannon Nelson
171a5aeeecSMaciej Sosnowski #define DCA_VERSION "1.12.1"
187589670fSShannon Nelson
197f1b358aSMaciej Sosnowski MODULE_VERSION(DCA_VERSION);
207f1b358aSMaciej Sosnowski MODULE_LICENSE("GPL");
217f1b358aSMaciej Sosnowski MODULE_AUTHOR("Intel Corporation");
227589670fSShannon Nelson
23a1741e7fSMike Galbraith static DEFINE_RAW_SPINLOCK(dca_lock);
247589670fSShannon Nelson
251a5aeeecSMaciej Sosnowski static LIST_HEAD(dca_domains);
261a5aeeecSMaciej Sosnowski
274e8cec26SSosnowski, Maciej static BLOCKING_NOTIFIER_HEAD(dca_provider_chain);
284e8cec26SSosnowski, Maciej
294e8cec26SSosnowski, Maciej static int dca_providers_blocked;
304e8cec26SSosnowski, Maciej
dca_pci_rc_from_dev(struct device * dev)311a5aeeecSMaciej Sosnowski static struct pci_bus *dca_pci_rc_from_dev(struct device *dev)
321a5aeeecSMaciej Sosnowski {
331a5aeeecSMaciej Sosnowski struct pci_dev *pdev = to_pci_dev(dev);
341a5aeeecSMaciej Sosnowski struct pci_bus *bus = pdev->bus;
351a5aeeecSMaciej Sosnowski
361a5aeeecSMaciej Sosnowski while (bus->parent)
371a5aeeecSMaciej Sosnowski bus = bus->parent;
381a5aeeecSMaciej Sosnowski
391a5aeeecSMaciej Sosnowski return bus;
401a5aeeecSMaciej Sosnowski }
411a5aeeecSMaciej Sosnowski
dca_allocate_domain(struct pci_bus * rc)421a5aeeecSMaciej Sosnowski static struct dca_domain *dca_allocate_domain(struct pci_bus *rc)
431a5aeeecSMaciej Sosnowski {
441a5aeeecSMaciej Sosnowski struct dca_domain *domain;
451a5aeeecSMaciej Sosnowski
461a5aeeecSMaciej Sosnowski domain = kzalloc(sizeof(*domain), GFP_NOWAIT);
471a5aeeecSMaciej Sosnowski if (!domain)
481a5aeeecSMaciej Sosnowski return NULL;
491a5aeeecSMaciej Sosnowski
501a5aeeecSMaciej Sosnowski INIT_LIST_HEAD(&domain->dca_providers);
511a5aeeecSMaciej Sosnowski domain->pci_rc = rc;
521a5aeeecSMaciej Sosnowski
531a5aeeecSMaciej Sosnowski return domain;
541a5aeeecSMaciej Sosnowski }
551a5aeeecSMaciej Sosnowski
dca_free_domain(struct dca_domain * domain)561a5aeeecSMaciej Sosnowski static void dca_free_domain(struct dca_domain *domain)
571a5aeeecSMaciej Sosnowski {
581a5aeeecSMaciej Sosnowski list_del(&domain->node);
591a5aeeecSMaciej Sosnowski kfree(domain);
601a5aeeecSMaciej Sosnowski }
611a5aeeecSMaciej Sosnowski
dca_provider_ioat_ver_3_0(struct device * dev)624e8cec26SSosnowski, Maciej static int dca_provider_ioat_ver_3_0(struct device *dev)
634e8cec26SSosnowski, Maciej {
644e8cec26SSosnowski, Maciej struct pci_dev *pdev = to_pci_dev(dev);
654e8cec26SSosnowski, Maciej
664e8cec26SSosnowski, Maciej return ((pdev->vendor == PCI_VENDOR_ID_INTEL) &&
674e8cec26SSosnowski, Maciej ((pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG0) ||
684e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG1) ||
694e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG2) ||
704e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG3) ||
714e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG4) ||
724e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG5) ||
734e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG6) ||
744e8cec26SSosnowski, Maciej (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_TBG7)));
754e8cec26SSosnowski, Maciej }
764e8cec26SSosnowski, Maciej
unregister_dca_providers(void)774e8cec26SSosnowski, Maciej static void unregister_dca_providers(void)
784e8cec26SSosnowski, Maciej {
794e8cec26SSosnowski, Maciej struct dca_provider *dca, *_dca;
804e8cec26SSosnowski, Maciej struct list_head unregistered_providers;
814e8cec26SSosnowski, Maciej struct dca_domain *domain;
824e8cec26SSosnowski, Maciej unsigned long flags;
834e8cec26SSosnowski, Maciej
844e8cec26SSosnowski, Maciej blocking_notifier_call_chain(&dca_provider_chain,
854e8cec26SSosnowski, Maciej DCA_PROVIDER_REMOVE, NULL);
864e8cec26SSosnowski, Maciej
874e8cec26SSosnowski, Maciej INIT_LIST_HEAD(&unregistered_providers);
884e8cec26SSosnowski, Maciej
89a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
904e8cec26SSosnowski, Maciej
914e8cec26SSosnowski, Maciej if (list_empty(&dca_domains)) {
92a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
934e8cec26SSosnowski, Maciej return;
944e8cec26SSosnowski, Maciej }
954e8cec26SSosnowski, Maciej
964e8cec26SSosnowski, Maciej /* at this point only one domain in the list is expected */
974e8cec26SSosnowski, Maciej domain = list_first_entry(&dca_domains, struct dca_domain, node);
984e8cec26SSosnowski, Maciej
993bb598fbSKirill A. Shutemov list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node)
1003bb598fbSKirill A. Shutemov list_move(&dca->node, &unregistered_providers);
1014e8cec26SSosnowski, Maciej
1024e8cec26SSosnowski, Maciej dca_free_domain(domain);
1034e8cec26SSosnowski, Maciej
104a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
1054e8cec26SSosnowski, Maciej
1064e8cec26SSosnowski, Maciej list_for_each_entry_safe(dca, _dca, &unregistered_providers, node) {
1074e8cec26SSosnowski, Maciej dca_sysfs_remove_provider(dca);
1084e8cec26SSosnowski, Maciej list_del(&dca->node);
1094e8cec26SSosnowski, Maciej }
1104e8cec26SSosnowski, Maciej }
1114e8cec26SSosnowski, Maciej
dca_find_domain(struct pci_bus * rc)1121a5aeeecSMaciej Sosnowski static struct dca_domain *dca_find_domain(struct pci_bus *rc)
1131a5aeeecSMaciej Sosnowski {
1141a5aeeecSMaciej Sosnowski struct dca_domain *domain;
1151a5aeeecSMaciej Sosnowski
1161a5aeeecSMaciej Sosnowski list_for_each_entry(domain, &dca_domains, node)
1171a5aeeecSMaciej Sosnowski if (domain->pci_rc == rc)
1181a5aeeecSMaciej Sosnowski return domain;
1191a5aeeecSMaciej Sosnowski
1201a5aeeecSMaciej Sosnowski return NULL;
1211a5aeeecSMaciej Sosnowski }
1221a5aeeecSMaciej Sosnowski
dca_get_domain(struct device * dev)1231a5aeeecSMaciej Sosnowski static struct dca_domain *dca_get_domain(struct device *dev)
1241a5aeeecSMaciej Sosnowski {
1251a5aeeecSMaciej Sosnowski struct pci_bus *rc;
1261a5aeeecSMaciej Sosnowski struct dca_domain *domain;
1271a5aeeecSMaciej Sosnowski
1281a5aeeecSMaciej Sosnowski rc = dca_pci_rc_from_dev(dev);
1291a5aeeecSMaciej Sosnowski domain = dca_find_domain(rc);
1301a5aeeecSMaciej Sosnowski
1311a5aeeecSMaciej Sosnowski if (!domain) {
132a1741e7fSMike Galbraith if (dca_provider_ioat_ver_3_0(dev) && !list_empty(&dca_domains))
1334e8cec26SSosnowski, Maciej dca_providers_blocked = 1;
1344e8cec26SSosnowski, Maciej }
1351a5aeeecSMaciej Sosnowski
1361a5aeeecSMaciej Sosnowski return domain;
1371a5aeeecSMaciej Sosnowski }
1387f1b358aSMaciej Sosnowski
dca_find_provider_by_dev(struct device * dev)1397f1b358aSMaciej Sosnowski static struct dca_provider *dca_find_provider_by_dev(struct device *dev)
1407f1b358aSMaciej Sosnowski {
1411a5aeeecSMaciej Sosnowski struct dca_provider *dca;
1421a5aeeecSMaciej Sosnowski struct pci_bus *rc;
1431a5aeeecSMaciej Sosnowski struct dca_domain *domain;
1447f1b358aSMaciej Sosnowski
1451a5aeeecSMaciej Sosnowski if (dev) {
1461a5aeeecSMaciej Sosnowski rc = dca_pci_rc_from_dev(dev);
1471a5aeeecSMaciej Sosnowski domain = dca_find_domain(rc);
1481a5aeeecSMaciej Sosnowski if (!domain)
1491a5aeeecSMaciej Sosnowski return NULL;
1501a5aeeecSMaciej Sosnowski } else {
1511a5aeeecSMaciej Sosnowski if (!list_empty(&dca_domains))
1521a5aeeecSMaciej Sosnowski domain = list_first_entry(&dca_domains,
1531a5aeeecSMaciej Sosnowski struct dca_domain,
1541a5aeeecSMaciej Sosnowski node);
1551a5aeeecSMaciej Sosnowski else
1561a5aeeecSMaciej Sosnowski return NULL;
1577f1b358aSMaciej Sosnowski }
1587f1b358aSMaciej Sosnowski
1591a5aeeecSMaciej Sosnowski list_for_each_entry(dca, &domain->dca_providers, node)
1601a5aeeecSMaciej Sosnowski if ((!dev) || (dca->ops->dev_managed(dca, dev)))
1611a5aeeecSMaciej Sosnowski return dca;
1621a5aeeecSMaciej Sosnowski
1631a5aeeecSMaciej Sosnowski return NULL;
1647f1b358aSMaciej Sosnowski }
1657589670fSShannon Nelson
1667589670fSShannon Nelson /**
1677589670fSShannon Nelson * dca_add_requester - add a dca client to the list
1687589670fSShannon Nelson * @dev - the device that wants dca service
1697589670fSShannon Nelson */
dca_add_requester(struct device * dev)1707589670fSShannon Nelson int dca_add_requester(struct device *dev)
1717589670fSShannon Nelson {
1727f1b358aSMaciej Sosnowski struct dca_provider *dca;
1737f1b358aSMaciej Sosnowski int err, slot = -ENODEV;
174eb4400e3SMaciej Sosnowski unsigned long flags;
1751a5aeeecSMaciej Sosnowski struct pci_bus *pci_rc;
1761a5aeeecSMaciej Sosnowski struct dca_domain *domain;
1777589670fSShannon Nelson
1787f1b358aSMaciej Sosnowski if (!dev)
1797f1b358aSMaciej Sosnowski return -EFAULT;
1807589670fSShannon Nelson
181a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
1827f1b358aSMaciej Sosnowski
1837f1b358aSMaciej Sosnowski /* check if the requester has not been added already */
1847f1b358aSMaciej Sosnowski dca = dca_find_provider_by_dev(dev);
1857f1b358aSMaciej Sosnowski if (dca) {
186a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
1877f1b358aSMaciej Sosnowski return -EEXIST;
1887f1b358aSMaciej Sosnowski }
1897589670fSShannon Nelson
1901a5aeeecSMaciej Sosnowski pci_rc = dca_pci_rc_from_dev(dev);
1911a5aeeecSMaciej Sosnowski domain = dca_find_domain(pci_rc);
1921a5aeeecSMaciej Sosnowski if (!domain) {
193a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
1941a5aeeecSMaciej Sosnowski return -ENODEV;
1951a5aeeecSMaciej Sosnowski }
1961a5aeeecSMaciej Sosnowski
1971a5aeeecSMaciej Sosnowski list_for_each_entry(dca, &domain->dca_providers, node) {
1987f1b358aSMaciej Sosnowski slot = dca->ops->add_requester(dca, dev);
1997f1b358aSMaciej Sosnowski if (slot >= 0)
2007f1b358aSMaciej Sosnowski break;
2017f1b358aSMaciej Sosnowski }
202eb4400e3SMaciej Sosnowski
203a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
204eb4400e3SMaciej Sosnowski
205eb4400e3SMaciej Sosnowski if (slot < 0)
2067f1b358aSMaciej Sosnowski return slot;
2077f1b358aSMaciej Sosnowski
2087f1b358aSMaciej Sosnowski err = dca_sysfs_add_req(dca, dev, slot);
2097589670fSShannon Nelson if (err) {
210a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
211eb4400e3SMaciej Sosnowski if (dca == dca_find_provider_by_dev(dev))
2127f1b358aSMaciej Sosnowski dca->ops->remove_requester(dca, dev);
213a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
2147589670fSShannon Nelson return err;
2157589670fSShannon Nelson }
2167589670fSShannon Nelson
2177589670fSShannon Nelson return 0;
2187589670fSShannon Nelson }
2197589670fSShannon Nelson EXPORT_SYMBOL_GPL(dca_add_requester);
2207589670fSShannon Nelson
2217589670fSShannon Nelson /**
2227589670fSShannon Nelson * dca_remove_requester - remove a dca client from the list
2237589670fSShannon Nelson * @dev - the device that wants dca service
2247589670fSShannon Nelson */
dca_remove_requester(struct device * dev)2257589670fSShannon Nelson int dca_remove_requester(struct device *dev)
2267589670fSShannon Nelson {
2277f1b358aSMaciej Sosnowski struct dca_provider *dca;
2287589670fSShannon Nelson int slot;
229eb4400e3SMaciej Sosnowski unsigned long flags;
2307f1b358aSMaciej Sosnowski
2317f1b358aSMaciej Sosnowski if (!dev)
2327f1b358aSMaciej Sosnowski return -EFAULT;
2337589670fSShannon Nelson
234a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
2357f1b358aSMaciej Sosnowski dca = dca_find_provider_by_dev(dev);
2367f1b358aSMaciej Sosnowski if (!dca) {
237a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
2387f1b358aSMaciej Sosnowski return -ENODEV;
2397f1b358aSMaciej Sosnowski }
2407f1b358aSMaciej Sosnowski slot = dca->ops->remove_requester(dca, dev);
241a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
242eb4400e3SMaciej Sosnowski
243eb4400e3SMaciej Sosnowski if (slot < 0)
2447589670fSShannon Nelson return slot;
2457589670fSShannon Nelson
2467f1b358aSMaciej Sosnowski dca_sysfs_remove_req(dca, slot);
2477f1b358aSMaciej Sosnowski
2487589670fSShannon Nelson return 0;
2497589670fSShannon Nelson }
2507589670fSShannon Nelson EXPORT_SYMBOL_GPL(dca_remove_requester);
2517589670fSShannon Nelson
2527589670fSShannon Nelson /**
2537f1b358aSMaciej Sosnowski * dca_common_get_tag - return the dca tag (serves both new and old api)
2547f1b358aSMaciej Sosnowski * @dev - the device that wants dca service
2557f1b358aSMaciej Sosnowski * @cpu - the cpuid as returned by get_cpu()
2567f1b358aSMaciej Sosnowski */
dca_common_get_tag(struct device * dev,int cpu)257064223c1SColin Ian King static u8 dca_common_get_tag(struct device *dev, int cpu)
2587f1b358aSMaciej Sosnowski {
2597f1b358aSMaciej Sosnowski struct dca_provider *dca;
2607f1b358aSMaciej Sosnowski u8 tag;
261eb4400e3SMaciej Sosnowski unsigned long flags;
2627f1b358aSMaciej Sosnowski
263a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
2647f1b358aSMaciej Sosnowski
2657f1b358aSMaciej Sosnowski dca = dca_find_provider_by_dev(dev);
2667f1b358aSMaciej Sosnowski if (!dca) {
267a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
2687f1b358aSMaciej Sosnowski return -ENODEV;
2697f1b358aSMaciej Sosnowski }
2707f1b358aSMaciej Sosnowski tag = dca->ops->get_tag(dca, dev, cpu);
2717f1b358aSMaciej Sosnowski
272a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
2737f1b358aSMaciej Sosnowski return tag;
2747f1b358aSMaciej Sosnowski }
2757f1b358aSMaciej Sosnowski
2767f1b358aSMaciej Sosnowski /**
2777f1b358aSMaciej Sosnowski * dca3_get_tag - return the dca tag to the requester device
2787f1b358aSMaciej Sosnowski * for the given cpu (new api)
2797f1b358aSMaciej Sosnowski * @dev - the device that wants dca service
2807f1b358aSMaciej Sosnowski * @cpu - the cpuid as returned by get_cpu()
2817f1b358aSMaciej Sosnowski */
dca3_get_tag(struct device * dev,int cpu)2827f1b358aSMaciej Sosnowski u8 dca3_get_tag(struct device *dev, int cpu)
2837f1b358aSMaciej Sosnowski {
2847f1b358aSMaciej Sosnowski if (!dev)
2857f1b358aSMaciej Sosnowski return -EFAULT;
2867f1b358aSMaciej Sosnowski
2877f1b358aSMaciej Sosnowski return dca_common_get_tag(dev, cpu);
2887f1b358aSMaciej Sosnowski }
2897f1b358aSMaciej Sosnowski EXPORT_SYMBOL_GPL(dca3_get_tag);
2907f1b358aSMaciej Sosnowski
2917f1b358aSMaciej Sosnowski /**
2927f1b358aSMaciej Sosnowski * dca_get_tag - return the dca tag for the given cpu (old api)
2937589670fSShannon Nelson * @cpu - the cpuid as returned by get_cpu()
2947589670fSShannon Nelson */
dca_get_tag(int cpu)2957589670fSShannon Nelson u8 dca_get_tag(int cpu)
2967589670fSShannon Nelson {
297*3ac39d20SDan Carpenter return dca_common_get_tag(NULL, cpu);
2987589670fSShannon Nelson }
2997589670fSShannon Nelson EXPORT_SYMBOL_GPL(dca_get_tag);
3007589670fSShannon Nelson
3017589670fSShannon Nelson /**
3027589670fSShannon Nelson * alloc_dca_provider - get data struct for describing a dca provider
3037589670fSShannon Nelson * @ops - pointer to struct of dca operation function pointers
3047589670fSShannon Nelson * @priv_size - size of extra mem to be added for provider's needs
3057589670fSShannon Nelson */
alloc_dca_provider(const struct dca_ops * ops,int priv_size)3062bb129ebSJulia Lawall struct dca_provider *alloc_dca_provider(const struct dca_ops *ops,
3072bb129ebSJulia Lawall int priv_size)
3087589670fSShannon Nelson {
3097589670fSShannon Nelson struct dca_provider *dca;
3107589670fSShannon Nelson int alloc_size;
3117589670fSShannon Nelson
3127589670fSShannon Nelson alloc_size = (sizeof(*dca) + priv_size);
3137589670fSShannon Nelson dca = kzalloc(alloc_size, GFP_KERNEL);
3147589670fSShannon Nelson if (!dca)
3157589670fSShannon Nelson return NULL;
3167589670fSShannon Nelson dca->ops = ops;
3177589670fSShannon Nelson
3187589670fSShannon Nelson return dca;
3197589670fSShannon Nelson }
3207589670fSShannon Nelson EXPORT_SYMBOL_GPL(alloc_dca_provider);
3217589670fSShannon Nelson
3227589670fSShannon Nelson /**
3237589670fSShannon Nelson * free_dca_provider - release the dca provider data struct
3247589670fSShannon Nelson * @ops - pointer to struct of dca operation function pointers
3257589670fSShannon Nelson * @priv_size - size of extra mem to be added for provider's needs
3267589670fSShannon Nelson */
free_dca_provider(struct dca_provider * dca)3277589670fSShannon Nelson void free_dca_provider(struct dca_provider *dca)
3287589670fSShannon Nelson {
3297589670fSShannon Nelson kfree(dca);
3307589670fSShannon Nelson }
3317589670fSShannon Nelson EXPORT_SYMBOL_GPL(free_dca_provider);
3327589670fSShannon Nelson
3337589670fSShannon Nelson /**
3347589670fSShannon Nelson * register_dca_provider - register a dca provider
3357589670fSShannon Nelson * @dca - struct created by alloc_dca_provider()
3367589670fSShannon Nelson * @dev - device providing dca services
3377589670fSShannon Nelson */
register_dca_provider(struct dca_provider * dca,struct device * dev)3387589670fSShannon Nelson int register_dca_provider(struct dca_provider *dca, struct device *dev)
3397589670fSShannon Nelson {
3407589670fSShannon Nelson int err;
341eb4400e3SMaciej Sosnowski unsigned long flags;
342a1741e7fSMike Galbraith struct dca_domain *domain, *newdomain = NULL;
3437589670fSShannon Nelson
344a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
3454e8cec26SSosnowski, Maciej if (dca_providers_blocked) {
346a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
3474e8cec26SSosnowski, Maciej return -ENODEV;
3484e8cec26SSosnowski, Maciej }
349a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
3504e8cec26SSosnowski, Maciej
3517589670fSShannon Nelson err = dca_sysfs_add_provider(dca, dev);
3527589670fSShannon Nelson if (err)
3537589670fSShannon Nelson return err;
354eb4400e3SMaciej Sosnowski
355a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
3561a5aeeecSMaciej Sosnowski domain = dca_get_domain(dev);
3571a5aeeecSMaciej Sosnowski if (!domain) {
358a1741e7fSMike Galbraith struct pci_bus *rc;
359a1741e7fSMike Galbraith
3604e8cec26SSosnowski, Maciej if (dca_providers_blocked) {
361a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
3624e8cec26SSosnowski, Maciej dca_sysfs_remove_provider(dca);
3634e8cec26SSosnowski, Maciej unregister_dca_providers();
3641a5aeeecSMaciej Sosnowski return -ENODEV;
3651a5aeeecSMaciej Sosnowski }
366a1741e7fSMike Galbraith
367a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
368a1741e7fSMike Galbraith rc = dca_pci_rc_from_dev(dev);
369a1741e7fSMike Galbraith newdomain = dca_allocate_domain(rc);
370a1741e7fSMike Galbraith if (!newdomain)
371a1741e7fSMike Galbraith return -ENODEV;
372a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
373a1741e7fSMike Galbraith /* Recheck, we might have raced after dropping the lock */
374a1741e7fSMike Galbraith domain = dca_get_domain(dev);
375a1741e7fSMike Galbraith if (!domain) {
376a1741e7fSMike Galbraith domain = newdomain;
377a1741e7fSMike Galbraith newdomain = NULL;
378a1741e7fSMike Galbraith list_add(&domain->node, &dca_domains);
379a1741e7fSMike Galbraith }
380a1741e7fSMike Galbraith }
3811a5aeeecSMaciej Sosnowski list_add(&dca->node, &domain->dca_providers);
382a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
383eb4400e3SMaciej Sosnowski
3847589670fSShannon Nelson blocking_notifier_call_chain(&dca_provider_chain,
3857589670fSShannon Nelson DCA_PROVIDER_ADD, NULL);
386a1741e7fSMike Galbraith kfree(newdomain);
3877589670fSShannon Nelson return 0;
3887589670fSShannon Nelson }
3897589670fSShannon Nelson EXPORT_SYMBOL_GPL(register_dca_provider);
3907589670fSShannon Nelson
3917589670fSShannon Nelson /**
3927589670fSShannon Nelson * unregister_dca_provider - remove a dca provider
3937589670fSShannon Nelson * @dca - struct created by alloc_dca_provider()
3947589670fSShannon Nelson */
unregister_dca_provider(struct dca_provider * dca,struct device * dev)3951a5aeeecSMaciej Sosnowski void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
3967589670fSShannon Nelson {
397eb4400e3SMaciej Sosnowski unsigned long flags;
3981a5aeeecSMaciej Sosnowski struct pci_bus *pci_rc;
3991a5aeeecSMaciej Sosnowski struct dca_domain *domain;
400eb4400e3SMaciej Sosnowski
4017589670fSShannon Nelson blocking_notifier_call_chain(&dca_provider_chain,
4027589670fSShannon Nelson DCA_PROVIDER_REMOVE, NULL);
403eb4400e3SMaciej Sosnowski
404a1741e7fSMike Galbraith raw_spin_lock_irqsave(&dca_lock, flags);
4051a5aeeecSMaciej Sosnowski
406c419fcfdSMaciej Sosnowski if (list_empty(&dca_domains)) {
407c419fcfdSMaciej Sosnowski raw_spin_unlock_irqrestore(&dca_lock, flags);
408c419fcfdSMaciej Sosnowski return;
409c419fcfdSMaciej Sosnowski }
410c419fcfdSMaciej Sosnowski
4117f1b358aSMaciej Sosnowski list_del(&dca->node);
4121a5aeeecSMaciej Sosnowski
4131a5aeeecSMaciej Sosnowski pci_rc = dca_pci_rc_from_dev(dev);
4141a5aeeecSMaciej Sosnowski domain = dca_find_domain(pci_rc);
4151a5aeeecSMaciej Sosnowski if (list_empty(&domain->dca_providers))
4161a5aeeecSMaciej Sosnowski dca_free_domain(domain);
4171a5aeeecSMaciej Sosnowski
418a1741e7fSMike Galbraith raw_spin_unlock_irqrestore(&dca_lock, flags);
419eb4400e3SMaciej Sosnowski
4207589670fSShannon Nelson dca_sysfs_remove_provider(dca);
4217589670fSShannon Nelson }
4227589670fSShannon Nelson EXPORT_SYMBOL_GPL(unregister_dca_provider);
4237589670fSShannon Nelson
4247589670fSShannon Nelson /**
4257589670fSShannon Nelson * dca_register_notify - register a client's notifier callback
4267589670fSShannon Nelson */
dca_register_notify(struct notifier_block * nb)4277589670fSShannon Nelson void dca_register_notify(struct notifier_block *nb)
4287589670fSShannon Nelson {
4297589670fSShannon Nelson blocking_notifier_chain_register(&dca_provider_chain, nb);
4307589670fSShannon Nelson }
4317589670fSShannon Nelson EXPORT_SYMBOL_GPL(dca_register_notify);
4327589670fSShannon Nelson
4337589670fSShannon Nelson /**
4347589670fSShannon Nelson * dca_unregister_notify - remove a client's notifier callback
4357589670fSShannon Nelson */
dca_unregister_notify(struct notifier_block * nb)4367589670fSShannon Nelson void dca_unregister_notify(struct notifier_block *nb)
4377589670fSShannon Nelson {
4387589670fSShannon Nelson blocking_notifier_chain_unregister(&dca_provider_chain, nb);
4397589670fSShannon Nelson }
4407589670fSShannon Nelson EXPORT_SYMBOL_GPL(dca_unregister_notify);
4417589670fSShannon Nelson
dca_init(void)4427589670fSShannon Nelson static int __init dca_init(void)
4437589670fSShannon Nelson {
444084dac53SStephen Hemminger pr_info("dca service started, version %s\n", DCA_VERSION);
4457589670fSShannon Nelson return dca_sysfs_init();
4467589670fSShannon Nelson }
4477589670fSShannon Nelson
dca_exit(void)4487589670fSShannon Nelson static void __exit dca_exit(void)
4497589670fSShannon Nelson {
4507589670fSShannon Nelson dca_sysfs_exit();
4517589670fSShannon Nelson }
4527589670fSShannon Nelson
453652afc27SDan Williams arch_initcall(dca_init);
4547589670fSShannon Nelson module_exit(dca_exit);
4557589670fSShannon Nelson
456