| @@ -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 | |
17 | static bool lverbose = false; | | 17 | static bool lverbose = false; |
18 | | | 18 | |
19 | static unsigned | | 19 | static unsigned |
20 | count_conns(npf_conndb_t *cd) | | 20 | count_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 | |
35 | static struct mbuf * | | 35 | static struct mbuf * |
36 | get_packet(unsigned i) | | 36 | get_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 | |
48 | static bool | | 48 | static bool |
49 | enqueue_connection(unsigned i, bool expire) | | 49 | enqueue_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 | |
65 | static bool | | 65 | static bool |
66 | run_conn_gc(unsigned active, unsigned expired, unsigned expected) | | 66 | run_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 | |
111 | static bool | | 111 | static bool |
112 | run_gc_tests(void) | | 112 | run_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 | |
157 | static bool | | 157 | static bool |
158 | run_conndb_tests(npf_t *npf) | | 158 | run_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 | |
175 | static void | | 175 | static void |
176 | worker_test_task(npf_t *npf) | | 176 | worker_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 | |
182 | static bool | | 182 | static bool |
183 | run_worker_tests(npf_t *npf) | | 183 | run_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 | |
252 | bool | | 252 | bool |
253 | npf_gc_test(bool verbose) | | 253 | npf_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 | } |