dispatch_enqueue()'s FIFO-tail path used list_empty(&dsq->list) to decide whether to set dsq->first_task on enqueue. dsq->list can contain parked BPF iterator cursors (SCX_DSQ_LNODE_ITER_CURSOR), so list_empty() is not a reliable "no real task" check. If the last real task is unlinked while a cursor is parked, first_task becomes NULL; the next FIFO-tail enqueue then sees list_empty() == false and skips the first_task update, leaving scx_bpf_dsq_peek() returning NULL for a non-empty DSQ. Test dsq->first_task directly, which already tracks only real tasks and is maintained under dsq->lock.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -1495,11 +1495,13 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq, · 1494 unmodified lines | |||
| 1495 | if (!(dsq->id & SCX_DSQ_FLAG_BUILTIN)) | 1495 | if (!(dsq->id & SCX_DSQ_FLAG_BUILTIN)) |
| 1496 | rcu_assign_pointer(dsq->first_task, p); | 1496 | rcu_assign_pointer(dsq->first_task, p); |
| 1497 | } else { | 1497 | } else { |
| 1498 | bool was_empty; | 1498 | /* |
| 1499 | 1499 | * dsq->list can contain parked BPF iterator cursors, so | |
| 1500 | was_empty = list_empty(&dsq->list); | 1500 | * list_empty() here isn't a reliable proxy for "no real |
| 1501 | * task in the DSQ". Test dsq->first_task directly. | ||
| 1502 | */ | ||
| 1501 | list_add_tail(&p->scx.dsq_list.node, &dsq->list); | 1503 | list_add_tail(&p->scx.dsq_list.node, &dsq->list); |
| 1502 | if (was_empty && !(dsq->id & SCX_DSQ_FLAG_BUILTIN)) | 1504 | if (!dsq->first_task && !(dsq->id & SCX_DSQ_FLAG_BUILTIN)) |
| 1503 | rcu_assign_pointer(dsq->first_task, p); | 1505 | rcu_assign_pointer(dsq->first_task, p); |
| 1504 | } | 1506 | } |
| 1505 | } | 1507 | } |