LCOV - code coverage report
Current view: top level - core/or - circuitmux_ewma.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 206 219 94.1 %
Date: 2021-11-24 03:28:48 Functions: 22 23 95.7 %

          Line data    Source code
       1             : /* * Copyright (c) 2012-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file circuitmux_ewma.c
       6             :  * \brief EWMA circuit selection as a circuitmux_t policy
       7             :  *
       8             :  * The "EWMA" in this module stands for the "exponentially weighted moving
       9             :  * average" of the number of cells sent on each circuit.  The goal is to
      10             :  * prioritize cells on circuits that have been quiet recently, by looking at
      11             :  * those that have sent few cells over time, prioritizing recent times
      12             :  * more than older ones.
      13             :  *
      14             :  * Specifically, a cell sent at time "now" has weight 1, but a time X ticks
      15             :  * before now has weight ewma_scale_factor ^ X , where ewma_scale_factor is
      16             :  * between 0.0 and 1.0.
      17             :  *
      18             :  * For efficiency, we do not re-scale these averages every time we send a
      19             :  * cell: that would be horribly inefficient.  Instead, we we keep the cell
      20             :  * count on all circuits on the same circuitmux scaled relative to a single
      21             :  * tick.  When we add a new cell, we scale its weight depending on the time
      22             :  * that has elapsed since the tick.  We do re-scale the circuits on the
      23             :  * circuitmux periodically, so that we don't overflow double.
      24             :  *
      25             :  *
      26             :  * This module should be used through the interfaces in circuitmux.c, which it
      27             :  * implements.
      28             :  *
      29             :  **/
      30             : 
      31             : #define CIRCUITMUX_EWMA_PRIVATE
      32             : 
      33             : #include "orconfig.h"
      34             : 
      35             : #include <math.h>
      36             : 
      37             : #include "core/or/or.h"
      38             : #include "core/or/circuitmux.h"
      39             : #include "core/or/circuitmux_ewma.h"
      40             : #include "lib/crypt_ops/crypto_rand.h"
      41             : #include "lib/crypt_ops/crypto_util.h"
      42             : #include "feature/nodelist/networkstatus.h"
      43             : #include "app/config/or_options_st.h"
      44             : 
      45             : /*** EWMA parameter #defines ***/
      46             : 
      47             : /** How long does a tick last (seconds)? */
      48             : #define EWMA_TICK_LEN 10
      49             : 
      50             : /** The default per-tick scale factor, if it hasn't been overridden by a
      51             :  * consensus or a configuration setting.  zero means "disabled". */
      52             : #define EWMA_DEFAULT_HALFLIFE 0.0
      53             : 
      54             : /*** Some useful constant #defines ***/
      55             : 
      56             : /** Any halflife smaller than this number of seconds is considered to be
      57             :  * "disabled". */
      58             : #define EPSILON 0.00001
      59             : /** The natural logarithm of 0.5. */
      60             : #define LOG_ONEHALF -0.69314718055994529
      61             : 
      62             : /*** Static declarations for circuitmux_ewma.c ***/
      63             : 
      64             : static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
      65             : static int compare_cell_ewma_counts(const void *p1, const void *p2);
      66             : static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma);
      67             : static inline double get_scale_factor(unsigned from_tick, unsigned to_tick);
      68             : static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol);
      69             : static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
      70             : static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick);
      71             : static void scale_active_circuits(ewma_policy_data_t *pol,
      72             :                                   unsigned cur_tick);
      73             : 
      74             : /*** Circuitmux policy methods ***/
      75             : 
      76             : static circuitmux_policy_data_t * ewma_alloc_cmux_data(circuitmux_t *cmux);
      77             : static void ewma_free_cmux_data(circuitmux_t *cmux,
      78             :                                 circuitmux_policy_data_t *pol_data);
      79             : static circuitmux_policy_circ_data_t *
      80             : ewma_alloc_circ_data(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data,
      81             :                      circuit_t *circ, cell_direction_t direction,
      82             :                      unsigned int cell_count);
      83             : static void
      84             : ewma_free_circ_data(circuitmux_t *cmux,
      85             :                     circuitmux_policy_data_t *pol_data,
      86             :                     circuit_t *circ,
      87             :                     circuitmux_policy_circ_data_t *pol_circ_data);
      88             : static void
      89             : ewma_notify_circ_active(circuitmux_t *cmux,
      90             :                         circuitmux_policy_data_t *pol_data,
      91             :                         circuit_t *circ,
      92             :                         circuitmux_policy_circ_data_t *pol_circ_data);
      93             : static void
      94             : ewma_notify_circ_inactive(circuitmux_t *cmux,
      95             :                           circuitmux_policy_data_t *pol_data,
      96             :                           circuit_t *circ,
      97             :                           circuitmux_policy_circ_data_t *pol_circ_data);
      98             : static void
      99             : ewma_notify_xmit_cells(circuitmux_t *cmux,
     100             :                        circuitmux_policy_data_t *pol_data,
     101             :                        circuit_t *circ,
     102             :                        circuitmux_policy_circ_data_t *pol_circ_data,
     103             :                        unsigned int n_cells);
     104             : static circuit_t *
     105             : ewma_pick_active_circuit(circuitmux_t *cmux,
     106             :                          circuitmux_policy_data_t *pol_data);
     107             : static int
     108             : ewma_cmp_cmux(circuitmux_t *cmux_1, circuitmux_policy_data_t *pol_data_1,
     109             :               circuitmux_t *cmux_2, circuitmux_policy_data_t *pol_data_2);
     110             : 
     111             : /*** EWMA global variables ***/
     112             : 
     113             : /** The per-tick scale factor to be used when computing cell-count EWMA
     114             :  * values.  (A cell sent N ticks before the start of the current tick
     115             :  * has value ewma_scale_factor ** N.)
     116             :  */
     117             : static double ewma_scale_factor = 0.1;
     118             : 
     119             : /*** EWMA circuitmux_policy_t method table ***/
     120             : 
     121             : circuitmux_policy_t ewma_policy = {
     122             :   /*.alloc_cmux_data =*/ ewma_alloc_cmux_data,
     123             :   /*.free_cmux_data =*/ ewma_free_cmux_data,
     124             :   /*.alloc_circ_data =*/ ewma_alloc_circ_data,
     125             :   /*.free_circ_data =*/ ewma_free_circ_data,
     126             :   /*.notify_circ_active =*/ ewma_notify_circ_active,
     127             :   /*.notify_circ_inactive =*/ ewma_notify_circ_inactive,
     128             :   /*.notify_set_n_cells =*/ NULL, /* EWMA doesn't need this */
     129             :   /*.notify_xmit_cells =*/ ewma_notify_xmit_cells,
     130             :   /*.pick_active_circuit =*/ ewma_pick_active_circuit,
     131             :   /*.cmp_cmux =*/ ewma_cmp_cmux
     132             : };
     133             : 
     134             : /** Have we initialized the ewma tick-counting logic? */
     135             : static int ewma_ticks_initialized = 0;
     136             : /** At what monotime_coarse_t did the current tick begin? */
     137             : static monotime_coarse_t start_of_current_tick;
     138             : /** What is the number of the current tick? */
     139             : static unsigned current_tick_num;
     140             : 
     141             : /*** EWMA method implementations using the below EWMA helper functions ***/
     142             : 
     143             : /** Compute and return the current cell_ewma tick. */
     144             : static inline unsigned int
     145         213 : cell_ewma_get_tick(void)
     146             : {
     147         213 :   monotime_coarse_t now;
     148         213 :   monotime_coarse_get(&now);
     149         213 :   int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
     150             :                                                   &now);
     151         213 :   return current_tick_num + msec_diff / (1000*EWMA_TICK_LEN);
     152             : }
     153             : 
     154             : /**
     155             :  * Allocate an ewma_policy_data_t and upcast it to a circuitmux_policy_data_t;
     156             :  * this is called when setting the policy on a circuitmux_t to ewma_policy.
     157             :  */
     158             : 
     159             : static circuitmux_policy_data_t *
     160         192 : ewma_alloc_cmux_data(circuitmux_t *cmux)
     161             : {
     162         192 :   ewma_policy_data_t *pol = NULL;
     163             : 
     164         192 :   tor_assert(cmux);
     165             : 
     166         192 :   pol = tor_malloc_zero(sizeof(*pol));
     167         192 :   pol->base_.magic = EWMA_POL_DATA_MAGIC;
     168         192 :   pol->active_circuit_pqueue = smartlist_new();
     169         192 :   pol->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick();
     170             : 
     171         192 :   return TO_CMUX_POL_DATA(pol);
     172             : }
     173             : 
     174             : /**
     175             :  * Free an ewma_policy_data_t allocated with ewma_alloc_cmux_data()
     176             :  */
     177             : 
     178             : static void
     179         192 : ewma_free_cmux_data(circuitmux_t *cmux,
     180             :                     circuitmux_policy_data_t *pol_data)
     181             : {
     182         192 :   ewma_policy_data_t *pol = NULL;
     183             : 
     184         192 :   tor_assert(cmux);
     185         192 :   if (!pol_data) return;
     186             : 
     187         192 :   pol = TO_EWMA_POL_DATA(pol_data);
     188             : 
     189         192 :   smartlist_free(pol->active_circuit_pqueue);
     190         192 :   memwipe(pol, 0xda, sizeof(ewma_policy_data_t));
     191         192 :   tor_free(pol);
     192             : }
     193             : 
     194             : /**
     195             :  * Allocate an ewma_policy_circ_data_t and upcast it to a
     196             :  * circuitmux_policy_data_t; this is called when attaching a circuit to a
     197             :  * circuitmux_t with ewma_policy.
     198             :  */
     199             : 
     200             : static circuitmux_policy_circ_data_t *
     201          21 : ewma_alloc_circ_data(circuitmux_t *cmux,
     202             :                      circuitmux_policy_data_t *pol_data,
     203             :                      circuit_t *circ,
     204             :                      cell_direction_t direction,
     205             :                      unsigned int cell_count)
     206             : {
     207          21 :   ewma_policy_circ_data_t *cdata = NULL;
     208             : 
     209          21 :   tor_assert(cmux);
     210          21 :   tor_assert(pol_data);
     211          21 :   tor_assert(circ);
     212          21 :   tor_assert(direction == CELL_DIRECTION_OUT ||
     213             :              direction == CELL_DIRECTION_IN);
     214             :   /* Shut the compiler up without triggering -Wtautological-compare */
     215          21 :   (void)cell_count;
     216             : 
     217          21 :   cdata = tor_malloc_zero(sizeof(*cdata));
     218          21 :   cdata->base_.magic = EWMA_POL_CIRC_DATA_MAGIC;
     219          21 :   cdata->circ = circ;
     220             : 
     221             :   /*
     222             :    * Initialize the cell_ewma_t structure (formerly in
     223             :    * init_circuit_base())
     224             :    */
     225          21 :   cdata->cell_ewma.last_adjusted_tick = cell_ewma_get_tick();
     226          21 :   cdata->cell_ewma.cell_count = 0.0;
     227          21 :   cdata->cell_ewma.heap_index = -1;
     228          21 :   if (direction == CELL_DIRECTION_IN) {
     229           9 :     cdata->cell_ewma.is_for_p_chan = 1;
     230             :   } else {
     231          12 :     cdata->cell_ewma.is_for_p_chan = 0;
     232             :   }
     233             : 
     234          21 :   return TO_CMUX_POL_CIRC_DATA(cdata);
     235             : }
     236             : 
     237             : /**
     238             :  * Free an ewma_policy_circ_data_t allocated with ewma_alloc_circ_data()
     239             :  */
     240             : 
     241             : static void
     242          21 : ewma_free_circ_data(circuitmux_t *cmux,
     243             :                     circuitmux_policy_data_t *pol_data,
     244             :                     circuit_t *circ,
     245             :                     circuitmux_policy_circ_data_t *pol_circ_data)
     246             : 
     247             : {
     248          21 :   ewma_policy_circ_data_t *cdata = NULL;
     249             : 
     250          21 :   tor_assert(cmux);
     251          21 :   tor_assert(circ);
     252          21 :   tor_assert(pol_data);
     253             : 
     254          21 :   if (!pol_circ_data) return;
     255             : 
     256          21 :   cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
     257          21 :   memwipe(cdata, 0xdc, sizeof(ewma_policy_circ_data_t));
     258          21 :   tor_free(cdata);
     259             : }
     260             : 
     261             : /**
     262             :  * Handle circuit activation; this inserts the circuit's cell_ewma into
     263             :  * the active_circuits_pqueue.
     264             :  */
     265             : 
     266             : static void
     267          16 : ewma_notify_circ_active(circuitmux_t *cmux,
     268             :                         circuitmux_policy_data_t *pol_data,
     269             :                         circuit_t *circ,
     270             :                         circuitmux_policy_circ_data_t *pol_circ_data)
     271             : {
     272          16 :   ewma_policy_data_t *pol = NULL;
     273          16 :   ewma_policy_circ_data_t *cdata = NULL;
     274             : 
     275          16 :   tor_assert(cmux);
     276          16 :   tor_assert(pol_data);
     277          16 :   tor_assert(circ);
     278          16 :   tor_assert(pol_circ_data);
     279             : 
     280          16 :   pol = TO_EWMA_POL_DATA(pol_data);
     281          16 :   cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
     282             : 
     283          16 :   add_cell_ewma(pol, &(cdata->cell_ewma));
     284          16 : }
     285             : 
     286             : /**
     287             :  * Handle circuit deactivation; this removes the circuit's cell_ewma from
     288             :  * the active_circuits_pqueue.
     289             :  */
     290             : 
     291             : static void
     292          14 : ewma_notify_circ_inactive(circuitmux_t *cmux,
     293             :                           circuitmux_policy_data_t *pol_data,
     294             :                           circuit_t *circ,
     295             :                           circuitmux_policy_circ_data_t *pol_circ_data)
     296             : {
     297          14 :   ewma_policy_data_t *pol = NULL;
     298          14 :   ewma_policy_circ_data_t *cdata = NULL;
     299             : 
     300          14 :   tor_assert(cmux);
     301          14 :   tor_assert(pol_data);
     302          14 :   tor_assert(circ);
     303          14 :   tor_assert(pol_circ_data);
     304             : 
     305          14 :   pol = TO_EWMA_POL_DATA(pol_data);
     306          14 :   cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
     307             : 
     308          14 :   remove_cell_ewma(pol, &(cdata->cell_ewma));
     309          14 : }
     310             : 
     311             : /**
     312             :  * Update cell_ewma for this circuit after we've sent some cells, and
     313             :  * remove/reinsert it in the queue.  This used to be done (brokenly,
     314             :  * see bug 6816) in channel_flush_from_first_active_circuit().
     315             :  */
     316             : 
     317             : static void
     318           5 : ewma_notify_xmit_cells(circuitmux_t *cmux,
     319             :                        circuitmux_policy_data_t *pol_data,
     320             :                        circuit_t *circ,
     321             :                        circuitmux_policy_circ_data_t *pol_circ_data,
     322             :                        unsigned int n_cells)
     323             : {
     324           5 :   ewma_policy_data_t *pol = NULL;
     325           5 :   ewma_policy_circ_data_t *cdata = NULL;
     326           5 :   unsigned int tick;
     327           5 :   double fractional_tick, ewma_increment;
     328           5 :   cell_ewma_t *cell_ewma, *tmp;
     329             : 
     330           5 :   tor_assert(cmux);
     331           5 :   tor_assert(pol_data);
     332           5 :   tor_assert(circ);
     333           5 :   tor_assert(pol_circ_data);
     334           5 :   tor_assert(n_cells > 0);
     335             : 
     336           5 :   pol = TO_EWMA_POL_DATA(pol_data);
     337           5 :   cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
     338             : 
     339             :   /* Rescale the EWMAs if needed */
     340           5 :   tick = cell_ewma_get_current_tick_and_fraction(&fractional_tick);
     341             : 
     342           5 :   if (tick != pol->active_circuit_pqueue_last_recalibrated) {
     343           1 :     scale_active_circuits(pol, tick);
     344             :   }
     345             : 
     346             :   /* How much do we adjust the cell count in cell_ewma by? */
     347           5 :   ewma_increment =
     348           5 :     ((double)(n_cells)) * pow(ewma_scale_factor, -fractional_tick);
     349             : 
     350             :   /* Do the adjustment */
     351           5 :   cell_ewma = &(cdata->cell_ewma);
     352           5 :   cell_ewma->cell_count += ewma_increment;
     353             : 
     354             :   /*
     355             :    * Since we just sent on this circuit, it should be at the head of
     356             :    * the queue.  Pop the head, assert that it matches, then re-add.
     357             :    */
     358           5 :   tmp = pop_first_cell_ewma(pol);
     359           5 :   tor_assert(tmp == cell_ewma);
     360           5 :   add_cell_ewma(pol, cell_ewma);
     361           5 : }
     362             : 
     363             : /**
     364             :  * Pick the preferred circuit to send from; this will be the one with
     365             :  * the lowest EWMA value in the priority queue.  This used to be done
     366             :  * in channel_flush_from_first_active_circuit().
     367             :  */
     368             : 
     369             : static circuit_t *
     370           3 : ewma_pick_active_circuit(circuitmux_t *cmux,
     371             :                          circuitmux_policy_data_t *pol_data)
     372             : {
     373           3 :   ewma_policy_data_t *pol = NULL;
     374           3 :   circuit_t *circ = NULL;
     375           3 :   cell_ewma_t *cell_ewma = NULL;
     376             : 
     377           3 :   tor_assert(cmux);
     378           3 :   tor_assert(pol_data);
     379             : 
     380           3 :   pol = TO_EWMA_POL_DATA(pol_data);
     381             : 
     382           3 :   if (smartlist_len(pol->active_circuit_pqueue) > 0) {
     383             :     /* Get the head of the queue */
     384           3 :     cell_ewma = smartlist_get(pol->active_circuit_pqueue, 0);
     385           3 :     circ = cell_ewma_to_circuit(cell_ewma);
     386             :   }
     387             : 
     388           3 :   return circ;
     389             : }
     390             : 
     391             : /**
     392             :  * Compare two EWMA cmuxes, and return -1, 0 or 1 to indicate which should
     393             :  * be more preferred - see circuitmux_compare_muxes() of circuitmux.c.
     394             :  */
     395             : 
     396             : static int
     397          20 : ewma_cmp_cmux(circuitmux_t *cmux_1, circuitmux_policy_data_t *pol_data_1,
     398             :               circuitmux_t *cmux_2, circuitmux_policy_data_t *pol_data_2)
     399             : {
     400          20 :   ewma_policy_data_t *p1 = NULL, *p2 = NULL;
     401          20 :   cell_ewma_t *ce1 = NULL, *ce2 = NULL;
     402             : 
     403          20 :   tor_assert(cmux_1);
     404          20 :   tor_assert(pol_data_1);
     405          20 :   tor_assert(cmux_2);
     406          20 :   tor_assert(pol_data_2);
     407             : 
     408          20 :   p1 = TO_EWMA_POL_DATA(pol_data_1);
     409          20 :   p2 = TO_EWMA_POL_DATA(pol_data_2);
     410             : 
     411          20 :   if (p1 != p2) {
     412             :     /* Get the head cell_ewma_t from each queue */
     413          20 :     if (smartlist_len(p1->active_circuit_pqueue) > 0) {
     414           0 :       ce1 = smartlist_get(p1->active_circuit_pqueue, 0);
     415             :     }
     416             : 
     417          20 :     if (smartlist_len(p2->active_circuit_pqueue) > 0) {
     418           0 :       ce2 = smartlist_get(p2->active_circuit_pqueue, 0);
     419             :     }
     420             : 
     421             :     /* Got both of them? */
     422          20 :     if (ce1 != NULL && ce2 != NULL) {
     423             :       /* Pick whichever one has the better best circuit */
     424           0 :       return compare_cell_ewma_counts(ce1, ce2);
     425             :     } else {
     426          20 :       if (ce1 != NULL) {
     427             :         /* We only have a circuit on cmux_1, so prefer it */
     428             :         return -1;
     429          20 :       } else if (ce2 != NULL) {
     430             :         /* We only have a circuit on cmux_2, so prefer it */
     431             :         return 1;
     432             :       } else {
     433             :         /* No circuits at all; no preference */
     434          20 :         return 0;
     435             :       }
     436             :     }
     437             :   } else {
     438             :     /* We got identical params */
     439             :     return 0;
     440             :   }
     441             : }
     442             : 
     443             : /** Helper for sorting cell_ewma_t values in their priority queue. */
     444             : static int
     445           0 : compare_cell_ewma_counts(const void *p1, const void *p2)
     446             : {
     447           0 :   const cell_ewma_t *e1 = p1, *e2 = p2;
     448             : 
     449           0 :   if (e1->cell_count < e2->cell_count)
     450             :     return -1;
     451           0 :   else if (e1->cell_count > e2->cell_count)
     452             :     return 1;
     453             :   else
     454           0 :     return 0;
     455             : }
     456             : 
     457             : /** Given a cell_ewma_t, return a pointer to the circuit containing it. */
     458             : static circuit_t *
     459           3 : cell_ewma_to_circuit(cell_ewma_t *ewma)
     460             : {
     461           3 :   ewma_policy_circ_data_t *cdata = NULL;
     462             : 
     463           3 :   tor_assert(ewma);
     464           3 :   cdata = SUBTYPE_P(ewma, ewma_policy_circ_data_t, cell_ewma);
     465           3 :   tor_assert(cdata);
     466             : 
     467           3 :   return cdata->circ;
     468             : }
     469             : 
     470             : /* ==== Functions for scaling cell_ewma_t ====
     471             : 
     472             :    When choosing which cells to relay first, we favor circuits that have been
     473             :    quiet recently.  This gives better latency on connections that aren't
     474             :    pushing lots of data, and makes the network feel more interactive.
     475             : 
     476             :    Conceptually, we take an exponentially weighted mean average of the number
     477             :    of cells a circuit has sent, and allow active circuits (those with cells to
     478             :    relay) to send cells in reverse order of their exponentially-weighted mean
     479             :    average (EWMA) cell count.  [That is, a cell sent N seconds ago 'counts'
     480             :    F^N times as much as a cell sent now, for 0<F<1.0, and we favor the
     481             :    circuit that has sent the fewest cells]
     482             : 
     483             :    If 'double' had infinite precision, we could do this simply by counting a
     484             :    cell sent at startup as having weight 1.0, and a cell sent N seconds later
     485             :    as having weight F^-N.  This way, we would never need to re-scale
     486             :    any already-sent cells.
     487             : 
     488             :    To prevent double from overflowing, we could count a cell sent now as
     489             :    having weight 1.0 and a cell sent N seconds ago as having weight F^N.
     490             :    This, however, would mean we'd need to re-scale *ALL* old circuits every
     491             :    time we wanted to send a cell.
     492             : 
     493             :    So as a compromise, we divide time into 'ticks' (currently, 10-second
     494             :    increments) and say that a cell sent at the start of a current tick is
     495             :    worth 1.0, a cell sent N seconds before the start of the current tick is
     496             :    worth F^N, and a cell sent N seconds after the start of the current tick is
     497             :    worth F^-N.  This way we don't overflow, and we don't need to constantly
     498             :    rescale.
     499             :  */
     500             : 
     501             : /**
     502             :  * Initialize the system that tells which ewma tick we are in.
     503             :  */
     504             : STATIC void
     505          29 : cell_ewma_initialize_ticks(void)
     506             : {
     507          29 :   if (ewma_ticks_initialized)
     508             :     return;
     509          24 :   monotime_coarse_get(&start_of_current_tick);
     510          24 :   crypto_rand((char*)&current_tick_num, sizeof(current_tick_num));
     511          24 :   ewma_ticks_initialized = 1;
     512             : }
     513             : 
     514             : /** Compute the current cell_ewma tick and the fraction of the tick that has
     515             :  * elapsed between the start of the tick and the current time.  Return the
     516             :  * former and store the latter in *<b>remainder_out</b>.
     517             :  *
     518             :  * These tick values are not meant to be shared between Tor instances, or used
     519             :  * for other purposes. */
     520             : STATIC unsigned
     521           8 : cell_ewma_get_current_tick_and_fraction(double *remainder_out)
     522             : {
     523           8 :   if (BUG(!ewma_ticks_initialized)) {
     524             :     cell_ewma_initialize_ticks(); // LCOV_EXCL_LINE
     525             :   }
     526           8 :   monotime_coarse_t now;
     527           8 :   monotime_coarse_get(&now);
     528           8 :   int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
     529             :                                                   &now);
     530           8 :   if (msec_diff > (1000*EWMA_TICK_LEN)) {
     531           1 :     unsigned ticks_difference = msec_diff / (1000*EWMA_TICK_LEN);
     532           1 :     monotime_coarse_add_msec(&start_of_current_tick,
     533             :                              &start_of_current_tick,
     534             :                              ticks_difference * 1000 * EWMA_TICK_LEN);
     535           1 :     current_tick_num += ticks_difference;
     536           1 :     msec_diff %= 1000*EWMA_TICK_LEN;
     537             :   }
     538           8 :   *remainder_out = ((double)msec_diff) / (1.0e3 * EWMA_TICK_LEN);
     539           8 :   return current_tick_num;
     540             : }
     541             : 
     542             : /* Default value for the CircuitPriorityHalflifeMsec consensus parameter in
     543             :  * msec. */
     544             : #define CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT 30000
     545             : /* Minimum and maximum value for the CircuitPriorityHalflifeMsec consensus
     546             :  * parameter. */
     547             : #define CMUX_PRIORITY_HALFLIFE_MSEC_MIN 1
     548             : #define CMUX_PRIORITY_HALFLIFE_MSEC_MAX INT32_MAX
     549             : 
     550             : /* Return the value of the circuit priority halflife from the options if
     551             :  * available or else from the consensus (in that order). If none can be found,
     552             :  * a default value is returned.
     553             :  *
     554             :  * The source_msg points to a string describing from where the value was
     555             :  * picked so it can be used for logging. */
     556             : static double
     557          15 : get_circuit_priority_halflife(const or_options_t *options,
     558             :                               const networkstatus_t *consensus,
     559             :                               const char **source_msg)
     560             : {
     561          15 :   int32_t halflife_ms;
     562          15 :   double halflife;
     563             :   /* Compute the default value now. We might need it. */
     564          15 :   double halflife_default =
     565             :     ((double) CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT) / 1000.0;
     566             : 
     567             :   /* Try to get it from configuration file first. */
     568          15 :   if (options && options->CircuitPriorityHalflife >= -EPSILON) {
     569           0 :     halflife = options->CircuitPriorityHalflife;
     570           0 :     *source_msg = "CircuitPriorityHalflife in configuration";
     571           0 :     goto end;
     572             :   }
     573             : 
     574             :   /* Try to get the msec value from the consensus. */
     575          15 :   halflife_ms = networkstatus_get_param(consensus,
     576             :                                         "CircuitPriorityHalflifeMsec",
     577             :                                         CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT,
     578             :                                         CMUX_PRIORITY_HALFLIFE_MSEC_MIN,
     579             :                                         CMUX_PRIORITY_HALFLIFE_MSEC_MAX);
     580          15 :   halflife = ((double) halflife_ms) / 1000.0;
     581          15 :   *source_msg = "CircuitPriorityHalflifeMsec in consensus";
     582             : 
     583          15 :  end:
     584             :   /* We should never go below the EPSILON else we would consider it disabled
     585             :    * and we can't have that. */
     586          15 :   if (halflife < EPSILON) {
     587           0 :     log_warn(LD_CONFIG, "CircuitPriorityHalflife is too small (%f). "
     588             :                         "Adjusting to the smallest value allowed: %f.",
     589             :              halflife, halflife_default);
     590           0 :     halflife = halflife_default;
     591             :   }
     592          15 :   return halflife;
     593             : }
     594             : 
     595             : /** Adjust the global cell scale factor based on <b>options</b> */
     596             : void
     597          15 : cmux_ewma_set_options(const or_options_t *options,
     598             :                       const networkstatus_t *consensus)
     599             : {
     600          15 :   double halflife;
     601          15 :   const char *source;
     602             : 
     603          15 :   cell_ewma_initialize_ticks();
     604             : 
     605             :   /* Both options and consensus can be NULL. This assures us to either get a
     606             :    * valid configured value or the default one. */
     607          15 :   halflife = get_circuit_priority_halflife(options, consensus, &source);
     608             : 
     609             :   /* convert halflife into halflife-per-tick. */
     610          15 :   halflife /= EWMA_TICK_LEN;
     611             :   /* compute per-tick scale factor. */
     612          15 :   ewma_scale_factor = exp(LOG_ONEHALF / halflife);
     613          15 :   log_info(LD_OR,
     614             :            "Enabled cell_ewma algorithm because of value in %s; "
     615             :            "scale factor is %f per %d seconds",
     616             :            source, ewma_scale_factor, EWMA_TICK_LEN);
     617          15 : }
     618             : 
     619             : /** Return the multiplier necessary to convert the value of a cell sent in
     620             :  * 'from_tick' to one sent in 'to_tick'. */
     621             : static inline double
     622          22 : get_scale_factor(unsigned from_tick, unsigned to_tick)
     623             : {
     624             :   /* This math can wrap around, but that's okay: unsigned overflow is
     625             :      well-defined */
     626          22 :   int diff = (int)(to_tick - from_tick);
     627          22 :   return pow(ewma_scale_factor, diff);
     628             : }
     629             : 
     630             : /** Adjust the cell count of <b>ewma</b> so that it is scaled with respect to
     631             :  * <b>cur_tick</b> */
     632             : static void
     633          21 : scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick)
     634             : {
     635          21 :   double factor = get_scale_factor(ewma->last_adjusted_tick, cur_tick);
     636          21 :   ewma->cell_count *= factor;
     637          21 :   ewma->last_adjusted_tick = cur_tick;
     638          21 : }
     639             : 
     640             : /** Adjust the cell count of every active circuit on <b>chan</b> so
     641             :  * that they are scaled with respect to <b>cur_tick</b> */
     642             : static void
     643           1 : scale_active_circuits(ewma_policy_data_t *pol, unsigned cur_tick)
     644             : {
     645           1 :   double factor;
     646             : 
     647           1 :   tor_assert(pol);
     648           1 :   tor_assert(pol->active_circuit_pqueue);
     649             : 
     650           1 :   factor =
     651           1 :     get_scale_factor(
     652             :       pol->active_circuit_pqueue_last_recalibrated,
     653             :       cur_tick);
     654             :   /** Ordinarily it isn't okay to change the value of an element in a heap,
     655             :    * but it's okay here, since we are preserving the order. */
     656           2 :   SMARTLIST_FOREACH_BEGIN(
     657             :       pol->active_circuit_pqueue,
     658             :       cell_ewma_t *, e) {
     659           1 :     tor_assert(e->last_adjusted_tick ==
     660             :                pol->active_circuit_pqueue_last_recalibrated);
     661           1 :     e->cell_count *= factor;
     662           1 :     e->last_adjusted_tick = cur_tick;
     663           1 :   } SMARTLIST_FOREACH_END(e);
     664           1 :   pol->active_circuit_pqueue_last_recalibrated = cur_tick;
     665           1 : }
     666             : 
     667             : /** Rescale <b>ewma</b> to the same scale as <b>pol</b>, and add it to
     668             :  * <b>pol</b>'s priority queue of active circuits */
     669             : static void
     670          21 : add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma)
     671             : {
     672          21 :   tor_assert(pol);
     673          21 :   tor_assert(pol->active_circuit_pqueue);
     674          21 :   tor_assert(ewma);
     675          21 :   tor_assert(ewma->heap_index == -1);
     676             : 
     677          21 :   scale_single_cell_ewma(
     678             :       ewma,
     679             :       pol->active_circuit_pqueue_last_recalibrated);
     680             : 
     681          21 :   smartlist_pqueue_add(pol->active_circuit_pqueue,
     682             :                        compare_cell_ewma_counts,
     683             :                        offsetof(cell_ewma_t, heap_index),
     684             :                        ewma);
     685          21 : }
     686             : 
     687             : /** Remove <b>ewma</b> from <b>pol</b>'s priority queue of active circuits */
     688             : static void
     689          14 : remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma)
     690             : {
     691          14 :   tor_assert(pol);
     692          14 :   tor_assert(pol->active_circuit_pqueue);
     693          14 :   tor_assert(ewma);
     694          14 :   tor_assert(ewma->heap_index != -1);
     695             : 
     696          14 :   smartlist_pqueue_remove(pol->active_circuit_pqueue,
     697             :                           compare_cell_ewma_counts,
     698             :                           offsetof(cell_ewma_t, heap_index),
     699             :                           ewma);
     700          14 : }
     701             : 
     702             : /** Remove and return the first cell_ewma_t from pol's priority queue of
     703             :  * active circuits.  Requires that the priority queue is nonempty. */
     704             : static cell_ewma_t *
     705           5 : pop_first_cell_ewma(ewma_policy_data_t *pol)
     706             : {
     707           5 :   tor_assert(pol);
     708           5 :   tor_assert(pol->active_circuit_pqueue);
     709             : 
     710           5 :   return smartlist_pqueue_pop(pol->active_circuit_pqueue,
     711             :                               compare_cell_ewma_counts,
     712             :                               offsetof(cell_ewma_t, heap_index));
     713             : }
     714             : 
     715             : /**
     716             :  * Drop all resources held by circuitmux_ewma.c, and deinitialize the
     717             :  * module. */
     718             : void
     719         249 : circuitmux_ewma_free_all(void)
     720             : {
     721         249 :   ewma_ticks_initialized = 0;
     722         249 : }

Generated by: LCOV version 1.14