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