Skip to content

Commit 8c8e827

Browse files
author
CKI KWF Bot
committed
Merge: x86/amd_node: Fix AMD root device caching
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/7659 JIRA: https://issues.redhat.com/browse/RHEL-127821 Recent AMD node rework removed the "search and count" method of caching AMD root devices. This depended on the value from a Data Fabric register that was expected to hold the PCI bus of one of the root devices attached to that fabric. As this expectation is incorrect the code needs to be adjusted. Signed-off-by: David Arcari <darcari@redhat.com> Approved-by: Lenny Szubowicz <lszubowi@redhat.com> Approved-by: Steve Best <sbest@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
2 parents 0bb284c + b9960a1 commit 8c8e827

File tree

2 files changed

+159
-68
lines changed

2 files changed

+159
-68
lines changed

arch/x86/include/asm/amd/node.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#define AMD_NODE0_PCI_SLOT 0x18
2424

2525
struct pci_dev *amd_node_get_func(u16 node, u8 func);
26-
struct pci_dev *amd_node_get_root(u16 node);
2726

2827
static inline u16 amd_num_nodes(void)
2928
{

arch/x86/kernel/amd_node.c

Lines changed: 159 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
99
*/
1010

11+
#include <linux/debugfs.h>
1112
#include <asm/amd/node.h>
1213

1314
/*
@@ -33,66 +34,11 @@ struct pci_dev *amd_node_get_func(u16 node, u8 func)
3334
return pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(AMD_NODE0_PCI_SLOT + node, func));
3435
}
3536

36-
#define DF_BLK_INST_CNT 0x040
37-
#define DF_CFG_ADDR_CNTL_LEGACY 0x084
38-
#define DF_CFG_ADDR_CNTL_DF4 0xC04
39-
40-
#define DF_MAJOR_REVISION GENMASK(27, 24)
41-
42-
static u16 get_cfg_addr_cntl_offset(struct pci_dev *df_f0)
43-
{
44-
u32 reg;
45-
46-
/*
47-
* Revision fields added for DF4 and later.
48-
*
49-
* Major revision of '0' is found pre-DF4. Field is Read-as-Zero.
50-
*/
51-
if (pci_read_config_dword(df_f0, DF_BLK_INST_CNT, &reg))
52-
return 0;
53-
54-
if (reg & DF_MAJOR_REVISION)
55-
return DF_CFG_ADDR_CNTL_DF4;
56-
57-
return DF_CFG_ADDR_CNTL_LEGACY;
58-
}
59-
60-
struct pci_dev *amd_node_get_root(u16 node)
61-
{
62-
struct pci_dev *root;
63-
u16 cntl_off;
64-
u8 bus;
65-
66-
if (!cpu_feature_enabled(X86_FEATURE_ZEN))
67-
return NULL;
68-
69-
/*
70-
* D18F0xXXX [Config Address Control] (DF::CfgAddressCntl)
71-
* Bits [7:0] (SecBusNum) holds the bus number of the root device for
72-
* this Data Fabric instance. The segment, device, and function will be 0.
73-
*/
74-
struct pci_dev *df_f0 __free(pci_dev_put) = amd_node_get_func(node, 0);
75-
if (!df_f0)
76-
return NULL;
77-
78-
cntl_off = get_cfg_addr_cntl_offset(df_f0);
79-
if (!cntl_off)
80-
return NULL;
81-
82-
if (pci_read_config_byte(df_f0, cntl_off, &bus))
83-
return NULL;
84-
85-
/* Grab the pointer for the actual root device instance. */
86-
root = pci_get_domain_bus_and_slot(0, bus, 0);
87-
88-
pci_dbg(root, "is root for AMD node %u\n", node);
89-
return root;
90-
}
91-
9237
static struct pci_dev **amd_roots;
9338

9439
/* Protect the PCI config register pairs used for SMN. */
9540
static DEFINE_MUTEX(smn_mutex);
41+
static bool smn_exclusive;
9642

9743
#define SMN_INDEX_OFFSET 0x60
9844
#define SMN_DATA_OFFSET 0x64
@@ -149,6 +95,9 @@ static int __amd_smn_rw(u8 i_off, u8 d_off, u16 node, u32 address, u32 *value, b
14995
if (!root)
15096
return err;
15197

98+
if (!smn_exclusive)
99+
return err;
100+
152101
guard(mutex)(&smn_mutex);
153102

154103
err = pci_write_config_dword(root, i_off, address);
@@ -188,23 +137,117 @@ int __must_check amd_smn_hsmp_rdwr(u16 node, u32 address, u32 *value, bool write
188137
}
189138
EXPORT_SYMBOL_GPL(amd_smn_hsmp_rdwr);
190139

191-
static int amd_cache_roots(void)
140+
static struct dentry *debugfs_dir;
141+
static u16 debug_node;
142+
static u32 debug_address;
143+
144+
static ssize_t smn_node_write(struct file *file, const char __user *userbuf,
145+
size_t count, loff_t *ppos)
192146
{
193-
u16 node, num_nodes = amd_num_nodes();
147+
u16 node;
148+
int ret;
194149

195-
amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL);
196-
if (!amd_roots)
197-
return -ENOMEM;
150+
ret = kstrtou16_from_user(userbuf, count, 0, &node);
151+
if (ret)
152+
return ret;
153+
154+
if (node >= amd_num_nodes())
155+
return -ENODEV;
156+
157+
debug_node = node;
158+
return count;
159+
}
160+
161+
static int smn_node_show(struct seq_file *m, void *v)
162+
{
163+
seq_printf(m, "0x%08x\n", debug_node);
164+
return 0;
165+
}
166+
167+
static ssize_t smn_address_write(struct file *file, const char __user *userbuf,
168+
size_t count, loff_t *ppos)
169+
{
170+
int ret;
171+
172+
ret = kstrtouint_from_user(userbuf, count, 0, &debug_address);
173+
if (ret)
174+
return ret;
175+
176+
return count;
177+
}
198178

199-
for (node = 0; node < num_nodes; node++)
200-
amd_roots[node] = amd_node_get_root(node);
179+
static int smn_address_show(struct seq_file *m, void *v)
180+
{
181+
seq_printf(m, "0x%08x\n", debug_address);
182+
return 0;
183+
}
184+
185+
static int smn_value_show(struct seq_file *m, void *v)
186+
{
187+
u32 val;
188+
int ret;
189+
190+
ret = amd_smn_read(debug_node, debug_address, &val);
191+
if (ret)
192+
return ret;
201193

194+
seq_printf(m, "0x%08x\n", val);
202195
return 0;
203196
}
204197

198+
static ssize_t smn_value_write(struct file *file, const char __user *userbuf,
199+
size_t count, loff_t *ppos)
200+
{
201+
u32 val;
202+
int ret;
203+
204+
ret = kstrtouint_from_user(userbuf, count, 0, &val);
205+
if (ret)
206+
return ret;
207+
208+
add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
209+
210+
ret = amd_smn_write(debug_node, debug_address, val);
211+
if (ret)
212+
return ret;
213+
214+
return count;
215+
}
216+
217+
DEFINE_SHOW_STORE_ATTRIBUTE(smn_node);
218+
DEFINE_SHOW_STORE_ATTRIBUTE(smn_address);
219+
DEFINE_SHOW_STORE_ATTRIBUTE(smn_value);
220+
221+
static struct pci_dev *get_next_root(struct pci_dev *root)
222+
{
223+
while ((root = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, root))) {
224+
/* Root device is Device 0 Function 0. */
225+
if (root->devfn)
226+
continue;
227+
228+
if (root->vendor != PCI_VENDOR_ID_AMD &&
229+
root->vendor != PCI_VENDOR_ID_HYGON)
230+
continue;
231+
232+
break;
233+
}
234+
235+
return root;
236+
}
237+
238+
static bool enable_dfs;
239+
240+
static int __init amd_smn_enable_dfs(char *str)
241+
{
242+
enable_dfs = true;
243+
return 1;
244+
}
245+
__setup("amd_smn_debugfs_enable", amd_smn_enable_dfs);
246+
205247
static int __init amd_smn_init(void)
206248
{
207-
int err;
249+
u16 count, num_roots, roots_per_node, node, num_nodes;
250+
struct pci_dev *root;
208251

209252
if (!cpu_feature_enabled(X86_FEATURE_ZEN))
210253
return 0;
@@ -214,9 +257,58 @@ static int __init amd_smn_init(void)
214257
if (amd_roots)
215258
return 0;
216259

217-
err = amd_cache_roots();
218-
if (err)
219-
return err;
260+
num_roots = 0;
261+
root = NULL;
262+
while ((root = get_next_root(root))) {
263+
pci_dbg(root, "Reserving PCI config space\n");
264+
265+
/*
266+
* There are a few SMN index/data pairs and other registers
267+
* that shouldn't be accessed by user space. So reserve the
268+
* entire PCI config space for simplicity rather than covering
269+
* specific registers piecemeal.
270+
*/
271+
if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) {
272+
pci_err(root, "Failed to reserve config space\n");
273+
return -EEXIST;
274+
}
275+
276+
num_roots++;
277+
}
278+
279+
pr_debug("Found %d AMD root devices\n", num_roots);
280+
281+
if (!num_roots)
282+
return -ENODEV;
283+
284+
num_nodes = amd_num_nodes();
285+
amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL);
286+
if (!amd_roots)
287+
return -ENOMEM;
288+
289+
roots_per_node = num_roots / num_nodes;
290+
291+
count = 0;
292+
node = 0;
293+
root = NULL;
294+
while (node < num_nodes && (root = get_next_root(root))) {
295+
/* Use one root for each node and skip the rest. */
296+
if (count++ % roots_per_node)
297+
continue;
298+
299+
pci_dbg(root, "is root for AMD node %u\n", node);
300+
amd_roots[node++] = root;
301+
}
302+
303+
if (enable_dfs) {
304+
debugfs_dir = debugfs_create_dir("amd_smn", arch_debugfs_dir);
305+
306+
debugfs_create_file("node", 0600, debugfs_dir, NULL, &smn_node_fops);
307+
debugfs_create_file("address", 0600, debugfs_dir, NULL, &smn_address_fops);
308+
debugfs_create_file("value", 0600, debugfs_dir, NULL, &smn_value_fops);
309+
}
310+
311+
smn_exclusive = true;
220312

221313
return 0;
222314
}

0 commit comments

Comments
 (0)