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

openrisc: add 1 and 2 byte cmpxchg support

OpenRISC only supports hardware instructions that perform 4 byte atomic
operations. For enabling qrwlocks for upcoming SMP support 1 and 2 byte
implementations are needed. To do this we leverage the 4 byte atomic
operations and shift/mask the 1 and 2 byte areas as needed.

This heavily borrows ideas and routines from sh and mips, which do
something similar.

Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Stafford Horne <shorne@gmail.com>

+119 -36
+119 -36
arch/openrisc/include/asm/cmpxchg.h
··· 1 1 /* 2 + * 1,2 and 4 byte cmpxchg and xchg implementations for OpenRISC. 3 + * 2 4 * Copyright (C) 2014 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> 5 + * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> 3 6 * 4 7 * This file is licensed under the terms of the GNU General Public License 5 8 * version 2. This program is licensed "as is" without any warranty of any 6 9 * kind, whether express or implied. 10 + * 11 + * Note: 12 + * The portable implementations of 1 and 2 byte xchg and cmpxchg using a 4 13 + * byte cmpxchg is sourced heavily from the sh and mips implementations. 7 14 */ 8 15 9 16 #ifndef __ASM_OPENRISC_CMPXCHG_H 10 17 #define __ASM_OPENRISC_CMPXCHG_H 11 18 12 19 #include <linux/types.h> 13 - 14 - /* 15 - * This function doesn't exist, so you'll get a linker error 16 - * if something tries to do an invalid cmpxchg(). 17 - */ 18 - extern void __cmpxchg_called_with_bad_pointer(void); 20 + #include <linux/bitops.h> 19 21 20 22 #define __HAVE_ARCH_CMPXCHG 1 21 23 22 - static inline unsigned long 23 - __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) 24 + static inline unsigned long cmpxchg_u32(volatile void *ptr, 25 + unsigned long old, unsigned long new) 24 26 { 25 - if (size != 4) { 26 - __cmpxchg_called_with_bad_pointer(); 27 - return old; 28 - } 29 - 30 27 __asm__ __volatile__( 31 28 "1: l.lwa %0, 0(%1) \n" 32 29 " l.sfeq %0, %2 \n" ··· 40 43 return old; 41 44 } 42 45 43 - #define cmpxchg(ptr, o, n) \ 44 - ({ \ 45 - (__typeof__(*(ptr))) __cmpxchg((ptr), \ 46 - (unsigned long)(o), \ 47 - (unsigned long)(n), \ 48 - sizeof(*(ptr))); \ 49 - }) 50 - 51 - /* 52 - * This function doesn't exist, so you'll get a linker error if 53 - * something tries to do an invalidly-sized xchg(). 54 - */ 55 - extern void __xchg_called_with_bad_pointer(void); 56 - 57 - static inline unsigned long __xchg(unsigned long val, volatile void *ptr, 58 - int size) 46 + static inline unsigned long xchg_u32(volatile void *ptr, 47 + unsigned long val) 59 48 { 60 - if (size != 4) { 61 - __xchg_called_with_bad_pointer(); 62 - return val; 63 - } 64 - 65 49 __asm__ __volatile__( 66 50 "1: l.lwa %0, 0(%1) \n" 67 51 " l.swa 0(%1), %2 \n" ··· 55 77 return val; 56 78 } 57 79 80 + static inline u32 cmpxchg_small(volatile void *ptr, u32 old, u32 new, 81 + int size) 82 + { 83 + int off = (unsigned long)ptr % sizeof(u32); 84 + volatile u32 *p = ptr - off; 85 + #ifdef __BIG_ENDIAN 86 + int bitoff = (sizeof(u32) - size - off) * BITS_PER_BYTE; 87 + #else 88 + int bitoff = off * BITS_PER_BYTE; 89 + #endif 90 + u32 bitmask = ((0x1 << size * BITS_PER_BYTE) - 1) << bitoff; 91 + u32 load32, old32, new32; 92 + u32 ret; 93 + 94 + load32 = READ_ONCE(*p); 95 + 96 + while (true) { 97 + ret = (load32 & bitmask) >> bitoff; 98 + if (old != ret) 99 + return ret; 100 + 101 + old32 = (load32 & ~bitmask) | (old << bitoff); 102 + new32 = (load32 & ~bitmask) | (new << bitoff); 103 + 104 + /* Do 32 bit cmpxchg */ 105 + load32 = cmpxchg_u32(p, old32, new32); 106 + if (load32 == old32) 107 + return old; 108 + } 109 + } 110 + 111 + /* xchg */ 112 + 113 + static inline u32 xchg_small(volatile void *ptr, u32 x, int size) 114 + { 115 + int off = (unsigned long)ptr % sizeof(u32); 116 + volatile u32 *p = ptr - off; 117 + #ifdef __BIG_ENDIAN 118 + int bitoff = (sizeof(u32) - size - off) * BITS_PER_BYTE; 119 + #else 120 + int bitoff = off * BITS_PER_BYTE; 121 + #endif 122 + u32 bitmask = ((0x1 << size * BITS_PER_BYTE) - 1) << bitoff; 123 + u32 oldv, newv; 124 + u32 ret; 125 + 126 + do { 127 + oldv = READ_ONCE(*p); 128 + ret = (oldv & bitmask) >> bitoff; 129 + newv = (oldv & ~bitmask) | (x << bitoff); 130 + } while (cmpxchg_u32(p, oldv, newv) != oldv); 131 + 132 + return ret; 133 + } 134 + 135 + /* 136 + * This function doesn't exist, so you'll get a linker error 137 + * if something tries to do an invalid cmpxchg(). 138 + */ 139 + extern unsigned long __cmpxchg_called_with_bad_pointer(void) 140 + __compiletime_error("Bad argument size for cmpxchg"); 141 + 142 + static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, 143 + unsigned long new, int size) 144 + { 145 + switch (size) { 146 + case 1: 147 + case 2: 148 + return cmpxchg_small(ptr, old, new, size); 149 + case 4: 150 + return cmpxchg_u32(ptr, old, new); 151 + default: 152 + return __cmpxchg_called_with_bad_pointer(); 153 + } 154 + } 155 + 156 + #define cmpxchg(ptr, o, n) \ 157 + ({ \ 158 + (__typeof__(*(ptr))) __cmpxchg((ptr), \ 159 + (unsigned long)(o), \ 160 + (unsigned long)(n), \ 161 + sizeof(*(ptr))); \ 162 + }) 163 + 164 + /* 165 + * This function doesn't exist, so you'll get a linker error if 166 + * something tries to do an invalidly-sized xchg(). 167 + */ 168 + extern unsigned long __xchg_called_with_bad_pointer(void) 169 + __compiletime_error("Bad argument size for xchg"); 170 + 171 + static inline unsigned long __xchg(volatile void *ptr, unsigned long with, 172 + int size) 173 + { 174 + switch (size) { 175 + case 1: 176 + case 2: 177 + return xchg_small(ptr, with, size); 178 + case 4: 179 + return xchg_u32(ptr, with); 180 + default: 181 + return __xchg_called_with_bad_pointer(); 182 + } 183 + } 184 + 58 185 #define xchg(ptr, with) \ 59 186 ({ \ 60 - (__typeof__(*(ptr))) __xchg((unsigned long)(with), \ 61 - (ptr), \ 187 + (__typeof__(*(ptr))) __xchg((ptr), \ 188 + (unsigned long)(with), \ 62 189 sizeof(*(ptr))); \ 63 190 }) 64 191