aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64
diff options
context:
space:
mode:
authorTony Luck <tony.luck@intel.com>2009-09-25 11:42:16 -0400
committerTony Luck <tony.luck@intel.com>2009-09-25 11:42:16 -0400
commit2c86963b093c1a0887dfc6b32c6e5ea3a80f2922 (patch)
treed1ed58dd0a644dd670e3724a575112fedd4b4250 /arch/ia64
parent53cddfcc0e760d2b364878b6dadbd0c6d087cfae (diff)
[IA64] implement ticket locks for Itanium
Back in January 2008 Nick Piggin implemented "ticket" spinlocks for X86 (See commit 314cdbefd1fd0a7acf3780e9628465b77ea6a836). IA64 implementation has a couple of differences because of the available atomic operations ... e.g. we have no fetchadd2 instruction that operates on a 16-bit quantity so we make ticket locks use a 32-bit word for each of the current ticket and now-serving values. Performance on uncontended locks is about 8% worse than the previous implementation, but this seems a good trade for determinism in the contended case. Performance impact on macro-level benchmarks is in the noise. Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch/ia64')
-rw-r--r--arch/ia64/Kconfig4
-rw-r--r--arch/ia64/include/asm/spinlock.h175
-rw-r--r--arch/ia64/include/asm/spinlock_types.h2
-rw-r--r--arch/ia64/kernel/head.S89
-rw-r--r--arch/ia64/kernel/ia64_ksyms.c20
-rw-r--r--arch/ia64/oprofile/backtrace.c20
6 files changed, 92 insertions, 218 deletions
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 6851e52ed5a2..1ee596cd942f 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -60,9 +60,7 @@ config IOMMU_HELPER
60 bool 60 bool
61 61
62config GENERIC_LOCKBREAK 62config GENERIC_LOCKBREAK
63 bool 63 def_bool n
64 default y
65 depends on SMP && PREEMPT
66 64
67config RWSEM_XCHGADD_ALGORITHM 65config RWSEM_XCHGADD_ALGORITHM
68 bool 66 bool
diff --git a/arch/ia64/include/asm/spinlock.h b/arch/ia64/include/asm/spinlock.h
index 13ab71576bc7..30bb930e1111 100644
--- a/arch/ia64/include/asm/spinlock.h
+++ b/arch/ia64/include/asm/spinlock.h
@@ -19,103 +19,106 @@
19 19
20#define __raw_spin_lock_init(x) ((x)->lock = 0) 20#define __raw_spin_lock_init(x) ((x)->lock = 0)
21 21
22#ifdef ASM_SUPPORTED
23/* 22/*
24 * Try to get the lock. If we fail to get the lock, make a non-standard call to 23 * Ticket locks are conceptually two parts, one indicating the current head of
25 * ia64_spinlock_contention(). We do not use a normal call because that would force all 24 * the queue, and the other indicating the current tail. The lock is acquired
26 * callers of __raw_spin_lock() to be non-leaf routines. Instead, ia64_spinlock_contention() is 25 * by atomically noting the tail and incrementing it by one (thus adding
27 * carefully coded to touch only those registers that __raw_spin_lock() marks "clobbered". 26 * ourself to the queue and noting our position), then waiting until the head
27 * becomes equal to the the initial value of the tail.
28 *
29 * 63 32 31 0
30 * +----------------------------------------------------+
31 * | next_ticket_number | now_serving |
32 * +----------------------------------------------------+
28 */ 33 */
29 34
30#define IA64_SPINLOCK_CLOBBERS "ar.ccv", "ar.pfs", "p14", "p15", "r27", "r28", "r29", "r30", "b6", "memory" 35#define TICKET_SHIFT 32
31 36
32static inline void 37static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
33__raw_spin_lock_flags (raw_spinlock_t *lock, unsigned long flags)
34{ 38{
35 register volatile unsigned int *ptr asm ("r31") = &lock->lock; 39 int *p = (int *)&lock->lock, turn, now_serving;
36 40
37#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3) 41 now_serving = *p;
38# ifdef CONFIG_ITANIUM 42 turn = ia64_fetchadd(1, p+1, acq);
39 /* don't use brl on Itanium... */ 43
40 asm volatile ("{\n\t" 44 if (turn == now_serving)
41 " mov ar.ccv = r0\n\t" 45 return;
42 " mov r28 = ip\n\t" 46
43 " mov r30 = 1;;\n\t" 47 do {
44 "}\n\t" 48 cpu_relax();
45 "cmpxchg4.acq r30 = [%1], r30, ar.ccv\n\t" 49 } while (ACCESS_ONCE(*p) != turn);
46 "movl r29 = ia64_spinlock_contention_pre3_4;;\n\t"
47 "cmp4.ne p14, p0 = r30, r0\n\t"
48 "mov b6 = r29;;\n\t"
49 "mov r27=%2\n\t"
50 "(p14) br.cond.spnt.many b6"
51 : "=r"(ptr) : "r"(ptr), "r" (flags) : IA64_SPINLOCK_CLOBBERS);
52# else
53 asm volatile ("{\n\t"
54 " mov ar.ccv = r0\n\t"
55 " mov r28 = ip\n\t"
56 " mov r30 = 1;;\n\t"
57 "}\n\t"
58 "cmpxchg4.acq r30 = [%1], r30, ar.ccv;;\n\t"
59 "cmp4.ne p14, p0 = r30, r0\n\t"
60 "mov r27=%2\n\t"
61 "(p14) brl.cond.spnt.many ia64_spinlock_contention_pre3_4;;"
62 : "=r"(ptr) : "r"(ptr), "r" (flags) : IA64_SPINLOCK_CLOBBERS);
63# endif /* CONFIG_MCKINLEY */
64#else
65# ifdef CONFIG_ITANIUM
66 /* don't use brl on Itanium... */
67 /* mis-declare, so we get the entry-point, not it's function descriptor: */
68 asm volatile ("mov r30 = 1\n\t"
69 "mov r27=%2\n\t"
70 "mov ar.ccv = r0;;\n\t"
71 "cmpxchg4.acq r30 = [%0], r30, ar.ccv\n\t"
72 "movl r29 = ia64_spinlock_contention;;\n\t"
73 "cmp4.ne p14, p0 = r30, r0\n\t"
74 "mov b6 = r29;;\n\t"
75 "(p14) br.call.spnt.many b6 = b6"
76 : "=r"(ptr) : "r"(ptr), "r" (flags) : IA64_SPINLOCK_CLOBBERS);
77# else
78 asm volatile ("mov r30 = 1\n\t"
79 "mov r27=%2\n\t"
80 "mov ar.ccv = r0;;\n\t"
81 "cmpxchg4.acq r30 = [%0], r30, ar.ccv;;\n\t"
82 "cmp4.ne p14, p0 = r30, r0\n\t"
83 "(p14) brl.call.spnt.many b6=ia64_spinlock_contention;;"
84 : "=r"(ptr) : "r"(ptr), "r" (flags) : IA64_SPINLOCK_CLOBBERS);
85# endif /* CONFIG_MCKINLEY */
86#endif
87} 50}
88 51
89#define __raw_spin_lock(lock) __raw_spin_lock_flags(lock, 0) 52static __always_inline int __ticket_spin_trylock(raw_spinlock_t *lock)
53{
54 long tmp = ACCESS_ONCE(lock->lock), try;
90 55
91/* Unlock by doing an ordered store and releasing the cacheline with nta */ 56 if (!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1L << TICKET_SHIFT) - 1))) {
92static inline void __raw_spin_unlock(raw_spinlock_t *x) { 57 try = tmp + (1L << TICKET_SHIFT);
93 barrier(); 58
94 asm volatile ("st4.rel.nta [%0] = r0\n\t" :: "r"(x)); 59 return ia64_cmpxchg(acq, &lock->lock, tmp, try, sizeof (tmp)) == tmp;
60 }
61 return 0;
95} 62}
96 63
97#else /* !ASM_SUPPORTED */ 64static __always_inline void __ticket_spin_unlock(raw_spinlock_t *lock)
98#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) 65{
99# define __raw_spin_lock(x) \ 66 int *p = (int *)&lock->lock;
100do { \ 67
101 __u32 *ia64_spinlock_ptr = (__u32 *) (x); \ 68 (void)ia64_fetchadd(1, p, rel);
102 __u64 ia64_spinlock_val; \ 69}
103 ia64_spinlock_val = ia64_cmpxchg4_acq(ia64_spinlock_ptr, 1, 0); \ 70
104 if (unlikely(ia64_spinlock_val)) { \ 71static inline int __ticket_spin_is_locked(raw_spinlock_t *lock)
105 do { \ 72{
106 while (*ia64_spinlock_ptr) \ 73 long tmp = ACCESS_ONCE(lock->lock);
107 ia64_barrier(); \ 74
108 ia64_spinlock_val = ia64_cmpxchg4_acq(ia64_spinlock_ptr, 1, 0); \ 75 return !!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1L << TICKET_SHIFT) - 1));
109 } while (ia64_spinlock_val); \ 76}
110 } \ 77
111} while (0) 78static inline int __ticket_spin_is_contended(raw_spinlock_t *lock)
112#define __raw_spin_unlock(x) do { barrier(); ((raw_spinlock_t *) x)->lock = 0; } while (0) 79{
113#endif /* !ASM_SUPPORTED */ 80 long tmp = ACCESS_ONCE(lock->lock);
114 81
115#define __raw_spin_is_locked(x) ((x)->lock != 0) 82 return (((tmp >> TICKET_SHIFT) - tmp) & ((1L << TICKET_SHIFT) - 1)) > 1;
116#define __raw_spin_trylock(x) (cmpxchg_acq(&(x)->lock, 0, 1) == 0) 83}
117#define __raw_spin_unlock_wait(lock) \ 84
118 do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0) 85static inline int __raw_spin_is_locked(raw_spinlock_t *lock)
86{
87 return __ticket_spin_is_locked(lock);
88}
89
90static inline int __raw_spin_is_contended(raw_spinlock_t *lock)
91{
92 return __ticket_spin_is_contended(lock);
93}
94#define __raw_spin_is_contended __raw_spin_is_contended
95
96static __always_inline void __raw_spin_lock(raw_spinlock_t *lock)
97{
98 __ticket_spin_lock(lock);
99}
100
101static __always_inline int __raw_spin_trylock(raw_spinlock_t *lock)
102{
103 return __ticket_spin_trylock(lock);
104}
105
106static __always_inline void __raw_spin_unlock(raw_spinlock_t *lock)
107{
108 __ticket_spin_unlock(lock);
109}
110
111static __always_inline void __raw_spin_lock_flags(raw_spinlock_t *lock,
112 unsigned long flags)
113{
114 __raw_spin_lock(lock);
115}
116
117static inline void __raw_spin_unlock_wait(raw_spinlock_t *lock)
118{
119 while (__raw_spin_is_locked(lock))
120 cpu_relax();
121}
119 122
120#define __raw_read_can_lock(rw) (*(volatile int *)(rw) >= 0) 123#define __raw_read_can_lock(rw) (*(volatile int *)(rw) >= 0)
121#define __raw_write_can_lock(rw) (*(volatile int *)(rw) == 0) 124#define __raw_write_can_lock(rw) (*(volatile int *)(rw) == 0)
diff --git a/arch/ia64/include/asm/spinlock_types.h b/arch/ia64/include/asm/spinlock_types.h
index 474e46f1ab4a..b61d136d9bc2 100644
--- a/arch/ia64/include/asm/spinlock_types.h
+++ b/arch/ia64/include/asm/spinlock_types.h
@@ -6,7 +6,7 @@
6#endif 6#endif
7 7
8typedef struct { 8typedef struct {
9 volatile unsigned int lock; 9 volatile unsigned long lock;
10} raw_spinlock_t; 10} raw_spinlock_t;
11 11
12#define __RAW_SPIN_LOCK_UNLOCKED { 0 } 12#define __RAW_SPIN_LOCK_UNLOCKED { 0 }
diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
index 1a6e44515eb4..696eff28a0c4 100644
--- a/arch/ia64/kernel/head.S
+++ b/arch/ia64/kernel/head.S
@@ -1130,95 +1130,6 @@ SET_REG(b5);
1130#endif /* CONFIG_IA64_BRL_EMU */ 1130#endif /* CONFIG_IA64_BRL_EMU */
1131 1131
1132#ifdef CONFIG_SMP 1132#ifdef CONFIG_SMP
1133 /*
1134 * This routine handles spinlock contention. It uses a non-standard calling
1135 * convention to avoid converting leaf routines into interior routines. Because
1136 * of this special convention, there are several restrictions:
1137 *
1138 * - do not use gp relative variables, this code is called from the kernel
1139 * and from modules, r1 is undefined.
1140 * - do not use stacked registers, the caller owns them.
1141 * - do not use the scratch stack space, the caller owns it.
1142 * - do not use any registers other than the ones listed below
1143 *
1144 * Inputs:
1145 * ar.pfs - saved CFM of caller
1146 * ar.ccv - 0 (and available for use)
1147 * r27 - flags from spin_lock_irqsave or 0. Must be preserved.
1148 * r28 - available for use.
1149 * r29 - available for use.
1150 * r30 - available for use.
1151 * r31 - address of lock, available for use.
1152 * b6 - return address
1153 * p14 - available for use.
1154 * p15 - used to track flag status.
1155 *
1156 * If you patch this code to use more registers, do not forget to update
1157 * the clobber lists for spin_lock() in arch/ia64/include/asm/spinlock.h.
1158 */
1159
1160#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
1161
1162GLOBAL_ENTRY(ia64_spinlock_contention_pre3_4)
1163 .prologue
1164 .save ar.pfs, r0 // this code effectively has a zero frame size
1165 .save rp, r28
1166 .body
1167 nop 0
1168 tbit.nz p15,p0=r27,IA64_PSR_I_BIT
1169 .restore sp // pop existing prologue after next insn
1170 mov b6 = r28
1171 .prologue
1172 .save ar.pfs, r0
1173 .altrp b6
1174 .body
1175 ;;
1176(p15) ssm psr.i // reenable interrupts if they were on
1177 // DavidM says that srlz.d is slow and is not required in this case
1178.wait:
1179 // exponential backoff, kdb, lockmeter etc. go in here
1180 hint @pause
1181 ld4 r30=[r31] // don't use ld4.bias; if it's contended, we won't write the word
1182 nop 0
1183 ;;
1184 cmp4.ne p14,p0=r30,r0
1185(p14) br.cond.sptk.few .wait
1186(p15) rsm psr.i // disable interrupts if we reenabled them
1187 br.cond.sptk.few b6 // lock is now free, try to acquire
1188 .global ia64_spinlock_contention_pre3_4_end // for kernprof
1189ia64_spinlock_contention_pre3_4_end:
1190END(ia64_spinlock_contention_pre3_4)
1191
1192#else
1193
1194GLOBAL_ENTRY(ia64_spinlock_contention)
1195 .prologue
1196 .altrp b6
1197 .body
1198 tbit.nz p15,p0=r27,IA64_PSR_I_BIT
1199 ;;
1200.wait:
1201(p15) ssm psr.i // reenable interrupts if they were on
1202 // DavidM says that srlz.d is slow and is not required in this case
1203.wait2:
1204 // exponential backoff, kdb, lockmeter etc. go in here
1205 hint @pause
1206 ld4 r30=[r31] // don't use ld4.bias; if it's contended, we won't write the word
1207 ;;
1208 cmp4.ne p14,p0=r30,r0
1209 mov r30 = 1
1210(p14) br.cond.sptk.few .wait2
1211(p15) rsm psr.i // disable interrupts if we reenabled them
1212 ;;
1213 cmpxchg4.acq r30=[r31], r30, ar.ccv
1214 ;;
1215 cmp4.ne p14,p0=r0,r30
1216(p14) br.cond.sptk.few .wait
1217
1218 br.ret.sptk.many b6 // lock is now taken
1219END(ia64_spinlock_contention)
1220
1221#endif
1222 1133
1223#ifdef CONFIG_HOTPLUG_CPU 1134#ifdef CONFIG_HOTPLUG_CPU
1224GLOBAL_ENTRY(ia64_jump_to_sal) 1135GLOBAL_ENTRY(ia64_jump_to_sal)
diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
index 8ebccb589e1c..14d39e300627 100644
--- a/arch/ia64/kernel/ia64_ksyms.c
+++ b/arch/ia64/kernel/ia64_ksyms.c
@@ -84,26 +84,6 @@ EXPORT_SYMBOL(ia64_save_scratch_fpregs);
84#include <asm/unwind.h> 84#include <asm/unwind.h>
85EXPORT_SYMBOL(unw_init_running); 85EXPORT_SYMBOL(unw_init_running);
86 86
87#ifdef ASM_SUPPORTED
88# ifdef CONFIG_SMP
89# if (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
90/*
91 * This is not a normal routine and we don't want a function descriptor for it, so we use
92 * a fake declaration here.
93 */
94extern char ia64_spinlock_contention_pre3_4;
95EXPORT_SYMBOL(ia64_spinlock_contention_pre3_4);
96# else
97/*
98 * This is not a normal routine and we don't want a function descriptor for it, so we use
99 * a fake declaration here.
100 */
101extern char ia64_spinlock_contention;
102EXPORT_SYMBOL(ia64_spinlock_contention);
103# endif
104# endif
105#endif
106
107#if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE) 87#if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
108extern void esi_call_phys (void); 88extern void esi_call_phys (void);
109EXPORT_SYMBOL_GPL(esi_call_phys); 89EXPORT_SYMBOL_GPL(esi_call_phys);
diff --git a/arch/ia64/oprofile/backtrace.c b/arch/ia64/oprofile/backtrace.c
index adb01566bd57..5cdd7e4a597c 100644
--- a/arch/ia64/oprofile/backtrace.c
+++ b/arch/ia64/oprofile/backtrace.c
@@ -32,24 +32,6 @@ typedef struct
32 u64 *prev_pfs_loc; /* state for WAR for old spinlock ool code */ 32 u64 *prev_pfs_loc; /* state for WAR for old spinlock ool code */
33} ia64_backtrace_t; 33} ia64_backtrace_t;
34 34
35#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
36/*
37 * Returns non-zero if the PC is in the spinlock contention out-of-line code
38 * with non-standard calling sequence (on older compilers).
39 */
40static __inline__ int in_old_ool_spinlock_code(unsigned long pc)
41{
42 extern const char ia64_spinlock_contention_pre3_4[] __attribute__ ((weak));
43 extern const char ia64_spinlock_contention_pre3_4_end[] __attribute__ ((weak));
44 unsigned long sc_start = (unsigned long)ia64_spinlock_contention_pre3_4;
45 unsigned long sc_end = (unsigned long)ia64_spinlock_contention_pre3_4_end;
46 return (sc_start && sc_end && pc >= sc_start && pc < sc_end);
47}
48#else
49/* Newer spinlock code does a proper br.call and works fine with the unwinder */
50#define in_old_ool_spinlock_code(pc) 0
51#endif
52
53/* Returns non-zero if the PC is in the Interrupt Vector Table */ 35/* Returns non-zero if the PC is in the Interrupt Vector Table */
54static __inline__ int in_ivt_code(unsigned long pc) 36static __inline__ int in_ivt_code(unsigned long pc)
55{ 37{
@@ -80,7 +62,7 @@ static __inline__ int next_frame(ia64_backtrace_t *bt)
80 */ 62 */
81 if (bt->prev_pfs_loc && bt->regs && bt->frame.pfs_loc == bt->prev_pfs_loc) 63 if (bt->prev_pfs_loc && bt->regs && bt->frame.pfs_loc == bt->prev_pfs_loc)
82 bt->frame.pfs_loc = &bt->regs->ar_pfs; 64 bt->frame.pfs_loc = &bt->regs->ar_pfs;
83 bt->prev_pfs_loc = (in_old_ool_spinlock_code(bt->frame.ip) ? bt->frame.pfs_loc : NULL); 65 bt->prev_pfs_loc = NULL;
84 66
85 return unw_unwind(&bt->frame) == 0; 67 return unw_unwind(&bt->frame) == 0;
86} 68}