On Wed, 19 May 2021 00:27:51 -0400 George Koehler <kern...@gmail.com> wrote:
> Here's my new diff. It is a copy of what I sent to ppc@ a moment ago. > I would ask, "Is it ok to commit?", but while writing this mail, I > found a 2nd potential problem with memory barriers, so I will need to > edit this diff yet again. I edited the diff to add a 2nd membar_exit() and a comment. I want to commit this version, ok? Index: arch/powerpc/include/mplock.h =================================================================== RCS file: /cvs/src/sys/arch/powerpc/include/mplock.h,v retrieving revision 1.4 diff -u -p -r1.4 mplock.h --- arch/powerpc/include/mplock.h 15 Apr 2020 08:09:00 -0000 1.4 +++ arch/powerpc/include/mplock.h 20 May 2021 19:47:52 -0000 @@ -30,13 +30,14 @@ #define __USE_MI_MPLOCK /* + * __ppc_lock exists because pte_spill_r() can't use __mp_lock. * Really simple spinlock implementation with recursive capabilities. * Correctness is paramount, no fancyness allowed. */ struct __ppc_lock { - volatile struct cpu_info *mpl_cpu; - volatile long mpl_count; + struct cpu_info *volatile mpl_cpu; + long mpl_count; }; #ifndef _LOCORE @@ -44,10 +45,6 @@ struct __ppc_lock { void __ppc_lock_init(struct __ppc_lock *); void __ppc_lock(struct __ppc_lock *); void __ppc_unlock(struct __ppc_lock *); -int __ppc_release_all(struct __ppc_lock *); -int __ppc_release_all_but_one(struct __ppc_lock *); -void __ppc_acquire_count(struct __ppc_lock *, int); -int __ppc_lock_held(struct __ppc_lock *, struct cpu_info *); #endif Index: arch/powerpc/powerpc/lock_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/powerpc/powerpc/lock_machdep.c,v retrieving revision 1.9 diff -u -p -r1.9 lock_machdep.c --- arch/powerpc/powerpc/lock_machdep.c 15 Apr 2020 08:09:00 -0000 1.9 +++ arch/powerpc/powerpc/lock_machdep.c 20 May 2021 19:47:52 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: lock_machdep.c,v 1.9 2020/04/15 08:09:00 mpi Exp $ */ /* + * Copyright (c) 2021 George Koehler <gkoeh...@openbsd.org> * Copyright (c) 2007 Artur Grabowski <a...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -22,10 +23,21 @@ #include <sys/atomic.h> #include <machine/cpu.h> -#include <machine/psl.h> #include <ddb/db_output.h> +/* + * If __ppc_lock() crosses a page boundary in the kernel text, then it + * may cause a page fault (on G5 with ppc_nobat), and pte_spill_r() + * would recursively call __ppc_lock(). The lock must be in a valid + * state when the page fault happens. We acquire or release the lock + * with a 32-bit atomic write to mpl_owner, so the lock is always in a + * valid state, before or after the write. + * + * Acquired the lock: mpl->mpl_cpu == curcpu() + * Released the lock: mpl->mpl_cpu == NULL + */ + void __ppc_lock_init(struct __ppc_lock *lock) { @@ -46,12 +58,12 @@ static __inline void __ppc_lock_spin(struct __ppc_lock *mpl) { #ifndef MP_LOCKDEBUG - while (mpl->mpl_count != 0) + while (mpl->mpl_cpu != NULL) CPU_BUSY_CYCLE(); #else int nticks = __mp_lock_spinout; - while (mpl->mpl_count != 0 && --nticks > 0) + while (mpl->mpl_cpu != NULL && --nticks > 0) CPU_BUSY_CYCLE(); if (nticks == 0) { @@ -65,32 +77,33 @@ void __ppc_lock(struct __ppc_lock *mpl) { /* - * Please notice that mpl_count gets incremented twice for the - * first lock. This is on purpose. The way we release the lock - * in mp_unlock is to decrement the mpl_count and then check if - * the lock should be released. Since mpl_count is what we're - * spinning on, decrementing it in mpl_unlock to 0 means that - * we can't clear mpl_cpu, because we're no longer holding the - * lock. In theory mpl_cpu doesn't need to be cleared, but it's - * safer to clear it and besides, setting mpl_count to 2 on the - * first lock makes most of this code much simpler. + * Please notice that mpl_count stays at 0 for the first lock. + * A page fault might recursively call __ppc_lock() after we + * set mpl_cpu, but before we can increase mpl_count. + * + * After we acquire the lock, we need a "bc; isync" memory + * barrier, but we might not reach the barrier before the next + * page fault. Then the fault's recursive __ppc_lock() must + * have a barrier. membar_enter() is just "isync" and must + * come after a conditional branch for holding the lock. */ while (1) { - int s; + struct cpu_info *owner = mpl->mpl_cpu; + struct cpu_info *ci = curcpu(); - s = ppc_intr_disable(); - if (atomic_cas_ulong(&mpl->mpl_count, 0, 1) == 0) { + if (owner == NULL) { + /* Try to acquire the lock. */ + if (atomic_cas_ptr(&mpl->mpl_cpu, NULL, ci) == NULL) { + membar_enter(); + break; + } + } else if (owner == ci) { + /* We hold the lock, but might need a barrier. */ membar_enter(); - mpl->mpl_cpu = curcpu(); - } - - if (mpl->mpl_cpu == curcpu()) { mpl->mpl_count++; - ppc_intr_enable(s); break; } - ppc_intr_enable(s); __ppc_lock_spin(mpl); } @@ -99,8 +112,6 @@ __ppc_lock(struct __ppc_lock *mpl) void __ppc_unlock(struct __ppc_lock *mpl) { - int s; - #ifdef MP_LOCKDEBUG if (mpl->mpl_cpu != curcpu()) { db_printf("__ppc_unlock(%p): not held lock\n", mpl); @@ -108,63 +119,16 @@ __ppc_unlock(struct __ppc_lock *mpl) } #endif - s = ppc_intr_disable(); - if (--mpl->mpl_count == 1) { - mpl->mpl_cpu = NULL; + /* + * If we get a page fault after membar_exit() and before + * releasing the lock, then then recursive call to + * __ppc_unlock() must also membar_exit(). + */ + if (mpl->mpl_count == 0) { membar_exit(); - mpl->mpl_count = 0; - } - ppc_intr_enable(s); -} - -int -__ppc_release_all(struct __ppc_lock *mpl) -{ - int rv = mpl->mpl_count - 1; - int s; - -#ifdef MP_LOCKDEBUG - if (mpl->mpl_cpu != curcpu()) { - db_printf("__ppc_release_all(%p): not held lock\n", mpl); - db_enter(); - } -#endif - - s = ppc_intr_disable(); - mpl->mpl_cpu = NULL; - membar_exit(); - mpl->mpl_count = 0; - ppc_intr_enable(s); - - return (rv); -} - -int -__ppc_release_all_but_one(struct __ppc_lock *mpl) -{ - int rv = mpl->mpl_count - 2; - -#ifdef MP_LOCKDEBUG - if (mpl->mpl_cpu != curcpu()) { - db_printf("__ppc_release_all_but_one(%p): not held lock\n", mpl); - db_enter(); + mpl->mpl_cpu = NULL; /* Release the lock. */ + } else { + membar_exit(); + mpl->mpl_count--; } -#endif - - mpl->mpl_count = 2; - - return (rv); -} - -void -__ppc_acquire_count(struct __ppc_lock *mpl, int count) -{ - while (count--) - __ppc_lock(mpl); -} - -int -__ppc_lock_held(struct __ppc_lock *mpl, struct cpu_info *ci) -{ - return mpl->mpl_cpu == ci; }