sched/hrtimer: Refactor the hrtimer_test.

This commit refactored the hrtimer_test and provided significantly
improved test-cases for both SMP and non-SMP.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen 2026-02-25 09:48:19 +08:00 committed by Matteo Golin
parent d33830e83f
commit 256d6685d5

View file

@ -41,9 +41,9 @@
/* Timer constants */
#define HRTIMER_PERIOD_TEST_NR 15
#define HRTIMER_THREAD_LOOP_NR 50
#define HRTIMER_TEST_THREAD_NR (CONFIG_SMP_NCPUS * 5)
#define HRTIMER_TEST_RAND_ITER (1024 * 2)
#define HRTIMER_TEST_CSECTION 1024
#define HRTIMER_TEST_THREAD_NR (CONFIG_SMP_NCPUS * 8)
/* Set the tolerent latency to 10ms to allow hrtimer_test to pass
* in QEMU.
@ -62,6 +62,8 @@
#define HRTIMER_TEST_TOLERENT_LATENCY (10 * NSEC_PER_MSEC)
#define hrtimer_test_ndelay(delay_ns) usleep(delay_ns / 1000 + 1)
/****************************************************************************
* Private Types
****************************************************************************/
@ -71,99 +73,458 @@
typedef struct hrtimer_test_s
{
struct hrtimer_s timer; /* HRTimer instance */
spinlock_t lock; /* Spinlock */
volatile uint64_t timestamp; /* Previous timestamp in nanoseconds */
volatile uint64_t count; /* Number of timer expirations */
uint64_t period; /* Expected period between expirations */
volatile uint8_t state; /* Test state */
} hrtimer_test_t;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: test_hrtimer_callback
*
* Description:
* HRTimer callback function for test.
*
* - Verifies the timer interval is exactly 500ms (nanosecond precision)
* - Stops the test after 15 expirations
* - Re-arms the timer in absolute mode
*
* Input Parameters:
* hrtimer - Pointer to the expired HRTimer instance
* expired - The expired value of hrtimer
*
* Returned Value:
* Timer period in nanoseconds (NSEC_PER_50MS)
*
****************************************************************************/
static uint64_t
test_hrtimer_callback(FAR const hrtimer_t *hrtimer, uint64_t expired)
static uint64_t hrtimer_test_callback_oneshot(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
struct timespec ts;
int64_t diff;
uint64_t now;
int ret;
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
FAR struct hrtimer_test_s *test =
(FAR struct hrtimer_test_s *)hrtimer;
/* Save the timestamp when the callback was triggered */
/* Increment expiration count */
param->timestamp = clock_systime_nsec();
test->count++;
/* Increment the callback count */
/* Get current system time */
param->count++;
clock_systime_timespec(&ts);
now = clock_time2nsec(&ts);
return 0;
}
/* Verify the timer interval is exactly
* 500ms with nsec resolution
*/
static void hrtimer_test_checkdelay(uint64_t timestamp, uint64_t expected)
{
int64_t diff = timestamp - expected;
diff = now - expired;
/* Ensure the time diff is valid. */
/* Ensure the hrtimer trigger time is not earlier than expected. */
ASSERT(diff >= 0);
/* If the timer latency exceeds the tolerance, print a warning. */
if (diff > HRTIMER_TEST_TOLERENT_LATENCY)
{
printf("hrtimer_test: warning diff=%" PRIu64 " > %" PRIu64 "\n",
diff, HRTIMER_TEST_TOLERENT_LATENCY);
}
test->timestamp = now;
/* Stop the test after HRTIMER_PERIOD_TEST_NR expirations */
if (test->count < HRTIMER_PERIOD_TEST_NR)
{
return test->period;
}
else
{
test->active = false;
return 0;
printf("hrtimer_test: [WARNING] hrtimer latency %" PRId64
" is too late!!! (> %u)\n", diff,
(unsigned)HRTIMER_TEST_TOLERENT_LATENCY);
}
}
/****************************************************************************
* Name: hrtimer_test_callback
*
* Description:
* Simple HRTimer callback for threaded tests.
*
****************************************************************************/
static uint64_t
hrtimer_test_callback(FAR const hrtimer_t *hrtimer, uint64_t expired)
static void hrtimer_test_oneshot(FAR hrtimer_test_t *param, uint64_t delay)
{
uint64_t count;
uint64_t now;
FAR hrtimer_t *timer = &param->timer;
printf("hrtimer_test_oneshot %" PRIu64 " ns\n", delay);
/* Save the current callback count. */
count = param->count;
/* Save the current system time. */
now = clock_systime_nsec();
ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot,
delay + now, HRTIMER_MODE_ABS) == OK);
/* Wait until the callback is triggered exactly once. */
while (count + 1 != param->count)
{
hrtimer_test_ndelay(delay);
}
/* Check if the delay is within the acceptable tolerance. */
hrtimer_test_checkdelay(param->timestamp, now + delay);
/* Cancel the timer. */
hrtimer_cancel_sync(timer);
}
static void hrtimer_test_maximum(FAR hrtimer_test_t *param)
{
uint64_t count;
uint64_t rest;
FAR hrtimer_t *timer = &param->timer;
count = param->count;
/* Start the hrtimer with maximum */
ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot, UINT64_MAX,
HRTIMER_MODE_REL) == OK);
/* Sleep for at least 1s */
hrtimer_test_ndelay(USEC_PER_SEC / 100);
/* Ensure hrtimer is not alarmed */
ASSERT(count == param->count);
rest = hrtimer_gettime(timer);
ASSERT(rest < UINT64_MAX);
ASSERT(hrtimer_cancel_sync(timer) == OK);
printf("hrtimer_start with maximum delay, rest %" PRIu64 "\n", rest);
}
static void hrtimer_test_rand(FAR hrtimer_test_t *param, uint64_t rand_ns)
{
uint64_t count;
uint64_t now;
unsigned int idx;
uint64_t delay;
irqstate_t flags;
FAR hrtimer_t *timer = &param->timer;
printf("hrtimer_test_rand %" PRIu64 " ns\n", rand_ns);
/* Perform multiple iterations with random delays. */
for (idx = 0; idx < HRTIMER_TEST_RAND_ITER; idx++)
{
/* Generate a random delay within the specified range. */
delay = rand() % rand_ns;
ASSERT(timer->func == NULL);
/* Enter critical section if the callback count is odd. */
count = param->count;
if (count % 2u)
{
flags = up_irq_save();
}
now = clock_systime_nsec();
ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot,
delay, HRTIMER_MODE_REL) == 0);
if (count % 2u)
{
up_irq_restore(flags);
}
/* Decide to wait for the callback or cancel the hrtimer. */
if (delay % 2u)
{
/* Wait for the callback. */
while (count + 1u != param->count)
{
hrtimer_test_ndelay(delay);
}
/* Check the delay if the callback count is odd. */
if (count % 2u)
{
hrtimer_test_checkdelay(param->timestamp, now + delay);
}
}
hrtimer_cancel_sync(timer);
ASSERT(timer->func == NULL);
}
hrtimer_cancel_sync(timer);
}
static uint64_t hrtimer_test_cancel_callback(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
FAR spinlock_t *lock = &param->lock;
uint64_t delay = 0;
irqstate_t flags = spin_lock_irqsave(lock);
/* Random sleep */
delay = expired_ns % param->period;
/* Check if the version is same. */
if (expired_ns == timer->expired)
{
param->timestamp = clock_systime_nsec();
/* Increment the callback count */
param->count++;
}
spin_unlock_irqrestore(lock, flags);
up_ndelay(delay);
return 0;
}
static void hrtimer_test_rand_cancel(FAR hrtimer_test_t *param,
uint64_t rand_ns)
{
uint64_t now;
unsigned int idx;
uint64_t count;
uint64_t delay;
irqstate_t flags;
spinlock_t *lock = &param->lock;
printf("hrtimer_test_rand cancel %" PRIu64 " ns\n", rand_ns);
param->period = rand_ns;
/* Perform multiple iterations with random delays. */
for (idx = 0; idx < HRTIMER_TEST_RAND_ITER; idx++)
{
/* Generate a random delay within the specified range. */
delay = rand() % rand_ns;
flags = spin_lock_irqsave(lock);
now = clock_systime_nsec();
count = param->count;
ASSERT(hrtimer_start(&param->timer, hrtimer_test_cancel_callback,
delay, HRTIMER_MODE_REL) == 0);
spin_unlock_irqrestore(lock, flags);
/* Decide to wait for the callback or cancel the hrtimer. */
if (delay % 2u)
{
/* Wait for the callback finished. */
while (param->count != count + 1u)
{
hrtimer_test_ndelay(delay);
}
hrtimer_test_checkdelay(param->timestamp, now + delay);
}
hrtimer_cancel(&param->timer);
}
hrtimer_cancel_sync(&param->timer);
}
static uint64_t hrtimer_test_callback_period(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
uint64_t interval = param->period;
param->count++;
param->timestamp = clock_systime_nsec();
return interval;
}
static void hrtimer_test_period(FAR hrtimer_test_t *param,
uint64_t delay_ns,
unsigned int iters)
{
uint64_t timestamp;
uint64_t count = param->count;
FAR hrtimer_t *timer = &param->timer;
printf("hrtimer_test_period %" PRIu64 " ns\n", delay_ns);
param->period = delay_ns;
ASSERT(param->period > 0);
timestamp = clock_systime_nsec();
ASSERT(hrtimer_start(timer, hrtimer_test_callback_period,
delay_ns, HRTIMER_MODE_REL) == OK);
hrtimer_test_ndelay(iters * delay_ns);
hrtimer_cancel_sync(timer);
ASSERT(timer->func == NULL);
printf("periodical hrtimer triggered %" PRIu64 " times, "
"elapsed nsec %" PRIu64 "\n", param->count - count,
param->timestamp - timestamp);
if (param->count - count < iters)
{
printf("hrtimer_test: [WARNING] periodical hrtimer"
"triggered times < %u\n", iters);
}
}
#ifdef CONFIG_SMP
static uint64_t hrtimer_test_callback_crita(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
/* change status */
if (param->state == 0)
{
param->state = 1;
param->count++;
}
/* check whether parameter be changed by another critical section */
ASSERT(param->state == 1);
param->state = 0;
return 0;
}
static uint64_t hrtimer_test_callback_critb(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
/* change status */
if (param->state == 1)
{
param->state = 0;
param->count++;
}
/* check whether parameter be changed by another critical section */
ASSERT(param->state == 0);
param->state = 1;
return 0;
}
static uint64_t hrtimer_test_callback_critdelay(FAR const hrtimer_t *timer,
uint64_t expired_ns)
{
FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer;
FAR spinlock_t *lock = &param->lock;
irqstate_t flags;
flags = spin_lock_irqsave(lock);
param->count++;
spin_unlock_irqrestore(lock, flags);
up_ndelay(100 * NSEC_PER_USEC);
return 300 * NSEC_PER_USEC;
}
static void hrtimer_test_cancel_sync(FAR hrtimer_test_t *param)
{
unsigned int idx = 0;
ASSERT(!param->timer.func);
param->count = 0;
/* This test is to validate if the hrtimer can ensure the
* callback function be finished after the hrtimer_cancel_sync
* is called.
*/
for (idx = 0; idx < HRTIMER_TEST_CSECTION; )
{
param->state = 0;
hrtimer_start(&param->timer, hrtimer_test_callback_crita,
0, HRTIMER_MODE_REL);
hrtimer_cancel_sync(&param->timer);
param->state = 1;
hrtimer_start(&param->timer, hrtimer_test_callback_critb,
0, HRTIMER_MODE_REL);
if (++idx % (HRTIMER_TEST_CSECTION / 4) == 0)
{
printf("hrtimer_test_cancel_sync passed %d times.\n", idx);
}
hrtimer_cancel_sync(&param->timer);
}
}
static void hrtimer_test_cancel_periodic(FAR hrtimer_test_t *param)
{
uint64_t count;
unsigned int idx = 0;
FAR spinlock_t *lock = &param->lock;
ASSERT(!param->timer.func);
param->count = 0;
/* This test to check if the hrtimer can ensure the perodical callback
* can not restart the timer again after the hrtimer_cancel_sync is
* called.
*/
for (idx = 0; idx < HRTIMER_TEST_CSECTION; idx++)
{
irqstate_t flags = spin_lock_irqsave(lock);
hrtimer_start(&param->timer, hrtimer_test_callback_critdelay,
0, HRTIMER_MODE_REL);
spin_unlock_irqrestore(lock, flags);
up_ndelay(10000);
flags = spin_lock_irqsave(lock);
hrtimer_start(&param->timer, hrtimer_test_callback_critdelay,
0, HRTIMER_MODE_REL);
spin_unlock_irqrestore(lock, flags);
hrtimer_cancel(&param->timer);
up_ndelay(10000);
/* The hrtimer should not be restarted again after the cancellation. */
ASSERT(!param->timer.func);
hrtimer_cancel_sync(&param->timer);
count = param->count;
hrtimer_test_ndelay(10000);
ASSERT(count == param->count);
if (++idx % (HRTIMER_TEST_CSECTION / 4) == 0)
{
printf("hrtimer_test_cancel_periodic passed %d times. count %"
PRIu64 "\n", idx, param->count);
}
}
hrtimer_cancel_sync(&param->timer);
}
#endif
/****************************************************************************
* Name: hrtimer_test_thread
*
@ -174,56 +535,55 @@ hrtimer_test_callback(FAR const hrtimer_t *hrtimer, uint64_t expired)
static void * hrtimer_test_thread(void *arg)
{
hrtimer_test_t test;
int ret;
uint64_t stamp;
int loop_cnt = 0;
hrtimer_t *timer = &test.timer;
/* Initialize the high-resolution timer */
hrtimer_init(timer);
/* Start the timer with 500ms relative timeout */
stamp = test.timestamp;
ASSERT(hrtimer_start(timer, test_hrtimer_callback,
50 * NSEC_PER_MSEC, HRTIMER_MODE_REL) == OK);
/* Wait until the test completes */
while (test.timestamp != stamp)
hrtimer_test_t param =
{
usleep(USEC_PER_MSEC);
}
0
};
while (loop_cnt++ < HRTIMER_THREAD_LOOP_NR)
{
uint64_t delay = rand() % NSEC_PER_MSEC;
hrtimer_init(&param.timer);
/* Cancel timer */
/* Delay = 0 */
ret = hrtimer_cancel(&timer);
ASSERT(ret == OK);
hrtimer_test_oneshot(&param, 0u);
/* Start timer with fixed period */
/* 0 < Delay < 10000 */
ret = hrtimer_start(&timer, hrtimer_test_callback,
10 * NSEC_PER_USEC, HRTIMER_MODE_REL);
ASSERT(ret == OK);
hrtimer_test_oneshot(&param, 1u);
hrtimer_test_oneshot(&param, 10u);
hrtimer_test_oneshot(&param, 100u);
hrtimer_test_oneshot(&param, 1000u);
hrtimer_test_oneshot(&param, 10000u);
/* Start timer with random delay */
/* 10000 < Delay < 10000000 */
ret = hrtimer_start(&timer, hrtimer_test_callback,
delay, HRTIMER_MODE_REL);
ASSERT(ret == OK);
}
hrtimer_test_oneshot(&param, 100000u);
hrtimer_test_oneshot(&param, 1000000u);
hrtimer_test_oneshot(&param, 10000000u);
/* Cancel the timer synchronously */
#ifdef CONFIG_SMP
/* Test hrtimer_cancel_sync */
ASSERT(hrtimer_cancel_sync(&timer) == OK);
hrtimer_test_cancel_sync(&param);
return NULL;
/* Test hrtimer_cancel */
hrtimer_test_cancel_periodic(&param);
#endif
/* Maximum hrtimer delay test. */
hrtimer_test_maximum(&param);
/* Period hrtimer delay 100000ns */
hrtimer_test_period(&param, 1000000u, 128u);
/* Random delay 12345ns and 67890ns */
hrtimer_test_rand(&param, 12345u);
hrtimer_test_rand_cancel(&param, 67890u);
return 0;
}
/****************************************************************************