// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Copyright Red Hat #include #include #include #include #include #include #include "ice_adapter.h" #include "ice.h" static DEFINE_XARRAY(ice_adapters); static DEFINE_MUTEX(ice_adapters_mutex); #define ICE_ADAPTER_FIXED_INDEX BIT_ULL(63) #define ICE_ADAPTER_INDEX_E825C \ (ICE_DEV_ID_E825C_BACKPLANE | ICE_ADAPTER_FIXED_INDEX) static u64 ice_adapter_index(struct pci_dev *pdev) { switch (pdev->device) { case ICE_DEV_ID_E825C_BACKPLANE: case ICE_DEV_ID_E825C_QSFP: case ICE_DEV_ID_E825C_SFP: case ICE_DEV_ID_E825C_SGMII: /* E825C devices have multiple NACs which are connected to the * same clock source, and which must share the same * ice_adapter structure. We can't use the serial number since * each NAC has its own NVM generated with its own unique * Device Serial Number. Instead, rely on the embedded nature * of the E825C devices, and use a fixed index. This relies on * the fact that all E825C physical functions in a given * system are part of the same overall device. */ return ICE_ADAPTER_INDEX_E825C; default: return pci_get_dsn(pdev) & ~ICE_ADAPTER_FIXED_INDEX; } } static unsigned long ice_adapter_xa_index(struct pci_dev *pdev) { u64 index = ice_adapter_index(pdev); #if BITS_PER_LONG == 64 return index; #else return (u32)index ^ (u32)(index >> 32); #endif } static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev) { struct ice_adapter *adapter; adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) return NULL; adapter->index = ice_adapter_index(pdev); spin_lock_init(&adapter->ptp_gltsyn_time_lock); spin_lock_init(&adapter->txq_ctx_lock); refcount_set(&adapter->refcount, 1); mutex_init(&adapter->ports.lock); INIT_LIST_HEAD(&adapter->ports.ports); return adapter; } static void ice_adapter_free(struct ice_adapter *adapter) { WARN_ON(!list_empty(&adapter->ports.ports)); mutex_destroy(&adapter->ports.lock); 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(struct pci_dev *pdev) { struct ice_adapter *adapter; unsigned long index; int err; index = ice_adapter_xa_index(pdev); 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); WARN_ON_ONCE(adapter->index != ice_adapter_index(pdev)); return adapter; } if (err) return ERR_PTR(err); adapter = ice_adapter_new(pdev); 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(struct pci_dev *pdev) { struct ice_adapter *adapter; unsigned long index; index = ice_adapter_xa_index(pdev); 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); }