[tor-commits] [tor/master] sched: Add Schedulers torrc option

nickm at torproject.org nickm at torproject.org
Fri Sep 15 16:07:57 UTC 2017


commit 7cc9621d115303ab71d8cf8e49f5dfe428636145
Author: David Goulet <dgoulet at torproject.org>
Date:   Thu Sep 14 13:23:43 2017 -0400

    sched: Add Schedulers torrc option
    
    This option is a list of possible scheduler type tor can use ordered by
    priority. Its default value is "KIST,KISTLite,Vanilla" which means that KIST
    will be used first and if unavailable will fallback to KISTLite and so on.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/config.c           | 77 +++++++++++++++++++++++++++++++++++++++--------
 src/or/or.h               |  6 ++++
 src/or/scheduler.c        | 57 +++++++++++++++++++++++++++--------
 src/or/scheduler.h        | 14 +++++++--
 src/or/scheduler_kist.c   | 45 ++++++++++++++++++++++-----
 src/test/test_scheduler.c | 14 ++++-----
 6 files changed, 172 insertions(+), 41 deletions(-)

diff --git a/src/or/config.c b/src/or/config.c
index f332ac97f..95f27b071 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -491,6 +491,7 @@ static config_var_t option_vars_[] = {
   OBSOLETE("SchedulerMaxFlushCells__"),
   V(KISTSchedRunInterval,        MSEC_INTERVAL, "0 msec"),
   V(KISTSockBufSizeFactor,       DOUBLE,   "1.0"),
+  V(Schedulers,                  CSV,      "KIST,KISTLite,Vanilla"),
   V(ShutdownWaitLength,          INTERVAL, "30 seconds"),
   OBSOLETE("SocksListenAddress"),
   V(SocksPolicy,                 LINELIST, NULL),
@@ -907,6 +908,10 @@ or_options_free(or_options_t *options)
                       rs, routerset_free(rs));
     smartlist_free(options->NodeFamilySets);
   }
+  if (options->SchedulerTypes_) {
+    SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i));
+    smartlist_free(options->SchedulerTypes_);
+  }
   tor_free(options->BridgePassword_AuthDigest_);
   tor_free(options->command_arg);
   tor_free(options->master_key_fname);
@@ -2888,6 +2893,61 @@ warn_about_relative_paths(or_options_t *options)
   }
 }
 
+/* Validate options related to the scheduler. From the Schedulers list, the
+ * SchedulerTypes_ list is created with int values so once we select the
+ * scheduler, which can happen anytime at runtime, we don't have to parse
+ * strings and thus be quick.
+ *
+ * Return 0 on success else -1 and msg is set with an error message. */
+static int
+options_validate_scheduler(or_options_t *options, char **msg)
+{
+  tor_assert(options);
+  tor_assert(msg);
+
+  if (!options->Schedulers || smartlist_len(options->Schedulers) == 0) {
+    REJECT("Empty Schedulers list. Either remove the option so the defaults "
+           "can be used or set at least one value.");
+  }
+  /* Ok, we do have scheduler types, validate them. */
+  options->SchedulerTypes_ = smartlist_new();
+  SMARTLIST_FOREACH_BEGIN(options->Schedulers, const char *, type) {
+    int *sched_type;
+    if (!strcasecmp("KISTLite", type)) {
+      sched_type = tor_malloc_zero(sizeof(int));
+      *sched_type = SCHEDULER_KIST_LITE;
+      smartlist_add(options->SchedulerTypes_, sched_type);
+    } else if (!strcasecmp("KIST", type)) {
+      sched_type = tor_malloc_zero(sizeof(int));
+      *sched_type = SCHEDULER_KIST;
+      smartlist_add(options->SchedulerTypes_, sched_type);
+    } else if (!strcasecmp("Vanilla", type)) {
+      sched_type = tor_malloc_zero(sizeof(int));
+      *sched_type = SCHEDULER_VANILLA;
+      smartlist_add(options->SchedulerTypes_, sched_type);
+    } else {
+      tor_asprintf(msg, "Unknown type %s in option Schedulers. "
+                        "Possible values are KIST, KISTLite and Vanilla.",
+                   escaped(type));
+      return -1;
+    }
+  } SMARTLIST_FOREACH_END(type);
+
+  if (options->KISTSockBufSizeFactor < 0) {
+    REJECT("KISTSockBufSizeFactor must be at least 0");
+  }
+
+  /* Don't need to validate that the Interval is less than anything because
+   * zero is valid and all negative values are valid. */
+  if (options->KISTSchedRunInterval > KIST_SCHED_RUN_INTERVAL_MAX) {
+    tor_asprintf(msg, "KISTSchedRunInterval must not be more than %d (ms)",
+                 KIST_SCHED_RUN_INTERVAL_MAX);
+    return -1;
+  }
+
+  return 0;
+}
+
 /* Validate options related to single onion services.
  * Modifies some options that are incompatible with single onion services.
  * On failure returns -1, and sets *msg to an error string.
@@ -3112,19 +3172,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
     routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeNodes);
   }
 
-  if (options->KISTSockBufSizeFactor < 0) {
-    REJECT("KISTSockBufSizeFactor must be at least 0");
-  }
-  /* Don't need to validate that the Interval is less than anything because
-   * zero is valid and all negative values are valid. */
-  if (options->KISTSchedRunInterval > KIST_SCHED_RUN_INTERVAL_MAX) {
-    char *buf = tor_calloc(80, sizeof(char));
-    tor_snprintf(buf, 80, "KISTSchedRunInterval must not be more than %d (ms)",
-                 KIST_SCHED_RUN_INTERVAL_MAX);
-    *msg = buf;
-    return -1;
-  }
-
   if (options->NodeFamilies) {
     options->NodeFamilySets = smartlist_new();
     for (cl = options->NodeFamilies; cl; cl = cl->next) {
@@ -4241,6 +4288,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
       REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
              "combination.");
 
+  if (options_validate_scheduler(options, msg) < 0) {
+    return -1;
+  }
+
   return 0;
 }
 
diff --git a/src/or/or.h b/src/or/or.h
index 7116fbac7..fbeaf70ab 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -4616,6 +4616,12 @@ typedef struct {
 
   /** A multiplier for the KIST per-socket limit calculation. */
   double KISTSockBufSizeFactor;
+
+  /** The list of scheduler type string ordered by priority that is first one
+   * has to be tried first. Default: KIST,KISTLite,Vanilla */
+  smartlist_t *Schedulers;
+  /* An ordered list of scheduler_types mapped from Schedulers. */
+  smartlist_t *SchedulerTypes_;
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 8e4cec095..aaf991407 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -274,6 +274,48 @@ scheduler_compare_channels, (const void *c1_v, const void *c2_v))
  * Functions that can be accessed from anywhere in Tor.
  *****************************************************************************/
 
+/* Using the global options, select the scheduler we should be using. */
+static void
+select_scheduler(void)
+{
+  const char *chosen_sched_type = NULL;
+
+  /* This list is ordered that is first entry has the first priority. Thus, as
+   * soon as we find a scheduler type that we can use, we use it and stop. */
+  SMARTLIST_FOREACH_BEGIN(get_options()->SchedulerTypes_, int *, type) {
+    switch (*type) {
+    case SCHEDULER_VANILLA:
+      the_scheduler = get_vanilla_scheduler();
+      chosen_sched_type = "Vanilla";
+      goto end;
+    case SCHEDULER_KIST:
+      if (!scheduler_can_use_kist()) {
+        log_warn(LD_SCHED, "Scheduler KIST can't be used. Consider removing "
+                           "it from Schedulers or if you have a tor built "
+                           "with KIST support, you should make sure "
+                           "KISTSchedRunInterval is a non zero value");
+        continue;
+      }
+      the_scheduler = get_kist_scheduler();
+      chosen_sched_type = "KIST";
+      scheduler_kist_set_full_mode();
+      goto end;
+    case SCHEDULER_KIST_LITE:
+      chosen_sched_type = "KISTLite";
+      the_scheduler = get_kist_scheduler();
+      scheduler_kist_set_lite_mode();
+      goto end;
+    default:
+      /* Our option validation should have caught this. */
+      tor_assert_unreached();
+    }
+  } SMARTLIST_FOREACH_END(type);
+
+ end:
+  log_notice(LD_CONFIG, "Scheduler type %s has been enabled.",
+             chosen_sched_type);
+}
+
 /*
  * Little helper function called from a few different places. It changes the
  * scheduler implementation, if necessary. And if it did, it then tells the
@@ -282,17 +324,10 @@ scheduler_compare_channels, (const void *c1_v, const void *c2_v))
 static void
 set_scheduler(void)
 {
-  int have_kist = 0;
-
-  /* Switch, if needed */
   scheduler_t *old_scheduler = the_scheduler;
-  if (scheduler_should_use_kist()) {
-    the_scheduler = get_kist_scheduler();
-    have_kist = 1;
-  } else {
-    the_scheduler = get_vanilla_scheduler();
-  }
-  tor_assert(the_scheduler);
+
+  /* From the options, select the scheduler type to set. */
+  select_scheduler();
 
   if (old_scheduler != the_scheduler) {
     /* Allow the old scheduler to clean up, if needed. */
@@ -306,8 +341,6 @@ set_scheduler(void)
     if (the_scheduler->init) {
       the_scheduler->init();
     }
-    log_notice(LD_CONFIG, "Using the %s scheduler.",
-               have_kist ? "KIST" : "vanilla");
   }
 }
 
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index 68de5cf66..b1863b119 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -82,6 +82,15 @@ typedef struct scheduler_s {
   void (*on_new_options)(void);
 } scheduler_t;
 
+/** Scheduler type, we build an ordered list with those values from the
+ * parsed strings in Schedulers. The reason to do such a thing is so we can
+ * quickly and without parsing strings select the scheduler at anytime. */
+typedef enum {
+  SCHEDULER_VANILLA =   1,
+  SCHEDULER_KIST =      2,
+  SCHEDULER_KIST_LITE = 3,
+} scheduler_types_t;
+
 /*****************************************************************************
  * Globally visible scheduler variables/values
  *
@@ -95,7 +104,6 @@ typedef struct scheduler_s {
 /* Maximum interval that KIST runs (in ms). */
 #define KIST_SCHED_RUN_INTERVAL_MAX 100
 
-
 /*****************************************************************************
  * Globally visible scheduler functions
  *
@@ -169,7 +177,9 @@ MOCK_DECL(int, channel_should_write_to_kernel,
 MOCK_DECL(void, channel_write_to_kernel, (channel_t *chan));
 MOCK_DECL(void, update_socket_info_impl, (socket_table_ent_t *ent));
 
-int scheduler_should_use_kist(void);
+int scheduler_can_use_kist(void);
+void scheduler_kist_set_full_mode(void);
+void scheduler_kist_set_lite_mode(void);
 scheduler_t *get_kist_scheduler(void);
 int32_t kist_scheduler_run_interval(const networkstatus_t *ns);
 
diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c
index aec8192ed..8646169ee 100644
--- a/src/or/scheduler_kist.c
+++ b/src/or/scheduler_kist.c
@@ -26,12 +26,18 @@
  * Data structures and supporting functions
  *****************************************************************************/
 
+#ifdef HAVE_KIST_SUPPORT
+/* Indicate if KIST lite mode is on or off. We can disable it at runtime.
+ * Important to have because of the KISTLite -> KIST possible transition. */
+static unsigned int kist_lite_mode = 0;
 /* Indicate if we don't have the kernel support. This can happen if the kernel
  * changed and it doesn't recognized the values passed to the syscalls needed
  * by KIST. In that case, fallback to the naive approach. */
-#ifdef HAVE_KIST_SUPPORT
 static unsigned int kist_no_kernel_support = 0;
-#endif /* HAVE_KIST_SUPPORT */
+#else
+static unsigned int kist_no_kernel_support = 1;
+static unsigned int kist_lite_mode = 1;
+#endif
 
 /* Socket_table hash table stuff. The socket_table keeps track of per-socket
  * limit information imposed by kist and used by kist. */
@@ -193,7 +199,7 @@ update_socket_info_impl, (socket_table_ent_t *ent))
   struct tcp_info tcp;
   socklen_t tcp_info_len = sizeof(tcp);
 
-  if (kist_no_kernel_support) {
+  if (kist_no_kernel_support || kist_lite_mode) {
     goto fallback;
   }
 
@@ -686,15 +692,40 @@ kist_scheduler_run_interval(const networkstatus_t *ns)
   return run_interval;
 }
 
+/* Set KISTLite mode that is KIST without kernel support. */
+void
+scheduler_kist_set_lite_mode(void)
+{
+  kist_lite_mode = 1;
+  log_info(LD_SCHED,
+           "Setting KIST scheduler without kernel support (KISTLite mode)");
+}
+
+/* Set KIST mode that is KIST with kernel support. */
+void
+scheduler_kist_set_full_mode(void)
+{
+  kist_lite_mode = 0;
+  log_info(LD_SCHED,
+           "Setting KIST scheduler with kernel support (KIST mode)");
+}
+
 #ifdef HAVE_KIST_SUPPORT
 
 /* Return true iff the scheduler subsystem should use KIST. */
 int
-scheduler_should_use_kist(void)
+scheduler_can_use_kist(void)
 {
+  if (kist_no_kernel_support) {
+    /* We have no kernel support so we can't use KIST. */
+    return 0;
+  }
+
+  /* We do have the support, time to check if we can get the interval that the
+   * consensus can be disabling. */
   int64_t run_interval = kist_scheduler_run_interval(NULL);
-  log_info(LD_SCHED, "Determined sched_run_interval should be %" PRId64 ". "
-                     "Will%s use KIST.",
+  log_debug(LD_SCHED, "Determined KIST sched_run_interval should be "
+                      "%" PRId64 ". Can%s use KIST.",
            run_interval, (run_interval > 0 ? "" : " not"));
   return run_interval > 0;
 }
@@ -702,7 +733,7 @@ scheduler_should_use_kist(void)
 #else /* HAVE_KIST_SUPPORT */
 
 int
-scheduler_should_use_kist(void)
+scheduler_can_use_kist(void)
 {
   return 0;
 }
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 7c24bf146..0cc6d88db 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -887,7 +887,7 @@ test_scheduler_initfree(void *arg)
 }
 
 static void
-test_scheduler_should_use_kist(void *arg)
+test_scheduler_can_use_kist(void *arg)
 {
   (void)arg;
 
@@ -897,7 +897,7 @@ test_scheduler_should_use_kist(void *arg)
   /* Test force disabling of KIST */
   clear_options();
   mocked_options.KISTSchedRunInterval = -1;
-  res_should = scheduler_should_use_kist();
+  res_should = scheduler_can_use_kist();
   res_freq = kist_scheduler_run_interval(NULL);
   tt_int_op(res_should, ==, 0);
   tt_int_op(res_freq, ==, -1);
@@ -905,7 +905,7 @@ test_scheduler_should_use_kist(void *arg)
   /* Test force enabling of KIST */
   clear_options();
   mocked_options.KISTSchedRunInterval = 1234;
-  res_should = scheduler_should_use_kist();
+  res_should = scheduler_can_use_kist();
   res_freq = kist_scheduler_run_interval(NULL);
 #ifdef HAVE_KIST_SUPPORT
   tt_int_op(res_should, ==, 1);
@@ -917,7 +917,7 @@ test_scheduler_should_use_kist(void *arg)
   /* Test defer to consensus, but no consensus available */
   clear_options();
   mocked_options.KISTSchedRunInterval = 0;
-  res_should = scheduler_should_use_kist();
+  res_should = scheduler_can_use_kist();
   res_freq = kist_scheduler_run_interval(NULL);
 #ifdef HAVE_KIST_SUPPORT
   tt_int_op(res_should, ==, 1);
@@ -930,7 +930,7 @@ test_scheduler_should_use_kist(void *arg)
   MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
   clear_options();
   mocked_options.KISTSchedRunInterval = 0;
-  res_should = scheduler_should_use_kist();
+  res_should = scheduler_can_use_kist();
   res_freq = kist_scheduler_run_interval(NULL);
 #ifdef HAVE_KIST_SUPPORT
   tt_int_op(res_should, ==, 1);
@@ -944,7 +944,7 @@ test_scheduler_should_use_kist(void *arg)
   MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
   clear_options();
   mocked_options.KISTSchedRunInterval = 0;
-  res_should = scheduler_should_use_kist();
+  res_should = scheduler_can_use_kist();
   res_freq = kist_scheduler_run_interval(NULL);
   tt_int_op(res_should, ==, 0);
   tt_int_op(res_freq, ==, -1);
@@ -1023,7 +1023,7 @@ struct testcase_t scheduler_tests[] = {
   { "loop_vanilla", test_scheduler_loop_vanilla, TT_FORK, NULL, NULL },
   { "loop_kist", test_scheduler_loop_kist, TT_FORK, NULL, NULL },
   { "ns_changed", test_scheduler_ns_changed, TT_FORK, NULL, NULL},
-  { "should_use_kist", test_scheduler_should_use_kist, TT_FORK, NULL, NULL },
+  { "should_use_kist", test_scheduler_can_use_kist, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };
 





More information about the tor-commits mailing list