1 // SPDX-License-Identifier: GPL-2.0-only 2 // SPDX-FileCopyrightText: Copyright Red Hat 3 4 #include <linux/cleanup.h> 5 #include <linux/mutex.h> 6 #include <linux/pci.h> 7 #include <linux/slab.h> 8 #include <linux/spinlock.h> 9 #include <linux/xarray.h> 10 #include "ice_adapter.h" 11 #include "ice.h" 12 13 static DEFINE_XARRAY(ice_adapters); 14 static DEFINE_MUTEX(ice_adapters_mutex); 15 16 static unsigned long ice_adapter_index(u64 dsn) 17 { 18 #if BITS_PER_LONG == 64 19 return dsn; 20 #else 21 return (u32)dsn ^ (u32)(dsn >> 32); 22 #endif 23 } 24 25 static struct ice_adapter *ice_adapter_new(u64 dsn) 26 { 27 struct ice_adapter *adapter; 28 29 adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); 30 if (!adapter) 31 return NULL; 32 33 adapter->device_serial_number = dsn; 34 spin_lock_init(&adapter->ptp_gltsyn_time_lock); 35 spin_lock_init(&adapter->txq_ctx_lock); 36 refcount_set(&adapter->refcount, 1); 37 38 mutex_init(&adapter->ports.lock); 39 INIT_LIST_HEAD(&adapter->ports.ports); 40 41 return adapter; 42 } 43 44 static void ice_adapter_free(struct ice_adapter *adapter) 45 { 46 WARN_ON(!list_empty(&adapter->ports.ports)); 47 mutex_destroy(&adapter->ports.lock); 48 49 kfree(adapter); 50 } 51 52 /** 53 * ice_adapter_get - Get a shared ice_adapter structure. 54 * @pdev: Pointer to the pci_dev whose driver is getting the ice_adapter. 55 * 56 * Gets a pointer to a shared ice_adapter structure. Physical functions (PFs) 57 * of the same multi-function PCI device share one ice_adapter structure. 58 * The ice_adapter is reference-counted. The PF driver must use ice_adapter_put 59 * to release its reference. 60 * 61 * Context: Process, may sleep. 62 * Return: Pointer to ice_adapter on success. 63 * ERR_PTR() on error. -ENOMEM is the only possible error. 64 */ 65 struct ice_adapter *ice_adapter_get(struct pci_dev *pdev) 66 { 67 u64 dsn = pci_get_dsn(pdev); 68 struct ice_adapter *adapter; 69 unsigned long index; 70 int err; 71 72 index = ice_adapter_index(dsn); 73 scoped_guard(mutex, &ice_adapters_mutex) { 74 err = xa_insert(&ice_adapters, index, NULL, GFP_KERNEL); 75 if (err == -EBUSY) { 76 adapter = xa_load(&ice_adapters, index); 77 refcount_inc(&adapter->refcount); 78 WARN_ON_ONCE(adapter->device_serial_number != dsn); 79 return adapter; 80 } 81 if (err) 82 return ERR_PTR(err); 83 84 adapter = ice_adapter_new(dsn); 85 if (!adapter) 86 return ERR_PTR(-ENOMEM); 87 xa_store(&ice_adapters, index, adapter, GFP_KERNEL); 88 } 89 return adapter; 90 } 91 92 /** 93 * ice_adapter_put - Release a reference to the shared ice_adapter structure. 94 * @pdev: Pointer to the pci_dev whose driver is releasing the ice_adapter. 95 * 96 * Releases the reference to ice_adapter previously obtained with 97 * ice_adapter_get. 98 * 99 * Context: Process, may sleep. 100 */ 101 void ice_adapter_put(struct pci_dev *pdev) 102 { 103 u64 dsn = pci_get_dsn(pdev); 104 struct ice_adapter *adapter; 105 unsigned long index; 106 107 index = ice_adapter_index(dsn); 108 scoped_guard(mutex, &ice_adapters_mutex) { 109 adapter = xa_load(&ice_adapters, index); 110 if (WARN_ON(!adapter)) 111 return; 112 if (!refcount_dec_and_test(&adapter->refcount)) 113 return; 114 115 WARN_ON(xa_erase(&ice_adapters, index) != adapter); 116 } 117 ice_adapter_free(adapter); 118 } 119