FreeBSD manual
download PDF document: kern_testfrwk.9.pdf
KERN_TESTFRWK(9) FreeBSD Kernel Developer's Manual KERN_TESTFRWK(9)
NAME
kern_testfrwk - A kernel testing framework
SYNOPSIS
kldload kern_testfrwk
DESCRIPTION
So what is this sys/tests directory in the kernel all about?
Have you ever wanted to test a part of the FreeBSD kernel in some way and
you had no real way from user-land to make what you want to occur happen?
Say an error path or situation where locking occurs in a particular
manner that happens only once in a blue moon?
If so, then the kernel test framework is just what you are looking for.
It is designed to help you create the situation you want.
There are two components to the system: the test framework and your test.
This document will describe both components and use the test submitted
with the initial commit of this code to discuss the test
(callout_test(4)). All of the tests become kernel loadable modules. The
test you write should have a dependency on the test framework. That way
it will be loaded automatically with your test. For example, you can see
how to do this in the bottom of callout_test.c in
sys/tests/callout_test/callout_test.c.
The framework itself is in sys/tests/framework/kern_testfrwk.c. Its job
is to manage the tests that are loaded. (More than one can be loaded.)
The idea is pretty simple; you load the test framework and then load your
test.
When your test loads, you register your tests with the kernel test
framework. You do that through a call to kern_testframework_register().
Usually this is done at the module load event as shown below:
switch (type) {
case MOD_LOAD:
err = kern_testframework_register("callout_test",
run_callout_test);
Here the test is "callout_test" and it is registered to run the function
run_callout_test() passing it a struct kern_test *ptr. The kern_test
structure is defined in kern_testfrwk.h.
struct kern_test {
char name[TEST_NAME_LEN];
int num_threads; /* Fill in how many threads you want */
int tot_threads_running; /* Private to framework */
uint8_t test_options[TEST_OPTION_SPACE];
};
The user sends this structure down via a sysctl to start your test. He
or she places the same name you registered ("callout_test" in our
example) in the name field. The user can also set the number of threads
to run with num_threads.
The framework will start the requested number of kernel threads, all
The test_options field is a test-specific set of information that is an
opaque blob. It is passed in from user space and has a maximum size of
256 bytes. You can pass arbitrary test input in the space. In the case
of callout_test we reshape that to:
struct callout_test {
int number_of_callouts;
int test_number;
};
So the first lines of run_callout_test() does the following to get at the
user specific data:
struct callout_test *u;
size_t sz;
int i;
struct callout_run *rn;
int index = test->tot_threads_running;
u = (struct callout_test *)test->test_options;
That way it can access: u->test_number (there are two types of tests
provided with this test) and u->number_of_callouts (how many simultaneous
callouts to run).
Your test can do anything with these bytes. So the callout_test in
question wants to create a situation where multiple callouts are all run,
that is the number_of_callouts, and it tries to cancel the callout with
the new callout_async_drain(). The threads do this by acquiring the lock
in question, and then starting each of the callouts. It waits for the
callouts to all go off (the executor spins waits). This forces the
situation that the callouts have expired and are all waiting on the lock
that the executor holds. After the callouts are all blocked, the
executor calls callout_async_drain() on each callout and releases the
lock.
After all the callouts are done, a total status is printed showing the
results via printf(9). The human tester can run dmesg(8) to see the
results. In this case it is expected that if you are running test 0, all
the callouts expire on the same CPU so only one callout_drain function
would have been called. the number of zero_returns should match the
number of callout_drains that were called, i.e., 1. The one_returns
should be the remainder of the callouts. If the test number was 1, the
callouts were spread across all CPUs. The number of zero_returns will
again match the number of drain calls made which matches the number of
CPUs that were put in use.
More than one thread can be used with this test, though in the example
case it is probably not necessary.
You should not need to change the framework. Just add tests and register
them after loading.
AUTHORS
The kernel test framework was written by Randall Stewart
<rrs@FreeBSD.org> with help from
John Mark Gurney <jmg@FreeBSD.org>.