mirror of
https://github.com/torvalds/linux.git
synced 2026-09-26 10:02:02 +02:00
selftests/sched_ext: Test that ops.dequeue() can iterate the consumed DSQ
Add a scheduler whose ops.dequeue() iterates the source user DSQ with bpf_iter_scx_dsq. The iteration takes the DSQ's raw spinlock; on a kernel that runs ops.dequeue() while the consume path still holds that lock, the first task consumed self-deadlocks the CPU with IRQs disabled. The watchdog cannot recover from that state, so on an unfixed kernel this test wedges the system instead of failing cleanly. On a fixed kernel the scheduler runs clean and the test passes. Signed-off-by: fangqiurong <fangqiurong@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
parent
cb86607ada
commit
9ec7ba20c9
|
|
@ -164,6 +164,7 @@ all_test_bpfprogs := $(foreach prog,$(wildcard *.bpf.c),$(INCLUDE_DIR)/$(patsubs
|
|||
auto-test-targets := \
|
||||
create_dsq \
|
||||
dequeue \
|
||||
dequeue_iter \
|
||||
enq_last_no_enq_fails \
|
||||
ddsp_bogus_dsq_fail \
|
||||
ddsp_vtimelocal_fail \
|
||||
|
|
|
|||
73
tools/testing/selftests/sched_ext/dequeue_iter.bpf.c
Normal file
73
tools/testing/selftests/sched_ext/dequeue_iter.bpf.c
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ops.dequeue() of this scheduler iterates the user DSQ it consumes
|
||||
* tasks from with bpf_iter_scx_dsq, which takes the DSQ lock.
|
||||
* On a kernel that still runs ops.dequeue() with that lock held, the
|
||||
* iteration self-deadlocks the CPU - this test wedges the system on
|
||||
* unfixed kernels instead of failing cleanly.
|
||||
*
|
||||
* Copyright (c) 2026 fangqiurong <fangqiurong@kylinos.cn>
|
||||
*/
|
||||
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
UEI_DEFINE(uei);
|
||||
|
||||
#define TEST_DSQ_ID 1000
|
||||
|
||||
u64 dq_count;
|
||||
|
||||
s32 BPF_STRUCT_OPS_SLEEPABLE(dequeue_iter_init)
|
||||
{
|
||||
return scx_bpf_create_dsq(TEST_DSQ_ID, -1);
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS(dequeue_iter_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
return prev_cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dequeue_iter_enqueue, struct task_struct *p, u64 enq_flags)
|
||||
{
|
||||
scx_bpf_dsq_insert(p, TEST_DSQ_ID, SCX_SLICE_DFL, enq_flags);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dequeue_iter_dispatch, s32 cpu, struct task_struct *task)
|
||||
{
|
||||
scx_bpf_dsq_move_to_local(TEST_DSQ_ID, 0);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dequeue_iter_dequeue, struct task_struct *p, u64 deq_flags)
|
||||
{
|
||||
struct bpf_iter_scx_dsq it;
|
||||
struct task_struct *t;
|
||||
|
||||
if (!bpf_iter_scx_dsq_new(&it, TEST_DSQ_ID, 0)) {
|
||||
while ((t = bpf_iter_scx_dsq_next(&it)))
|
||||
;
|
||||
}
|
||||
bpf_iter_scx_dsq_destroy(&it);
|
||||
|
||||
__sync_fetch_and_add(&dq_count, 1);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dequeue_iter_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
scx_bpf_destroy_dsq(TEST_DSQ_ID);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops dequeue_iter_ops = {
|
||||
.init = (void *)dequeue_iter_init,
|
||||
.select_cpu = (void *)dequeue_iter_select_cpu,
|
||||
.enqueue = (void *)dequeue_iter_enqueue,
|
||||
.dispatch = (void *)dequeue_iter_dispatch,
|
||||
.dequeue = (void *)dequeue_iter_dequeue,
|
||||
.exit = (void *)dequeue_iter_exit,
|
||||
.timeout_ms = 1000U,
|
||||
.name = "dequeue_iter",
|
||||
};
|
||||
79
tools/testing/selftests/sched_ext/dequeue_iter.c
Normal file
79
tools/testing/selftests/sched_ext/dequeue_iter.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2026 fangqiurong <fangqiurong@kylinos.cn>
|
||||
*/
|
||||
#include <bpf/bpf.h>
|
||||
#include <scx/common.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "dequeue_iter.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
#define DQ_TARGET 10
|
||||
#define DQ_DEADLINE_MS 3000
|
||||
|
||||
static unsigned long long now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct dequeue_iter *skel;
|
||||
|
||||
skel = dequeue_iter__open();
|
||||
SCX_FAIL_IF(!skel, "Failed to open");
|
||||
SCX_ENUM_INIT(skel);
|
||||
SCX_FAIL_IF(dequeue_iter__load(skel), "Failed to load skel");
|
||||
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct dequeue_iter *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
unsigned long long end;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.dequeue_iter_ops);
|
||||
SCX_FAIL_IF(!link, "Failed to attach scheduler");
|
||||
|
||||
end = now_ms() + DQ_DEADLINE_MS;
|
||||
while (skel->bss->dq_count < DQ_TARGET && !UEI_EXITED(skel, uei) &&
|
||||
now_ms() < end)
|
||||
usleep(100);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
|
||||
SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG));
|
||||
|
||||
if (skel->bss->dq_count < DQ_TARGET) {
|
||||
SCX_ERR("ops.dequeue() fired only %llu times",
|
||||
(unsigned long long)skel->bss->dq_count);
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct dequeue_iter *skel = ctx;
|
||||
|
||||
dequeue_iter__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test dequeue_iter = {
|
||||
.name = "dequeue_iter",
|
||||
.description = "Verify ops.dequeue() can iterate its source user DSQ",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&dequeue_iter)
|
||||
Loading…
Reference in New Issue
Block a user