Merge patch series "RISC-V: Apply Zicboz to clear_page"

Andrew Jones <ajones@ventanamicro.com> says:

When the Zicboz extension is available we can more rapidly zero naturally
aligned Zicboz block sized chunks of memory. As pages are always page
aligned and are larger than any Zicboz block size will be, then
clear_page() appears to be a good candidate for the extension. While cycle
count and energy consumption should also be considered, we can be pretty
certain that implementing clear_page() with the Zicboz extension is a win
by comparing the new dynamic instruction count with its current count[1].
Doing so we see that the new count is just over a quarter of the old count
(see patch6's commit message for more details).

For those of you who reviewed v1[2], you may be looking for the memset()
patches. As pointed out in v1, and a couple follow-up emails, it's not
clear that patching memset() is a win yet. When I get a chance to test
on real hardware with a comprehensive benchmark collection then I can
post the memset() patches separately (assuming the benchmarks show it's
worthwhile).

* b4-shazam-merge:
  RISC-V: KVM: Expose Zicboz to the guest
  RISC-V: KVM: Provide UAPI for Zicboz block size
  RISC-V: Use Zicboz in clear_page when available
  RISC-V: cpufeatures: Put the upper 16 bits of patch ID to work
  RISC-V: Add Zicboz detection and block size parsing
  dt-bindings: riscv: Document cboz-block-size
  RISC-V: Factor out body of riscv_init_cbom_blocksize loop
  RISC-V: alternatives: Support patching multiple insns in assembly

Link: https://lore.kernel.org/r/20230224162631.405473-1-ajones@ventanamicro.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
Palmer Dabbelt 2023-03-15 07:11:08 -07:00
commit 4b740779ac
No known key found for this signature in database
GPG Key ID: 2E1319F35FBB1889
16 changed files with 219 additions and 36 deletions

View File

@ -72,6 +72,11 @@ properties:
description:
The blocksize in bytes for the Zicbom cache operations.
riscv,cboz-block-size:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The blocksize in bytes for the Zicboz cache operations.
riscv,isa:
description:
Identifies the specific RISC-V instruction set architecture

View File

@ -476,6 +476,19 @@ config RISCV_ISA_ZICBOM
If you don't know what to do here, say Y.
config RISCV_ISA_ZICBOZ
bool "Zicboz extension support for faster zeroing of memory"
depends on !XIP_KERNEL && MMU
select RISCV_ALTERNATIVE
default y
help
Enable the use of the ZICBOZ extension (cbo.zero instruction)
when available.
The Zicboz extension is used for faster zeroing of memory.
If you don't know what to do here, say Y.
config TOOLCHAIN_HAS_ZIHINTPAUSE
bool
default y

View File

@ -14,7 +14,7 @@
.4byte \patch_id
.endm
.macro ALT_NEW_CONTENT vendor_id, patch_id, enable = 1, new_c : vararg
.macro ALT_NEW_CONTENT vendor_id, patch_id, enable = 1, new_c
.if \enable
.pushsection .alternative, "a"
ALT_ENTRY 886b, 888f, \vendor_id, \patch_id, 889f - 888f
@ -41,13 +41,13 @@
\old_c
.option pop
887 :
ALT_NEW_CONTENT \vendor_id, \patch_id, \enable, \new_c
ALT_NEW_CONTENT \vendor_id, \patch_id, \enable, "\new_c"
.endm
.macro ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, patch_id_1, enable_1, \
new_c_2, vendor_id_2, patch_id_2, enable_2
ALTERNATIVE_CFG "\old_c", "\new_c_1", \vendor_id_1, \patch_id_1, \enable_1
ALT_NEW_CONTENT \vendor_id_2, \patch_id_2, \enable_2, \new_c_2
ALT_NEW_CONTENT \vendor_id_2, \patch_id_2, \enable_2, "\new_c_2"
.endm
#define __ALTERNATIVE_CFG(...) ALTERNATIVE_CFG __VA_ARGS__

View File

@ -13,10 +13,14 @@
#ifdef CONFIG_RISCV_ALTERNATIVE
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <asm/hwcap.h>
#define PATCH_ID_CPUFEATURE_ID(p) lower_16_bits(p)
#define PATCH_ID_CPUFEATURE_VALUE(p) upper_16_bits(p)
#define RISCV_ALTERNATIVES_BOOT 0 /* alternatives applied during regular boot */
#define RISCV_ALTERNATIVES_MODULE 1 /* alternatives applied during module-init */
#define RISCV_ALTERNATIVES_EARLY_BOOT 2 /* alternatives applied before mmu start */

View File

@ -50,7 +50,8 @@ void flush_icache_mm(struct mm_struct *mm, bool local);
#endif /* CONFIG_SMP */
extern unsigned int riscv_cbom_block_size;
void riscv_init_cbom_blocksize(void);
extern unsigned int riscv_cboz_block_size;
void riscv_init_cbo_blocksizes(void);
#ifdef CONFIG_RISCV_DMA_NONCOHERENT
void riscv_noncoherent_supported(void);

View File

@ -43,6 +43,7 @@
#define RISCV_ISA_EXT_ZICBOM 31
#define RISCV_ISA_EXT_ZIHINTPAUSE 32
#define RISCV_ISA_EXT_SVNAPOT 33
#define RISCV_ISA_EXT_ZICBOZ 34
#define RISCV_ISA_EXT_MAX 64
#define RISCV_ISA_EXT_NAME_LEN_MAX 32

View File

@ -192,4 +192,8 @@
INSN_I(OPCODE_MISC_MEM, FUNC3(2), __RD(0), \
RS1(base), SIMM12(2))
#define CBO_zero(base) \
INSN_I(OPCODE_MISC_MEM, FUNC3(2), __RD(0), \
RS1(base), SIMM12(4))
#endif /* __ASM_INSN_DEF_H */

View File

@ -44,10 +44,14 @@
#ifndef __ASSEMBLY__
#ifdef CONFIG_RISCV_ISA_ZICBOZ
void clear_page(void *page);
#else
#define clear_page(pgaddr) memset((pgaddr), 0, PAGE_SIZE)
#endif
#define copy_page(to, from) memcpy((to), (from), PAGE_SIZE)
#define clear_user_page(pgaddr, vaddr, page) memset((pgaddr), 0, PAGE_SIZE)
#define clear_user_page(pgaddr, vaddr, page) clear_page(pgaddr)
#define copy_user_page(vto, vfrom, vaddr, topg) \
memcpy((vto), (vfrom), PAGE_SIZE)

View File

@ -52,6 +52,7 @@ struct kvm_riscv_config {
unsigned long mvendorid;
unsigned long marchid;
unsigned long mimpid;
unsigned long zicboz_block_size;
};
/* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
@ -105,6 +106,7 @@ enum KVM_RISCV_ISA_EXT_ID {
KVM_RISCV_ISA_EXT_SVINVAL,
KVM_RISCV_ISA_EXT_ZIHINTPAUSE,
KVM_RISCV_ISA_EXT_ZICBOM,
KVM_RISCV_ISA_EXT_ZICBOZ,
KVM_RISCV_ISA_EXT_MAX,
};

View File

@ -186,6 +186,7 @@ arch_initcall(riscv_cpuinfo_init);
*/
static struct riscv_isa_ext_data isa_ext_arr[] = {
__RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM),
__RISCV_ISA_EXT_DATA(zicboz, RISCV_ISA_EXT_ZICBOZ),
__RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
__RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),

View File

@ -74,6 +74,15 @@ static bool riscv_isa_extension_check(int id)
return false;
}
return true;
case RISCV_ISA_EXT_ZICBOZ:
if (!riscv_cboz_block_size) {
pr_err("Zicboz detected in ISA string, but no cboz-block-size found\n");
return false;
} else if (!is_power_of_2(riscv_cboz_block_size)) {
pr_err("cboz-block-size present, but is not a power-of-2\n");
return false;
}
return true;
}
return true;
@ -223,6 +232,7 @@ void __init riscv_fill_hwcap(void)
SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
SET_ISA_EXT_MAP("zbb", RISCV_ISA_EXT_ZBB);
SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
SET_ISA_EXT_MAP("zicboz", RISCV_ISA_EXT_ZICBOZ);
SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
}
#undef SET_ISA_EXT_MAP
@ -265,12 +275,46 @@ void __init riscv_fill_hwcap(void)
}
#ifdef CONFIG_RISCV_ALTERNATIVE
/*
* Alternative patch sites consider 48 bits when determining when to patch
* the old instruction sequence with the new. These bits are broken into a
* 16-bit vendor ID and a 32-bit patch ID. A non-zero vendor ID means the
* patch site is for an erratum, identified by the 32-bit patch ID. When
* the vendor ID is zero, the patch site is for a cpufeature. cpufeatures
* further break down patch ID into two 16-bit numbers. The lower 16 bits
* are the cpufeature ID and the upper 16 bits are used for a value specific
* to the cpufeature and patch site. If the upper 16 bits are zero, then it
* implies no specific value is specified. cpufeatures that want to control
* patching on a per-site basis will provide non-zero values and implement
* checks here. The checks return true when patching should be done, and
* false otherwise.
*/
static bool riscv_cpufeature_patch_check(u16 id, u16 value)
{
if (!value)
return true;
switch (id) {
case RISCV_ISA_EXT_ZICBOZ:
/*
* Zicboz alternative applications provide the maximum
* supported block size order, or zero when it doesn't
* matter. If the current block size exceeds the maximum,
* then the alternative cannot be applied.
*/
return riscv_cboz_block_size <= (1U << value);
}
return false;
}
void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
struct alt_entry *end,
unsigned int stage)
{
struct alt_entry *alt;
void *oldptr, *altptr;
u16 id, value;
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
return;
@ -278,13 +322,19 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
for (alt = begin; alt < end; alt++) {
if (alt->vendor_id != 0)
continue;
if (alt->patch_id >= RISCV_ISA_EXT_MAX) {
WARN(1, "This extension id:%d is not in ISA extension list",
alt->patch_id);
id = PATCH_ID_CPUFEATURE_ID(alt->patch_id);
if (id >= RISCV_ISA_EXT_MAX) {
WARN(1, "This extension id:%d is not in ISA extension list", id);
continue;
}
if (!__riscv_isa_extension_available(NULL, alt->patch_id))
if (!__riscv_isa_extension_available(NULL, id))
continue;
value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
if (!riscv_cpufeature_patch_check(id, value))
continue;
oldptr = ALT_OLD_PTR(alt);

View File

@ -297,7 +297,7 @@ void __init setup_arch(char **cmdline_p)
setup_smp();
#endif
riscv_init_cbom_blocksize();
riscv_init_cbo_blocksizes();
riscv_fill_hwcap();
apply_boot_alternatives();
if (IS_ENABLED(CONFIG_RISCV_ISA_ZICBOM) &&

View File

@ -63,6 +63,7 @@ static const unsigned long kvm_isa_ext_arr[] = {
KVM_ISA_EXT_ARR(SVPBMT),
KVM_ISA_EXT_ARR(ZIHINTPAUSE),
KVM_ISA_EXT_ARR(ZICBOM),
KVM_ISA_EXT_ARR(ZICBOZ),
};
static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext)
@ -283,6 +284,11 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu,
return -EINVAL;
reg_val = riscv_cbom_block_size;
break;
case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size):
if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOZ))
return -EINVAL;
reg_val = riscv_cboz_block_size;
break;
case KVM_REG_RISCV_CONFIG_REG(mvendorid):
reg_val = vcpu->arch.mvendorid;
break;
@ -354,6 +360,8 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu,
break;
case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size):
return -EOPNOTSUPP;
case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size):
return -EOPNOTSUPP;
case KVM_REG_RISCV_CONFIG_REG(mvendorid):
if (!vcpu->arch.ran_atleast_once)
vcpu->arch.mvendorid = reg_val;
@ -865,6 +873,9 @@ static void kvm_riscv_vcpu_update_config(const unsigned long *isa)
if (riscv_isa_extension_available(isa, ZICBOM))
henvcfg |= (ENVCFG_CBIE | ENVCFG_CBCFE);
if (riscv_isa_extension_available(isa, ZICBOZ))
henvcfg |= ENVCFG_CBZE;
csr_write(CSR_HENVCFG, henvcfg);
#ifdef CONFIG_32BIT
csr_write(CSR_HENVCFGH, henvcfg >> 32);

View File

@ -8,5 +8,6 @@ lib-y += strlen.o
lib-y += strncmp.o
lib-$(CONFIG_MMU) += uaccess.o
lib-$(CONFIG_64BIT) += tishift.o
lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o

View File

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Ventana Micro Systems Inc.
*/
#include <linux/linkage.h>
#include <asm/asm.h>
#include <asm/alternative-macros.h>
#include <asm-generic/export.h>
#include <asm/hwcap.h>
#include <asm/insn-def.h>
#include <asm/page.h>
#define CBOZ_ALT(order, old, new) \
ALTERNATIVE(old, new, 0, \
((order) << 16) | RISCV_ISA_EXT_ZICBOZ, \
CONFIG_RISCV_ISA_ZICBOZ)
/* void clear_page(void *page) */
SYM_FUNC_START(clear_page)
li a2, PAGE_SIZE
/*
* If Zicboz isn't present, or somehow has a block
* size larger than 4K, then fallback to memset.
*/
CBOZ_ALT(12, "j .Lno_zicboz", "nop")
lw a1, riscv_cboz_block_size
add a2, a0, a2
.Lzero_loop:
CBO_zero(a0)
add a0, a0, a1
CBOZ_ALT(11, "bltu a0, a2, .Lzero_loop; ret", "nop; nop")
CBO_zero(a0)
add a0, a0, a1
CBOZ_ALT(10, "bltu a0, a2, .Lzero_loop; ret", "nop; nop")
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBOZ_ALT(9, "bltu a0, a2, .Lzero_loop; ret", "nop; nop")
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBOZ_ALT(8, "bltu a0, a2, .Lzero_loop; ret", "nop; nop")
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
CBO_zero(a0)
add a0, a0, a1
bltu a0, a2, .Lzero_loop
ret
.Lno_zicboz:
li a1, 0
tail __memset
SYM_FUNC_END(clear_page)
EXPORT_SYMBOL(clear_page)

View File

@ -100,36 +100,48 @@ void flush_icache_pte(pte_t pte)
unsigned int riscv_cbom_block_size;
EXPORT_SYMBOL_GPL(riscv_cbom_block_size);
void riscv_init_cbom_blocksize(void)
unsigned int riscv_cboz_block_size;
EXPORT_SYMBOL_GPL(riscv_cboz_block_size);
static void cbo_get_block_size(struct device_node *node,
const char *name, u32 *block_size,
unsigned long *first_hartid)
{
unsigned long hartid;
u32 val;
if (riscv_of_processor_hartid(node, &hartid))
return;
if (of_property_read_u32(node, name, &val))
return;
if (!*block_size) {
*block_size = val;
*first_hartid = hartid;
} else if (*block_size != val) {
pr_warn("%s mismatched between harts %lu and %lu\n",
name, *first_hartid, hartid);
}
}
void riscv_init_cbo_blocksizes(void)
{
unsigned long cbom_hartid, cboz_hartid;
u32 cbom_block_size = 0, cboz_block_size = 0;
struct device_node *node;
unsigned long cbom_hartid;
u32 val, probed_block_size;
int ret;
probed_block_size = 0;
for_each_of_cpu_node(node) {
unsigned long hartid;
ret = riscv_of_processor_hartid(node, &hartid);
if (ret)
continue;
/* set block-size for cbom extension if available */
ret = of_property_read_u32(node, "riscv,cbom-block-size", &val);
if (ret)
continue;
if (!probed_block_size) {
probed_block_size = val;
cbom_hartid = hartid;
} else {
if (probed_block_size != val)
pr_warn("cbom-block-size mismatched between harts %lu and %lu\n",
cbom_hartid, hartid);
}
/* set block-size for cbom and/or cboz extension if available */
cbo_get_block_size(node, "riscv,cbom-block-size",
&cbom_block_size, &cbom_hartid);
cbo_get_block_size(node, "riscv,cboz-block-size",
&cboz_block_size, &cboz_hartid);
}
if (probed_block_size)
riscv_cbom_block_size = probed_block_size;
if (cbom_block_size)
riscv_cbom_block_size = cbom_block_size;
if (cboz_block_size)
riscv_cboz_block_size = cboz_block_size;
}