tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

scheduler_vanilla.c (5759B)


      1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * @file scheduler_vanilla.c
      6 * @brief "Vanilla" (pre-KIST) cell scheduler code.
      7 **/
      8 
      9 #include "core/or/or.h"
     10 #include "app/config/config.h"
     11 #define CHANNEL_OBJECT_PRIVATE
     12 #include "core/or/channel.h"
     13 #define SCHEDULER_PRIVATE
     14 #include "core/or/scheduler.h"
     15 
     16 /*****************************************************************************
     17 * Other internal data
     18 *****************************************************************************/
     19 
     20 /* Maximum cells to flush in a single call to channel_flush_some_cells(); */
     21 #define MAX_FLUSH_CELLS 1000
     22 
     23 /*****************************************************************************
     24 * Externally called function implementations
     25 *****************************************************************************/
     26 
     27 /* Return true iff the scheduler has work to perform. */
     28 static int
     29 have_work(void)
     30 {
     31  smartlist_t *cp = get_channels_pending();
     32  IF_BUG_ONCE(!cp) {
     33    return 0; // channels_pending doesn't exist so... no work?
     34  }
     35  return smartlist_len(cp) > 0;
     36 }
     37 
     38 /** Re-trigger the scheduler in a way safe to use from the callback */
     39 
     40 static void
     41 vanilla_scheduler_schedule(void)
     42 {
     43  if (!have_work()) {
     44    return;
     45  }
     46 
     47  /* Activate our event so it can process channels. */
     48  scheduler_ev_active();
     49 }
     50 
     51 static void
     52 vanilla_scheduler_run(void)
     53 {
     54  int n_cells, n_chans_before, n_chans_after;
     55  ssize_t flushed, flushed_this_time;
     56  smartlist_t *cp = get_channels_pending();
     57  smartlist_t *to_readd = NULL;
     58  channel_t *chan = NULL;
     59 
     60  log_debug(LD_SCHED, "We have a chance to run the scheduler");
     61 
     62  n_chans_before = smartlist_len(cp);
     63 
     64  while (smartlist_len(cp) > 0) {
     65    /* Pop off a channel */
     66    chan = smartlist_pqueue_pop(cp,
     67                                scheduler_compare_channels,
     68                                offsetof(channel_t, sched_heap_idx));
     69    IF_BUG_ONCE(!chan) {
     70      /* Some-freaking-how a NULL got into the channels_pending. That should
     71       * never happen, but it should be harmless to ignore it and keep looping.
     72       */
     73      continue;
     74    }
     75 
     76    /* Figure out how many cells we can write */
     77    n_cells = channel_num_cells_writeable(chan);
     78    if (n_cells > 0) {
     79      log_debug(LD_SCHED,
     80                "Scheduler saw pending channel %"PRIu64 " at %p with "
     81                "%d cells writeable",
     82                (chan->global_identifier), chan, n_cells);
     83 
     84      flushed = 0;
     85      while (flushed < n_cells) {
     86        flushed_this_time =
     87          channel_flush_some_cells(chan,
     88                        MIN(MAX_FLUSH_CELLS, (size_t) n_cells - flushed));
     89        if (flushed_this_time <= 0) break;
     90        flushed += flushed_this_time;
     91      }
     92 
     93      if (flushed < n_cells) {
     94        /* We ran out of cells to flush */
     95        scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
     96      } else {
     97        /* The channel may still have some cells */
     98        if (channel_more_to_flush(chan)) {
     99        /* The channel goes to either pending or waiting_to_write */
    100          if (channel_num_cells_writeable(chan) > 0) {
    101            /* Add it back to pending later */
    102            if (!to_readd) to_readd = smartlist_new();
    103            smartlist_add(to_readd, chan);
    104            log_debug(LD_SCHED,
    105                      "Channel %"PRIu64 " at %p "
    106                      "is still pending",
    107                      (chan->global_identifier),
    108                      chan);
    109          } else {
    110            /* It's waiting to be able to write more */
    111            scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
    112          }
    113        } else {
    114          /* No cells left; it can go to idle or waiting_for_cells */
    115          if (channel_num_cells_writeable(chan) > 0) {
    116            /*
    117             * It can still accept writes, so it goes to
    118             * waiting_for_cells
    119             */
    120            scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
    121          } else {
    122            /*
    123             * We exactly filled up the output queue with all available
    124             * cells; go to idle.
    125             */
    126            scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
    127          }
    128        }
    129      }
    130 
    131      log_debug(LD_SCHED,
    132                "Scheduler flushed %d cells onto pending channel "
    133                "%"PRIu64 " at %p",
    134                (int)flushed, (chan->global_identifier),
    135                chan);
    136    } else {
    137      log_info(LD_SCHED,
    138               "Scheduler saw pending channel %"PRIu64 " at %p with "
    139               "no cells writeable",
    140               (chan->global_identifier), chan);
    141      /* Put it back to WAITING_TO_WRITE */
    142      scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
    143    }
    144  }
    145 
    146  /* Readd any channels we need to */
    147  if (to_readd) {
    148    SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
    149      scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING);
    150      smartlist_pqueue_add(cp,
    151                           scheduler_compare_channels,
    152                           offsetof(channel_t, sched_heap_idx),
    153                           readd_chan);
    154    } SMARTLIST_FOREACH_END(readd_chan);
    155    smartlist_free(to_readd);
    156  }
    157 
    158  n_chans_after = smartlist_len(cp);
    159  log_debug(LD_SCHED, "Scheduler handled %d of %d pending channels",
    160            n_chans_before - n_chans_after, n_chans_before);
    161 }
    162 
    163 /* Stores the vanilla scheduler function pointers. */
    164 static scheduler_t vanilla_scheduler = {
    165  .type = SCHEDULER_VANILLA,
    166  .free_all = NULL,
    167  .on_channel_free = NULL,
    168  .init = NULL,
    169  .on_new_consensus = NULL,
    170  .schedule = vanilla_scheduler_schedule,
    171  .run = vanilla_scheduler_run,
    172  .on_new_options = NULL,
    173 };
    174 
    175 scheduler_t *
    176 get_vanilla_scheduler(void)
    177 {
    178  return &vanilla_scheduler;
    179 }