@@ -19,12 +19,34 @@
#include <asm/barrier.h>
#include <asm/fence.h>
+extern unsigned long __xchg_relaxed_small(volatile void *ptr, unsigned long new,
+ unsigned int size);
+extern unsigned long __xchg_acquire_small(volatile void *ptr, unsigned long new,
+ unsigned int size);
+extern unsigned long __xchg_release_small(volatile void *ptr, unsigned long new,
+ unsigned int size);
+extern unsigned long __xchg_small(volatile void *ptr, unsigned long new,
+ unsigned int size);
+
+extern unsigned long __cmpxchg_relaxed_small(volatile void *ptr, unsigned long old,
+ unsigned long new, unsigned int size);
+extern unsigned long __cmpxchg_acquire_small(volatile void *ptr, unsigned long old,
+ unsigned long new, unsigned int size);
+extern unsigned long __cmpxchg_release_small(volatile void *ptr, unsigned long old,
+ unsigned long new, unsigned int size);
+extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
+ unsigned long new, unsigned int size);
+
#define __xchg_relaxed(ptr, new, size) \
({ \
__typeof__(ptr) __ptr = (ptr); \
__typeof__(new) __new = (new); \
__typeof__(*(ptr)) __ret; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__xchg_relaxed_small( \
+ (void*)ptr, (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
" amoswap.w %0, %2, %1\n" \
@@ -58,6 +80,10 @@
__typeof__(new) __new = (new); \
__typeof__(*(ptr)) __ret; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__xchg_acquire_small( \
+ (void*)ptr, (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
" amoswap.w %0, %2, %1\n" \
@@ -93,6 +119,10 @@
__typeof__(new) __new = (new); \
__typeof__(*(ptr)) __ret; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__xchg_release_small( \
+ (void*)ptr, (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
RISCV_RELEASE_BARRIER \
@@ -128,6 +158,10 @@
__typeof__(new) __new = (new); \
__typeof__(*(ptr)) __ret; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__xchg_small( \
+ (void*)ptr, (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
" amoswap.w.aqrl %0, %2, %1\n" \
@@ -179,6 +213,11 @@
__typeof__(*(ptr)) __ret; \
register unsigned int __rc; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__cmpxchg_relaxed_small( \
+ (void*)__ptr, (unsigned long)__old, \
+ (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
"0: lr.w %0, %2\n" \
@@ -223,6 +262,11 @@
__typeof__(*(ptr)) __ret; \
register unsigned int __rc; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__cmpxchg_acquire_small( \
+ (void*)__ptr, (unsigned long)__old, \
+ (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
"0: lr.w %0, %2\n" \
@@ -269,6 +313,11 @@
__typeof__(*(ptr)) __ret; \
register unsigned int __rc; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__cmpxchg_release_small( \
+ (void*)__ptr, (unsigned long)__old, \
+ (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
RISCV_RELEASE_BARRIER \
@@ -315,6 +364,11 @@
__typeof__(*(ptr)) __ret; \
register unsigned int __rc; \
switch (size) { \
+ case 1: \
+ case 2: \
+ __ret = (__typeof__(*(ptr)))__cmpxchg_small( \
+ (void*)__ptr, (unsigned long)__old, \
+ (unsigned long)__new, size); \
case 4: \
__asm__ __volatile__ ( \
"0: lr.w %0, %2\n" \
@@ -27,6 +27,7 @@ obj-y += riscv_ksyms.o
obj-y += stacktrace.o
obj-y += vdso.o
obj-y += cacheinfo.o
+obj-y += cmpxchg.o
obj-y += vdso/
CFLAGS_setup.o := -mcmodel=medany
new file mode 100644
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 Imagination Technologies
+ * Author: Paul Burton <paul.burton@mips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <asm/cmpxchg.h>
+
+#define TEMPLATE_XCGH_SMALL(__func,__op) \
+unsigned long __func(volatile void *ptr, unsigned long new, \
+ unsigned int size) \
+{ \
+ u32 old32, new32, load32, mask; \
+ volatile u32 *ptr32; \
+ unsigned int shift; \
+ \
+ /* Check that ptr is naturally aligned */ \
+ WARN_ON((unsigned long)ptr & (size - 1)); \
+ \
+ /* Mask value to the correct size. */ \
+ mask = GENMASK((size * BITS_PER_BYTE) - 1, 0); \
+ new &= mask; \
+ \
+ /* \
+ * Calculate a shift & mask that corresponds to the value \
+ * we wish to exchange within the naturally aligned 4 byte \
+ * integer that includes it. \
+ */ \
+ shift = (unsigned long)ptr & 0x3; \
+ shift *= BITS_PER_BYTE; \
+ mask <<= shift; \
+ \
+ /* \
+ * Calculate a pointer to the naturally aligned 4 byte \
+ * integer that includes our byte, and load its value. \
+ */ \
+ ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3); \
+ load32 = *ptr32; \
+ \
+ do { \
+ old32 = load32; \
+ new32 = (load32 & ~mask) | (new << shift); \
+ load32 = __op(ptr32, old32, new32); \
+ } while (load32 != old32); \
+ \
+ return (load32 & mask) >> shift; \
+}
+
+TEMPLATE_XCGH_SMALL(__xchg_small,cmpxchg)
+TEMPLATE_XCGH_SMALL(__xchg_relaxed_small,cmpxchg_relaxed)
+TEMPLATE_XCGH_SMALL(__xchg_acquire_small,cmpxchg_acquire)
+TEMPLATE_XCGH_SMALL(__xchg_release_small,cmpxchg_release)
+
+#define TEMPLATE_CMPXCGH_SMALL(__func,__op) \
+unsigned long __func(volatile void *ptr, unsigned long old, \
+ unsigned long new, unsigned int size) \
+{ \
+ u32 old32, new32, load32, mask; \
+ volatile u32 *ptr32; \
+ unsigned int shift; \
+ u32 load; \
+ \
+ /* Check that ptr is naturally aligned */ \
+ WARN_ON((unsigned long)ptr & (size - 1)); \
+ \
+ /* Mask inputs to the correct size. */ \
+ mask = GENMASK((size * BITS_PER_BYTE) - 1, 0); \
+ old &= mask; \
+ new &= mask; \
+ \
+ /* \
+ * Calculate a shift & mask that corresponds to the value \
+ * we wish to exchange within the naturally aligned 4 byte \
+ * integer that includes it. \
+ */ \
+ shift = (unsigned long)ptr & 0x3; \
+ shift *= BITS_PER_BYTE; \
+ mask <<= shift; \
+ \
+ /* \
+ * Calculate a pointer to the naturally aligned 4 byte \
+ * integer that includes our byte, and load its value. \
+ */ \
+ ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3); \
+ load32 = *ptr32; \
+ \
+ while (true) { \
+ /* \
+ * Ensure the subword we want to exchange matches \
+ * the expected old value, and if not then bail. \
+ */ \
+ load = (load32 & mask) >> shift; \
+ if (load != old) \
+ return load; \
+ \
+ /* \
+ * Calculate the old & new values of the naturally \
+ * aligned 4 byte integer including the byte we want \
+ * to exchange. Attempt to exchange the old value \
+ * for the new value, and return if we succeed. \
+ */ \
+ old32 = (load32 & ~mask) | (old << shift); \
+ new32 = (load32 & ~mask) | (new << shift); \
+ load32 = __op(ptr32, old32, new32); \
+ if (load32 == old32) \
+ return old; \
+ } \
+}
+
+TEMPLATE_CMPXCGH_SMALL(__cmpxchg_small,cmpxchg)
+TEMPLATE_CMPXCGH_SMALL(__cmpxchg_relaxed_small,cmpxchg_relaxed)
+TEMPLATE_CMPXCGH_SMALL(__cmpxchg_acquire_small,cmpxchg_acquire)
+TEMPLATE_CMPXCGH_SMALL(__cmpxchg_release_small,cmpxchg_release)
This patch implements xchg and cmpxchg for char and short. xchg and cmpxchg on small words are necessary to use the generic qspinlock and qrwlock which are enabled in a subsequent patch. The MIPS cmpxchg code is adapted into a macro template to implement the additional three variants (relaxed|acquire|release)] supported by the RISC-V memory model. Cc: RISC-V Patches <patches@groups.riscv.org> Cc: Linux RISC-V <linux-riscv@lists.infradead.org> Signed-off-by: Michael Clark <michaeljclark@mac.com> --- arch/riscv/include/asm/cmpxchg.h | 54 ++++++++++++++ arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/cmpxchg.c | 118 +++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 arch/riscv/kernel/cmpxchg.c