Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * @file
 * @brief Test nanokernel sleep and wakeup APIs
 *
 * This module tests the following sleep and wakeup scenarios:
 * 1. fiber_sleep() without cancellation
 * 2. fiber_sleep() cancelled via fiber_fiber_wakeup()
 * 3. fiber_sleep() cancelled via isr_fiber_wakeup()
 * 4. fiber_sleep() cancelled via task_fiber_wakeup()
 * 5. task_sleep() - no cancellation exists
 */

#include <tc_util.h>
#include <arch/cpu.h>
#include <misc/util.h>
#include <irq_offload.h>
#include <stdbool.h>

#include <util_test_common.h>

#define FIBER_STACKSIZE    256

#define TEST_FIBER_PRIORITY       4
#define HELPER_FIBER_PRIORITY    10

#define ONE_SECOND    (sys_clock_ticks_per_sec)

static struct nano_sem test_fiber_sem;
static struct nano_sem helper_fiber_sem;
static struct nano_sem task_sem;

static char __stack test_fiber_stack[FIBER_STACKSIZE];
static char __stack helper_fiber_stack[FIBER_STACKSIZE];

static nano_thread_id_t  test_fiber_id;
static nano_thread_id_t  helper_fiber_id;

static bool test_failure = true;     /* Assume the test will fail */

static void test_objects_init(void)
{
	nano_sem_init(&test_fiber_sem);
	nano_sem_init(&helper_fiber_sem);
	nano_sem_init(&task_sem);

	TC_PRINT("Nanokernel objects initialized\n");
}

static void align_to_tick_boundary(void)
{
	uint32_t tick;

	tick = sys_tick_get_32();
	while (sys_tick_get_32() == tick) {
		/* Busy wait to align to tick boundary */
	}
}

/* Shouldn't ever sleep for less than requested time, but allow for 1
 * tick of "too long" slop for aliasing between wakeup and
 * measurement. Qemu at least will leak the external world's clock
 * rate into the simulator when the host is under load.
 */
static int sleep_time_valid(uint32_t start, uint32_t end, uint32_t dur)
{
	uint32_t dt = end - start;

	return dt >= dur && dt <= (dur + 1);
}

static void test_fiber(int arg1, int arg2)
{
	uint32_t start_tick;
	uint32_t end_tick;

	nano_fiber_sem_take(&test_fiber_sem, TICKS_UNLIMITED);

	TC_PRINT("Testing normal expiration of fiber_sleep()\n");
	align_to_tick_boundary();

	start_tick = sys_tick_get_32();
	fiber_sleep(ONE_SECOND);
	end_tick = sys_tick_get_32();

	if (!sleep_time_valid(start_tick, end_tick, ONE_SECOND)) {
		TC_ERROR(" *** fiber_sleep() slept for %d ticks not %d.",
				 end_tick - start_tick, ONE_SECOND);

		return;
	}

	TC_PRINT("Testing fiber_sleep() + fiber_fiber_wakeup()\n");
	nano_fiber_sem_give(&helper_fiber_sem);   /* Activate helper fiber */
	align_to_tick_boundary();

	start_tick = sys_tick_get_32();
	fiber_sleep(ONE_SECOND);
	end_tick = sys_tick_get_32();

	if (end_tick - start_tick > 1) {
		TC_ERROR(" *** fiber_fiber_wakeup() took too long (%d ticks)\n",
				 end_tick - start_tick);
		return;
	}

	TC_PRINT("Testing fiber_sleep() + isr_fiber_wakeup()\n");
	nano_fiber_sem_give(&helper_fiber_sem);   /* Activate helper fiber */
	align_to_tick_boundary();

	start_tick = sys_tick_get_32();
	fiber_sleep(ONE_SECOND);
	end_tick = sys_tick_get_32();

	if (end_tick - start_tick > 1) {
		TC_ERROR(" *** isr_fiber_wakeup() took too long (%d ticks)\n",
				 end_tick - start_tick);
		return;
	}

	TC_PRINT("Testing fiber_sleep() + task_fiber_wakeup()\n");
	nano_task_sem_give(&task_sem);    /* Activate task */
	align_to_tick_boundary();

	start_tick = sys_tick_get_32();
	fiber_sleep(ONE_SECOND);           /* Task will execute */
	end_tick = sys_tick_get_32();

	if (end_tick - start_tick > 1) {
		TC_ERROR(" *** task_fiber_wakeup() took too long (%d ticks)\n",
				 end_tick - start_tick);
		return;
	}

	test_failure = false;
}

static void irq_offload_isr(void *arg)
{
	isr_fiber_wakeup((nano_thread_id_t) arg);
}

static void helper_fiber(int arg1, int arg2)
{
	nano_fiber_sem_take(&helper_fiber_sem, TICKS_UNLIMITED);

	/* Wake the test fiber */
	fiber_fiber_wakeup(test_fiber_id);
	nano_fiber_sem_take(&helper_fiber_sem, TICKS_UNLIMITED);

	/* Wake the test fiber from an ISR */
	irq_offload(irq_offload_isr, (void *)test_fiber_id);
}

void main(void)
{
	int       status = TC_FAIL;
	uint32_t  start_tick;
	uint32_t  end_tick;

	TC_START("Test Nanokernel Sleep and Wakeup APIs\n");

	test_objects_init();

	test_fiber_id = task_fiber_start(test_fiber_stack, FIBER_STACKSIZE,
					 test_fiber, 0, 0, TEST_FIBER_PRIORITY, 0);
	TC_PRINT("Test fiber started: id = 0x%x\n", test_fiber_id);

	helper_fiber_id = task_fiber_start(helper_fiber_stack, FIBER_STACKSIZE,
						helper_fiber, 0, 0, HELPER_FIBER_PRIORITY, 0);
	TC_PRINT("Helper fiber started: id = 0x%x\n", helper_fiber_id);

	/* Activate test_fiber */
	nano_task_sem_give(&test_fiber_sem);

	/* Wait for test_fiber to activate us */
	nano_task_sem_take(&task_sem, TICKS_UNLIMITED);

	/* Wake the test fiber */
	task_fiber_wakeup(test_fiber_id);

	if (test_failure) {
		goto done_tests;
	}

	TC_PRINT("Testing nanokernel task_sleep()\n");
	align_to_tick_boundary();
	start_tick = sys_tick_get_32();
	task_sleep(ONE_SECOND);
	end_tick = sys_tick_get_32();

	if (!sleep_time_valid(start_tick, end_tick, ONE_SECOND)) {
		TC_ERROR("task_sleep() slept for %d ticks, not %d\n",
				 end_tick - start_tick, ONE_SECOND);
		goto done_tests;
	}

	status = TC_PASS;

done_tests:
	TC_END_REPORT(status);
}