xref: /linux/drivers/edac/intel-bff.c (revision ae171014b7988fabf1dbbc7cc4036c416a5b0230)
1d4ffdf94STony Luck // SPDX-License-Identifier: GPL-2.0-only
2d4ffdf94STony Luck /* Copyright(c) 2026 Intel Corporation. */
3d4ffdf94STony Luck 
4d4ffdf94STony Luck /*
5d4ffdf94STony Luck  * Intel driver to reset bitfix filters when they overflow.
6d4ffdf94STony Luck  *
7d4ffdf94STony Luck  * Each bitfix filter has limited slots to track corrected errors.
8d4ffdf94STony Luck  * These slots can be filled by transient corrected errors (e.g.,
9d4ffdf94STony Luck  * bit flips from particle strikes) that don't represent permanent
10d4ffdf94STony Luck  * hardware defects.
11d4ffdf94STony Luck  *
12d4ffdf94STony Luck  * When the filter overflows (yellow status), reset it to reclaim
13d4ffdf94STony Luck  * slots occupied by transient corrected errors. If the overflow
14d4ffdf94STony Luck  * repeats frequently after reset, it indicates persistent hardware
15d4ffdf94STony Luck  * defects that need attention: the system should be scheduled for
16d4ffdf94STony Luck  * servicing.
17d4ffdf94STony Luck  */
18d4ffdf94STony Luck #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19d4ffdf94STony Luck 
20666192e1STony Luck #include <linux/bitfield.h>
21666192e1STony Luck #include <linux/bits.h>
224835abedSTony Luck #include <linux/cacheinfo.h>
234835abedSTony Luck #include <linux/cleanup.h>
24d4ffdf94STony Luck #include <linux/cpufeature.h>
254835abedSTony Luck #include <linux/cpuhplock.h>
26a2808f91STony Luck #include <linux/device-id/x86_cpu.h>
27d4ffdf94STony Luck #include <linux/errno.h>
28*30713abbSTony Luck #include <linux/gfp_types.h>
29d4ffdf94STony Luck #include <linux/init.h>
30*30713abbSTony Luck #include <linux/jiffies.h>
314835abedSTony Luck #include <linux/limits.h>
32d4ffdf94STony Luck #include <linux/module.h>
33666192e1STony Luck #include <linux/notifier.h>
34a2808f91STony Luck #include <linux/printk.h>
35*30713abbSTony Luck #include <linux/slab.h>
364835abedSTony Luck #include <linux/topology.h>
37d4ffdf94STony Luck #include <linux/types.h>
38*30713abbSTony Luck #include <linux/xarray.h>
39d4ffdf94STony Luck 
40a2808f91STony Luck #include <asm/cpu_device_id.h>
41d4ffdf94STony Luck #include <asm/cpufeatures.h>
42a2808f91STony Luck #include <asm/intel-family.h>
43d4ffdf94STony Luck #include <asm/mce.h>
44d4ffdf94STony Luck #include <asm/msr.h>
45d4ffdf94STony Luck #include <asm/msr-index.h>
46d4ffdf94STony Luck 
47*30713abbSTony Luck /*
48*30713abbSTony Luck  * A 10-minute observation period helps distinguish between:
49*30713abbSTony Luck  *
50*30713abbSTony Luck  *  - A long-term accumulation of transient corrected errors
51*30713abbSTony Luck  *    (filter stays clear after reset).
52*30713abbSTony Luck  *
53*30713abbSTony Luck  *  - Permanent defects (filter overflows again quickly).
54*30713abbSTony Luck  */
55*30713abbSTony Luck #define BFF_OVERFLOW_INTERVAL	secs_to_jiffies(10 * 60)
56*30713abbSTony Luck 
57666192e1STony Luck /* Intel bitfix filter control register defines */
58666192e1STony Luck #define MSR_MC0_BFF_CTL		0x000006c0
59666192e1STony Luck #define MSR_MCx_BFF_CTL(x)	(MSR_MC0_BFF_CTL + (x))
60666192e1STony Luck #define  MCI_BFF_RESET		BIT_ULL(0)
61666192e1STony Luck 
62a2808f91STony Luck /* Machine check bank hardware unit types */
63a2808f91STony Luck enum bff_bank_type {
64a2808f91STony Luck 	BFF_BANK_NONE = 0,
65a2808f91STony Luck 	BFF_BANK_DCU,
66a2808f91STony Luck 	BFF_BANK_DTLB,
67a2808f91STony Luck 	BFF_BANK_MLC,
68a2808f91STony Luck 	BFF_BANK_CCF,
69a2808f91STony Luck 	BFF_BANK_HSF,
70a2808f91STony Luck 	BFF_BANK_IOCACHE,
71a2808f91STony Luck };
72a2808f91STony Luck 
73a2808f91STony Luck /*
74a2808f91STony Luck  * Mapping from machine check bank numbers on Diamond Rapids CPU, that are
75a2808f91STony Luck  * supported by a bitfix filter, to hardware unit type.
76a2808f91STony Luck  */
77a2808f91STony Luck static const enum bff_bank_type dmr_mcbanks[MAX_NR_BANKS] = {
78a2808f91STony Luck 	[1]	= BFF_BANK_DCU,
79a2808f91STony Luck 	[2]	= BFF_BANK_DTLB,
80a2808f91STony Luck 	[3]	= BFF_BANK_MLC,
81a2808f91STony Luck 	[6]	= BFF_BANK_CCF,
82a2808f91STony Luck 	[13]	= BFF_BANK_HSF,
83a2808f91STony Luck 	[17]	= BFF_BANK_IOCACHE
84a2808f91STony Luck };
85a2808f91STony Luck 
86a2808f91STony Luck static const struct x86_cpu_id bff_cpu_ids[] __initconst = {
87a2808f91STony Luck 	X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, dmr_mcbanks),
88a2808f91STony Luck 	{}
89a2808f91STony Luck };
90a2808f91STony Luck MODULE_DEVICE_TABLE(x86cpu, bff_cpu_ids);
91a2808f91STony Luck 
92a2808f91STony Luck static const enum bff_bank_type *bff_bank_types;
93a2808f91STony Luck 
94*30713abbSTony Luck static DEFINE_XARRAY(bff_bank_xa);
95*30713abbSTony Luck 
964835abedSTony Luck /* Diamond Rapids maps APICID[2] to the IMH instance within a socket. */
974835abedSTony Luck #define APICID_IMH_NUM		GENMASK(2, 2)
984835abedSTony Luck #define IMH_NUM(apicid)		FIELD_GET(APICID_IMH_NUM, apicid)
994835abedSTony Luck #define NUM_IMH_PER_SOCKET	2
1004835abedSTony Luck 
bff_set_imh_id(struct mce * mce,unsigned long * id)1014835abedSTony Luck static void bff_set_imh_id(struct mce *mce, unsigned long *id)
1024835abedSTony Luck {
1034835abedSTony Luck 	int imh_num;
1044835abedSTony Luck 
1054835abedSTony Luck 	imh_num = NUM_IMH_PER_SOCKET * topology_physical_package_id(mce->extcpu) +
1064835abedSTony Luck 			IMH_NUM(mce->apicid);
1074835abedSTony Luck 
1084835abedSTony Luck 	*id |= imh_num;
1094835abedSTony Luck }
1104835abedSTony Luck 
bff_set_cache_id(int cpu,int level,unsigned long * id)1114835abedSTony Luck static bool bff_set_cache_id(int cpu, int level, unsigned long *id)
1124835abedSTony Luck {
1134835abedSTony Luck 	int cacheid;
1144835abedSTony Luck 
1154835abedSTony Luck 	guard(cpus_read_lock)();
1164835abedSTony Luck 
1174835abedSTony Luck 	cacheid = get_cpu_cacheinfo_id(cpu, level);
1184835abedSTony Luck 	if (cacheid == -1) {
1194835abedSTony Luck 		pr_warn("Could not get L%d cache id for CPU %d\n", level, cpu);
1204835abedSTony Luck 		return false;
1214835abedSTony Luck 	}
1224835abedSTony Luck 
1234835abedSTony Luck 	*id |= cacheid;
1244835abedSTony Luck 
1254835abedSTony Luck 	return true;
1264835abedSTony Luck }
1274835abedSTony Luck 
1284835abedSTony Luck /*
1294835abedSTony Luck  * Cache IDs are only unique within a cache level.
1304835abedSTony Luck  * Include the MCA bank number so each BFF-capable hardware
1314835abedSTony Luck  * resource has a unique tracking ID.
1324835abedSTony Luck  */
1334835abedSTony Luck #define BFF_ID_BANK_FIELD	GENMASK(63, 32)
1344835abedSTony Luck 
bff_get_id(struct mce * mce)1354835abedSTony Luck static unsigned long bff_get_id(struct mce *mce)
1364835abedSTony Luck {
1374835abedSTony Luck 	unsigned long id = FIELD_PREP(BFF_ID_BANK_FIELD, mce->bank);
1384835abedSTony Luck 
1394835abedSTony Luck 	switch (bff_bank_types[mce->bank]) {
1404835abedSTony Luck 	case BFF_BANK_DCU:
1414835abedSTony Luck 	case BFF_BANK_DTLB:
1424835abedSTony Luck 		if (!bff_set_cache_id(mce->extcpu, 1, &id))
1434835abedSTony Luck 			return ULONG_MAX;
1444835abedSTony Luck 		break;
1454835abedSTony Luck 
1464835abedSTony Luck 	case BFF_BANK_MLC:
1474835abedSTony Luck 		if (!bff_set_cache_id(mce->extcpu, 2, &id))
1484835abedSTony Luck 			return ULONG_MAX;
1494835abedSTony Luck 		break;
1504835abedSTony Luck 
1514835abedSTony Luck 	case BFF_BANK_CCF:
1524835abedSTony Luck 		if (!bff_set_cache_id(mce->extcpu, 3, &id))
1534835abedSTony Luck 			return ULONG_MAX;
1544835abedSTony Luck 		break;
1554835abedSTony Luck 
1564835abedSTony Luck 	case BFF_BANK_HSF:
1574835abedSTony Luck 	case BFF_BANK_IOCACHE:
1584835abedSTony Luck 		bff_set_imh_id(mce, &id);
1594835abedSTony Luck 		break;
1604835abedSTony Luck 
1614835abedSTony Luck 	default:
1624835abedSTony Luck 		return ULONG_MAX;
1634835abedSTony Luck 	}
1644835abedSTony Luck 
1654835abedSTony Luck 	return id;
1664835abedSTony Luck }
1674835abedSTony Luck 
168*30713abbSTony Luck /*
169*30713abbSTony Luck  * Save current timestamp for bff_id. Return true if it is within
170*30713abbSTony Luck  * BFF_OVERFLOW_INTERVAL of previous timestamp for this bff_id.
171*30713abbSTony Luck  */
bff_overflow_is_frequent(unsigned long bff_id)172*30713abbSTony Luck static bool bff_overflow_is_frequent(unsigned long bff_id)
173*30713abbSTony Luck {
174*30713abbSTony Luck 	unsigned long now = jiffies, interval_end;
175*30713abbSTony Luck 	unsigned long *ts;
176*30713abbSTony Luck 
177*30713abbSTony Luck 	if (bff_id == ULONG_MAX)
178*30713abbSTony Luck 		return false;
179*30713abbSTony Luck 
180*30713abbSTony Luck 	ts = xa_load(&bff_bank_xa, bff_id);
181*30713abbSTony Luck 	if (!ts) {
182*30713abbSTony Luck 		ts = kzalloc_obj(*ts);
183*30713abbSTony Luck 		if (!ts) {
184*30713abbSTony Luck 			pr_warn("Failed to allocate timestamp for bitfix filter 0x%lx\n", bff_id);
185*30713abbSTony Luck 			return false;
186*30713abbSTony Luck 		}
187*30713abbSTony Luck 		if (xa_is_err(xa_store(&bff_bank_xa, bff_id, ts, GFP_KERNEL))) {
188*30713abbSTony Luck 			kfree(ts);
189*30713abbSTony Luck 			pr_warn("Failed to record timestamp for bitfix filter 0x%lx\n", bff_id);
190*30713abbSTony Luck 			return false;
191*30713abbSTony Luck 		}
192*30713abbSTony Luck 		*ts = now;
193*30713abbSTony Luck 
194*30713abbSTony Luck 		return false;
195*30713abbSTony Luck 	}
196*30713abbSTony Luck 
197*30713abbSTony Luck 	interval_end = *ts + BFF_OVERFLOW_INTERVAL;
198*30713abbSTony Luck 	*ts = now;
199*30713abbSTony Luck 
200*30713abbSTony Luck 	return time_before(now, interval_end);
201*30713abbSTony Luck }
202*30713abbSTony Luck 
bff_reset_and_report(struct mce * mce)203666192e1STony Luck static void bff_reset_and_report(struct mce *mce)
204666192e1STony Luck {
205666192e1STony Luck 	/* Reset bitfix filter using the CPU that logged the yellow status */
206666192e1STony Luck 	if (wrmsrq_on_cpu(mce->extcpu, MSR_MCx_BFF_CTL(mce->bank), MCI_BFF_RESET))
207666192e1STony Luck 		pr_warn("Failed to reset bitfix filter for CPU %d Bank %d\n",
208666192e1STony Luck 			mce->extcpu, mce->bank);
2094835abedSTony Luck 
210*30713abbSTony Luck 	/*
211*30713abbSTony Luck 	 * Use the unique id for the bitfix filter instance that overflowed and
212*30713abbSTony Luck 	 * check if this is a repeat within the BFF_OVERFLOW_INTERVAL. If it
213*30713abbSTony Luck 	 * is, then report at WARN severity as the user may want to take action.
214*30713abbSTony Luck 	 */
215*30713abbSTony Luck 	if (bff_overflow_is_frequent(bff_get_id(mce))) {
216*30713abbSTony Luck 		pr_warn_ratelimited(HW_ERR "Socket %d CPU %d Bank %d bitfix filter overflowed frequently\n",
217*30713abbSTony Luck 				    mce->socketid, mce->extcpu, mce->bank);
218*30713abbSTony Luck 	} else {
219*30713abbSTony Luck 		pr_notice_ratelimited(HW_ERR "Socket %d CPU %d Bank %d bitfix filter overflowed\n",
220*30713abbSTony Luck 				      mce->socketid, mce->extcpu, mce->bank);
221*30713abbSTony Luck 	}
222666192e1STony Luck }
223666192e1STony Luck 
bff_mce_notify(struct notifier_block * nb,unsigned long val,void * data)224666192e1STony Luck static int bff_mce_notify(struct notifier_block *nb, unsigned long val, void *data)
225666192e1STony Luck {
226666192e1STony Luck 	struct mce *mce = data;
227666192e1STony Luck 
228666192e1STony Luck 	/* TES is undefined for uncorrected errors. */
229666192e1STony Luck 	if (mce->status & MCI_STATUS_UC)
230666192e1STony Luck 		return NOTIFY_DONE;
231666192e1STony Luck 
232666192e1STony Luck 	if (MCI_STATUS_TES(mce->status) != MCI_STATUS_TES_YELLOW)
233666192e1STony Luck 		return NOTIFY_DONE;
234666192e1STony Luck 
235666192e1STony Luck 	if (mce->bank >= MAX_NR_BANKS || bff_bank_types[mce->bank] == BFF_BANK_NONE)
236666192e1STony Luck 		return NOTIFY_DONE;
237666192e1STony Luck 
238666192e1STony Luck 	bff_reset_and_report(mce);
239666192e1STony Luck 
240666192e1STony Luck 	return NOTIFY_DONE;
241666192e1STony Luck }
242666192e1STony Luck 
243666192e1STony Luck static struct notifier_block bff_notifier = {
244666192e1STony Luck 	.notifier_call	= bff_mce_notify,
245666192e1STony Luck 	.priority	= MCE_PRIO_EDAC,
246666192e1STony Luck };
247666192e1STony Luck 
bff_init(void)248d4ffdf94STony Luck static int __init bff_init(void)
249d4ffdf94STony Luck {
250a2808f91STony Luck 	const struct x86_cpu_id *m;
251d4ffdf94STony Luck 	u64 core_caps, mcg_cap;
252d4ffdf94STony Luck 
253d4ffdf94STony Luck 	if (!cpu_feature_enabled(X86_FEATURE_MCA))
254d4ffdf94STony Luck 		return -ENODEV;
255d4ffdf94STony Luck 	rdmsrq(MSR_IA32_MCG_CAP, mcg_cap);
256d4ffdf94STony Luck 	if (!(mcg_cap & MCG_TES_P))
257d4ffdf94STony Luck 		return -ENODEV;
258d4ffdf94STony Luck 
259d4ffdf94STony Luck 	if (!cpu_feature_enabled(X86_FEATURE_CORE_CAPABILITIES))
260d4ffdf94STony Luck 		return -ENODEV;
261d4ffdf94STony Luck 	rdmsrq(MSR_IA32_CORE_CAPS, core_caps);
262d4ffdf94STony Luck 	if (!(core_caps & MSR_IA32_CORE_CAPS_BFF_RESET))
263d4ffdf94STony Luck 		return -ENODEV;
264d4ffdf94STony Luck 
265a2808f91STony Luck 	m = x86_match_cpu(bff_cpu_ids);
266a2808f91STony Luck 	if (!m) {
267a2808f91STony Luck 		pr_info("CPU model not supported by the bitfix filter reset driver\n");
268a2808f91STony Luck 		return -ENODEV;
269a2808f91STony Luck 	}
270a2808f91STony Luck 
271a2808f91STony Luck 	bff_bank_types = (const enum bff_bank_type *)m->driver_data;
272a2808f91STony Luck 
273666192e1STony Luck 	mce_register_decode_chain(&bff_notifier);
274666192e1STony Luck 
275d4ffdf94STony Luck 	return 0;
276d4ffdf94STony Luck }
277d4ffdf94STony Luck 
bff_exit(void)278666192e1STony Luck static void __exit bff_exit(void)
279666192e1STony Luck {
280*30713abbSTony Luck 	unsigned long bff_id;
281*30713abbSTony Luck 	unsigned long *ts;
282*30713abbSTony Luck 
283666192e1STony Luck 	mce_unregister_decode_chain(&bff_notifier);
284*30713abbSTony Luck 
285*30713abbSTony Luck 	xa_for_each(&bff_bank_xa, bff_id, ts)
286*30713abbSTony Luck 		kfree(ts);
287*30713abbSTony Luck 	xa_destroy(&bff_bank_xa);
288666192e1STony Luck }
289666192e1STony Luck 
290d4ffdf94STony Luck module_init(bff_init);
291666192e1STony Luck module_exit(bff_exit);
292d4ffdf94STony Luck 
293d4ffdf94STony Luck MODULE_LICENSE("GPL");
294d4ffdf94STony Luck MODULE_AUTHOR("Tony Luck");
295d4ffdf94STony Luck MODULE_DESCRIPTION("Intel bitfix filter reset driver");
296