aboutsummaryrefslogtreecommitdiff
path: root/sys/tests/callout_test
diff options
context:
space:
mode:
Diffstat (limited to 'sys/tests/callout_test')
-rw-r--r--sys/tests/callout_test/callout_test.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/sys/tests/callout_test/callout_test.c b/sys/tests/callout_test/callout_test.c
new file mode 100644
index 000000000000..f3d3a6714db8
--- /dev/null
+++ b/sys/tests/callout_test/callout_test.c
@@ -0,0 +1,284 @@
+/*-
+ * 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>
+#ifdef SMP
+#include <machine/cpu.h>
+#else
+#define cpu_spinwait()
+#endif
+
+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();
+
+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);