The branch main has been updated by olce: URL: https://cgit.FreeBSD.org/src/commit/?id=be8e84c2e9240110ce99bb8d14259073340e4ef6
commit be8e84c2e9240110ce99bb8d14259073340e4ef6 Author: Olivier Certner <o...@freebsd.org> AuthorDate: 2025-06-20 02:36:09 +0000 Commit: Olivier Certner <o...@freebsd.org> CommitDate: 2025-06-20 02:42:16 +0000 runq: runq_not_empty() to support racy lookups again The 4BSD assumes that its sched_runnable(), which then calls runq_not_empty(), can be called racily (another CPU may be grabbing a thread from the global queue at the same time). This makes runq_not_empty() trip on an assertion in runq_find() detecting a mismatch between some bit in the status word and the state of the corresponding queue (empty/not empty). Re-implement runq_not_empty() in an independent and simpler way as an alternative to removing the assertion, which remains useful to detect races when removing or adding a thread in some runqueue. Fixes: 79f68322c892 ("runq: runq_check(): Re-implement on top of runq_findq()") MFC after: 1 month Event: Kitchener-Waterloo Hackathon 202506 Sponsored by: The FreeBSD Foundation --- sys/kern/kern_switch.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sys/kern/kern_switch.c b/sys/kern/kern_switch.c index 917f73682564..42fee61a99a0 100644 --- a/sys/kern/kern_switch.c +++ b/sys/kern/kern_switch.c @@ -493,7 +493,7 @@ int runq_findq(struct runq *const rq, const int lvl_min, const int lvl_max, runq_pred_t *const pred, void *const pred_data) { - rqsw_t const (*const rqsw)[RQSW_NB] = &rq->rq_status.rq_sw; + const rqsw_t (*const rqsw)[RQSW_NB] = &rq->rq_status.rq_sw; rqsw_t w; int i, last, idx; @@ -568,20 +568,27 @@ runq_first_thread(struct runq *const rq) /* * Return true if there are some processes of any priority on the run queue, - * false otherwise. Has no side effects. + * false otherwise. Has no side effects. Supports racy lookups (required by + * 4BSD). */ bool runq_not_empty(struct runq *rq) { - struct thread *const td = runq_first_thread(rq); + const rqsw_t (*const rqsw)[RQSW_NB] = &rq->rq_status.rq_sw; + int sw_idx; - if (td != NULL) { - CTR2(KTR_RUNQ, "runq_not_empty: idx=%d, td=%p", - td->td_rqindex, td); - return (true); + for (sw_idx = 0; sw_idx < RQSW_NB; ++sw_idx) { + const rqsw_t w = (*rqsw)[sw_idx]; + + if (w != 0) { + CTR3(KTR_RUNQ, "runq_not_empty: not empty; " + "rq=%p, sw_idx=%d, bits=" RQSW_PRI, + rq, sw_idx, w); + return (true); + } } - CTR0(KTR_RUNQ, "runq_not_empty: empty"); + CTR1(KTR_RUNQ, "runq_not_empty: empty; rq=%p", rq); return (false); }