Line data Source code
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 7 : have_work(void)
30 : {
31 7 : smartlist_t *cp = get_channels_pending();
32 7 : IF_BUG_ONCE(!cp) {
33 : return 0; // channels_pending doesn't exist so... no work?
34 : }
35 7 : 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 7 : vanilla_scheduler_schedule(void)
42 : {
43 7 : if (!have_work()) {
44 : return;
45 : }
46 :
47 : /* Activate our event so it can process channels. */
48 7 : scheduler_ev_active();
49 : }
50 :
51 : static void
52 2 : vanilla_scheduler_run(void)
53 : {
54 2 : int n_cells, n_chans_before, n_chans_after;
55 2 : ssize_t flushed, flushed_this_time;
56 2 : smartlist_t *cp = get_channels_pending();
57 2 : smartlist_t *to_readd = NULL;
58 2 : channel_t *chan = NULL;
59 :
60 2 : log_debug(LD_SCHED, "We have a chance to run the scheduler");
61 :
62 2 : n_chans_before = smartlist_len(cp);
63 :
64 6 : while (smartlist_len(cp) > 0) {
65 : /* Pop off a channel */
66 4 : chan = smartlist_pqueue_pop(cp,
67 : scheduler_compare_channels,
68 : offsetof(channel_t, sched_heap_idx));
69 4 : 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 0 : continue;
74 : }
75 :
76 : /* Figure out how many cells we can write */
77 4 : n_cells = channel_num_cells_writeable(chan);
78 4 : if (n_cells > 0) {
79 3 : 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 3 : flushed = 0;
85 5 : while (flushed < n_cells) {
86 4 : flushed_this_time =
87 8 : channel_flush_some_cells(chan,
88 4 : MIN(MAX_FLUSH_CELLS, (size_t) n_cells - flushed));
89 4 : if (flushed_this_time <= 0) break;
90 2 : flushed += flushed_this_time;
91 : }
92 :
93 3 : if (flushed < n_cells) {
94 : /* We ran out of cells to flush */
95 2 : scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
96 : } else {
97 : /* The channel may still have some cells */
98 1 : if (channel_more_to_flush(chan)) {
99 : /* The channel goes to either pending or waiting_to_write */
100 0 : if (channel_num_cells_writeable(chan) > 0) {
101 : /* Add it back to pending later */
102 0 : if (!to_readd) to_readd = smartlist_new();
103 0 : smartlist_add(to_readd, chan);
104 0 : 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 0 : 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 1 : if (channel_num_cells_writeable(chan) > 0) {
116 : /*
117 : * It can still accept writes, so it goes to
118 : * waiting_for_cells
119 : */
120 1 : 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 0 : scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
127 : }
128 : }
129 : }
130 :
131 3 : 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 1 : 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 1 : scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
143 : }
144 : }
145 :
146 : /* Readd any channels we need to */
147 2 : if (to_readd) {
148 0 : SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
149 0 : scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING);
150 0 : smartlist_pqueue_add(cp,
151 : scheduler_compare_channels,
152 : offsetof(channel_t, sched_heap_idx),
153 : readd_chan);
154 0 : } SMARTLIST_FOREACH_END(readd_chan);
155 0 : smartlist_free(to_readd);
156 : }
157 :
158 2 : n_chans_after = smartlist_len(cp);
159 2 : log_debug(LD_SCHED, "Scheduler handled %d of %d pending channels",
160 : n_chans_before - n_chans_after, n_chans_before);
161 2 : }
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 35 : get_vanilla_scheduler(void)
177 : {
178 35 : return &vanilla_scheduler;
179 : }
|