diff options
-rw-r--r-- | src/or/config.c | 77 | ||||
-rw-r--r-- | src/or/or.h | 6 | ||||
-rw-r--r-- | src/or/scheduler.c | 57 | ||||
-rw-r--r-- | src/or/scheduler.h | 14 | ||||
-rw-r--r-- | src/or/scheduler_kist.c | 45 | ||||
-rw-r--r-- | 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 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 }; |