Thu Aug 27 18:51:20 2020 UTC ()
npftest: Wait at least one tick in each gc busy wait iteration.

Otherwise the busy wait loop runs a little too fast for the gc about
half the times I run the test.

XXX We should really arrange mstohz to round up!


(riastradh)
diff -r1.1 -r1.2 src/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c

cvs diff -r1.1 -r1.2 src/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c (switch to unified diff)

--- src/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c 2020/05/30 14:16:57 1.1
+++ src/usr.sbin/npf/npftest/libnpftest/npf_gc_test.c 2020/08/27 18:51:20 1.2
@@ -1,267 +1,267 @@ @@ -1,267 +1,267 @@
1/* 1/*
2 * NPF connection tests. 2 * NPF connection tests.
3 * 3 *
4 * Public Domain. 4 * Public Domain.
5 */ 5 */
6 6
7#ifdef _KERNEL 7#ifdef _KERNEL
8#include <sys/types.h> 8#include <sys/types.h>
9#include <sys/kmem.h> 9#include <sys/kmem.h>
10#endif 10#endif
11 11
12#include "npf.h" 12#include "npf.h"
13#include "npf_impl.h" 13#include "npf_impl.h"
14#include "npf_conn.h" 14#include "npf_conn.h"
15#include "npf_test.h" 15#include "npf_test.h"
16 16
17static bool lverbose = false; 17static bool lverbose = false;
18 18
19static unsigned 19static unsigned
20count_conns(npf_conndb_t *cd) 20count_conns(npf_conndb_t *cd)
21{ 21{
22 npf_conn_t *head = npf_conndb_getlist(cd), *conn = head; 22 npf_conn_t *head = npf_conndb_getlist(cd), *conn = head;
23 unsigned n = 0; 23 unsigned n = 0;
24 24
25 while (conn) { 25 while (conn) {
26 n++; 26 n++;
27 conn = npf_conndb_getnext(cd, conn); 27 conn = npf_conndb_getnext(cd, conn);
28 if (conn == head) { 28 if (conn == head) {
29 break; 29 break;
30 } 30 }
31 } 31 }
32 return n; 32 return n;
33} 33}
34 34
35static struct mbuf * 35static struct mbuf *
36get_packet(unsigned i) 36get_packet(unsigned i)
37{ 37{
38 struct mbuf *m; 38 struct mbuf *m;
39 struct ip *ip; 39 struct ip *ip;
40 40
41 m = mbuf_get_pkt(AF_INET, IPPROTO_UDP, 41 m = mbuf_get_pkt(AF_INET, IPPROTO_UDP,
42 "10.0.0.1", "172.16.0.1", 9000, 9000); 42 "10.0.0.1", "172.16.0.1", 9000, 9000);
43 (void)mbuf_return_hdrs(m, false, &ip); 43 (void)mbuf_return_hdrs(m, false, &ip);
44 ip->ip_src.s_addr += i; 44 ip->ip_src.s_addr += i;
45 return m; 45 return m;
46} 46}
47 47
48static bool 48static bool
49enqueue_connection(unsigned i, bool expire) 49enqueue_connection(unsigned i, bool expire)
50{ 50{
51 struct mbuf *m = get_packet(i); 51 struct mbuf *m = get_packet(i);
52 npf_cache_t *npc = get_cached_pkt(m, NULL); 52 npf_cache_t *npc = get_cached_pkt(m, NULL);
53 npf_conn_t *con; 53 npf_conn_t *con;
54 54
55 con = npf_conn_establish(npc, PFIL_IN, true); 55 con = npf_conn_establish(npc, PFIL_IN, true);
56 CHECK_TRUE(con != NULL); 56 CHECK_TRUE(con != NULL);
57 if (expire) { 57 if (expire) {
58 npf_conn_expire(con); 58 npf_conn_expire(con);
59 } 59 }
60 npf_conn_release(con); 60 npf_conn_release(con);
61 put_cached_pkt(npc); 61 put_cached_pkt(npc);
62 return true; 62 return true;
63} 63}
64 64
65static bool 65static bool
66run_conn_gc(unsigned active, unsigned expired, unsigned expected) 66run_conn_gc(unsigned active, unsigned expired, unsigned expected)
67{ 67{
68 npf_t *npf = npf_getkernctx(); 68 npf_t *npf = npf_getkernctx();
69 npf_conndb_t *cd = npf_conndb_create(); 69 npf_conndb_t *cd = npf_conndb_create();
70 unsigned total, n = 0; 70 unsigned total, n = 0;
71 71
72 npf->conn_db = cd; 72 npf->conn_db = cd;
73 73
74 /* 74 /*
75 * Insert the given number of active and expired connections.. 75 * Insert the given number of active and expired connections..
76 */ 76 */
77 total = active + expired; 77 total = active + expired;
78 78
79 while (active || expired) { 79 while (active || expired) {
80 if (active) { 80 if (active) {
81 enqueue_connection(n++, false); 81 enqueue_connection(n++, false);
82 active--; 82 active--;
83 } 83 }
84 if (expired) { 84 if (expired) {
85 enqueue_connection(n++, true); 85 enqueue_connection(n++, true);
86 expired--; 86 expired--;
87 } 87 }
88 } 88 }
89 89
90 /* Verify the count. */ 90 /* Verify the count. */
91 n = count_conns(cd); 91 n = count_conns(cd);
92 CHECK_TRUE(n == total); 92 CHECK_TRUE(n == total);
93 93
94 /* 94 /*
95 * Run G/C. Check the remaining. 95 * Run G/C. Check the remaining.
96 */ 96 */
97 npf_conndb_gc(npf, cd, false, false); 97 npf_conndb_gc(npf, cd, false, false);
98 n = count_conns(cd); 98 n = count_conns(cd);
99 if (lverbose) { 99 if (lverbose) {
100 printf("in conndb -- %u (expected %u)\n", n, expected); 100 printf("in conndb -- %u (expected %u)\n", n, expected);
101 } 101 }
102 CHECK_TRUE(n == expected); 102 CHECK_TRUE(n == expected);
103 103
104 /* Flush and destroy. */ 104 /* Flush and destroy. */
105 npf_conndb_gc(npf, cd, true, false); 105 npf_conndb_gc(npf, cd, true, false);
106 npf_conndb_destroy(cd); 106 npf_conndb_destroy(cd);
107 npf->conn_db = NULL; 107 npf->conn_db = NULL;
108 return true; 108 return true;
109} 109}
110 110
111static bool 111static bool
112run_gc_tests(void) 112run_gc_tests(void)
113{ 113{
114 bool ok; 114 bool ok;
115 int val; 115 int val;
116 116
117 /* Check the default value. */ 117 /* Check the default value. */
118 npfk_param_get(npf_getkernctx(), "gc.step", &val); 118 npfk_param_get(npf_getkernctx(), "gc.step", &val);
119 CHECK_TRUE(val == 256); 119 CHECK_TRUE(val == 256);
120 120
121 /* Empty => GC => 0 in conndb. */ 121 /* Empty => GC => 0 in conndb. */
122 ok = run_conn_gc(0, 0, 0); 122 ok = run_conn_gc(0, 0, 0);
123 CHECK_TRUE(ok); 123 CHECK_TRUE(ok);
124 124
125 /* 1 active => GC => 1 in conndb. */ 125 /* 1 active => GC => 1 in conndb. */
126 ok = run_conn_gc(1, 0, 1); 126 ok = run_conn_gc(1, 0, 1);
127 CHECK_TRUE(ok); 127 CHECK_TRUE(ok);
128 128
129 /* 1 expired => GC => 0 in conndb. */ 129 /* 1 expired => GC => 0 in conndb. */
130 ok = run_conn_gc(0, 1, 0); 130 ok = run_conn_gc(0, 1, 0);
131 CHECK_TRUE(ok); 131 CHECK_TRUE(ok);
132 132
133 /* 1 active and 1 expired => GC => 1 in conndb. */ 133 /* 1 active and 1 expired => GC => 1 in conndb. */
134 ok = run_conn_gc(1, 1, 1); 134 ok = run_conn_gc(1, 1, 1);
135 CHECK_TRUE(ok); 135 CHECK_TRUE(ok);
136 136
137 /* 2 expired => GC => 0 in conndb. */ 137 /* 2 expired => GC => 0 in conndb. */
138 ok = run_conn_gc(0, 2, 0); 138 ok = run_conn_gc(0, 2, 0);
139 CHECK_TRUE(ok); 139 CHECK_TRUE(ok);
140 140
141 /* 128 expired => GC => 0 in conndb. */ 141 /* 128 expired => GC => 0 in conndb. */
142 ok = run_conn_gc(0, 128, 0); 142 ok = run_conn_gc(0, 128, 0);
143 CHECK_TRUE(ok); 143 CHECK_TRUE(ok);
144 144
145 /* 512 expired => GC => 256 in conndb. */ 145 /* 512 expired => GC => 256 in conndb. */
146 ok = run_conn_gc(0, 512, 256); 146 ok = run_conn_gc(0, 512, 256);
147 CHECK_TRUE(ok); 147 CHECK_TRUE(ok);
148 148
149 /* 512 expired => GC => 127 in conndb. */ 149 /* 512 expired => GC => 127 in conndb. */
150 npfk_param_set(npf_getkernctx(), "gc.step", 128); 150 npfk_param_set(npf_getkernctx(), "gc.step", 128);
151 ok = run_conn_gc(0, 512, 384); 151 ok = run_conn_gc(0, 512, 384);
152 CHECK_TRUE(ok); 152 CHECK_TRUE(ok);
153 153
154 return true; 154 return true;
155} 155}
156 156
157static bool 157static bool
158run_conndb_tests(npf_t *npf) 158run_conndb_tests(npf_t *npf)
159{ 159{
160 npf_conndb_t *orig_cd = npf->conn_db; 160 npf_conndb_t *orig_cd = npf->conn_db;
161 bool ok; 161 bool ok;
162 162
163 npf_config_enter(npf); 163 npf_config_enter(npf);
164 npf_conn_tracking(npf, true); 164 npf_conn_tracking(npf, true);
165 npf_config_exit(npf); 165 npf_config_exit(npf);
166 166
167 ok = run_gc_tests(); 167 ok = run_gc_tests();
168 168
169 /* We *MUST* restore the valid conndb. */ 169 /* We *MUST* restore the valid conndb. */
170 npf->conn_db = orig_cd; 170 npf->conn_db = orig_cd;
171 return ok; 171 return ok;
172} 172}
173 173
174 174
175static void 175static void
176worker_test_task(npf_t *npf) 176worker_test_task(npf_t *npf)
177{ 177{
178 bool *done = atomic_load_acquire(&npf->arg); 178 bool *done = atomic_load_acquire(&npf->arg);
179 atomic_store_release(done, true); 179 atomic_store_release(done, true);
180} 180}
181 181
182static bool 182static bool
183run_worker_tests(npf_t *npf) 183run_worker_tests(npf_t *npf)
184{ 184{
185 unsigned n = 100; 185 unsigned n = 100;
186 int error; 186 int error;
187 187
188 /* Spawn a worker thread. */ 188 /* Spawn a worker thread. */
189 error = npf_worker_sysinit(1); 189 error = npf_worker_sysinit(1);
190 assert(error == 0); 190 assert(error == 0);
191 191
192 /* 192 /*
193 * Enlist/discharge an instance, trying to trigger a race. 193 * Enlist/discharge an instance, trying to trigger a race.
194 */ 194 */
195 while (n--) { 195 while (n--) {
196 bool task_done = false; 196 bool task_done = false;
197 unsigned retry = 100; 197 unsigned retry = 100;
198 npf_t *test_npf; 198 npf_t *test_npf;
199 199
200 /* 200 /*
201 * Initialize a dummy NPF instance and add a test task. 201 * Initialize a dummy NPF instance and add a test task.
202 * We will (ab)use npf_t::arg here. 202 * We will (ab)use npf_t::arg here.
203 * 203 *
204 * XXX/TODO: We should use: 204 * XXX/TODO: We should use:
205 * 205 *
206 * npfk_create(NPF_NO_GC, &npftest_mbufops, 206 * npfk_create(NPF_NO_GC, &npftest_mbufops,
207 * &npftest_ifops, &task_done); 207 * &npftest_ifops, &task_done);
208 * 208 *
209 * However, it resets the interface state and breaks 209 * However, it resets the interface state and breaks
210 * other tests; to be refactor. 210 * other tests; to be refactor.
211 */ 211 */
212 test_npf = kmem_zalloc(sizeof(npf_t), KM_SLEEP); 212 test_npf = kmem_zalloc(sizeof(npf_t), KM_SLEEP);
213 atomic_store_release(&test_npf->arg, &task_done); 213 atomic_store_release(&test_npf->arg, &task_done);
214 test_npf->ebr = npf_ebr_create(); 214 test_npf->ebr = npf_ebr_create();
215 215
216 error = npf_worker_addfunc(test_npf, worker_test_task); 216 error = npf_worker_addfunc(test_npf, worker_test_task);
217 assert(error == 0); 217 assert(error == 0);
218 218
219 /* Enlist the NPF instance. */ 219 /* Enlist the NPF instance. */
220 npf_worker_enlist(test_npf); 220 npf_worker_enlist(test_npf);
221 221
222 /* Wait for the task to be done. */ 222 /* Wait for the task to be done. */
223 while (!atomic_load_acquire(&task_done) && retry--) { 223 while (!atomic_load_acquire(&task_done) && retry--) {
224 npf_worker_signal(test_npf); 224 npf_worker_signal(test_npf);
225 kpause("gctest", false, mstohz(1), NULL); 225 kpause("gctest", false, MAX(1, mstohz(1)), NULL);
226 } 226 }
227 227
228 CHECK_TRUE(atomic_load_acquire(&task_done)); 228 CHECK_TRUE(atomic_load_acquire(&task_done));
229 npf_worker_discharge(test_npf); 229 npf_worker_discharge(test_npf);
230 230
231 /* Clear the parameter and signal again. */ 231 /* Clear the parameter and signal again. */
232 atomic_store_release(&test_npf->arg, NULL); 232 atomic_store_release(&test_npf->arg, NULL);
233 npf_worker_signal(test_npf); 233 npf_worker_signal(test_npf);
234 234
235 npf_ebr_destroy(test_npf->ebr); 235 npf_ebr_destroy(test_npf->ebr);
236 kmem_free(test_npf, sizeof(npf_t)); // npfk_destroy() 236 kmem_free(test_npf, sizeof(npf_t)); // npfk_destroy()
237 } 237 }
238 238
239 /* 239 /*
240 * Destroy the worker. 240 * Destroy the worker.
241 * 241 *
242 * Attempts to enlist, discharge or signal should have no effect. 242 * Attempts to enlist, discharge or signal should have no effect.
243 */ 243 */
244 244
245 npf_worker_sysfini(); 245 npf_worker_sysfini();
246 npf_worker_enlist(npf); 246 npf_worker_enlist(npf);
247 npf_worker_signal(npf); 247 npf_worker_signal(npf);
248 npf_worker_discharge(npf); 248 npf_worker_discharge(npf);
249 return true; 249 return true;
250} 250}
251 251
252bool 252bool
253npf_gc_test(bool verbose) 253npf_gc_test(bool verbose)
254{ 254{
255 npf_t *npf = npf_getkernctx(); 255 npf_t *npf = npf_getkernctx();
256 bool ok; 256 bool ok;
257 257
258 lverbose = verbose; 258 lverbose = verbose;
259 259
260 ok = run_conndb_tests(npf); 260 ok = run_conndb_tests(npf);
261 CHECK_TRUE(ok); 261 CHECK_TRUE(ok);
262 262
263 ok = run_worker_tests(npf); 263 ok = run_worker_tests(npf);
264 CHECK_TRUE(ok); 264 CHECK_TRUE(ok);
265 265
266 return ok; 266 return ok;
267} 267}