/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _ASM_RQSPINLOCK_H #define _ASM_RQSPINLOCK_H #include /* * Hardcode res_smp_cond_load_acquire implementations for arm64 to a custom * version based on [0]. In rqspinlock code, our conditional expression involves * checking the value _and_ additionally a timeout. However, on arm64, the * WFE-based implementation may never spin again if no stores occur to the * locked byte in the lock word. As such, we may be stuck forever if * event-stream based unblocking is not available on the platform for WFE spin * loops (arch_timer_evtstrm_available). * * Once support for smp_cond_load_acquire_timewait [0] lands, we can drop this * copy-paste. * * While we rely on the implementation to amortize the cost of sampling * cond_expr for us, it will not happen when event stream support is * unavailable, time_expr check is amortized. This is not the common case, and * it would be difficult to fit our logic in the time_expr_ns >= time_limit_ns * comparison, hence just let it be. In case of event-stream, the loop is woken * up at microsecond granularity. * * [0]: https://lore.kernel.org/lkml/20250203214911.898276-1-ankur.a.arora@oracle.com */ #ifndef smp_cond_load_acquire_timewait #define smp_cond_time_check_count 200 #define __smp_cond_load_relaxed_spinwait(ptr, cond_expr, time_expr_ns, \ time_limit_ns) ({ \ typeof(ptr) __PTR = (ptr); \ __unqual_scalar_typeof(*ptr) VAL; \ unsigned int __count = 0; \ for (;;) { \ VAL = READ_ONCE(*__PTR); \ if (cond_expr) \ break; \ cpu_relax(); \ if (__count++ < smp_cond_time_check_count) \ continue; \ if ((time_expr_ns) >= (time_limit_ns)) \ break; \ __count = 0; \ } \ (typeof(*ptr))VAL; \ }) #define __smp_cond_load_acquire_timewait(ptr, cond_expr, \ time_expr_ns, time_limit_ns) \ ({ \ typeof(ptr) __PTR = (ptr); \ __unqual_scalar_typeof(*ptr) VAL; \ for (;;) { \ VAL = smp_load_acquire(__PTR); \ if (cond_expr) \ break; \ __cmpwait_relaxed(__PTR, VAL); \ if ((time_expr_ns) >= (time_limit_ns)) \ break; \ } \ (typeof(*ptr))VAL; \ }) #define smp_cond_load_acquire_timewait(ptr, cond_expr, \ time_expr_ns, time_limit_ns) \ ({ \ __unqual_scalar_typeof(*ptr) _val; \ int __wfe = arch_timer_evtstrm_available(); \ \ if (likely(__wfe)) { \ _val = __smp_cond_load_acquire_timewait(ptr, cond_expr, \ time_expr_ns, \ time_limit_ns); \ } else { \ _val = __smp_cond_load_relaxed_spinwait(ptr, cond_expr, \ time_expr_ns, \ time_limit_ns); \ smp_acquire__after_ctrl_dep(); \ } \ (typeof(*ptr))_val; \ }) #endif #define res_smp_cond_load_acquire_timewait(v, c) smp_cond_load_acquire_timewait(v, c, 0, 1) #include #endif /* _ASM_RQSPINLOCK_H */