]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tests/epoch/epoch_test.c
Update tcsh to 6.21.00.
[FreeBSD/FreeBSD.git] / sys / tests / epoch / epoch_test.c
1 /*-
2  * Copyright (c) 2018, Matthew Macy <mmacy@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright notice,
8  *     this list of conditions and the following disclaimer.
9  *
10  *  2. Neither the name of Matthew Macy nor the names of its
11  *     contributors may be used to endorse or promote products derived from
12  *     this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/proc.h>
33 #include <sys/counter.h>
34 #include <sys/epoch.h>
35 #include <sys/gtaskqueue.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/lock.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/proc.h>
43 #include <sys/sched.h>
44 #include <sys/smp.h>
45 #include <sys/sysctl.h>
46 #include <sys/systm.h>
47
48
49 struct epoch_test_instance {
50         int threadid;
51 };
52
53 static int inited;
54 static int iterations;
55 #define ET_EXITING 0x1
56 static volatile int state_flags;
57 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2);
58 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF);
59 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2);
60 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF);
61 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2);
62 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF);
63 epoch_t test_epoch;
64
65 static void
66 epoch_testcase1(struct epoch_test_instance *eti)
67 {
68         int i, startticks;
69         struct mtx *mtxp;
70         struct epoch_tracker et;
71
72         startticks = ticks;
73         i = 0;
74         if (eti->threadid & 0x1)
75                 mtxp = &mutexA;
76         else
77                 mtxp = &mutexB;
78
79         while (i < iterations) {
80                 epoch_enter_preempt(test_epoch, &et);
81                 mtx_lock(mtxp);
82                 i++;
83                 mtx_unlock(mtxp);
84                 epoch_exit_preempt(test_epoch, &et);
85                 epoch_wait_preempt(test_epoch);
86         }
87         printf("test1: thread: %d took %d ticks to complete %d iterations\n",
88                    eti->threadid, ticks - startticks, iterations);
89 }
90
91 static void
92 epoch_testcase2(struct epoch_test_instance *eti)
93 {
94         int i, startticks;
95         struct mtx *mtxp;
96         struct epoch_tracker et;
97
98         startticks = ticks;
99         i = 0;
100         mtxp = &mutexA;
101
102         while (i < iterations) {
103                 epoch_enter_preempt(test_epoch, &et);
104                 mtx_lock(mtxp);
105                 DELAY(1);
106                 i++;
107                 mtx_unlock(mtxp);
108                 epoch_exit_preempt(test_epoch, &et);
109                 epoch_wait_preempt(test_epoch);
110         }
111         printf("test2: thread: %d took %d ticks to complete %d iterations\n",
112                    eti->threadid, ticks - startticks, iterations);
113 }
114
115 static void
116 testloop(void *arg) {
117
118         mtx_lock(&state_mtx);
119         while ((state_flags & ET_EXITING) == 0) {
120                 msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0);
121                 if (state_flags & ET_EXITING)
122                         goto out;
123                 mtx_unlock(&state_mtx);
124                 epoch_testcase2(arg);
125                 pause("W", 500);
126                 epoch_testcase1(arg);
127                 mtx_lock(&state_mtx);
128         }
129  out:
130         mtx_unlock(&state_mtx);
131         kthread_exit();
132 }
133
134 static struct thread *testthreads[MAXCPU];
135 static struct epoch_test_instance etilist[MAXCPU];
136
137 static int
138 test_modinit(void)
139 {
140         struct thread *td;
141         int i, error, pri_range, pri_off;
142
143         pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME;
144         test_epoch = epoch_alloc(EPOCH_PREEMPT);
145         for (i = 0; i < mp_ncpus*2; i++) {
146                 etilist[i].threadid = i;
147                 error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i],
148                                                         0, 0, "epoch_test_%d", i);
149                 if (error) {
150                         printf("%s: kthread_add(epoch_test): error %d", __func__,
151                                    error);
152                 } else {
153                         pri_off = (i*4)%pri_range;
154                         td = testthreads[i];
155                         thread_lock(td);
156                         sched_prio(td, PRI_MIN_REALTIME + pri_off);
157                         thread_unlock(td);
158                 }
159         }
160         inited = 1;
161         return (0);
162 }
163
164 static int
165 epochtest_execute(SYSCTL_HANDLER_ARGS)
166 {
167         int error, v;
168
169         if (inited == 0)
170                 return (ENOENT);
171
172         v = 0;
173         error = sysctl_handle_int(oidp, &v, 0, req);
174         if (error)
175                 return (error);
176         if (req->newptr == NULL)
177                 return (error);
178         if (v == 0)
179                 return (0);
180         mtx_lock(&state_mtx);
181         iterations = v;
182         wakeup(&state_mtx);
183         mtx_unlock(&state_mtx);
184
185         return (0);
186 }
187
188 SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW, 0, "Epoch Test Framework");
189 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW),
190                         0, 0, epochtest_execute, "I", "Execute an epoch test");
191
192 static int
193 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused)
194 {
195         int err;
196
197         switch (what) {
198         case MOD_LOAD:
199                 if ((err = test_modinit()) != 0)
200                         return (err);
201                 break;
202         case MOD_UNLOAD:
203                 mtx_lock(&state_mtx);
204                 state_flags = ET_EXITING;
205                 wakeup(&state_mtx);
206                 mtx_unlock(&state_mtx);
207                 /* yes --- gross */
208                 pause("epoch unload", 3*hz);
209                 break;
210         default:
211                 return (EOPNOTSUPP);
212         }
213
214         return (0);
215 }
216
217 static moduledata_t epoch_test_moduledata = {
218         "epoch_test",
219         epoch_test_module_event_handler,
220         NULL
221 };
222
223 MODULE_VERSION(epoch_test, 1);
224 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);