Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

riscv: Add qspinlock support

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>

authored by

Alexandre Ghiti and committed by
Palmer Dabbelt
ab83647f 447b2afb

+126 -2
+1 -1
Documentation/features/locking/queued-spinlocks/arch-support.txt
··· 20 20 | openrisc: | ok | 21 21 | parisc: | TODO | 22 22 | powerpc: | ok | 23 - | riscv: | TODO | 23 + | riscv: | ok | 24 24 | s390: | TODO | 25 25 | sh: | TODO | 26 26 | sparc: | ok |
+34
arch/riscv/Kconfig
··· 82 82 select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP 83 83 select ARCH_WANTS_NO_INSTR 84 84 select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE 85 + select ARCH_WEAK_RELEASE_ACQUIRE if ARCH_USE_QUEUED_SPINLOCKS 85 86 select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU 86 87 select BUILDTIME_TABLE_SORT if MMU 87 88 select CLINT_TIMER if RISCV_M_MODE ··· 507 506 help 508 507 Specify the maximum number of NUMA Nodes available on the target 509 508 system. Increases memory reserved to accommodate various tables. 509 + 510 + choice 511 + prompt "RISC-V spinlock type" 512 + default RISCV_COMBO_SPINLOCKS 513 + 514 + config RISCV_TICKET_SPINLOCKS 515 + bool "Using ticket spinlock" 516 + 517 + config RISCV_QUEUED_SPINLOCKS 518 + bool "Using queued spinlock" 519 + depends on SMP && MMU && NONPORTABLE 520 + select ARCH_USE_QUEUED_SPINLOCKS 521 + help 522 + The queued spinlock implementation requires the forward progress 523 + guarantee of cmpxchg()/xchg() atomic operations: CAS with Zabha or 524 + LR/SC with Ziccrse provide such guarantee. 525 + 526 + Select this if and only if Zabha or Ziccrse is available on your 527 + platform, RISCV_QUEUED_SPINLOCKS must not be selected for platforms 528 + without one of those extensions. 529 + 530 + If unsure, select RISCV_COMBO_SPINLOCKS, which will use qspinlocks 531 + when supported and otherwise ticket spinlocks. 532 + 533 + config RISCV_COMBO_SPINLOCKS 534 + bool "Using combo spinlock" 535 + depends on SMP && MMU 536 + select ARCH_USE_QUEUED_SPINLOCKS 537 + help 538 + Embed both queued spinlock and ticket lock so that the spinlock 539 + implementation can be chosen at runtime. 540 + 541 + endchoice 510 542 511 543 config RISCV_ALTERNATIVE 512 544 bool
+3 -1
arch/riscv/include/asm/Kbuild
··· 6 6 generic-y += flat.h 7 7 generic-y += kvm_para.h 8 8 generic-y += mmzone.h 9 + generic-y += mcs_spinlock.h 9 10 generic-y += parport.h 10 - generic-y += spinlock.h 11 11 generic-y += spinlock_types.h 12 + generic-y += ticket_spinlock.h 12 13 generic-y += qrwlock.h 13 14 generic-y += qrwlock_types.h 15 + generic-y += qspinlock.h 14 16 generic-y += user.h 15 17 generic-y += vmlinux.lds.h
+47
arch/riscv/include/asm/spinlock.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __ASM_RISCV_SPINLOCK_H 4 + #define __ASM_RISCV_SPINLOCK_H 5 + 6 + #ifdef CONFIG_RISCV_COMBO_SPINLOCKS 7 + #define _Q_PENDING_LOOPS (1 << 9) 8 + 9 + #define __no_arch_spinlock_redefine 10 + #include <asm/ticket_spinlock.h> 11 + #include <asm/qspinlock.h> 12 + #include <asm/jump_label.h> 13 + 14 + /* 15 + * TODO: Use an alternative instead of a static key when we are able to parse 16 + * the extensions string earlier in the boot process. 17 + */ 18 + DECLARE_STATIC_KEY_TRUE(qspinlock_key); 19 + 20 + #define SPINLOCK_BASE_DECLARE(op, type, type_lock) \ 21 + static __always_inline type arch_spin_##op(type_lock lock) \ 22 + { \ 23 + if (static_branch_unlikely(&qspinlock_key)) \ 24 + return queued_spin_##op(lock); \ 25 + return ticket_spin_##op(lock); \ 26 + } 27 + 28 + SPINLOCK_BASE_DECLARE(lock, void, arch_spinlock_t *) 29 + SPINLOCK_BASE_DECLARE(unlock, void, arch_spinlock_t *) 30 + SPINLOCK_BASE_DECLARE(is_locked, int, arch_spinlock_t *) 31 + SPINLOCK_BASE_DECLARE(is_contended, int, arch_spinlock_t *) 32 + SPINLOCK_BASE_DECLARE(trylock, bool, arch_spinlock_t *) 33 + SPINLOCK_BASE_DECLARE(value_unlocked, int, arch_spinlock_t) 34 + 35 + #elif defined(CONFIG_RISCV_QUEUED_SPINLOCKS) 36 + 37 + #include <asm/qspinlock.h> 38 + 39 + #else 40 + 41 + #include <asm/ticket_spinlock.h> 42 + 43 + #endif 44 + 45 + #include <asm/qrwlock.h> 46 + 47 + #endif /* __ASM_RISCV_SPINLOCK_H */
+37
arch/riscv/kernel/setup.c
··· 244 244 #endif 245 245 } 246 246 247 + #if defined(CONFIG_RISCV_COMBO_SPINLOCKS) 248 + DEFINE_STATIC_KEY_TRUE(qspinlock_key); 249 + EXPORT_SYMBOL(qspinlock_key); 250 + #endif 251 + 252 + static void __init riscv_spinlock_init(void) 253 + { 254 + char *using_ext = NULL; 255 + 256 + if (IS_ENABLED(CONFIG_RISCV_TICKET_SPINLOCKS)) { 257 + pr_info("Ticket spinlock: enabled\n"); 258 + return; 259 + } 260 + 261 + if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && 262 + IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && 263 + riscv_isa_extension_available(NULL, ZABHA) && 264 + riscv_isa_extension_available(NULL, ZACAS)) { 265 + using_ext = "using Zabha"; 266 + } else if (riscv_isa_extension_available(NULL, ZICCRSE)) { 267 + using_ext = "using Ziccrse"; 268 + } 269 + #if defined(CONFIG_RISCV_COMBO_SPINLOCKS) 270 + else { 271 + static_branch_disable(&qspinlock_key); 272 + pr_info("Ticket spinlock: enabled\n"); 273 + return; 274 + } 275 + #endif 276 + 277 + if (!using_ext) 278 + pr_err("Queued spinlock without Zabha or Ziccrse"); 279 + else 280 + pr_info("Queued spinlock %s: enabled\n", using_ext); 281 + } 282 + 247 283 extern void __init init_rt_signal_env(void); 248 284 249 285 void __init setup_arch(char **cmdline_p) ··· 333 297 riscv_set_dma_cache_alignment(); 334 298 335 299 riscv_user_isa_enable(); 300 + riscv_spinlock_init(); 336 301 } 337 302 338 303 bool arch_cpu_is_hotpluggable(int cpu)
+2
include/asm-generic/qspinlock.h
··· 136 136 } 137 137 #endif 138 138 139 + #ifndef __no_arch_spinlock_redefine 139 140 /* 140 141 * Remapping spinlock architecture specific functions to the corresponding 141 142 * queued spinlock functions. ··· 147 146 #define arch_spin_lock(l) queued_spin_lock(l) 148 147 #define arch_spin_trylock(l) queued_spin_trylock(l) 149 148 #define arch_spin_unlock(l) queued_spin_unlock(l) 149 + #endif 150 150 151 151 #endif /* __ASM_GENERIC_QSPINLOCK_H */
+2
include/asm-generic/ticket_spinlock.h
··· 89 89 return (s16)((val >> 16) - (val & 0xffff)) > 1; 90 90 } 91 91 92 + #ifndef __no_arch_spinlock_redefine 92 93 /* 93 94 * Remapping spinlock architecture specific functions to the corresponding 94 95 * ticket spinlock functions. ··· 100 99 #define arch_spin_lock(l) ticket_spin_lock(l) 101 100 #define arch_spin_trylock(l) ticket_spin_trylock(l) 102 101 #define arch_spin_unlock(l) ticket_spin_unlock(l) 102 + #endif 103 103 104 104 #endif /* __ASM_GENERIC_TICKET_SPINLOCK_H */