From nobody Sun Feb 8 05:19:57 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7496B19E7F7 for ; Thu, 8 Jan 2026 02:43:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767840210; cv=none; b=qkR6Ot4V1O8uJDP29H4ffFQ3r1tlTbsTuluzhPzznWsH6r4iou9bw+k8+3ih2oY7U0I9JIiJJwYwXaXxbTBmJeI4y06zeEEyDkHH6GW+Y8+GgnxGf/vp5Lhm80TRlrEBrJm5Dgib4CiBgfzeSVq1m8ERzw2niQNXBth9raGKMSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767840210; c=relaxed/simple; bh=pV7nKUsmV0ycOI/J3NX0vIlJSSUJlDm8CIIePiC4f2E=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=M/TCsC78aSURmYJB7kbDxbwXJ+LNUpJBS46tiexDlgxHLQfP4xOtvVIdk7VgX9OX4jjlQ4jbb7Y1VWWF9oitf5iKXw9HDPZq+i7HInqImgxxMuG4d0i/dIGD2M06o8JsorxCk0/c4xBXunGFK1dQEUzRdq7ERRsJcM3tl6xy7RM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K1mZ3EZg; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K1mZ3EZg" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2a0c09bb78cso12393705ad.0 for ; Wed, 07 Jan 2026 18:43:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767840207; x=1768445007; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=1kcAJMO4egsMxZoR77v7VHbdTKin7I8kze8KTsR7pLU=; b=K1mZ3EZgLb3oAo6g/8edVpkv/vd9x9AkDgpvVvy/yWmsaBMHzdawjSl4zcBo4Y5g+U Xdvw1SQ7v4XF5A8TFGH81t+14FR77A6nD3E7KrloQ9FKzLe/wPcRmC4tdvzfnOxsp0jL HY90ROnCG4P8V4DRODweSfDyNs27eb5Vpgg92ntFkOpt7ivSsZ5UXoTBBv+7Lve/zR3p xHeeXvgWh/EAPFXbrTf4iuAV0zHWRBUNIZvtYZNNlD45SlPhr9X3E5txO1Qk4BxaG2nv nyU96CScsgOQm+ygBeowJgt2IXbxXGpkifPgfL2C+Fn49cCP9pUOBKHUWQmtRxxIOFKc Luew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767840207; x=1768445007; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=1kcAJMO4egsMxZoR77v7VHbdTKin7I8kze8KTsR7pLU=; b=f176s6hHmPsRWGsWEmrGEdX5YK6s/CGwNfsXOk4nAm4bdrWKA5guAbWx5kgOQgL4t7 87EPLffTfmyHawUpsJ64ZyuzTMh9HX9fIp/K9wZNw7eZ12TxmxwkV2cPSu9+B/0fNtHz k6syF7dRGBAovDSjz/M0l956NbhUU/IsKJfKAR8KJkk9dyVuPDs5zkeXd0m7cVHC5k9S BW3xqeU93Nt34Ef7dJQvmJz3cRJoHF6IOkms+B/Yw1VpFlv2ROkqdP6hJzG9NKdExXix DpPhWalwBO/oauPyPfoZaOZ7n9KN0R2klI9W9Z8EK8UAOlhybJGnBVskocjPZFExGXOr SGGw== X-Forwarded-Encrypted: i=1; AJvYcCU/qQEVXXfajNonnPxk+2ZfrqdWMJ2E3ULCLkgjCR+DKDDca3DqIzoheZ/KMofcMTVxXJixv1uUNDLOvmk=@vger.kernel.org X-Gm-Message-State: AOJu0YxoRkTfrj08T9D7KdQiyt2YHkEOThNS0sBG8OpP3NIXZVBfV664 40kk/VhFVpcn7BbQPFSIO9c9O29azybqBZDj+Fxvi4c1bNGVo6fVh6z3 X-Gm-Gg: AY/fxX4z+sb9sLC11FDmk34igxSnxbO3SzMfW83afw3FBjXpYLlaiFjDwVEifSCMRLR khOpeqKZT4DgmtNlGob6Ddx8meRLauVOAoHB8w3pzXzCIPrajtdNWbWPMOfa1kl9aWBQ2Rv1sBT qRl3eLLbPaw9poG9sd9WnBdzscwwhXuMwDG+F9Z1l8CHmUiVZ3BP7mnxnI9+e4785iEDVpt2Qws RAXinnBtgiMhAx0/Og1SuuvSRuBTy66LUXbCvacTOrJgsvtS3wtqBQs6OAaWl/utFzoY/+JGrv3 wU/mMislELi67of0YdCzvq1VyDHJGzLCGjxo7dIdUGgRxRt/RYhYllOzR38AeMB6fsY9jG7JJZK zwlBtbD/3M8hC3fn4C+guovN4kqdQ/9fm1qwwhuefIb96pSmF+U19cRUJXWqOJLf/8W2/INWfp2 DqYBchN3BZh2yWMQJ/k1JaZswfSKURpc9v/zAZ5SefhurpyiVhPg4a9LZRlrLAVw== X-Google-Smtp-Source: AGHT+IHEzq6SUR5Cuv7HfTy1t8yQHzRRNY7b6h/ul0CSCb5+Uy4M3ZeCnFTcc3IEYq3sFElKVS1QEA== X-Received: by 2002:a17:902:da8f:b0:2a0:c92e:a378 with SMTP id d9443c01a7336-2a3edb24516mr45800115ad.7.1767840206551; Wed, 07 Jan 2026 18:43:26 -0800 (PST) Received: from mi-HP-ProDesk-680-G6-PCI-Microtower-PC.mioffice.cn ([43.224.245.226]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a3e3cc8ddfsm61676225ad.74.2026.01.07.18.43.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Jan 2026 18:43:26 -0800 (PST) From: zhidao su X-Google-Original-From: zhidao su To: tj@kernel.org, void@manifault.com, arighi@nvidia.com, changwoo@igalia.com Cc: sched-ext@lists.linux.dev, linux-kernel@vger.kernel.org, zhidao su Subject: [PATCH] sched_ext: Add DSQ statistics collection functionality Date: Thu, 8 Jan 2026 10:35:54 +0800 Message-ID: <20260108023554.1735052-1-suzhidao@xiaomi.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds statistics collection to the Dispatch Queue (DSQ) system in the sched_ext scheduler. Statistics tracked: 1. Total enqueue count (number of tasks enqueued to a DSQ) 2. Total dequeue count (number of tasks dequeued from a DSQ) 3. Peak queue length (maximum number of tasks simultaneously queued) New BPF interface functions: - scx_bpf_dsq_enqueue_count(dsq_id): Get total enqueue count - scx_bpf_dsq_dequeue_count(dsq_id): Get total dequeue count - scx_bpf_dsq_peak_nr(dsq_id): Get peak queue length Implementation uses atomic operations for concurrency safety and follows existing kernel patterns for statistics collection. Compatibility macros are provided in compat.bpf.h for backward compatibility. A comprehensive test case (dsq_stats) is included to verify functionality. Signed-off-by: zhidao su --- include/linux/sched/ext.h | 6 + kernel/sched/ext.c | 156 +++++++++++++++++- tools/sched_ext/include/scx/compat.bpf.h | 17 ++ tools/testing/selftests/sched_ext/Makefile | 1 + .../selftests/sched_ext/dsq_stats.bpf.c | 101 ++++++++++++ tools/testing/selftests/sched_ext/dsq_stats.c | 147 +++++++++++++++++ 6 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/sched_ext/dsq_stats.bpf.c create mode 100644 tools/testing/selftests/sched_ext/dsq_stats.c diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index bcb962d5ee7d..b79bedc26388 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -13,6 +13,7 @@ =20 #include #include +#include =20 enum scx_public_consts { SCX_OPS_NAME_LEN =3D 128, @@ -79,6 +80,11 @@ struct scx_dispatch_q { struct rhash_head hash_node; struct llist_node free_node; struct rcu_head rcu; + + /* Statistics fields */ + atomic64_t enqueue_count; + atomic64_t dequeue_count; + atomic_t peak_nr; }; =20 /* scx_entity.flags */ diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 136b01950a62..a025d68acdef 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -972,8 +972,20 @@ static bool scx_dsq_priq_less(struct rb_node *node_a, =20 static void dsq_mod_nr(struct scx_dispatch_q *dsq, s32 delta) { + u32 new_nr; + /* scx_bpf_dsq_nr_queued() reads ->nr without locking, use WRITE_ONCE() */ - WRITE_ONCE(dsq->nr, dsq->nr + delta); + new_nr =3D dsq->nr + delta; + WRITE_ONCE(dsq->nr, new_nr); + + /* Update peak queue length */ + if (delta > 0) { + + u32 peak =3D atomic_read(&dsq->peak_nr); + + if (new_nr > peak) + atomic_set(&dsq->peak_nr, new_nr); + } } =20 static void refill_task_slice_dfl(struct scx_sched *sch, struct task_struc= t *p) @@ -1099,6 +1111,7 @@ static void dispatch_enqueue(struct scx_sched *sch, s= truct scx_dispatch_q *dsq, p->scx.dsq_seq =3D dsq->seq; =20 dsq_mod_nr(dsq, 1); + atomic64_inc(&dsq->enqueue_count); /* Increment enqueue count */ p->scx.dsq =3D dsq; =20 /* @@ -1136,6 +1149,7 @@ static void task_unlink_from_dsq(struct task_struct *= p, =20 list_del_init(&p->scx.dsq_list.node); dsq_mod_nr(dsq, -1); + atomic64_inc(&dsq->dequeue_count); /* Increment dequeue count */ =20 if (!(dsq->id & SCX_DSQ_FLAG_BUILTIN) && dsq->first_task =3D=3D p) { struct task_struct *first_task; @@ -3396,6 +3410,11 @@ static void init_dsq(struct scx_dispatch_q *dsq, u64= dsq_id) raw_spin_lock_init(&dsq->lock); INIT_LIST_HEAD(&dsq->list); dsq->id =3D dsq_id; + + /* Initialize statistics */ + atomic64_set(&dsq->enqueue_count, 0); + atomic64_set(&dsq->dequeue_count, 0); + atomic_set(&dsq->peak_nr, 0); } =20 static void free_dsq_irq_workfn(struct irq_work *irq_work) @@ -6476,6 +6495,138 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id) return ret; } =20 +/** + * scx_bpf_dsq_enqueue_count - Return the total number of enqueued tasks + * @dsq_id: id of the DSQ + * + * Return the total number of tasks that have been enqueued to the DSQ + * matching @dsq_id. If not found, -%ENOENT is returned. + */ +__bpf_kfunc s64 scx_bpf_dsq_enqueue_count(u64 dsq_id) +{ + struct scx_sched *sch; + struct scx_dispatch_q *dsq; + s64 ret; + + preempt_disable(); + + sch =3D rcu_dereference_sched(scx_root); + if (unlikely(!sch)) { + ret =3D -ENODEV; + goto out; + } + + if (dsq_id =3D=3D SCX_DSQ_LOCAL) { + ret =3D atomic64_read(&this_rq()->scx.local_dsq.enqueue_count); + goto out; + } else if ((dsq_id & SCX_DSQ_LOCAL_ON) =3D=3D SCX_DSQ_LOCAL_ON) { + s32 cpu =3D dsq_id & SCX_DSQ_LOCAL_CPU_MASK; + + if (ops_cpu_valid(sch, cpu, NULL)) { + ret =3D atomic64_read(&cpu_rq(cpu)->scx.local_dsq.enqueue_count); + goto out; + } + } else { + dsq =3D find_user_dsq(sch, dsq_id); + if (dsq) { + ret =3D atomic64_read(&dsq->enqueue_count); + goto out; + } + } + ret =3D -ENOENT; +out: + preempt_enable(); + return ret; +} + +/** + * scx_bpf_dsq_dequeue_count - Return the total number of dequeued tasks + * @dsq_id: id of the DSQ + * + * Return the total number of tasks that have been dequeued from the DSQ + * matching @dsq_id. If not found, -%ENOENT is returned. + */ +__bpf_kfunc s64 scx_bpf_dsq_dequeue_count(u64 dsq_id) +{ + struct scx_sched *sch; + struct scx_dispatch_q *dsq; + s64 ret; + + preempt_disable(); + + sch =3D rcu_dereference_sched(scx_root); + if (unlikely(!sch)) { + ret =3D -ENODEV; + goto out; + } + + if (dsq_id =3D=3D SCX_DSQ_LOCAL) { + ret =3D atomic64_read(&this_rq()->scx.local_dsq.dequeue_count); + goto out; + } else if ((dsq_id & SCX_DSQ_LOCAL_ON) =3D=3D SCX_DSQ_LOCAL_ON) { + s32 cpu =3D dsq_id & SCX_DSQ_LOCAL_CPU_MASK; + + if (ops_cpu_valid(sch, cpu, NULL)) { + ret =3D atomic64_read(&cpu_rq(cpu)->scx.local_dsq.dequeue_count); + goto out; + } + } else { + dsq =3D find_user_dsq(sch, dsq_id); + if (dsq) { + ret =3D atomic64_read(&dsq->dequeue_count); + goto out; + } + } + ret =3D -ENOENT; +out: + preempt_enable(); + return ret; +} + +/** + * scx_bpf_dsq_peak_nr - Return the peak number of queued tasks + * @dsq_id: id of the DSQ + * + * Return the peak number of tasks that have been simultaneously queued in + * the DSQ matching @dsq_id. If not found, -%ENOENT is returned. + */ +__bpf_kfunc s32 scx_bpf_dsq_peak_nr(u64 dsq_id) +{ + struct scx_sched *sch; + struct scx_dispatch_q *dsq; + s32 ret; + + preempt_disable(); + + sch =3D rcu_dereference_sched(scx_root); + if (unlikely(!sch)) { + ret =3D -ENODEV; + goto out; + } + + if (dsq_id =3D=3D SCX_DSQ_LOCAL) { + ret =3D atomic_read(&this_rq()->scx.local_dsq.peak_nr); + goto out; + } else if ((dsq_id & SCX_DSQ_LOCAL_ON) =3D=3D SCX_DSQ_LOCAL_ON) { + s32 cpu =3D dsq_id & SCX_DSQ_LOCAL_CPU_MASK; + + if (ops_cpu_valid(sch, cpu, NULL)) { + ret =3D atomic_read(&cpu_rq(cpu)->scx.local_dsq.peak_nr); + goto out; + } + } else { + dsq =3D find_user_dsq(sch, dsq_id); + if (dsq) { + ret =3D atomic_read(&dsq->peak_nr); + goto out; + } + } + ret =3D -ENOENT; +out: + preempt_enable(); + return ret; +} + /** * scx_bpf_destroy_dsq - Destroy a custom DSQ * @dsq_id: DSQ to destroy @@ -7221,6 +7372,9 @@ BTF_ID_FLAGS(func, scx_bpf_task_set_slice, KF_RCU); BTF_ID_FLAGS(func, scx_bpf_task_set_dsq_vtime, KF_RCU); BTF_ID_FLAGS(func, scx_bpf_kick_cpu) BTF_ID_FLAGS(func, scx_bpf_dsq_nr_queued) +BTF_ID_FLAGS(func, scx_bpf_dsq_enqueue_count) +BTF_ID_FLAGS(func, scx_bpf_dsq_dequeue_count) +BTF_ID_FLAGS(func, scx_bpf_dsq_peak_nr) BTF_ID_FLAGS(func, scx_bpf_destroy_dsq) BTF_ID_FLAGS(func, scx_bpf_dsq_peek, KF_RCU_PROTECTED | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_iter_scx_dsq_new, KF_ITER_NEW | KF_RCU_PROTECTED) diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/inc= lude/scx/compat.bpf.h index f2969c3061a7..8222ef8055df 100644 --- a/tools/sched_ext/include/scx/compat.bpf.h +++ b/tools/sched_ext/include/scx/compat.bpf.h @@ -375,6 +375,23 @@ static inline void scx_bpf_reenqueue_local(void) scx_bpf_reenqueue_local___v1(); } =20 +/* + * v6.20: Add DSQ statistics functions. + * + * Preserve the following macros until v6.22. + */ +#define __COMPAT_scx_bpf_dsq_enqueue_count(dsq_id) \ + (bpf_ksym_exists(scx_bpf_dsq_enqueue_count) ? \ + scx_bpf_dsq_enqueue_count(dsq_id) : -ENOENT) + +#define __COMPAT_scx_bpf_dsq_dequeue_count(dsq_id) \ + (bpf_ksym_exists(scx_bpf_dsq_dequeue_count) ? \ + scx_bpf_dsq_dequeue_count(dsq_id) : -ENOENT) + +#define __COMPAT_scx_bpf_dsq_peak_nr(dsq_id) \ + (bpf_ksym_exists(scx_bpf_dsq_peak_nr) ? \ + scx_bpf_dsq_peak_nr(dsq_id) : -ENOENT) + /* * Define sched_ext_ops. This may be expanded to define multiple variants = for * backward compatibility. See compat.h::SCX_OPS_LOAD/ATTACH(). diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/sel= ftests/sched_ext/Makefile index 5fe45f9c5f8f..546534159d74 100644 --- a/tools/testing/selftests/sched_ext/Makefile +++ b/tools/testing/selftests/sched_ext/Makefile @@ -161,6 +161,7 @@ all_test_bpfprogs :=3D $(foreach prog,$(wildcard *.bpf.= c),$(INCLUDE_DIR)/$(patsubs =20 auto-test-targets :=3D \ create_dsq \ + dsq_stats \ enq_last_no_enq_fails \ ddsp_bogus_dsq_fail \ ddsp_vtimelocal_fail \ diff --git a/tools/testing/selftests/sched_ext/dsq_stats.bpf.c b/tools/test= ing/selftests/sched_ext/dsq_stats.bpf.c new file mode 100644 index 000000000000..bd3f8ac78456 --- /dev/null +++ b/tools/testing/selftests/sched_ext/dsq_stats.bpf.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A BPF program for testing DSQ statistics functionality. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + */ + +#include +#include + +char _license[] SEC("license") =3D "GPL"; + +UEI_DEFINE(uei); /* Error handling */ + +#define TEST_DSQ_ID 1234 + +/* Test variables to store results */ +s64 test_enqueue_count =3D -1; +s64 test_dequeue_count =3D -1; +s32 test_peak_nr =3D -1; +s32 current_nr =3D -1; + +/* Test completion flag */ +int test_completed; + +/* Test task */ +int test_pid =3D -1; + +static void test_dsq_stats(void) +{ + s64 enqueue_count, dequeue_count; + s32 peak_nr, nr; + + /* Test the new statistics functions */ + enqueue_count =3D __COMPAT_scx_bpf_dsq_enqueue_count(TEST_DSQ_ID); + dequeue_count =3D __COMPAT_scx_bpf_dsq_dequeue_count(TEST_DSQ_ID); + peak_nr =3D __COMPAT_scx_bpf_dsq_peak_nr(TEST_DSQ_ID); + nr =3D scx_bpf_dsq_nr_queued(TEST_DSQ_ID); + + /* Store results */ + test_enqueue_count =3D enqueue_count; + test_dequeue_count =3D dequeue_count; + test_peak_nr =3D peak_nr; + current_nr =3D nr; + + test_completed =3D 1; +} + +void BPF_STRUCT_OPS(dsq_stats_enqueue, struct task_struct *p, u64 enq_flag= s) +{ + /* Create DSQ on first task */ + if (test_pid =3D=3D -1) { + test_pid =3D p->pid; + + /* Create test DSQ */ + if (scx_bpf_create_dsq(TEST_DSQ_ID, -1)) { + bpf_printk("Failed to create test DSQ\n"); + return; + } + + /* Insert task into DSQ to test statistics */ + scx_bpf_dsq_insert(p, TEST_DSQ_ID, 0, enq_flags); + } +} + +void BPF_STRUCT_OPS(dsq_stats_dispatch, s32 cpu, struct task_struct *prev) +{ + /* Run test if not completed */ + if (!test_completed && test_pid !=3D -1) { + test_dsq_stats(); + + /* Consume the task to complete the test */ + scx_bpf_dsq_move_to_local(TEST_DSQ_ID); + } +} + +s32 BPF_STRUCT_OPS_SLEEPABLE(dsq_stats_init) +{ + /* Initialize test variables */ + test_pid =3D -1; + test_completed =3D 0; + + return 0; +} + +void BPF_STRUCT_OPS(dsq_stats_exit, struct scx_exit_info *ei) +{ + /* Destroy test DSQ */ + scx_bpf_destroy_dsq(TEST_DSQ_ID); + + UEI_RECORD(uei, ei); +} + +SEC(".struct_ops.link") +struct sched_ext_ops dsq_stats_ops =3D { + .enqueue =3D (void *)dsq_stats_enqueue, + .dispatch =3D (void *)dsq_stats_dispatch, + .init =3D (void *)dsq_stats_init, + .exit =3D (void *)dsq_stats_exit, + .name =3D "dsq_stats", +}; diff --git a/tools/testing/selftests/sched_ext/dsq_stats.c b/tools/testing/= selftests/sched_ext/dsq_stats.c new file mode 100644 index 000000000000..b54912a0d2ca --- /dev/null +++ b/tools/testing/selftests/sched_ext/dsq_stats.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test for DSQ statistics functionality. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + */ +#include +#include +#include +#include +#include +#include +#include +#include "dsq_stats.bpf.skel.h" +#include "scx_test.h" + +#define NUM_WORKERS 2 + +static bool workload_running =3D true; +static pthread_t workload_threads[NUM_WORKERS]; + +/** + * Background workload thread that exercises the scheduler to trigger + * DSQ operations and statistics collection. + */ +static void *workload_thread_fn(void *arg) +{ + while (workload_running) { + /* Sleep for a very short time to trigger scheduler activity */ + usleep(1000); /* 1ms sleep */ + /* Yield to ensure we go through the scheduler */ + sched_yield(); + } + return NULL; +} + +static enum scx_test_status setup(void **ctx) +{ + struct dsq_stats *skel; + int i, ret; + + skel =3D dsq_stats__open(); + SCX_FAIL_IF(!skel, "Failed to open"); + + SCX_ENUM_INIT(skel); + SCX_FAIL_IF(dsq_stats__load(skel), "Failed to load skel"); + + /* Start background workload threads */ + for (i =3D 0; i < NUM_WORKERS; i++) { + ret =3D pthread_create(&workload_threads[i], NULL, workload_thread_fn, N= ULL); + SCX_FAIL_IF(ret, "Failed to create workload thread %d", i); + } + + *ctx =3D skel; + + return SCX_TEST_PASS; +} + +static enum scx_test_status run(void *ctx) +{ + struct dsq_stats *skel =3D ctx; + struct bpf_link *link; + int duration =3D 2; /* Run test for 2 seconds */ + + link =3D bpf_map__attach_struct_ops(skel->maps.dsq_stats_ops); + SCX_FAIL_IF(!link, "Failed to attach scheduler"); + + /* Let the scheduler run for a while to collect statistics */ + sleep(duration); + + workload_running =3D false; + + bpf_link__destroy(link); + + return SCX_TEST_PASS; +} + +static enum scx_test_status check_results(void *ctx) +{ + struct dsq_stats *skel =3D ctx; + + /* Wait for test to complete */ + int timeout =3D 50; /* 5 seconds timeout */ + + while (!skel->bss->test_completed && timeout > 0) { + usleep(100000); /* 100ms */ + timeout--; + } + + SCX_FAIL_IF(timeout <=3D 0, "Test timed out waiting for completion"); + + /* Check that statistics were collected */ + SCX_FAIL_IF(skel->bss->test_enqueue_count < 0, + "Enqueue count not collected: %lld", + (long long)skel->bss->test_enqueue_count); + + SCX_FAIL_IF(skel->bss->test_dequeue_count < 0, + "Dequeue count not collected: %lld", + (long long)skel->bss->test_dequeue_count); + + SCX_FAIL_IF(skel->bss->test_peak_nr < 0, + "Peak NR not collected: %d", + skel->bss->test_peak_nr); + + /* Basic sanity checks */ + SCX_FAIL_IF(skel->bss->test_enqueue_count !=3D skel->bss->test_dequeue_co= unt, + "Enqueue count (%lld) !=3D Dequeue count (%lld)", + (long long)skel->bss->test_enqueue_count, + (long long)skel->bss->test_dequeue_count); + + SCX_FAIL_IF(skel->bss->test_peak_nr < skel->bss->current_nr, + "Peak NR (%d) < Current NR (%d)", + skel->bss->test_peak_nr, skel->bss->current_nr); + + bpf_printk("DSQ Stats Test Results:\n"); + bpf_printk(" Enqueue Count: %lld\n", (long long)skel->bss->test_enqueue_= count); + bpf_printk(" Dequeue Count: %lld\n", (long long)skel->bss->test_dequeue_= count); + bpf_printk(" Peak NR: %d\n", skel->bss->test_peak_nr); + bpf_printk(" Current NR: %d\n", skel->bss->current_nr); + + return SCX_TEST_PASS; +} + +static void cleanup(void *ctx) +{ + struct dsq_stats *skel =3D ctx; + int i; + + /* Stop workload threads */ + workload_running =3D false; + for (i =3D 0; i < NUM_WORKERS; i++) { + if (workload_threads[i]) + pthread_join(workload_threads[i], NULL); + } + + dsq_stats__destroy(skel); +} + +struct scx_test dsq_stats =3D { + .name =3D "dsq_stats", + .description =3D "Test DSQ statistics functionality", + .setup =3D setup, + .run =3D run, + .check_results =3D check_results, + .cleanup =3D cleanup, +}; +REGISTER_SCX_TEST(&dsq_stats) --=20 2.43.0