Skip to content

Commit 6fc3e18

Browse files
GuEe-GUIRbb666
authored andcommitted
fix: stabilize smp_bind_affinity_tc on SMP CI
This change fixes intermittent failures of the `core.smp_bind_affinity` utest across SMP CI targets. The original failure was a flaky assertion on unbound threads (`thread_inc[x] != thread_tic[x]` in thread_entry), not incorrect bind_cpu behavior. CI logs already showed T0 binding worked (thread_inc[0] == thread_tic[0] == 100) while the not_equal check failed. The root issue is that unbound-thread CPU placement is platform-dependent and outside the bind_cpu contract. On dual-core RISC-V/ARMv7 CI, an unbound thread may legitimately run all iterations on core 0 while T0 delays on the same core, so any assertion requiring cross-core migration (thread_inc != thread_tic, or core_mask with multiple bits) remains flaky. This patch narrows the test to what bind_affinity actually verifies: T0 bound to core 0 must always execute on core 0. Unbound threads only add scheduling pressure; their core_mask is printed for observation but not asserted. Changes: - Sample `rt_hw_cpu_id()` and update counters under `rt_sched_lock` to avoid migration skew between counting and CPU sampling. - Track `thread_core_mask` for diagnostics; assert only T0 binding via `thread_inc[0] == thread_tic[0]` and `thread_core_mask[0] == 1`. - Remove flaky unbound-thread assertions (`thread_inc != thread_tic` and multi-core core_mask checks). - Move T0 assertions to the main test thread after all workers finish. - Use atomic `finsh_flag`; reset counters and `threads[]` in `utest_tc_init`. - Spin in worker loops after `run_num` until cleanup deletes them (do not return from thread_entry or use `RT_WAITING_FOREVER` with `rt_thread_delay`). - Guard `rt_thread_delete` in cleanup with non-NULL checks. Signed-off-by: GuEe-GUI <2991707448@qq.com>
1 parent 72a09a6 commit 6fc3e18

2 files changed

Lines changed: 47 additions & 17 deletions

File tree

components/utilities/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ config RT_USING_UTEST
214214
if RT_USING_UTEST
215215
config UTEST_THR_STACK_SIZE
216216
int "The utest thread stack size"
217+
default 8192 if ARCH_CPU_64BIT
217218
default 4096
218219
config UTEST_THR_PRIORITY
219220
int "The utest thread priority"

src/utest/smp/smp_bind_affinity_tc.c

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@
2424
* - Verify that threads bound to specific cores run on those cores.
2525
*
2626
* Test Scenarios:
27-
* - RT_CPUS_NR threads (T0~T(RT_CPUS_NR-1)) are created, with only T0 bound to core 0. When thread Tx is running,
28-
* - thread_tic[x] increments by 1; if x is equal to the core ID, thread_inc[x] also increments by 1. After the
29-
* - program runs for a period of time, observe the value relationship between the thread_inc and thread_tic arrays.
30-
* - If thread_inc[x] is equal to thread_tic[x], it indicates that thread Tx is correctly bound to core x. In this test case,
31-
* - only thread_inc[0] will be equal to thread_tic[0].
27+
* - RT_CPUS_NR threads (T0~T(RT_CPUS_NR-1)) are created, with only T0 bound to core 0.
28+
* - Each thread samples rt_hw_cpu_id() while running; T0 must stay on core 0.
29+
* - Other threads are unbound and only used to add scheduling pressure; their CPU
30+
* - placement is printed for observation but not asserted (platform-dependent).
3231
*
3332
* Verification Metrics:
3433
* - Output message: [I/utest] [ PASSED ] [ result ] testcase (core.smp_bind_affinity)
@@ -50,29 +49,39 @@ static rt_thread_t threads[RT_CPUS_NR];
5049
static struct rt_spinlock lock;
5150
static int thread_inc[RT_CPUS_NR] = {0};
5251
static int thread_tic[RT_CPUS_NR] = {0};
53-
static int finsh_flag = 0;
52+
static rt_uint32_t thread_core_mask[RT_CPUS_NR] = {0};
53+
static rt_atomic_t finsh_flag;
5454
static int num = 0;
5555

5656
static void thread_entry(void *parameter)
5757
{
58-
int id = 0;
58+
int id;
5959
int para = *(int *)parameter;
60+
6061
while (1)
6162
{
62-
thread_tic[para]++;
63+
if (thread_tic[para] >= run_num)
64+
{
65+
/* Spin until cleanup deletes this thread (same as other smp utests) */
66+
while (1);
67+
}
68+
69+
rt_sched_lock_level_t slvl;
70+
71+
/* Sample CPU and counters atomically to avoid migration skew */
72+
rt_sched_lock(&slvl);
6373
id = rt_hw_cpu_id();
74+
thread_tic[para]++;
75+
thread_core_mask[para] |= (1u << id);
6476
if (para == id)
6577
{
6678
thread_inc[para]++;
6779
}
80+
rt_sched_unlock(slvl);
6881

6982
if (thread_tic[para] == run_num)
7083
{
71-
if (para == 0)
72-
uassert_int_equal(thread_inc[para], thread_tic[para]);
73-
else
74-
uassert_int_not_equal(thread_inc[para], thread_tic[para]);
75-
finsh_flag ++;
84+
rt_atomic_add(&finsh_flag, 1);
7685
}
7786
rt_thread_delay(5);
7887
}
@@ -104,28 +113,48 @@ static void thread_bind_affinity_tc(void)
104113
}
105114
}
106115

107-
while (finsh_flag != RT_CPUS_NR);
116+
while (rt_atomic_load(&finsh_flag) != RT_CPUS_NR);
108117

109-
/* Displays the number of times a thread was executed on the relevant core */
118+
/* Bound thread must always run on core 0 */
119+
uassert_int_equal(thread_inc[0], thread_tic[0]);
120+
uassert_int_equal(thread_core_mask[0], 1u);
121+
122+
/* Displays per-thread CPU statistics (unbound threads: observe only) */
110123
for (j = 0; j < RT_CPUS_NR; j++)
111124
{
112125
rt_spin_lock(&lock);
113-
rt_kprintf("Total runs[%d], Number of times thread[%d] run on [core%d]: [%4d], always run at core%d ? %s \r\n", run_num, j, j, thread_inc[j], j, (thread_inc[j] == run_num) ? "yes" : "no");
126+
rt_kprintf("Total runs[%d], Number of times thread[%d] run on [core%d]: [%4d], core mask: 0x%x, always run at core%d ? %s \r\n",
127+
run_num, j, j, thread_inc[j], thread_core_mask[j], j,
128+
(thread_inc[j] == run_num) ? "yes" : "no");
114129
rt_spin_unlock(&lock);
115130
}
116131
}
117132

118133
static rt_err_t utest_tc_init(void)
119134
{
135+
int i;
136+
120137
rt_spin_lock_init(&lock);
138+
rt_atomic_store(&finsh_flag, 0);
139+
for (i = 0; i < RT_CPUS_NR; i++)
140+
{
141+
threads[i] = RT_NULL;
142+
thread_inc[i] = 0;
143+
thread_tic[i] = 0;
144+
thread_core_mask[i] = 0;
145+
}
121146
return RT_EOK;
122147
}
123148

124149
static rt_err_t utest_tc_cleanup(void)
125150
{
126151
for (num = 0; num < RT_CPUS_NR; num++)
127152
{
128-
rt_thread_delete(threads[num]);
153+
if (threads[num] != RT_NULL)
154+
{
155+
rt_thread_delete(threads[num]);
156+
threads[num] = RT_NULL;
157+
}
129158
}
130159
return RT_EOK;
131160
}

0 commit comments

Comments
 (0)