diff options
Diffstat (limited to 'arch/x86/xen/spinlock.c')
-rw-r--r-- | arch/x86/xen/spinlock.c | 46 |
1 files changed, 40 insertions, 6 deletions
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 546112ed463f..0438b9324a72 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c | |||
@@ -142,7 +142,20 @@ static void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want) | |||
142 | * partially setup state. | 142 | * partially setup state. |
143 | */ | 143 | */ |
144 | local_irq_save(flags); | 144 | local_irq_save(flags); |
145 | 145 | /* | |
146 | * We don't really care if we're overwriting some other | ||
147 | * (lock,want) pair, as that would mean that we're currently | ||
148 | * in an interrupt context, and the outer context had | ||
149 | * interrupts enabled. That has already kicked the VCPU out | ||
150 | * of xen_poll_irq(), so it will just return spuriously and | ||
151 | * retry with newly setup (lock,want). | ||
152 | * | ||
153 | * The ordering protocol on this is that the "lock" pointer | ||
154 | * may only be set non-NULL if the "want" ticket is correct. | ||
155 | * If we're updating "want", we must first clear "lock". | ||
156 | */ | ||
157 | w->lock = NULL; | ||
158 | smp_wmb(); | ||
146 | w->want = want; | 159 | w->want = want; |
147 | smp_wmb(); | 160 | smp_wmb(); |
148 | w->lock = lock; | 161 | w->lock = lock; |
@@ -157,24 +170,43 @@ static void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want) | |||
157 | /* Only check lock once pending cleared */ | 170 | /* Only check lock once pending cleared */ |
158 | barrier(); | 171 | barrier(); |
159 | 172 | ||
160 | /* Mark entry to slowpath before doing the pickup test to make | 173 | /* |
161 | sure we don't deadlock with an unlocker. */ | 174 | * Mark entry to slowpath before doing the pickup test to make |
175 | * sure we don't deadlock with an unlocker. | ||
176 | */ | ||
162 | __ticket_enter_slowpath(lock); | 177 | __ticket_enter_slowpath(lock); |
163 | 178 | ||
164 | /* check again make sure it didn't become free while | 179 | /* |
165 | we weren't looking */ | 180 | * check again make sure it didn't become free while |
181 | * we weren't looking | ||
182 | */ | ||
166 | if (ACCESS_ONCE(lock->tickets.head) == want) { | 183 | if (ACCESS_ONCE(lock->tickets.head) == want) { |
167 | add_stats(TAKEN_SLOW_PICKUP, 1); | 184 | add_stats(TAKEN_SLOW_PICKUP, 1); |
168 | goto out; | 185 | goto out; |
169 | } | 186 | } |
187 | |||
188 | /* Allow interrupts while blocked */ | ||
189 | local_irq_restore(flags); | ||
190 | |||
191 | /* | ||
192 | * If an interrupt happens here, it will leave the wakeup irq | ||
193 | * pending, which will cause xen_poll_irq() to return | ||
194 | * immediately. | ||
195 | */ | ||
196 | |||
170 | /* Block until irq becomes pending (or perhaps a spurious wakeup) */ | 197 | /* Block until irq becomes pending (or perhaps a spurious wakeup) */ |
171 | xen_poll_irq(irq); | 198 | xen_poll_irq(irq); |
172 | add_stats(TAKEN_SLOW_SPURIOUS, !xen_test_irq_pending(irq)); | 199 | add_stats(TAKEN_SLOW_SPURIOUS, !xen_test_irq_pending(irq)); |
200 | |||
201 | local_irq_save(flags); | ||
202 | |||
173 | kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); | 203 | kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); |
174 | out: | 204 | out: |
175 | cpumask_clear_cpu(cpu, &waiting_cpus); | 205 | cpumask_clear_cpu(cpu, &waiting_cpus); |
176 | w->lock = NULL; | 206 | w->lock = NULL; |
207 | |||
177 | local_irq_restore(flags); | 208 | local_irq_restore(flags); |
209 | |||
178 | spin_time_accum_blocked(start); | 210 | spin_time_accum_blocked(start); |
179 | } | 211 | } |
180 | PV_CALLEE_SAVE_REGS_THUNK(xen_lock_spinning); | 212 | PV_CALLEE_SAVE_REGS_THUNK(xen_lock_spinning); |
@@ -188,7 +220,9 @@ static void xen_unlock_kick(struct arch_spinlock *lock, __ticket_t next) | |||
188 | for_each_cpu(cpu, &waiting_cpus) { | 220 | for_each_cpu(cpu, &waiting_cpus) { |
189 | const struct xen_lock_waiting *w = &per_cpu(lock_waiting, cpu); | 221 | const struct xen_lock_waiting *w = &per_cpu(lock_waiting, cpu); |
190 | 222 | ||
191 | if (w->lock == lock && w->want == next) { | 223 | /* Make sure we read lock before want */ |
224 | if (ACCESS_ONCE(w->lock) == lock && | ||
225 | ACCESS_ONCE(w->want) == next) { | ||
192 | add_stats(RELEASED_SLOW_KICKED, 1); | 226 | add_stats(RELEASED_SLOW_KICKED, 1); |
193 | xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); | 227 | xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); |
194 | break; | 228 | break; |