Skip to content

Commit

Permalink
riscv: Add qspinlock support
Browse files Browse the repository at this point in the history
In order to produce a generic kernel, a user can select
CONFIG_COMBO_SPINLOCKS which will fallback at runtime to the ticket
spinlock implementation if Zabha or Ziccrse are not present.

Note that we can't use alternatives here because the discovery of
extensions is done too late and we need to start with the qspinlock
implementation because the ticket spinlock implementation would pollute
the spinlock value, so let's use static keys.

This is largely based on Guo's work and Leonardo reviews at [1].

Link: https://lore.kernel.org/linux-riscv/20231225125847.2778638-1-guoren@kernel.org/ [1]
Signed-off-by: Guo Ren <guoren@kernel.org>
Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com>
Reviewed-by: Andrea Parri <parri.andrea@gmail.com>
Link: https://lore.kernel.org/r/20241103145153.105097-14-alexghiti@rivosinc.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
  • Loading branch information
Alexandre Ghiti authored and Palmer Dabbelt committed Nov 11, 2024
1 parent 447b2af commit ab83647
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
| openrisc: | ok |
| parisc: | TODO |
| powerpc: | ok |
| riscv: | TODO |
| riscv: | ok |
| s390: | TODO |
| sh: | TODO |
| sparc: | ok |
Expand Down
34 changes: 34 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ config RISCV
select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
select ARCH_WANTS_NO_INSTR
select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE
select ARCH_WEAK_RELEASE_ACQUIRE if ARCH_USE_QUEUED_SPINLOCKS
select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU
select BUILDTIME_TABLE_SORT if MMU
select CLINT_TIMER if RISCV_M_MODE
Expand Down Expand Up @@ -507,6 +508,39 @@ config NODES_SHIFT
Specify the maximum number of NUMA Nodes available on the target
system. Increases memory reserved to accommodate various tables.

choice
prompt "RISC-V spinlock type"
default RISCV_COMBO_SPINLOCKS

config RISCV_TICKET_SPINLOCKS
bool "Using ticket spinlock"

config RISCV_QUEUED_SPINLOCKS
bool "Using queued spinlock"
depends on SMP && MMU && NONPORTABLE
select ARCH_USE_QUEUED_SPINLOCKS
help
The queued spinlock implementation requires the forward progress
guarantee of cmpxchg()/xchg() atomic operations: CAS with Zabha or
LR/SC with Ziccrse provide such guarantee.

Select this if and only if Zabha or Ziccrse is available on your
platform, RISCV_QUEUED_SPINLOCKS must not be selected for platforms
without one of those extensions.

If unsure, select RISCV_COMBO_SPINLOCKS, which will use qspinlocks
when supported and otherwise ticket spinlocks.

config RISCV_COMBO_SPINLOCKS
bool "Using combo spinlock"
depends on SMP && MMU
select ARCH_USE_QUEUED_SPINLOCKS
help
Embed both queued spinlock and ticket lock so that the spinlock
implementation can be chosen at runtime.

endchoice

config RISCV_ALTERNATIVE
bool
depends on !XIP_KERNEL
Expand Down
4 changes: 3 additions & 1 deletion arch/riscv/include/asm/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ generic-y += early_ioremap.h
generic-y += flat.h
generic-y += kvm_para.h
generic-y += mmzone.h
generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += spinlock.h
generic-y += spinlock_types.h
generic-y += ticket_spinlock.h
generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
generic-y += user.h
generic-y += vmlinux.lds.h
47 changes: 47 additions & 0 deletions arch/riscv/include/asm/spinlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __ASM_RISCV_SPINLOCK_H
#define __ASM_RISCV_SPINLOCK_H

#ifdef CONFIG_RISCV_COMBO_SPINLOCKS
#define _Q_PENDING_LOOPS (1 << 9)

#define __no_arch_spinlock_redefine
#include <asm/ticket_spinlock.h>
#include <asm/qspinlock.h>
#include <asm/jump_label.h>

/*
* TODO: Use an alternative instead of a static key when we are able to parse
* the extensions string earlier in the boot process.
*/
DECLARE_STATIC_KEY_TRUE(qspinlock_key);

#define SPINLOCK_BASE_DECLARE(op, type, type_lock) \
static __always_inline type arch_spin_##op(type_lock lock) \
{ \
if (static_branch_unlikely(&qspinlock_key)) \
return queued_spin_##op(lock); \
return ticket_spin_##op(lock); \
}

SPINLOCK_BASE_DECLARE(lock, void, arch_spinlock_t *)
SPINLOCK_BASE_DECLARE(unlock, void, arch_spinlock_t *)
SPINLOCK_BASE_DECLARE(is_locked, int, arch_spinlock_t *)
SPINLOCK_BASE_DECLARE(is_contended, int, arch_spinlock_t *)
SPINLOCK_BASE_DECLARE(trylock, bool, arch_spinlock_t *)
SPINLOCK_BASE_DECLARE(value_unlocked, int, arch_spinlock_t)

#elif defined(CONFIG_RISCV_QUEUED_SPINLOCKS)

#include <asm/qspinlock.h>

#else

#include <asm/ticket_spinlock.h>

#endif

#include <asm/qrwlock.h>

#endif /* __ASM_RISCV_SPINLOCK_H */
37 changes: 37 additions & 0 deletions arch/riscv/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,42 @@ static void __init parse_dtb(void)
#endif
}

#if defined(CONFIG_RISCV_COMBO_SPINLOCKS)
DEFINE_STATIC_KEY_TRUE(qspinlock_key);
EXPORT_SYMBOL(qspinlock_key);
#endif

static void __init riscv_spinlock_init(void)
{
char *using_ext = NULL;

if (IS_ENABLED(CONFIG_RISCV_TICKET_SPINLOCKS)) {
pr_info("Ticket spinlock: enabled\n");
return;
}

if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) &&
riscv_isa_extension_available(NULL, ZABHA) &&
riscv_isa_extension_available(NULL, ZACAS)) {
using_ext = "using Zabha";
} else if (riscv_isa_extension_available(NULL, ZICCRSE)) {
using_ext = "using Ziccrse";
}
#if defined(CONFIG_RISCV_COMBO_SPINLOCKS)
else {
static_branch_disable(&qspinlock_key);
pr_info("Ticket spinlock: enabled\n");
return;
}
#endif

if (!using_ext)
pr_err("Queued spinlock without Zabha or Ziccrse");
else
pr_info("Queued spinlock %s: enabled\n", using_ext);
}

extern void __init init_rt_signal_env(void);

void __init setup_arch(char **cmdline_p)
Expand Down Expand Up @@ -297,6 +333,7 @@ void __init setup_arch(char **cmdline_p)
riscv_set_dma_cache_alignment();

riscv_user_isa_enable();
riscv_spinlock_init();
}

bool arch_cpu_is_hotpluggable(int cpu)
Expand Down
2 changes: 2 additions & 0 deletions include/asm-generic/qspinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock)
}
#endif

#ifndef __no_arch_spinlock_redefine
/*
* Remapping spinlock architecture specific functions to the corresponding
* queued spinlock functions.
Expand All @@ -146,5 +147,6 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock)
#define arch_spin_lock(l) queued_spin_lock(l)
#define arch_spin_trylock(l) queued_spin_trylock(l)
#define arch_spin_unlock(l) queued_spin_unlock(l)
#endif

#endif /* __ASM_GENERIC_QSPINLOCK_H */
2 changes: 2 additions & 0 deletions include/asm-generic/ticket_spinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ static __always_inline int ticket_spin_is_contended(arch_spinlock_t *lock)
return (s16)((val >> 16) - (val & 0xffff)) > 1;
}

#ifndef __no_arch_spinlock_redefine
/*
* Remapping spinlock architecture specific functions to the corresponding
* ticket spinlock functions.
Expand All @@ -99,5 +100,6 @@ static __always_inline int ticket_spin_is_contended(arch_spinlock_t *lock)
#define arch_spin_lock(l) ticket_spin_lock(l)
#define arch_spin_trylock(l) ticket_spin_trylock(l)
#define arch_spin_unlock(l) ticket_spin_unlock(l)
#endif

#endif /* __ASM_GENERIC_TICKET_SPINLOCK_H */

0 comments on commit ab83647

Please sign in to comment.