Skip to content

Commit

Permalink
riscv: qspinlock: errata: Add ERRATA_THEAD_WRITE_ONCE fixup
Browse files Browse the repository at this point in the history
The early version of T-Head C9xx cores 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 <[email protected]>
Signed-off-by: Guo Ren <[email protected]>
Signed-off-by: Guo Ren <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Han Gao <[email protected]>
  • Loading branch information
guoren83 authored and RevySR committed Jan 13, 2025
1 parent e17c978 commit 94aa5e1
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 1 deletion.
19 changes: 19 additions & 0 deletions arch/riscv/Kconfig.errata
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,23 @@ config ERRATA_THEAD_PMU

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().

If you don't know what to do here, say "Y".

endmenu # "CPU errata selection"
20 changes: 20 additions & 0 deletions arch/riscv/errata/thead/errata.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ static bool errata_probe_pmu(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)
{
Expand All @@ -155,6 +172,9 @@ static u32 thead_errata_probe(unsigned int stage,
if (errata_probe_pmu(stage, archid, impid))
cpu_req_errata |= BIT(ERRATA_THEAD_PMU);

if (errata_probe_write_once(stage, archid, impid))
cpu_req_errata |= BIT(ERRATA_THEAD_WRITE_ONCE);

return cpu_req_errata;
}

Expand Down
33 changes: 33 additions & 0 deletions arch/riscv/include/asm/rwonce.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* 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>

#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 */
3 changes: 2 additions & 1 deletion arch/riscv/include/asm/vendorid_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
#ifdef CONFIG_ERRATA_THEAD
#define ERRATA_THEAD_MAE 0
#define ERRATA_THEAD_PMU 1
#define ERRATA_THEAD_NUMBER 2
#define ERRATA_THEAD_WRITE_ONCE 2
#define ERRATA_THEAD_NUMBER 3
#endif

#endif
2 changes: 2 additions & 0 deletions include/asm-generic/rwonce.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 { \
Expand Down

0 comments on commit 94aa5e1

Please sign in to comment.