LCOV - code coverage report
Current view: top level - test - test_scheduler.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 607 628 96.7 %
Date: 2021-11-24 03:28:48 Functions: 29 30 96.7 %

          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             : 

Generated by: LCOV version 1.14