Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _ASM_IA64_SPINLOCK_H #define _ASM_IA64_SPINLOCK_H /* * Copyright (C) 1998-2003 Hewlett-Packard Co * David Mosberger-Tang <davidm@hpl.hp.com> * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> * * This file is used for SMP configurations only. */ #include <linux/compiler.h> #include <linux/kernel.h> #include <linux/bitops.h> #include <linux/atomic.h> #include <asm/intrinsics.h> #include <asm/barrier.h> #include <asm/processor.h> #define arch_spin_lock_init(x) ((x)->lock = 0) /* * Ticket locks are conceptually two parts, one indicating the current head of * the queue, and the other indicating the current tail. The lock is acquired * by atomically noting the tail and incrementing it by one (thus adding * ourself to the queue and noting our position), then waiting until the head * becomes equal to the initial value of the tail. * The pad bits in the middle are used to prevent the next_ticket number * overflowing into the now_serving number. * * 31 17 16 15 14 0 * +----------------------------------------------------+ * | now_serving | padding | next_ticket | * +----------------------------------------------------+ */ #define TICKET_SHIFT 17 #define TICKET_BITS 15 #define TICKET_MASK ((1 << TICKET_BITS) - 1) static __always_inline void __ticket_spin_lock(arch_spinlock_t *lock) { int *p = (int *)&lock->lock, ticket, serve; ticket = ia64_fetchadd(1, p, acq); if (!(((ticket >> TICKET_SHIFT) ^ ticket) & TICKET_MASK)) return; ia64_invala(); for (;;) { asm volatile ("ld4.c.nc %0=[%1]" : "=r"(serve) : "r"(p) : "memory"); if (!(((serve >> TICKET_SHIFT) ^ ticket) & TICKET_MASK)) return; cpu_relax(); } } static __always_inline int __ticket_spin_trylock(arch_spinlock_t *lock) { int tmp = READ_ONCE(lock->lock); if (!(((tmp >> TICKET_SHIFT) ^ tmp) & TICKET_MASK)) return ia64_cmpxchg(acq, &lock->lock, tmp, tmp + 1, sizeof (tmp)) == tmp; return 0; } static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock) { unsigned short *p = (unsigned short *)&lock->lock + 1, tmp; /* This could be optimised with ARCH_HAS_MMIOWB */ mmiowb(); asm volatile ("ld2.bias %0=[%1]" : "=r"(tmp) : "r"(p)); WRITE_ONCE(*p, (tmp + 2) & ~1); } static inline int __ticket_spin_is_locked(arch_spinlock_t *lock) { long tmp = READ_ONCE(lock->lock); return !!(((tmp >> TICKET_SHIFT) ^ tmp) & TICKET_MASK); } static inline int __ticket_spin_is_contended(arch_spinlock_t *lock) { long tmp = READ_ONCE(lock->lock); return ((tmp - (tmp >> TICKET_SHIFT)) & TICKET_MASK) > 1; } static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) { return !(((lock.lock >> TICKET_SHIFT) ^ lock.lock) & TICKET_MASK); } static inline int arch_spin_is_locked(arch_spinlock_t *lock) { return __ticket_spin_is_locked(lock); } static inline int arch_spin_is_contended(arch_spinlock_t *lock) { return __ticket_spin_is_contended(lock); } #define arch_spin_is_contended arch_spin_is_contended static __always_inline void arch_spin_lock(arch_spinlock_t *lock) { __ticket_spin_lock(lock); } static __always_inline int arch_spin_trylock(arch_spinlock_t *lock) { return __ticket_spin_trylock(lock); } static __always_inline void arch_spin_unlock(arch_spinlock_t *lock) { __ticket_spin_unlock(lock); } #ifdef ASM_SUPPORTED static __always_inline void arch_read_lock(arch_rwlock_t *lock) { unsigned long flags = 0; __asm__ __volatile__ ( "tbit.nz p6, p0 = %1,%2\n" "br.few 3f\n" "1:\n" "fetchadd4.rel r2 = [%0], -1;;\n" "(p6) ssm psr.i\n" "2:\n" "hint @pause\n" "ld4 r2 = [%0];;\n" "cmp4.lt p7,p0 = r2, r0\n" "(p7) br.cond.spnt.few 2b\n" "(p6) rsm psr.i\n" ";;\n" "3:\n" "fetchadd4.acq r2 = [%0], 1;;\n" "cmp4.lt p7,p0 = r2, r0\n" "(p7) br.cond.spnt.few 1b\n" : : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT) : "p6", "p7", "r2", "memory"); } #else /* !ASM_SUPPORTED */ #define arch_read_lock(rw) \ do { \ arch_rwlock_t *__read_lock_ptr = (rw); \ \ while (unlikely(ia64_fetchadd(1, (int *) __read_lock_ptr, acq) < 0)) { \ ia64_fetchadd(-1, (int *) __read_lock_ptr, rel); \ while (*(volatile int *)__read_lock_ptr < 0) \ cpu_relax(); \ } \ } while (0) #endif /* !ASM_SUPPORTED */ #define arch_read_unlock(rw) \ do { \ arch_rwlock_t *__read_lock_ptr = (rw); \ ia64_fetchadd(-1, (int *) __read_lock_ptr, rel); \ } while (0) #ifdef ASM_SUPPORTED static __always_inline void arch_write_lock(arch_rwlock_t *lock) { unsigned long flags = 0; __asm__ __volatile__ ( "tbit.nz p6, p0 = %1, %2\n" "mov ar.ccv = r0\n" "dep r29 = -1, r0, 31, 1\n" "br.few 3f;;\n" "1:\n" "(p6) ssm psr.i\n" "2:\n" "hint @pause\n" "ld4 r2 = [%0];;\n" "cmp4.eq p0,p7 = r0, r2\n" "(p7) br.cond.spnt.few 2b\n" "(p6) rsm psr.i\n" ";;\n" "3:\n" "cmpxchg4.acq r2 = [%0], r29, ar.ccv;;\n" "cmp4.eq p0,p7 = r0, r2\n" "(p7) br.cond.spnt.few 1b;;\n" : : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT) : "ar.ccv", "p6", "p7", "r2", "r29", "memory"); } #define arch_write_trylock(rw) \ ({ \ register long result; \ \ __asm__ __volatile__ ( \ "mov ar.ccv = r0\n" \ "dep r29 = -1, r0, 31, 1;;\n" \ "cmpxchg4.acq %0 = [%1], r29, ar.ccv\n" \ : "=r"(result) : "r"(rw) : "ar.ccv", "r29", "memory"); \ (result == 0); \ }) static inline void arch_write_unlock(arch_rwlock_t *x) { u8 *y = (u8 *)x; barrier(); asm volatile ("st1.rel.nta [%0] = r0\n\t" :: "r"(y+3) : "memory" ); } #else /* !ASM_SUPPORTED */ #define arch_write_lock(l) \ ({ \ __u64 ia64_val, ia64_set_val = ia64_dep_mi(-1, 0, 31, 1); \ __u32 *ia64_write_lock_ptr = (__u32 *) (l); \ do { \ while (*ia64_write_lock_ptr) \ ia64_barrier(); \ ia64_val = ia64_cmpxchg4_acq(ia64_write_lock_ptr, ia64_set_val, 0); \ } while (ia64_val); \ }) #define arch_write_trylock(rw) \ ({ \ __u64 ia64_val; \ __u64 ia64_set_val = ia64_dep_mi(-1, 0, 31,1); \ ia64_val = ia64_cmpxchg4_acq((__u32 *)(rw), ia64_set_val, 0); \ (ia64_val == 0); \ }) static inline void arch_write_unlock(arch_rwlock_t *x) { barrier(); x->write_lock = 0; } #endif /* !ASM_SUPPORTED */ static inline int arch_read_trylock(arch_rwlock_t *x) { union { arch_rwlock_t lock; __u32 word; } old, new; old.lock = new.lock = *x; old.lock.write_lock = new.lock.write_lock = 0; ++new.lock.read_counter; return (u32)ia64_cmpxchg4_acq((__u32 *)(x), new.word, old.word) == old.word; } #endif /* _ASM_IA64_SPINLOCK_H */ |