// 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); static unsigned long ice_adapter_index(u64 dsn) { #if BITS_PER_LONG == 64 return dsn; #else return (u32)dsn ^ (u32)(dsn >> 32); #endif } static struct ice_adapter *ice_adapter_new(u64 dsn) { struct ice_adapter *adapter; adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) return NULL; adapter->device_serial_number = dsn; spin_lock_init(&adapter->ptp_gltsyn_time_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) { u64 dsn = pci_get_dsn(pdev); struct ice_adapter *adapter; unsigned long index; int err; index = ice_adapter_index(dsn); 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->device_serial_number != dsn); return adapter; } if (err) return ERR_PTR(err); adapter = ice_adapter_new(dsn); 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) { u64 dsn = pci_get_dsn(pdev); struct ice_adapter *adapter; unsigned long index; index = ice_adapter_index(dsn); 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); }