Line data Source code
1 : /* Copyright (c) 2014-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : #include "orconfig.h"
5 :
6 : #include <math.h>
7 :
8 : #define SCHEDULER_KIST_PRIVATE
9 : #define CHANNEL_OBJECT_PRIVATE
10 : #define CHANNEL_FILE_PRIVATE
11 : #include "core/or/or.h"
12 : #include "app/config/config.h"
13 : #include "lib/evloop/compat_libevent.h"
14 : #include "core/or/channel.h"
15 : #include "core/or/channeltls.h"
16 : #include "core/mainloop/connection.h"
17 : #include "feature/nodelist/networkstatus.h"
18 : #define SCHEDULER_PRIVATE
19 : #include "core/or/scheduler.h"
20 :
21 : /* Test suite stuff */
22 : #include "test/test.h"
23 : #include "test/fakechans.h"
24 :
25 : /* Shamelessly stolen from compat_libevent.c */
26 : #define V(major, minor, patch) \
27 : (((major) << 24) | ((minor) << 16) | ((patch) << 8))
28 :
29 : /******************************************************************************
30 : * Statistical info
31 : *****************************************************************************/
32 : static int scheduler_compare_channels_mock_ctr = 0;
33 : static int scheduler_run_mock_ctr = 0;
34 :
35 : /******************************************************************************
36 : * Utility functions and things we need to mock
37 : *****************************************************************************/
38 : static or_options_t mocked_options;
39 : static const or_options_t *
40 73 : mock_get_options(void)
41 : {
42 73 : return &mocked_options;
43 : }
44 :
45 : static void
46 16 : cleanup_scheduler_options(void)
47 : {
48 16 : if (mocked_options.SchedulerTypes_) {
49 15 : SMARTLIST_FOREACH(mocked_options.SchedulerTypes_, int *, i, tor_free(i));
50 6 : smartlist_free(mocked_options.SchedulerTypes_);
51 6 : mocked_options.SchedulerTypes_ = NULL;
52 : }
53 16 : }
54 :
55 : static void
56 11 : set_scheduler_options(int val)
57 : {
58 11 : int *type;
59 :
60 11 : if (mocked_options.SchedulerTypes_ == NULL) {
61 8 : mocked_options.SchedulerTypes_ = smartlist_new();
62 : }
63 11 : type = tor_malloc_zero(sizeof(int));
64 11 : *type = val;
65 11 : smartlist_add(mocked_options.SchedulerTypes_, type);
66 11 : }
67 :
68 : static void
69 10 : clear_options(void)
70 : {
71 10 : cleanup_scheduler_options();
72 10 : memset(&mocked_options, 0, sizeof(mocked_options));
73 10 : }
74 :
75 : static int32_t
76 4 : mock_vanilla_networkstatus_get_param(
77 : const networkstatus_t *ns, const char *param_name, int32_t default_val,
78 : int32_t min_val, int32_t max_val)
79 : {
80 4 : (void)ns;
81 4 : (void)default_val;
82 4 : (void)min_val;
83 4 : (void)max_val;
84 : // only support KISTSchedRunInterval right now
85 4 : tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
86 4 : return 0;
87 : }
88 :
89 : static int32_t
90 7 : mock_kist_networkstatus_get_param(
91 : const networkstatus_t *ns, const char *param_name, int32_t default_val,
92 : int32_t min_val, int32_t max_val)
93 : {
94 7 : (void)ns;
95 7 : (void)default_val;
96 7 : (void)min_val;
97 7 : (void)max_val;
98 : // only support KISTSchedRunInterval right now
99 7 : tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
100 7 : return 12;
101 : }
102 :
103 : static int
104 11 : scheduler_compare_channels_mock(const void *c1_v,
105 : const void *c2_v)
106 : {
107 11 : uintptr_t p1, p2;
108 :
109 11 : p1 = (uintptr_t)(c1_v);
110 11 : p2 = (uintptr_t)(c2_v);
111 :
112 11 : ++scheduler_compare_channels_mock_ctr;
113 :
114 11 : if (p1 == p2) return 0;
115 11 : else if (p1 < p2) return 1;
116 6 : else return -1;
117 : }
118 :
119 : static void
120 0 : scheduler_run_noop_mock(void)
121 : {
122 0 : ++scheduler_run_mock_ctr;
123 0 : }
124 :
125 : static circuitmux_t *mock_ccm_tgt_1 = NULL;
126 : static circuitmux_t *mock_ccm_tgt_2 = NULL;
127 : static circuitmux_t *mock_cgp_tgt_1 = NULL;
128 : static circuitmux_policy_t *mock_cgp_val_1 = NULL;
129 : static circuitmux_t *mock_cgp_tgt_2 = NULL;
130 : static circuitmux_policy_t *mock_cgp_val_2 = NULL;
131 :
132 : static const circuitmux_policy_t *
133 12 : circuitmux_get_policy_mock(circuitmux_t *cmux)
134 : {
135 12 : const circuitmux_policy_t *result = NULL;
136 :
137 12 : tt_assert(cmux != NULL);
138 12 : if (cmux) {
139 12 : if (cmux == mock_cgp_tgt_1) result = mock_cgp_val_1;
140 6 : else if (cmux == mock_cgp_tgt_2) result = mock_cgp_val_2;
141 0 : else result = circuitmux_get_policy__real(cmux);
142 : }
143 :
144 12 : done:
145 12 : return result;
146 : }
147 :
148 : static int
149 2 : circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
150 : circuitmux_t *cmux_2)
151 : {
152 2 : int result = 0;
153 :
154 2 : tt_assert(cmux_1 != NULL);
155 2 : tt_assert(cmux_2 != NULL);
156 :
157 2 : if (cmux_1 != cmux_2) {
158 2 : if (cmux_1 == mock_ccm_tgt_1 && cmux_2 == mock_ccm_tgt_2) result = -1;
159 1 : else if (cmux_1 == mock_ccm_tgt_2 && cmux_2 == mock_ccm_tgt_1) {
160 : result = 1;
161 : } else {
162 0 : if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_2) result = -1;
163 0 : else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) {
164 : result = 1;
165 : } else {
166 0 : result = circuitmux_compare_muxes__real(cmux_1, cmux_2);
167 : }
168 : }
169 : }
170 : /* else result = 0 always */
171 :
172 0 : done:
173 2 : return result;
174 : }
175 :
176 : typedef struct {
177 : const channel_t *chan;
178 : ssize_t cells;
179 : } flush_mock_channel_t;
180 :
181 : static smartlist_t *chans_for_flush_mock = NULL;
182 :
183 : static void
184 2 : channel_flush_some_cells_mock_free_all(void)
185 : {
186 2 : if (chans_for_flush_mock) {
187 6 : SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
188 : flush_mock_channel_t *,
189 : flush_mock_ch) {
190 4 : SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch);
191 4 : tor_free(flush_mock_ch);
192 4 : } SMARTLIST_FOREACH_END(flush_mock_ch);
193 :
194 2 : smartlist_free(chans_for_flush_mock);
195 2 : chans_for_flush_mock = NULL;
196 : }
197 2 : }
198 :
199 : static void
200 7 : channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells)
201 : {
202 7 : int found = 0;
203 :
204 7 : if (!chan) return;
205 7 : if (num_cells <= 0) return;
206 :
207 7 : if (!chans_for_flush_mock) {
208 2 : chans_for_flush_mock = smartlist_new();
209 : }
210 :
211 10 : SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
212 : flush_mock_channel_t *,
213 : flush_mock_ch) {
214 6 : if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) {
215 6 : if (flush_mock_ch->chan == chan) {
216 : /* Found it */
217 3 : flush_mock_ch->cells = num_cells;
218 3 : found = 1;
219 3 : break;
220 : }
221 : } else {
222 : /* That shouldn't be there... */
223 0 : SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch);
224 0 : tor_free(flush_mock_ch);
225 : }
226 3 : } SMARTLIST_FOREACH_END(flush_mock_ch);
227 :
228 3 : if (! found) {
229 : /* The loop didn't find it */
230 4 : flush_mock_channel_t *flush_mock_ch;
231 4 : flush_mock_ch = tor_malloc_zero(sizeof(*flush_mock_ch));
232 4 : flush_mock_ch->chan = chan;
233 4 : flush_mock_ch->cells = num_cells;
234 4 : smartlist_add(chans_for_flush_mock, flush_mock_ch);
235 : }
236 : }
237 :
238 : static int
239 50 : channel_more_to_flush_mock(channel_t *chan)
240 : {
241 50 : tor_assert(chan);
242 :
243 50 : flush_mock_channel_t *found_mock_ch = NULL;
244 :
245 70 : SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
246 : flush_mock_channel_t *,
247 : flush_mock_ch) {
248 70 : if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) {
249 70 : if (flush_mock_ch->chan == chan) {
250 : /* Found it */
251 : found_mock_ch = flush_mock_ch;
252 : break;
253 : }
254 : } else {
255 : /* That shouldn't be there... */
256 0 : SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch);
257 0 : tor_free(flush_mock_ch);
258 : }
259 20 : } SMARTLIST_FOREACH_END(flush_mock_ch);
260 :
261 50 : tor_assert(found_mock_ch);
262 :
263 : /* Check if any circuits would like to queue some */
264 : /* special for the mock: return the number of cells (instead of 1), or zero
265 : * if nothing to flush */
266 50 : return (found_mock_ch->cells > 0 ? (int)found_mock_ch->cells : 0 );
267 : }
268 :
269 : static void
270 26 : channel_write_to_kernel_mock(channel_t *chan)
271 : {
272 26 : (void)chan;
273 : //log_debug(LD_SCHED, "chan=%d writing to kernel",
274 : // (int)chan->global_identifier);
275 26 : }
276 :
277 : static int
278 19 : channel_should_write_to_kernel_mock(outbuf_table_t *ot, channel_t *chan)
279 : {
280 19 : (void)ot;
281 19 : (void)chan;
282 19 : return 1;
283 : /* We could make this more complicated if we wanted. But I don't think doing
284 : * so tests much of anything */
285 : //static int called_counter = 0;
286 : //if (++called_counter >= 3) {
287 : // called_counter -= 3;
288 : // log_debug(LD_SCHED, "chan=%d should write to kernel",
289 : // (int)chan->global_identifier);
290 : // return 1;
291 : //}
292 : //return 0;
293 : }
294 :
295 : static ssize_t
296 29 : channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells)
297 : {
298 29 : ssize_t flushed = 0, max;
299 29 : char unlimited = 0;
300 29 : flush_mock_channel_t *found = NULL;
301 :
302 29 : tt_ptr_op(chan, OP_NE, NULL);
303 29 : if (chan) {
304 29 : if (num_cells < 0) {
305 0 : num_cells = 0;
306 0 : unlimited = 1;
307 : }
308 :
309 : /* Check if we have it */
310 29 : if (chans_for_flush_mock != NULL) {
311 39 : SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
312 : flush_mock_channel_t *,
313 : flush_mock_ch) {
314 39 : if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) {
315 39 : if (flush_mock_ch->chan == chan) {
316 : /* Found it */
317 : found = flush_mock_ch;
318 : break;
319 : }
320 : } else {
321 : /* That shouldn't be there... */
322 0 : SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch);
323 0 : tor_free(flush_mock_ch);
324 : }
325 11 : } SMARTLIST_FOREACH_END(flush_mock_ch);
326 :
327 28 : if (found) {
328 : /* We found one */
329 28 : if (found->cells < 0) found->cells = 0;
330 :
331 28 : if (unlimited) max = found->cells;
332 28 : else max = MIN(found->cells, num_cells);
333 :
334 28 : flushed += max;
335 28 : found->cells -= max;
336 : }
337 : }
338 : }
339 :
340 1 : done:
341 29 : return flushed;
342 : }
343 :
344 : static void
345 6 : update_socket_info_impl_mock(socket_table_ent_t *ent)
346 : {
347 6 : ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0;
348 6 : ent->limit = INT_MAX;
349 6 : }
350 :
351 : static void
352 3 : perform_channel_state_tests(int KISTSchedRunInterval, int sched_type)
353 : {
354 3 : channel_t *ch1 = NULL, *ch2 = NULL;
355 3 : int old_count;
356 :
357 : /* setup options so we're sure about what sched we are running */
358 3 : MOCK(get_options, mock_get_options);
359 3 : clear_options();
360 3 : mocked_options.KISTSchedRunInterval = KISTSchedRunInterval;
361 3 : set_scheduler_options(sched_type);
362 :
363 : /* Set up scheduler */
364 3 : scheduler_init();
365 : /*
366 : * Install the compare channels mock so we can test
367 : * scheduler_touch_channel().
368 : */
369 3 : MOCK(scheduler_compare_channels, scheduler_compare_channels_mock);
370 : /*
371 : * Disable scheduler_run so we can just check the state transitions
372 : * without having to make everything it might call work too.
373 : */
374 3 : ((scheduler_t *) the_scheduler)->run = scheduler_run_noop_mock;
375 :
376 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
377 :
378 : /* Set up a fake channel */
379 3 : ch1 = new_fake_channel();
380 3 : tt_assert(ch1);
381 :
382 : /* Start it off in OPENING */
383 3 : ch1->state = CHANNEL_STATE_OPENING;
384 : /* Try to register it */
385 3 : channel_register(ch1);
386 3 : tt_assert(ch1->registered);
387 :
388 : /* It should start off in SCHED_CHAN_IDLE */
389 3 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
390 :
391 : /* Now get another one */
392 3 : ch2 = new_fake_channel();
393 3 : tt_assert(ch2);
394 3 : ch2->state = CHANNEL_STATE_OPENING;
395 3 : channel_register(ch2);
396 3 : tt_assert(ch2->registered);
397 :
398 : /* Send ch1 to SCHED_CHAN_WAITING_TO_WRITE */
399 3 : scheduler_channel_has_waiting_cells(ch1);
400 3 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
401 :
402 : /* This should send it to SCHED_CHAN_PENDING */
403 3 : scheduler_channel_wants_writes(ch1);
404 3 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
405 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
406 :
407 : /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */
408 3 : scheduler_channel_wants_writes(ch2);
409 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
410 :
411 : /* Drop ch2 back to idle */
412 3 : scheduler_channel_doesnt_want_writes(ch2);
413 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
414 :
415 : /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */
416 3 : scheduler_channel_wants_writes(ch2);
417 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
418 :
419 : /* ...and this should kick ch2 into SCHED_CHAN_PENDING */
420 3 : scheduler_channel_has_waiting_cells(ch2);
421 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
422 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
423 :
424 : /* This should send ch2 to SCHED_CHAN_WAITING_TO_WRITE */
425 3 : scheduler_channel_doesnt_want_writes(ch2);
426 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
427 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
428 :
429 : /* ...and back to SCHED_CHAN_PENDING */
430 3 : scheduler_channel_wants_writes(ch2);
431 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
432 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
433 :
434 : /* Now we exercise scheduler_touch_channel */
435 3 : old_count = scheduler_compare_channels_mock_ctr;
436 3 : scheduler_touch_channel(ch1);
437 3 : tt_assert(scheduler_compare_channels_mock_ctr > old_count);
438 :
439 : /* Release the ch2 and then do it another time to make sure it doesn't blow
440 : * up and we are still in a quiescent state. */
441 3 : scheduler_release_channel(ch2);
442 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
443 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
444 : /* Cheat a bit so make the release more confused but also will tells us if
445 : * the release did put the channel in the right state. */
446 3 : ch2->scheduler_state = SCHED_CHAN_PENDING;
447 3 : scheduler_release_channel(ch2);
448 3 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
449 3 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
450 :
451 : /* Close */
452 3 : channel_mark_for_close(ch1);
453 3 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSING);
454 3 : channel_mark_for_close(ch2);
455 3 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING);
456 3 : channel_closed(ch1);
457 3 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSED);
458 3 : ch1 = NULL;
459 3 : channel_closed(ch2);
460 3 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSED);
461 3 : ch2 = NULL;
462 :
463 : /* Shut things down */
464 :
465 3 : channel_free_all();
466 3 : scheduler_free_all();
467 :
468 3 : done:
469 3 : tor_free(ch1);
470 3 : tor_free(ch2);
471 :
472 3 : UNMOCK(scheduler_compare_channels);
473 3 : UNMOCK(get_options);
474 3 : cleanup_scheduler_options();
475 :
476 3 : return;
477 : }
478 :
479 : static void
480 1 : test_scheduler_compare_channels(void *arg)
481 : {
482 : /* We don't actually need whole fake channels... */
483 1 : channel_t c1, c2;
484 : /* ...and some dummy circuitmuxes too */
485 1 : circuitmux_t *cm1 = NULL, *cm2 = NULL;
486 1 : int result;
487 :
488 1 : (void)arg;
489 :
490 : /* We can't actually see sizeof(circuitmux_t) from here */
491 1 : cm1 = tor_malloc_zero(sizeof(void *));
492 1 : cm2 = tor_malloc_zero(sizeof(void *));
493 :
494 1 : c1.cmux = cm1;
495 1 : c2.cmux = cm2;
496 :
497 : /* Configure circuitmux_get_policy() mock */
498 1 : mock_cgp_tgt_1 = cm1;
499 1 : mock_cgp_tgt_2 = cm2;
500 :
501 : /*
502 : * This is to test the different-policies case, which uses the policy
503 : * cast to an uintptr_t as an arbitrary but definite thing to compare.
504 : */
505 1 : mock_cgp_val_1 = tor_malloc_zero(16);
506 1 : mock_cgp_val_2 = tor_malloc_zero(16);
507 1 : if ( ((uintptr_t) mock_cgp_val_1) > ((uintptr_t) mock_cgp_val_2) ) {
508 0 : void *tmp = mock_cgp_val_1;
509 0 : mock_cgp_val_1 = mock_cgp_val_2;
510 0 : mock_cgp_val_2 = tmp;
511 : }
512 :
513 1 : MOCK(circuitmux_get_policy, circuitmux_get_policy_mock);
514 :
515 : /* Now set up circuitmux_compare_muxes() mock using cm1/cm2 */
516 1 : mock_ccm_tgt_1 = cm1;
517 1 : mock_ccm_tgt_2 = cm2;
518 1 : MOCK(circuitmux_compare_muxes, circuitmux_compare_muxes_mock);
519 :
520 : /* Equal-channel case */
521 1 : result = scheduler_compare_channels(&c1, &c1);
522 1 : tt_int_op(result, OP_EQ, 0);
523 :
524 : /* Distinct channels, distinct policies */
525 1 : result = scheduler_compare_channels(&c1, &c2);
526 1 : tt_int_op(result, OP_EQ, -1);
527 1 : result = scheduler_compare_channels(&c2, &c1);
528 1 : tt_int_op(result, OP_EQ, 1);
529 :
530 : /* Distinct channels, same policy */
531 1 : tor_free(mock_cgp_val_2);
532 1 : mock_cgp_val_2 = mock_cgp_val_1;
533 1 : result = scheduler_compare_channels(&c1, &c2);
534 1 : tt_int_op(result, OP_EQ, -1);
535 1 : result = scheduler_compare_channels(&c2, &c1);
536 1 : tt_int_op(result, OP_EQ, 1);
537 :
538 1 : done:
539 :
540 1 : UNMOCK(circuitmux_compare_muxes);
541 1 : mock_ccm_tgt_1 = NULL;
542 1 : mock_ccm_tgt_2 = NULL;
543 :
544 1 : UNMOCK(circuitmux_get_policy);
545 1 : mock_cgp_tgt_1 = NULL;
546 1 : mock_cgp_tgt_2 = NULL;
547 :
548 1 : tor_free(cm1);
549 1 : tor_free(cm2);
550 :
551 1 : if (mock_cgp_val_1 != mock_cgp_val_2)
552 0 : tor_free(mock_cgp_val_1);
553 1 : tor_free(mock_cgp_val_2);
554 1 : mock_cgp_val_1 = NULL;
555 1 : mock_cgp_val_2 = NULL;
556 :
557 1 : return;
558 : }
559 :
560 : /******************************************************************************
561 : * The actual tests!
562 : *****************************************************************************/
563 :
564 : static void
565 1 : test_scheduler_loop_vanilla(void *arg)
566 : {
567 1 : (void)arg;
568 1 : channel_t *ch1 = NULL, *ch2 = NULL;
569 1 : void (*run_func_ptr)(void);
570 :
571 : /* setup options so we're sure about what sched we are running */
572 1 : MOCK(get_options, mock_get_options);
573 1 : clear_options();
574 1 : set_scheduler_options(SCHEDULER_VANILLA);
575 1 : mocked_options.KISTSchedRunInterval = 0;
576 :
577 : /* Set up scheduler */
578 1 : scheduler_init();
579 : /*
580 : * Install the compare channels mock so we can test
581 : * scheduler_touch_channel().
582 : */
583 1 : MOCK(scheduler_compare_channels, scheduler_compare_channels_mock);
584 : /*
585 : * Disable scheduler_run so we can just check the state transitions
586 : * without having to make everything it might call work too.
587 : */
588 1 : run_func_ptr = the_scheduler->run;
589 1 : ((scheduler_t *) the_scheduler)->run = scheduler_run_noop_mock;
590 :
591 1 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
592 :
593 : /* Set up a fake channel */
594 1 : ch1 = new_fake_channel();
595 1 : ch1->magic = TLS_CHAN_MAGIC;
596 1 : tt_assert(ch1);
597 :
598 : /* Start it off in OPENING */
599 1 : ch1->state = CHANNEL_STATE_OPENING;
600 : /* Try to register it */
601 1 : channel_register(ch1);
602 1 : tt_assert(ch1->registered);
603 : /* Finish opening it */
604 1 : channel_change_state_open(ch1);
605 :
606 : /* It should start off in SCHED_CHAN_IDLE */
607 1 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
608 :
609 : /* Now get another one */
610 1 : ch2 = new_fake_channel();
611 1 : ch2->magic = TLS_CHAN_MAGIC;
612 1 : tt_assert(ch2);
613 1 : ch2->state = CHANNEL_STATE_OPENING;
614 1 : channel_register(ch2);
615 1 : tt_assert(ch2->registered);
616 : /*
617 : * Don't open ch2; then channel_num_cells_writeable() will return
618 : * zero and we'll get coverage of that exception case in scheduler_run()
619 : */
620 :
621 1 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
622 1 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPENING);
623 :
624 : /* Send it to SCHED_CHAN_WAITING_TO_WRITE */
625 1 : scheduler_channel_has_waiting_cells(ch1);
626 1 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
627 :
628 : /* This should send it to SCHED_CHAN_PENDING */
629 1 : scheduler_channel_wants_writes(ch1);
630 1 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
631 1 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
632 :
633 : /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */
634 1 : scheduler_channel_wants_writes(ch2);
635 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
636 :
637 : /* Drop ch2 back to idle */
638 1 : scheduler_channel_doesnt_want_writes(ch2);
639 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
640 :
641 : /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */
642 1 : scheduler_channel_wants_writes(ch2);
643 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
644 :
645 : /* ...and this should kick ch2 into SCHED_CHAN_PENDING */
646 1 : scheduler_channel_has_waiting_cells(ch2);
647 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
648 1 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
649 :
650 : /*
651 : * Now we've got two pending channels and need to fire off
652 : * the scheduler run() that we kept.
653 : */
654 1 : run_func_ptr();
655 :
656 : /*
657 : * Assert that they're still in the states we left and aren't still
658 : * pending
659 : */
660 1 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
661 1 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPENING);
662 1 : tt_assert(ch1->scheduler_state != SCHED_CHAN_PENDING);
663 1 : tt_assert(ch2->scheduler_state != SCHED_CHAN_PENDING);
664 1 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
665 :
666 : /* Now, finish opening ch2, and get both back to pending */
667 1 : channel_change_state_open(ch2);
668 1 : scheduler_channel_wants_writes(ch1);
669 1 : scheduler_channel_wants_writes(ch2);
670 1 : scheduler_channel_has_waiting_cells(ch1);
671 1 : scheduler_channel_has_waiting_cells(ch2);
672 1 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
673 1 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPEN);
674 1 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
675 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
676 1 : tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
677 :
678 : /* Now, set up the channel_flush_some_cells() mock */
679 1 : MOCK(channel_flush_some_cells, channel_flush_some_cells_mock);
680 : /*
681 : * 16 cells on ch1 means it'll completely drain into the 32 cells
682 : * fakechan's num_cells_writeable() returns.
683 : */
684 1 : channel_flush_some_cells_mock_set(ch1, 16);
685 : /*
686 : * This one should get sent back to pending, since num_cells_writeable()
687 : * will still return non-zero.
688 : */
689 1 : channel_flush_some_cells_mock_set(ch2, 48);
690 :
691 : /*
692 : * And re-run the scheduler run() loop with non-zero returns from
693 : * channel_flush_some_cells() this time.
694 : */
695 1 : run_func_ptr();
696 :
697 : /*
698 : * ch1 should have gone to SCHED_CHAN_WAITING_FOR_CELLS, with 16 flushed
699 : * and 32 writeable.
700 : */
701 1 : tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
702 : /*
703 : * ...ch2 should also have gone to SCHED_CHAN_WAITING_FOR_CELLS, with
704 : * channel_more_to_flush() returning false and channel_num_cells_writeable()
705 : * > 0/
706 : */
707 1 : tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
708 :
709 : /* Close */
710 1 : channel_mark_for_close(ch1);
711 1 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSING);
712 1 : channel_mark_for_close(ch2);
713 1 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING);
714 1 : channel_closed(ch1);
715 1 : tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSED);
716 1 : ch1 = NULL;
717 1 : channel_closed(ch2);
718 1 : tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSED);
719 1 : ch2 = NULL;
720 :
721 : /* Shut things down */
722 1 : channel_flush_some_cells_mock_free_all();
723 1 : channel_free_all();
724 1 : scheduler_free_all();
725 :
726 1 : done:
727 1 : tor_free(ch1);
728 1 : tor_free(ch2);
729 1 : cleanup_scheduler_options();
730 :
731 1 : UNMOCK(channel_flush_some_cells);
732 1 : UNMOCK(scheduler_compare_channels);
733 1 : UNMOCK(get_options);
734 1 : }
735 :
736 : static void
737 1 : test_scheduler_loop_kist(void *arg)
738 : {
739 1 : (void) arg;
740 :
741 : #ifndef HAVE_KIST_SUPPORT
742 : return;
743 : #endif
744 :
745 1 : channel_t *ch1 = new_fake_channel(), *ch2 = new_fake_channel();
746 1 : channel_t *ch3 = new_fake_channel();
747 :
748 : /* setup options so we're sure about what sched we are running */
749 1 : MOCK(get_options, mock_get_options);
750 1 : MOCK(channel_flush_some_cells, channel_flush_some_cells_mock);
751 1 : MOCK(channel_more_to_flush, channel_more_to_flush_mock);
752 1 : MOCK(channel_write_to_kernel, channel_write_to_kernel_mock);
753 1 : MOCK(channel_should_write_to_kernel, channel_should_write_to_kernel_mock);
754 1 : MOCK(update_socket_info_impl, update_socket_info_impl_mock);
755 1 : clear_options();
756 1 : mocked_options.KISTSchedRunInterval = 11;
757 1 : set_scheduler_options(SCHEDULER_KIST);
758 1 : scheduler_init();
759 :
760 1 : tt_assert(ch1);
761 1 : ch1->magic = TLS_CHAN_MAGIC;
762 1 : ch1->state = CHANNEL_STATE_OPENING;
763 1 : channel_register(ch1);
764 1 : tt_assert(ch1->registered);
765 1 : channel_change_state_open(ch1);
766 1 : scheduler_channel_has_waiting_cells(ch1);
767 1 : scheduler_channel_wants_writes(ch1);
768 1 : channel_flush_some_cells_mock_set(ch1, 5);
769 :
770 1 : tt_assert(ch2);
771 1 : ch2->magic = TLS_CHAN_MAGIC;
772 1 : ch2->state = CHANNEL_STATE_OPENING;
773 1 : channel_register(ch2);
774 1 : tt_assert(ch2->registered);
775 1 : channel_change_state_open(ch2);
776 1 : scheduler_channel_has_waiting_cells(ch2);
777 1 : scheduler_channel_wants_writes(ch2);
778 1 : channel_flush_some_cells_mock_set(ch2, 5);
779 :
780 1 : the_scheduler->run();
781 :
782 1 : scheduler_channel_has_waiting_cells(ch1);
783 1 : channel_flush_some_cells_mock_set(ch1, 5);
784 :
785 1 : the_scheduler->run();
786 :
787 1 : scheduler_channel_has_waiting_cells(ch1);
788 1 : channel_flush_some_cells_mock_set(ch1, 5);
789 1 : scheduler_channel_has_waiting_cells(ch2);
790 1 : channel_flush_some_cells_mock_set(ch2, 5);
791 :
792 1 : the_scheduler->run();
793 :
794 1 : channel_flush_some_cells_mock_free_all();
795 :
796 : /* We'll try to run this closed channel threw the scheduler loop and make
797 : * sure it ends up in the right state. */
798 1 : tt_assert(ch3);
799 1 : ch3->magic = TLS_CHAN_MAGIC;
800 1 : ch3->state = CHANNEL_STATE_OPEN;
801 1 : circuitmux_free(ch3->cmux);
802 1 : ch3->cmux = circuitmux_alloc();
803 1 : channel_register(ch3);
804 1 : tt_assert(ch3->registered);
805 :
806 1 : ch3->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
807 1 : scheduler_channel_has_waiting_cells(ch3);
808 : /* Should be in the pending list now waiting to be handled. */
809 1 : tt_int_op(ch3->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
810 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
811 : /* By running the scheduler on a closed channel, it should end up in the
812 : * IDLE state and not in the pending channel list. */
813 1 : ch3->state = CHANNEL_STATE_CLOSED;
814 1 : the_scheduler->run();
815 1 : tt_int_op(ch3->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
816 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
817 :
818 1 : done:
819 : /* Prep the channel so the free() function doesn't explode. */
820 1 : ch1->state = ch2->state = ch3->state = CHANNEL_STATE_CLOSED;
821 1 : ch1->registered = ch2->registered = ch3->registered = 0;
822 1 : channel_free(ch1);
823 1 : channel_free(ch2);
824 1 : channel_free(ch3);
825 1 : UNMOCK(update_socket_info_impl);
826 1 : UNMOCK(channel_should_write_to_kernel);
827 1 : UNMOCK(channel_write_to_kernel);
828 1 : UNMOCK(channel_more_to_flush);
829 1 : UNMOCK(channel_flush_some_cells);
830 1 : UNMOCK(get_options);
831 1 : scheduler_free_all();
832 1 : return;
833 : }
834 :
835 : static void
836 1 : test_scheduler_channel_states(void *arg)
837 : {
838 1 : (void)arg;
839 1 : perform_channel_state_tests(-1, SCHEDULER_VANILLA);
840 1 : perform_channel_state_tests(11, SCHEDULER_KIST_LITE);
841 : #ifdef HAVE_KIST_SUPPORT
842 1 : perform_channel_state_tests(11, SCHEDULER_KIST);
843 : #endif
844 1 : }
845 :
846 : static void
847 1 : test_scheduler_initfree(void *arg)
848 : {
849 1 : (void)arg;
850 :
851 1 : tt_ptr_op(channels_pending, OP_EQ, NULL);
852 1 : tt_ptr_op(run_sched_ev, OP_EQ, NULL);
853 :
854 1 : MOCK(get_options, mock_get_options);
855 1 : set_scheduler_options(SCHEDULER_KIST);
856 1 : set_scheduler_options(SCHEDULER_KIST_LITE);
857 1 : set_scheduler_options(SCHEDULER_VANILLA);
858 :
859 1 : scheduler_init();
860 :
861 1 : tt_ptr_op(channels_pending, OP_NE, NULL);
862 1 : tt_ptr_op(run_sched_ev, OP_NE, NULL);
863 : /* We have specified nothing in the torrc and there's no consensus so the
864 : * KIST scheduler is what should be in use */
865 1 : tt_ptr_op(the_scheduler, OP_EQ, get_kist_scheduler());
866 1 : tt_int_op(sched_run_interval, OP_EQ, 10);
867 :
868 1 : scheduler_free_all();
869 :
870 1 : tt_ptr_op(channels_pending, OP_EQ, NULL);
871 1 : tt_ptr_op(run_sched_ev, OP_EQ, NULL);
872 :
873 1 : done:
874 1 : UNMOCK(get_options);
875 1 : cleanup_scheduler_options();
876 1 : return;
877 : }
878 :
879 : static void
880 1 : test_scheduler_can_use_kist(void *arg)
881 : {
882 1 : (void)arg;
883 :
884 1 : int res_should, res_freq;
885 1 : MOCK(get_options, mock_get_options);
886 :
887 : /* Test force enabling of KIST */
888 1 : clear_options();
889 1 : mocked_options.KISTSchedRunInterval = 1234;
890 1 : res_should = scheduler_can_use_kist();
891 1 : res_freq = kist_scheduler_run_interval();
892 : #ifdef HAVE_KIST_SUPPORT
893 1 : tt_int_op(res_should, OP_EQ, 1);
894 : #else /* HAVE_KIST_SUPPORT */
895 : tt_int_op(res_should, OP_EQ, 0);
896 : #endif /* HAVE_KIST_SUPPORT */
897 1 : tt_int_op(res_freq, OP_EQ, 1234);
898 :
899 : /* Test defer to consensus, but no consensus available */
900 1 : clear_options();
901 1 : mocked_options.KISTSchedRunInterval = 0;
902 1 : res_should = scheduler_can_use_kist();
903 1 : res_freq = kist_scheduler_run_interval();
904 : #ifdef HAVE_KIST_SUPPORT
905 1 : tt_int_op(res_should, OP_EQ, 1);
906 : #else /* HAVE_KIST_SUPPORT */
907 : tt_int_op(res_should, OP_EQ, 0);
908 : #endif /* HAVE_KIST_SUPPORT */
909 1 : tt_int_op(res_freq, OP_EQ, 10);
910 :
911 : /* Test defer to consensus, and kist consensus available */
912 1 : MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
913 1 : clear_options();
914 1 : mocked_options.KISTSchedRunInterval = 0;
915 1 : res_should = scheduler_can_use_kist();
916 1 : res_freq = kist_scheduler_run_interval();
917 : #ifdef HAVE_KIST_SUPPORT
918 1 : tt_int_op(res_should, OP_EQ, 1);
919 : #else /* HAVE_KIST_SUPPORT */
920 : tt_int_op(res_should, OP_EQ, 0);
921 : #endif /* HAVE_KIST_SUPPORT */
922 1 : tt_int_op(res_freq, OP_EQ, 12);
923 1 : UNMOCK(networkstatus_get_param);
924 :
925 : /* Test defer to consensus, and vanilla consensus available */
926 1 : MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
927 1 : clear_options();
928 1 : mocked_options.KISTSchedRunInterval = 0;
929 1 : res_should = scheduler_can_use_kist();
930 1 : res_freq = kist_scheduler_run_interval();
931 1 : tt_int_op(res_should, OP_EQ, 0);
932 1 : tt_int_op(res_freq, OP_EQ, 0);
933 1 : UNMOCK(networkstatus_get_param);
934 :
935 1 : done:
936 1 : UNMOCK(get_options);
937 1 : return;
938 : }
939 :
940 : static void
941 1 : test_scheduler_ns_changed(void *arg)
942 : {
943 1 : (void) arg;
944 :
945 : /*
946 : * Currently no scheduler implementations use the old/new consensuses passed
947 : * in scheduler_notify_networkstatus_changed, so it is okay to pass NULL.
948 : *
949 : * "But then what does test actually exercise???" It tests that
950 : * scheduler_notify_networkstatus_changed fetches the correct value from the
951 : * consensus, and then switches the scheduler if necessasry.
952 : */
953 :
954 1 : MOCK(get_options, mock_get_options);
955 1 : clear_options();
956 1 : set_scheduler_options(SCHEDULER_KIST);
957 1 : set_scheduler_options(SCHEDULER_VANILLA);
958 :
959 1 : tt_ptr_op(the_scheduler, OP_EQ, NULL);
960 :
961 : /* Change from vanilla to kist via consensus */
962 1 : the_scheduler = get_vanilla_scheduler();
963 1 : MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
964 1 : scheduler_notify_networkstatus_changed();
965 1 : UNMOCK(networkstatus_get_param);
966 : #ifdef HAVE_KIST_SUPPORT
967 1 : tt_ptr_op(the_scheduler, OP_EQ, get_kist_scheduler());
968 : #else
969 : tt_ptr_op(the_scheduler, OP_EQ, get_vanilla_scheduler());
970 : #endif
971 :
972 : /* Change from kist to vanilla via consensus */
973 1 : the_scheduler = get_kist_scheduler();
974 1 : MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
975 1 : scheduler_notify_networkstatus_changed();
976 1 : UNMOCK(networkstatus_get_param);
977 1 : tt_ptr_op(the_scheduler, OP_EQ, get_vanilla_scheduler());
978 :
979 : /* Doesn't change when using KIST */
980 1 : the_scheduler = get_kist_scheduler();
981 1 : MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
982 1 : scheduler_notify_networkstatus_changed();
983 1 : UNMOCK(networkstatus_get_param);
984 : #ifdef HAVE_KIST_SUPPORT
985 1 : tt_ptr_op(the_scheduler, OP_EQ, get_kist_scheduler());
986 : #else
987 : tt_ptr_op(the_scheduler, OP_EQ, get_vanilla_scheduler());
988 : #endif
989 :
990 : /* Doesn't change when using vanilla */
991 1 : the_scheduler = get_vanilla_scheduler();
992 1 : MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
993 1 : scheduler_notify_networkstatus_changed();
994 1 : UNMOCK(networkstatus_get_param);
995 1 : tt_ptr_op(the_scheduler, OP_EQ, get_vanilla_scheduler());
996 :
997 1 : done:
998 1 : UNMOCK(get_options);
999 1 : cleanup_scheduler_options();
1000 1 : return;
1001 : }
1002 :
1003 : /*
1004 : * Mocked functions for the kist_pending_list test.
1005 : */
1006 :
1007 : static int mock_flush_some_cells_num = 1;
1008 : static int mock_more_to_flush = 0;
1009 : static int mock_update_socket_info_limit = 0;
1010 :
1011 : static ssize_t
1012 5 : channel_flush_some_cells_mock_var(channel_t *chan, ssize_t num_cells)
1013 : {
1014 5 : (void) chan;
1015 5 : (void) num_cells;
1016 5 : return mock_flush_some_cells_num;
1017 : }
1018 :
1019 : /* Because when we flush cells, it is possible that the connection outbuf gets
1020 : * fully drained, the wants to write scheduler event is fired back while we
1021 : * are in the scheduler loop so this mock function does it for us.
1022 : * Furthermore, the socket limit is set to 0 so once this is triggered, it
1023 : * informs the scheduler that it can't write on the socket anymore. */
1024 : static void
1025 2 : channel_write_to_kernel_mock_trigger_24700(channel_t *chan)
1026 : {
1027 2 : static int chan_id_seen[2] = {0};
1028 2 : if (++chan_id_seen[chan->global_identifier - 1] > 1) {
1029 0 : tt_assert(0);
1030 : }
1031 :
1032 2 : scheduler_channel_wants_writes(chan);
1033 :
1034 2 : done:
1035 2 : return;
1036 : }
1037 :
1038 : static int
1039 10 : channel_more_to_flush_mock_var(channel_t *chan)
1040 : {
1041 10 : (void) chan;
1042 10 : return mock_more_to_flush;
1043 : }
1044 :
1045 : static void
1046 6 : update_socket_info_impl_mock_var(socket_table_ent_t *ent)
1047 : {
1048 6 : ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0;
1049 6 : ent->limit = mock_update_socket_info_limit;
1050 6 : }
1051 :
1052 : static void
1053 1 : test_scheduler_kist_pending_list(void *arg)
1054 : {
1055 1 : (void) arg;
1056 :
1057 : #ifndef HAVE_KIST_SUPPORT
1058 : return;
1059 : #endif
1060 :
1061 : /* This is for testing the channel flow with the pending list that is
1062 : * depending on the channel state, what will be the expected behavior of the
1063 : * scheduler with that list.
1064 : *
1065 : * For instance, we want to catch double channel add or removing a channel
1066 : * that doesn't exists, or putting a channel in the list in a wrong state.
1067 : * Essentially, this will articifically test cases of the KIST main loop and
1068 : * entry point in the channel subsystem.
1069 : *
1070 : * In part, this is to also catch things like #24700 and provide a test bed
1071 : * for more testing in the future like so. */
1072 :
1073 : /* Mocking a series of scheduler function to control the flow of the
1074 : * scheduler loop to test every use cases and assess the pending list. */
1075 1 : MOCK(get_options, mock_get_options);
1076 1 : MOCK(channel_flush_some_cells, channel_flush_some_cells_mock_var);
1077 1 : MOCK(channel_more_to_flush, channel_more_to_flush_mock_var);
1078 1 : MOCK(update_socket_info_impl, update_socket_info_impl_mock_var);
1079 1 : MOCK(channel_write_to_kernel, channel_write_to_kernel_mock);
1080 1 : MOCK(channel_should_write_to_kernel, channel_should_write_to_kernel_mock);
1081 :
1082 : /* Setup options so we're sure about what sched we are running */
1083 1 : mocked_options.KISTSchedRunInterval = 10;
1084 1 : set_scheduler_options(SCHEDULER_KIST);
1085 :
1086 : /* Init scheduler. */
1087 1 : scheduler_init();
1088 :
1089 : /* Initialize a channel. We'll need a second channel for the #24700 bug
1090 : * test. */
1091 1 : channel_t *chan1 = new_fake_channel();
1092 1 : channel_t *chan2 = new_fake_channel();
1093 1 : tt_assert(chan1);
1094 1 : tt_assert(chan2);
1095 1 : chan1->magic = chan2->magic = TLS_CHAN_MAGIC;
1096 1 : channel_register(chan1);
1097 1 : channel_register(chan2);
1098 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
1099 1 : tt_int_op(chan1->sched_heap_idx, OP_EQ, -1);
1100 1 : tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
1101 1 : tt_int_op(chan2->sched_heap_idx, OP_EQ, -1);
1102 :
1103 : /* Once a channel becomes OPEN, it always have at least one cell in it so
1104 : * the scheduler is notified that the channel wants to write so this is the
1105 : * first step. Might not make sense to you but it is the way it is. */
1106 1 : scheduler_channel_wants_writes(chan1);
1107 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
1108 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1109 :
1110 : /* Signal the scheduler that it has waiting cells which means the channel
1111 : * will get scheduled. */
1112 1 : scheduler_channel_has_waiting_cells(chan1);
1113 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1114 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1115 : /* Subsequent call should not add it more times. It is possible we add many
1116 : * cells in rapid succession before the channel is scheduled. */
1117 1 : scheduler_channel_has_waiting_cells(chan1);
1118 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1119 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1120 1 : scheduler_channel_has_waiting_cells(chan1);
1121 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1122 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1123 :
1124 : /* We'll flush one cell and make it that the socket can write but no more to
1125 : * flush else we end up in an infinite loop. We expect the channel to be put
1126 : * in waiting for cells state and the pending list empty. */
1127 1 : mock_update_socket_info_limit = INT_MAX;
1128 1 : mock_more_to_flush = 0;
1129 1 : the_scheduler->run();
1130 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1131 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
1132 :
1133 : /* Lets make believe that a cell is now in the channel but this time the
1134 : * channel can't write so obviously it has more to flush. We expect the
1135 : * channel to be back in the pending list. */
1136 1 : scheduler_channel_has_waiting_cells(chan1);
1137 1 : mock_update_socket_info_limit = 0;
1138 1 : mock_more_to_flush = 1;
1139 1 : the_scheduler->run();
1140 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1141 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1142 :
1143 : /* Channel is in the pending list now, during that time, we'll trigger a
1144 : * wants to write event because maybe the channel buffers were emptied in
1145 : * the meantime. This is possible because once the connection outbuf is
1146 : * flushed down the low watermark, the scheduler is notified.
1147 : *
1148 : * We expect the channel to NOT be added in the pending list again and stay
1149 : * in PENDING state. */
1150 1 : scheduler_channel_wants_writes(chan1);
1151 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1152 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1153 :
1154 : /* Make it that the channel can write now but has nothing else to flush. We
1155 : * expect that it is removed from the pending list and waiting for cells. */
1156 1 : mock_update_socket_info_limit = INT_MAX;
1157 1 : mock_more_to_flush = 0;
1158 1 : the_scheduler->run();
1159 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1160 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
1161 :
1162 : /* While waiting for cells, lets say we were able to write more things on
1163 : * the connection outbuf (unlikely that this can happen but let say it
1164 : * does). We expect the channel to stay in waiting for cells. */
1165 1 : scheduler_channel_wants_writes(chan1);
1166 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1167 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
1168 :
1169 : /* We'll not put it in the pending list and make the flush cell fail with 0
1170 : * cell flushed. We expect that it is put back in waiting for cells. */
1171 1 : scheduler_channel_has_waiting_cells(chan1);
1172 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1173 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1174 1 : mock_flush_some_cells_num = 0;
1175 1 : the_scheduler->run();
1176 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1177 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
1178 :
1179 : /* Set the channel to a state where it doesn't want to write more. We expect
1180 : * that the channel becomes idle. */
1181 1 : scheduler_channel_doesnt_want_writes(chan1);
1182 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1183 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
1184 :
1185 : /* Some cells arrive on the channel now. We expect it to go back in waiting
1186 : * to write. You might wonder why it is not put in the pending list? Because
1187 : * once the channel becomes OPEN again (the doesn't want to write event only
1188 : * occurs if the channel goes in MAINT mode), if there are cells in the
1189 : * channel, the wants to write event is triggered thus putting the channel
1190 : * in pending mode.
1191 : *
1192 : * Else, if no cells, it stays IDLE and then once a cell comes in, it should
1193 : * go in waiting to write which is a BUG itself because the channel can't be
1194 : * scheduled until a second cell comes in. Hopefully, #24554 will fix that
1195 : * for KIST. */
1196 1 : scheduler_channel_has_waiting_cells(chan1);
1197 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1198 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
1199 :
1200 : /* Second cell comes in, unfortunately, it won't get scheduled until a wants
1201 : * to write event occurs like described above. */
1202 1 : scheduler_channel_has_waiting_cells(chan1);
1203 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
1204 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
1205 :
1206 : /* Unblock everything putting the channel in the pending list. */
1207 1 : scheduler_channel_wants_writes(chan1);
1208 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
1209 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1210 :
1211 : /* Testing bug #24700 which is the situation where we have at least two
1212 : * different channels in the pending list. The first one gets flushed and
1213 : * bytes are written on the wire which triggers a wants to write event
1214 : * because the outbuf is below the low watermark. The bug was that this
1215 : * exact channel was added back in the pending list because its state wasn't
1216 : * PENDING.
1217 : *
1218 : * The following does some ninja-tsu to try to make it happen. We need two
1219 : * different channels so we create a second one and add it to the pending
1220 : * list. Then, we have a custom function when we write to kernel that does
1221 : * two important things:
1222 : *
1223 : * 1) Calls scheduler_channel_wants_writes(chan) on the channel.
1224 : * 2) Keeps track of how many times it sees the channel going through. If
1225 : * that limit goes > 1, it means we've added the channel twice in the
1226 : * pending list.
1227 : *
1228 : * In the end, we expect both channels to be in the pending list after this
1229 : * scheduler run. */
1230 :
1231 : /* Put the second channel in the pending list. */
1232 1 : scheduler_channel_wants_writes(chan2);
1233 1 : scheduler_channel_has_waiting_cells(chan2);
1234 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2);
1235 1 : tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1236 :
1237 : /* This makes it that the first pass on socket_can_write() will be true but
1238 : * then when a single cell is flushed (514 + 29 bytes), the second call to
1239 : * socket_can_write() will be false. If it wasn't sending back false on the
1240 : * second run, we end up in an infinite loop of the scheduler. */
1241 1 : mock_update_socket_info_limit = 600;
1242 : /* We want to hit "Case 3:" of the scheduler so channel_more_to_flush() is
1243 : * true but socket_can_write() has to be false on the second check on the
1244 : * channel. */
1245 1 : mock_more_to_flush = 1;
1246 1 : mock_flush_some_cells_num = 1;
1247 1 : MOCK(channel_write_to_kernel, channel_write_to_kernel_mock_trigger_24700);
1248 1 : the_scheduler->run();
1249 1 : tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2);
1250 1 : tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1251 1 : tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
1252 :
1253 1 : done:
1254 1 : chan1->state = chan2->state = CHANNEL_STATE_CLOSED;
1255 1 : chan1->registered = chan2->registered = 0;
1256 1 : channel_free(chan1);
1257 1 : channel_free(chan2);
1258 1 : scheduler_free_all();
1259 :
1260 1 : UNMOCK(get_options);
1261 1 : UNMOCK(channel_flush_some_cells);
1262 1 : UNMOCK(channel_more_to_flush);
1263 1 : UNMOCK(update_socket_info_impl);
1264 1 : UNMOCK(channel_write_to_kernel);
1265 1 : UNMOCK(channel_should_write_to_kernel);
1266 1 : }
1267 :
1268 : struct testcase_t scheduler_tests[] = {
1269 : { "compare_channels", test_scheduler_compare_channels,
1270 : TT_FORK, NULL, NULL },
1271 : { "channel_states", test_scheduler_channel_states, TT_FORK, NULL, NULL },
1272 : { "initfree", test_scheduler_initfree, TT_FORK, NULL, NULL },
1273 : { "loop_vanilla", test_scheduler_loop_vanilla, TT_FORK, NULL, NULL },
1274 : { "loop_kist", test_scheduler_loop_kist, TT_FORK, NULL, NULL },
1275 : { "ns_changed", test_scheduler_ns_changed, TT_FORK, NULL, NULL},
1276 : { "should_use_kist", test_scheduler_can_use_kist, TT_FORK, NULL, NULL },
1277 : { "kist_pending_list", test_scheduler_kist_pending_list, TT_FORK,
1278 : NULL, NULL },
1279 : END_OF_TESTCASES
1280 : };
1281 :
|