Apply the recoverable per-CPU address handling to exchange operations. Use AMOSWAP for native-width and Zabha subword operations. Where Zabha is unavailable, use a masked LR/SC sequence that preserves neighboring fields in the same word. Signed-off-by: Yunhui Cui --- arch/riscv/include/asm/percpu.h | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/arch/riscv/include/asm/percpu.h b/arch/riscv/include/asm/percpu.h index 9ff02f3811550..810f4c5eaa508 100644 --- a/arch/riscv/include/asm/percpu.h +++ b/arch/riscv/include/asm/percpu.h @@ -326,6 +326,81 @@ __percpu_add_return_amo_case_16(void __percpu *pcp, unsigned long val) return __percpu_add_return_8_16(pcp, val, 16); } +#define __PERCPU_XCHG_AMO_CASE(sfx, sz) \ +static __always_inline unsigned long \ +__percpu_xchg_case_##sz(void __percpu *pcp, unsigned long val) \ +{ \ + __PCPU_OP_CONTEXT(); \ + u##sz ret; \ + \ + asm volatile (__PCPU_GPRS_BEGIN \ + "amoswap" #sfx " %[ret], %[val], 0(%[addr])\n" \ + __PCPU_GPRS_END \ + : [gprs] "=m" (__ti->pcpu_gprs), \ + [off] "=&r" (__off), [addr] "=&r" (__addr), \ + [ret] "=&r" (ret) \ + : [pcpu_offset] "m" (__ti->pcpu_offset), \ + [pcp] "r" (pcp), [val] "r" ((u##sz)val) \ + : "memory"); \ + \ + return ret; \ +} + +__PERCPU_XCHG_AMO_CASE(.w, 32) +#ifdef CONFIG_64BIT +__PERCPU_XCHG_AMO_CASE(.d, 64) +#endif + +#define __PERCPU_8_16_XCHG_CASE(sz, sfx) \ +static __always_inline unsigned long \ +__percpu_xchg_case_##sz(void __percpu *pcp, unsigned long val) \ +{ \ + __PCPU_OP_CONTEXT(); \ + const u32 field_mask = PERCPU_8_16_GET_MASK(sz); \ + const unsigned long shift = PERCPU_8_16_GET_SHIFT(pcp); \ + const u32 mask = field_mask << shift; \ + const u32 new_field = (val & field_mask) << shift; \ + void __percpu *pcp32 = PERCPU_8_16_GET_PCP32(pcp); \ + u32 old, new, rc; \ + u##sz ret; \ + \ + if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && \ + riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA)) { \ + asm volatile (__PCPU_GPRS_BEGIN \ + ".option push\n" \ + ".option arch, +zabha\n" \ + "amoswap" #sfx " %[ret], %[val], 0(%[addr])\n" \ + ".option pop\n" \ + __PCPU_GPRS_END \ + : [gprs] "=m" (__ti->pcpu_gprs), \ + [off] "=&r" (__off), [addr] "=&r" (__addr), \ + [ret] "=&r" (ret) \ + : [pcpu_offset] "m" (__ti->pcpu_offset), \ + [pcp] "r" (pcp), [val] "r" ((u##sz)val) \ + : "memory"); \ + return ret; \ + } \ + \ + asm volatile (__PCPU_GPRS_BEGIN \ + "0: lr.w %[old], 0(%[addr])\n" \ + "and %[new], %[old], %[inv_mask]\n" \ + "or %[new], %[new], %[new_field]\n" \ + "sc.w %[rc], %[new], 0(%[addr])\n" \ + "bnez %[rc], 0b\n" \ + __PCPU_GPRS_END \ + : [gprs] "=m" (__ti->pcpu_gprs), \ + [off] "=&r" (__off), [addr] "=&r" (__addr), \ + [old] "=&r" (old), [new] "=&r" (new), [rc] "=&r" (rc) \ + : [pcpu_offset] "m" (__ti->pcpu_offset), \ + [pcp] "r" (pcp32), [inv_mask] "r" (~mask), \ + [new_field] "r" (new_field) \ + : "memory"); \ + \ + return (old & mask) >> shift; \ +} + +__PERCPU_8_16_XCHG_CASE(8, .b) +__PERCPU_8_16_XCHG_CASE(16, .h) #define _pcp_wrap(op, pcp, ...) \ ({ \ op(&(pcp), __VA_ARGS__); \ @@ -338,6 +413,14 @@ __percpu_add_return_amo_case_16(void __percpu *pcp, unsigned long val) (TYPEOF_UNQUAL(*__pcp_ptr))op(__pcp_ptr, ##args); \ }) +#define _pcp_wrap_xchg(op, pcp, val) \ +({ \ + __auto_type __pcp_ptr = &(pcp); \ + \ + (TYPEOF_UNQUAL(*__pcp_ptr))op(__pcp_ptr, \ + (unsigned long)(val)); \ +}) + #define this_cpu_read_1(pcp) _pcp_wrap_return(__percpu_read_8, pcp) #define this_cpu_read_2(pcp) _pcp_wrap_return(__percpu_read_16, pcp) #define this_cpu_read_4(pcp) _pcp_wrap_return(__percpu_read_32, pcp) @@ -390,9 +473,18 @@ __percpu_add_return_amo_case_16(void __percpu *pcp, unsigned long val) _pcp_wrap_return(__percpu_add_return_amo_case_64, pcp, val) #endif +#define this_cpu_xchg_1(pcp, val) _pcp_wrap_xchg(__percpu_xchg_case_8, pcp, val) +#define this_cpu_xchg_2(pcp, val) _pcp_wrap_xchg(__percpu_xchg_case_16, pcp, val) +#define this_cpu_xchg_4(pcp, val) _pcp_wrap_xchg(__percpu_xchg_case_32, pcp, val) +#ifdef CONFIG_64BIT +#define this_cpu_xchg_8(pcp, val) _pcp_wrap_xchg(__percpu_xchg_case_64, pcp, val) +#endif + #undef PERCPU_RW_OPS #undef __PERCPU_AMO_OP_CASE #undef __PERCPU_8_16_OP_CASE +#undef __PERCPU_XCHG_AMO_CASE +#undef __PERCPU_8_16_XCHG_CASE #undef PERCPU_8_16_GET_SHIFT #undef PERCPU_8_16_GET_MASK #undef PERCPU_8_16_GET_PCP32 -- 2.39.5