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 }