]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tests/epoch/epoch_test.c
Test priority handling in epoch test.
[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/counter.h>
33 #include <sys/epoch.h>
34 #include <sys/gtaskqueue.h>
35 #include <sys/kernel.h>
36 #include <sys/kthread.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <sys/proc.h>
42 #include <sys/sched.h>
43 #include <sys/smp.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
46
47
48 struct epoch_test_instance {
49         int threadid;
50 };
51
52 static int inited;
53 static int iterations;
54 #define ET_EXITING 0x1
55 static volatile int state_flags;
56 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2);
57 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF);
58 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2);
59 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF);
60 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2);
61 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF);
62 epoch_t test_epoch;
63
64 static void
65 epoch_testcase1(struct epoch_test_instance *eti)
66 {
67         int i, startticks;
68         struct mtx *mtxp;
69
70         startticks = ticks;
71         i = 0;
72         if (eti->threadid & 0x1)
73                 mtxp = &mutexA;
74         else
75                 mtxp = &mutexB;
76
77         while (i < iterations) {
78                 epoch_enter(test_epoch);
79                 mtx_lock(mtxp);
80                 i++;
81                 mtx_unlock(mtxp);
82                 epoch_exit(test_epoch);
83                 epoch_wait(test_epoch);
84         }
85         printf("test1: thread: %d took %d ticks to complete %d iterations\n",
86                    eti->threadid, ticks - startticks, iterations);
87 }
88
89 static void
90 epoch_testcase2(struct epoch_test_instance *eti)
91 {
92         int i, startticks;
93         struct mtx *mtxp;
94
95         startticks = ticks;
96         i = 0;
97         mtxp = &mutexA;
98
99         while (i < iterations) {
100                 epoch_enter(test_epoch);
101                 mtx_lock(mtxp);
102                 DELAY(1);
103                 i++;
104                 mtx_unlock(mtxp);
105                 epoch_exit(test_epoch);
106                 epoch_wait(test_epoch);
107         }
108         printf("test2: thread: %d took %d ticks to complete %d iterations\n",
109                    eti->threadid, ticks - startticks, iterations);
110 }
111
112 static void
113 testloop(void *arg) {
114
115         mtx_lock(&state_mtx);
116         while ((state_flags & ET_EXITING) == 0) {
117                 msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0);
118                 if (state_flags & ET_EXITING)
119                         goto out;
120                 mtx_unlock(&state_mtx);
121                 epoch_testcase2(arg);
122                 pause("W", 500);
123                 epoch_testcase1(arg);
124                 mtx_lock(&state_mtx);
125         }
126  out:
127         mtx_unlock(&state_mtx);
128         kthread_exit();
129 }
130
131 static struct thread *testthreads[MAXCPU];
132 static struct epoch_test_instance etilist[MAXCPU];
133
134 static int
135 test_modinit(void)
136 {
137         struct thread *td;
138         int i, error, pri_range, pri_off;
139
140         pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME;
141         test_epoch = epoch_alloc();
142         for (i = 0; i < mp_ncpus*2; i++) {
143                 etilist[i].threadid = i;
144                 error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i],
145                                                         0, 0, "epoch_test_%d", i);
146                 if (error) {
147                         printf("%s: kthread_add(epoch_test): error %d", __func__,
148                                    error);
149                 } else {
150                         pri_off = (i*4)%pri_range;
151                         td = testthreads[i];
152                         thread_lock(td);
153                         sched_prio(td, PRI_MIN_REALTIME + pri_off);
154                         thread_unlock(td);
155                 }
156         }
157         inited = 1;
158         return (0);
159 }
160
161 static int
162 epochtest_execute(SYSCTL_HANDLER_ARGS)
163 {
164         int error, v;
165
166         if (inited == 0)
167                 return (ENOENT);
168
169         v = 0;
170         error = sysctl_handle_int(oidp, &v, 0, req);
171         if (error)
172                 return (error);
173         if (req->newptr == NULL)
174                 return (error);
175         if (v == 0)
176                 return (0);
177         mtx_lock(&state_mtx);
178         iterations = v;
179         wakeup(&state_mtx);
180         mtx_unlock(&state_mtx);
181
182         return (0);
183 }
184
185 SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW, 0, "Epoch Test Framework");
186 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW),
187                         0, 0, epochtest_execute, "I", "Execute an epoch test");
188
189 static int
190 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused)
191 {
192         int err;
193
194         switch (what) {
195         case MOD_LOAD:
196                 if ((err = test_modinit()) != 0)
197                         return (err);
198                 break;
199         case MOD_UNLOAD:
200                 mtx_lock(&state_mtx);
201                 state_flags = ET_EXITING;
202                 wakeup(&state_mtx);
203                 mtx_unlock(&state_mtx);
204                 /* yes --- gross */
205                 pause("epoch unload", 3*hz);
206                 break;
207         default:
208                 return (EOPNOTSUPP);
209         }
210
211         return (0);
212 }
213
214 static moduledata_t epoch_test_moduledata = {
215         "epoch_test",
216         epoch_test_module_event_handler,
217         NULL
218 };
219
220 MODULE_VERSION(epoch_test, 1);
221 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);