1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright Red Hat
#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"
#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);
}
|