aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/or/config.c77
-rw-r--r--src/or/or.h6
-rw-r--r--src/or/scheduler.c57
-rw-r--r--src/or/scheduler.h14
-rw-r--r--src/or/scheduler_kist.c45
-rw-r--r--src/test/test_scheduler.c14
6 files changed, 172 insertions, 41 deletions
diff --git a/src/or/config.c b/src/or/config.c
index f332ac97f8..95f27b0717 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 7116fbac76..fbeaf70ab1 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 8e4cec095e..aaf991407a 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 68de5cf66e..b1863b1193 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 aec8192ed1..8646169ee3 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 7c24bf1465..0cc6d88dbb 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
};