FROMLIST: riscv: errata: Add ERRATA_THEAD_WRITE_ONCE fixup
The early version of XuanTie C910 core has a store merge buffer delay problem. The store merge buffer could improve the store queue performance by merging multi-store requests, but when there are not continued store requests, the prior single store request would be waiting in the store queue for a long time. That would cause significant problems for communication between multi-cores. This problem was found on sg2042 & th1520 platforms with the qspinlock lock torture test. So appending a fence w.o could immediately flush the store merge buffer and let other cores see the write result. This will apply the WRITE_ONCE errata to handle the non-standard behavior via appending a fence w.o instruction for WRITE_ONCE(). This problem is only observed on the sg2042 hardware platform by running the lock_torture test program for half an hour. The problem was not found in the user space application, because interrupt can break the livelock. Reviewed-by: Leonardo Bras <leobras@redhat.com> Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org> Link: https://lore.kernel.org/r/20250713155321.2064856-3-guoren@kernel.org Signed-off-by: Han Gao <rabenda.cn@gmail.com>
This commit is contained in:
@@ -130,4 +130,21 @@ config ERRATA_THEAD_GHOSTWRITE
|
||||
|
||||
If you don't know what to do here, say "Y".
|
||||
|
||||
config ERRATA_THEAD_WRITE_ONCE
|
||||
bool "Apply T-Head WRITE_ONCE errata"
|
||||
depends on ERRATA_THEAD
|
||||
default y
|
||||
help
|
||||
The early version of T-Head C9xx cores of sg2042 & th1520 have a store
|
||||
merge buffer delay problem. The store merge buffer could improve the
|
||||
store queue performance by merging multi-store requests, but when there
|
||||
are no continued store requests, the prior single store request would be
|
||||
waiting in the store queue for a long time. That would cause signifi-
|
||||
cant problems for communication between multi-cores. Appending a
|
||||
fence w.o could immediately flush the store merge buffer and let other
|
||||
cores see the write result.
|
||||
|
||||
This will apply the WRITE_ONCE errata to handle the non-standard beh-
|
||||
avior via appending a fence w.o instruction for WRITE_ONCE().
|
||||
|
||||
endmenu # "CPU errata selection"
|
||||
|
||||
@@ -168,6 +168,23 @@ static bool errata_probe_ghostwrite(unsigned int stage,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool errata_probe_write_once(unsigned int stage,
|
||||
unsigned long arch_id, unsigned long impid)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ERRATA_THEAD_WRITE_ONCE))
|
||||
return false;
|
||||
|
||||
/* target-c9xx cores report arch_id and impid as 0 */
|
||||
if (arch_id != 0 || impid != 0)
|
||||
return false;
|
||||
|
||||
if (stage == RISCV_ALTERNATIVES_BOOT ||
|
||||
stage == RISCV_ALTERNATIVES_MODULE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32 thead_errata_probe(unsigned int stage,
|
||||
unsigned long archid, unsigned long impid)
|
||||
{
|
||||
@@ -183,6 +200,9 @@ static u32 thead_errata_probe(unsigned int stage,
|
||||
|
||||
errata_probe_ghostwrite(stage, archid, impid);
|
||||
|
||||
if (errata_probe_write_once(stage, archid, impid))
|
||||
cpu_req_errata |= BIT(ERRATA_THEAD_WRITE_ONCE);
|
||||
|
||||
return cpu_req_errata;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#define ERRATA_THEAD_MAE 0
|
||||
#define ERRATA_THEAD_PMU 1
|
||||
#define ERRATA_THEAD_GHOSTWRITE 2
|
||||
#define ERRATA_THEAD_NUMBER 3
|
||||
#define ERRATA_THEAD_WRITE_ONCE 3
|
||||
#define ERRATA_THEAD_NUMBER 4
|
||||
#endif
|
||||
|
||||
#endif /* ASM_ERRATA_LIST_VENDORS_H */
|
||||
|
||||
34
arch/riscv/include/asm/rwonce.h
Normal file
34
arch/riscv/include/asm/rwonce.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __ASM_RWONCE_H
|
||||
#define __ASM_RWONCE_H
|
||||
|
||||
#include <linux/compiler_types.h>
|
||||
#include <asm/alternative-macros.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
#include <asm/errata_list_vendors.h>
|
||||
|
||||
#if defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE)
|
||||
|
||||
#define write_once_fence() \
|
||||
do { \
|
||||
asm volatile(ALTERNATIVE( \
|
||||
"nop", \
|
||||
"fence w, o", \
|
||||
THEAD_VENDOR_ID, \
|
||||
ERRATA_THEAD_WRITE_ONCE, \
|
||||
CONFIG_ERRATA_THEAD_WRITE_ONCE) \
|
||||
: : : "memory"); \
|
||||
} while (0)
|
||||
|
||||
#define __WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
*(volatile typeof(x) *)&(x) = (val); \
|
||||
write_once_fence(); \
|
||||
} while (0)
|
||||
|
||||
#endif /* defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE) */
|
||||
|
||||
#include <asm-generic/rwonce.h>
|
||||
|
||||
#endif /* __ASM_RWONCE_H */
|
||||
@@ -50,10 +50,12 @@
|
||||
__READ_ONCE(x); \
|
||||
})
|
||||
|
||||
#ifndef __WRITE_ONCE
|
||||
#define __WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
*(volatile typeof(x) *)&(x) = (val); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define WRITE_ONCE(x, val) \
|
||||
do { \
|
||||
|
||||
Reference in New Issue
Block a user