2d0289e25f
MFC after: 1 Month
284 lines
7.0 KiB
C
284 lines
7.0 KiB
C
/*-
|
|
* Copyright (c) 2015 Netflix Inc. All rights reserved.
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/ioccom.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/priv.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/sched.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/libkern.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/pcpu.h>
|
|
#include <sys/smp.h>
|
|
#include <sys/pmckern.h>
|
|
#include <sys/cpuctl.h>
|
|
#include <tests/kern_testfrwk.h>
|
|
#include <tests/callout_test.h>
|
|
#include <machine/cpu.h>
|
|
|
|
MALLOC_DEFINE(M_CALLTMP, "Temp callout Memory", "CalloutTest");
|
|
|
|
struct callout_run {
|
|
struct mtx lock;
|
|
struct callout *co_array;
|
|
int co_test;
|
|
int co_number_callouts;
|
|
int co_return_npa;
|
|
int co_completed;
|
|
int callout_waiting;
|
|
int drain_calls;
|
|
int cnt_zero;
|
|
int cnt_one;
|
|
int index;
|
|
};
|
|
|
|
static struct callout_run *comaster[MAXCPU];
|
|
|
|
uint64_t callout_total = 0;
|
|
|
|
static void execute_the_co_test(struct callout_run *rn);
|
|
|
|
static void
|
|
co_saydone(void *arg)
|
|
{
|
|
struct callout_run *rn;
|
|
|
|
rn = (struct callout_run *)arg;
|
|
printf("The callout test is now complete for thread %d\n",
|
|
rn->index);
|
|
printf("number_callouts:%d\n",
|
|
rn->co_number_callouts);
|
|
printf("Callouts that bailed (Not PENDING or ACTIVE cleared):%d\n",
|
|
rn->co_return_npa);
|
|
printf("Callouts that completed:%d\n", rn->co_completed);
|
|
printf("Drain calls:%d\n", rn->drain_calls);
|
|
printf("Zero returns:%d non-zero:%d\n",
|
|
rn->cnt_zero,
|
|
rn->cnt_one);
|
|
|
|
}
|
|
|
|
static void
|
|
drainit(void *arg)
|
|
{
|
|
struct callout_run *rn;
|
|
|
|
rn = (struct callout_run *)arg;
|
|
mtx_lock(&rn->lock);
|
|
rn->drain_calls++;
|
|
mtx_unlock(&rn->lock);
|
|
}
|
|
|
|
static void
|
|
test_callout(void *arg)
|
|
{
|
|
struct callout_run *rn;
|
|
int cpu;
|
|
|
|
critical_enter();
|
|
cpu = curcpu;
|
|
critical_exit();
|
|
rn = (struct callout_run *)arg;
|
|
atomic_add_int(&rn->callout_waiting, 1);
|
|
mtx_lock(&rn->lock);
|
|
if (callout_pending(&rn->co_array[cpu]) ||
|
|
!callout_active(&rn->co_array[cpu])) {
|
|
rn->co_return_npa++;
|
|
atomic_subtract_int(&rn->callout_waiting, 1);
|
|
mtx_unlock(&rn->lock);
|
|
return;
|
|
}
|
|
callout_deactivate(&rn->co_array[cpu]);
|
|
rn->co_completed++;
|
|
mtx_unlock(&rn->lock);
|
|
atomic_subtract_int(&rn->callout_waiting, 1);
|
|
}
|
|
|
|
void
|
|
execute_the_co_test(struct callout_run *rn)
|
|
{
|
|
int i, ret, cpu;
|
|
uint32_t tk_s, tk_e, tk_d;
|
|
|
|
mtx_lock(&rn->lock);
|
|
rn->callout_waiting = 0;
|
|
for (i = 0; i < rn->co_number_callouts; i++) {
|
|
if (rn->co_test == 1) {
|
|
/* start all on spread out cpu's */
|
|
cpu = i % mp_ncpus;
|
|
callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
|
|
cpu, 0);
|
|
} else {
|
|
/* Start all on the same CPU */
|
|
callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
|
|
rn->index, 0);
|
|
}
|
|
}
|
|
tk_s = ticks;
|
|
while (rn->callout_waiting != rn->co_number_callouts) {
|
|
cpu_spinwait();
|
|
tk_e = ticks;
|
|
tk_d = tk_e - tk_s;
|
|
if (tk_d > 100) {
|
|
break;
|
|
}
|
|
}
|
|
/* OK everyone is waiting and we have the lock */
|
|
for (i = 0; i < rn->co_number_callouts; i++) {
|
|
ret = callout_async_drain(&rn->co_array[i], drainit);
|
|
if (ret) {
|
|
rn->cnt_one++;
|
|
} else {
|
|
rn->cnt_zero++;
|
|
}
|
|
}
|
|
rn->callout_waiting -= rn->cnt_one;
|
|
mtx_unlock(&rn->lock);
|
|
/* Now wait until all are done */
|
|
tk_s = ticks;
|
|
while (rn->callout_waiting > 0) {
|
|
cpu_spinwait();
|
|
tk_e = ticks;
|
|
tk_d = tk_e - tk_s;
|
|
if (tk_d > 100) {
|
|
break;
|
|
}
|
|
}
|
|
co_saydone((void *)rn);
|
|
}
|
|
|
|
|
|
static void
|
|
run_callout_test(struct kern_test *test)
|
|
{
|
|
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;
|
|
if (comaster[index] == NULL) {
|
|
rn = comaster[index] = malloc(sizeof(struct callout_run), M_CALLTMP, M_WAITOK);
|
|
memset(comaster[index], 0, sizeof(struct callout_run));
|
|
mtx_init(&rn->lock, "callouttest", NULL, MTX_DUPOK);
|
|
rn->index = index;
|
|
} else {
|
|
rn = comaster[index];
|
|
rn->co_number_callouts = rn->co_return_npa = 0;
|
|
rn->co_completed = rn->callout_waiting = 0;
|
|
rn->drain_calls = rn->cnt_zero = rn->cnt_one = 0;
|
|
if (rn->co_array) {
|
|
free(rn->co_array, M_CALLTMP);
|
|
rn->co_array = NULL;
|
|
}
|
|
}
|
|
rn->co_number_callouts = u->number_of_callouts;
|
|
rn->co_test = u->test_number;
|
|
sz = sizeof(struct callout) * rn->co_number_callouts;
|
|
rn->co_array = malloc(sz, M_CALLTMP, M_WAITOK);
|
|
for (i = 0; i < rn->co_number_callouts; i++) {
|
|
callout_init(&rn->co_array[i], CALLOUT_MPSAFE);
|
|
}
|
|
execute_the_co_test(rn);
|
|
}
|
|
|
|
int callout_test_is_loaded = 0;
|
|
|
|
static void
|
|
cocleanup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAXCPU; i++) {
|
|
if (comaster[i]) {
|
|
if (comaster[i]->co_array) {
|
|
free(comaster[i]->co_array, M_CALLTMP);
|
|
comaster[i]->co_array = NULL;
|
|
}
|
|
free(comaster[i], M_CALLTMP);
|
|
comaster[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
callout_test_modevent(module_t mod, int type, void *data)
|
|
{
|
|
int err = 0;
|
|
|
|
switch (type) {
|
|
case MOD_LOAD:
|
|
err = kern_testframework_register("callout_test",
|
|
run_callout_test);
|
|
if (err) {
|
|
printf("Can't load callout_test err:%d returned\n",
|
|
err);
|
|
} else {
|
|
memset(comaster, 0, sizeof(comaster));
|
|
callout_test_is_loaded = 1;
|
|
}
|
|
break;
|
|
case MOD_QUIESCE:
|
|
err = kern_testframework_deregister("callout_test");
|
|
if (err == 0) {
|
|
callout_test_is_loaded = 0;
|
|
cocleanup();
|
|
}
|
|
break;
|
|
case MOD_UNLOAD:
|
|
if (callout_test_is_loaded) {
|
|
err = kern_testframework_deregister("callout_test");
|
|
if (err == 0) {
|
|
cocleanup();
|
|
callout_test_is_loaded = 0;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return (EOPNOTSUPP);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
static moduledata_t callout_test_mod = {
|
|
.name = "callout_test",
|
|
.evhand = callout_test_modevent,
|
|
.priv = 0
|
|
};
|
|
|
|
MODULE_DEPEND(callout_test, kern_testframework, 1, 1, 1);
|
|
DECLARE_MODULE(callout_test, callout_test_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
|