| // SPDX-License-Identifier: GPL-2.0-only |
| // SPDX-FileCopyrightText: Copyright Red Hat |
| |
| #include <linux/bitfield.h> |
| #include <linux/cleanup.h> |
| #include <linux/mutex.h> |
| #include <linux/pci.h> |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <linux/xarray.h> |
| #include "ice_adapter.h" |
| |
| static DEFINE_XARRAY(ice_adapters); |
| static DEFINE_MUTEX(ice_adapters_mutex); |
| |
| /* PCI bus number is 8 bits. Slot is 5 bits. Domain can have the rest. */ |
| #define INDEX_FIELD_DOMAIN GENMASK(BITS_PER_LONG - 1, 13) |
| #define INDEX_FIELD_BUS GENMASK(12, 5) |
| #define INDEX_FIELD_SLOT GENMASK(4, 0) |
| |
| static unsigned long ice_adapter_index(const struct pci_dev *pdev) |
| { |
| unsigned int domain = pci_domain_nr(pdev->bus); |
| |
| WARN_ON(domain > FIELD_MAX(INDEX_FIELD_DOMAIN)); |
| |
| return FIELD_PREP(INDEX_FIELD_DOMAIN, domain) | |
| FIELD_PREP(INDEX_FIELD_BUS, pdev->bus->number) | |
| FIELD_PREP(INDEX_FIELD_SLOT, PCI_SLOT(pdev->devfn)); |
| } |
| |
| static struct ice_adapter *ice_adapter_new(void) |
| { |
| struct ice_adapter *adapter; |
| |
| adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); |
| if (!adapter) |
| return NULL; |
| |
| spin_lock_init(&adapter->ptp_gltsyn_time_lock); |
| refcount_set(&adapter->refcount, 1); |
| |
| return adapter; |
| } |
| |
| static void ice_adapter_free(struct ice_adapter *adapter) |
| { |
| kfree(adapter); |
| } |
| |
| /** |
| * ice_adapter_get - Get a shared ice_adapter structure. |
| * @pdev: Pointer to the pci_dev whose driver is getting the ice_adapter. |
| * |
| * Gets a pointer to a shared ice_adapter structure. Physical functions (PFs) |
| * of the same multi-function PCI device share one ice_adapter structure. |
| * The ice_adapter is reference-counted. The PF driver must use ice_adapter_put |
| * to release its reference. |
| * |
| * Context: Process, may sleep. |
| * Return: Pointer to ice_adapter on success. |
| * ERR_PTR() on error. -ENOMEM is the only possible error. |
| */ |
| struct ice_adapter *ice_adapter_get(const struct pci_dev *pdev) |
| { |
| unsigned long index = ice_adapter_index(pdev); |
| struct ice_adapter *adapter; |
| int err; |
| |
| scoped_guard(mutex, &ice_adapters_mutex) { |
| err = xa_insert(&ice_adapters, index, NULL, GFP_KERNEL); |
| if (err == -EBUSY) { |
| adapter = xa_load(&ice_adapters, index); |
| refcount_inc(&adapter->refcount); |
| return adapter; |
| } |
| if (err) |
| return ERR_PTR(err); |
| |
| adapter = ice_adapter_new(); |
| if (!adapter) |
| return ERR_PTR(-ENOMEM); |
| xa_store(&ice_adapters, index, adapter, GFP_KERNEL); |
| } |
| return adapter; |
| } |
| |
| /** |
| * ice_adapter_put - Release a reference to the shared ice_adapter structure. |
| * @pdev: Pointer to the pci_dev whose driver is releasing the ice_adapter. |
| * |
| * Releases the reference to ice_adapter previously obtained with |
| * ice_adapter_get. |
| * |
| * Context: Process, may sleep. |
| */ |
| void ice_adapter_put(const struct pci_dev *pdev) |
| { |
| unsigned long index = ice_adapter_index(pdev); |
| struct ice_adapter *adapter; |
| |
| scoped_guard(mutex, &ice_adapters_mutex) { |
| adapter = xa_load(&ice_adapters, index); |
| if (WARN_ON(!adapter)) |
| return; |
| if (!refcount_dec_and_test(&adapter->refcount)) |
| return; |
| |
| WARN_ON(xa_erase(&ice_adapters, index) != adapter); |
| } |
| ice_adapter_free(adapter); |
| } |