diff options
Diffstat (limited to 'src/ats/plugin_ats2_simple.c')
-rw-r--r-- | src/ats/plugin_ats2_simple.c | 1087 |
1 files changed, 0 insertions, 1087 deletions
diff --git a/src/ats/plugin_ats2_simple.c b/src/ats/plugin_ats2_simple.c deleted file mode 100644 index 3062b6019..000000000 --- a/src/ats/plugin_ats2_simple.c +++ /dev/null | |||
@@ -1,1087 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011-2015, 2018 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file ats/plugin_ats2_simple.c | ||
22 | * @brief ATS simple solver | ||
23 | * @author Matthias Wachs | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - needs testing | ||
28 | */ | ||
29 | #include "platform.h" | ||
30 | #include "gnunet_ats_plugin_new.h" | ||
31 | #include "gnunet_hello_lib.h" | ||
32 | #include "gnunet_peerstore_service.h" | ||
33 | |||
34 | #define LOG(kind, ...) GNUNET_log_from (kind, "ats-simple", __VA_ARGS__) | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Base frequency at which we suggest addresses to transport. | ||
39 | * Multiplied by the square of the number of active connections | ||
40 | * (and randomized) to calculate the actual frequency at which | ||
41 | * we will suggest addresses to the transport. Furthermore, each | ||
42 | * address is also bounded by an exponential back-off. | ||
43 | */ | ||
44 | #define SUGGEST_FREQ GNUNET_TIME_UNIT_SECONDS | ||
45 | |||
46 | /** | ||
47 | * What is the minimum bandwidth we always try to allocate for | ||
48 | * any session that is up? (May still be scaled down lower if | ||
49 | * the number of sessions is so high that the total bandwidth | ||
50 | * is insufficient to allow for this value to be granted.) | ||
51 | */ | ||
52 | #define MIN_BANDWIDTH_PER_SESSION 1024 | ||
53 | |||
54 | |||
55 | /** | ||
56 | * A handle for the proportional solver | ||
57 | */ | ||
58 | struct SimpleHandle; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Information about preferences and sessions we track | ||
63 | * per peer. | ||
64 | */ | ||
65 | struct Peer; | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Entry in list of addresses we could try per peer. | ||
70 | */ | ||
71 | struct Hello | ||
72 | { | ||
73 | /** | ||
74 | * Kept in a DLL. | ||
75 | */ | ||
76 | struct Hello *next; | ||
77 | |||
78 | /** | ||
79 | * Kept in a DLL. | ||
80 | */ | ||
81 | struct Hello *prev; | ||
82 | |||
83 | /** | ||
84 | * Peer this hello belongs to. | ||
85 | */ | ||
86 | struct Peer *peer; | ||
87 | |||
88 | /** | ||
89 | * The address we could try. | ||
90 | */ | ||
91 | const char *address; | ||
92 | |||
93 | /** | ||
94 | * Is a session with this address already up? | ||
95 | * If not, set to NULL. | ||
96 | */ | ||
97 | struct GNUNET_ATS_SessionHandle *sh; | ||
98 | |||
99 | /** | ||
100 | * When does the HELLO expire? | ||
101 | */ | ||
102 | struct GNUNET_TIME_Absolute expiration; | ||
103 | |||
104 | /** | ||
105 | * When did we try it last? | ||
106 | */ | ||
107 | struct GNUNET_TIME_Absolute last_attempt; | ||
108 | |||
109 | /** | ||
110 | * Current exponential backoff value. | ||
111 | */ | ||
112 | struct GNUNET_TIME_Relative backoff; | ||
113 | |||
114 | /** | ||
115 | * Type of the network for this HELLO. | ||
116 | */ | ||
117 | enum GNUNET_NetworkType nt; | ||
118 | }; | ||
119 | |||
120 | |||
121 | /** | ||
122 | * Internal representation of a session by the plugin. | ||
123 | * (If desired, plugin may just use NULL.) | ||
124 | */ | ||
125 | struct GNUNET_ATS_SessionHandle | ||
126 | { | ||
127 | /** | ||
128 | * Kept in DLL per peer. | ||
129 | */ | ||
130 | struct GNUNET_ATS_SessionHandle *next; | ||
131 | |||
132 | /** | ||
133 | * Kept in DLL per peer. | ||
134 | */ | ||
135 | struct GNUNET_ATS_SessionHandle *prev; | ||
136 | |||
137 | /** | ||
138 | * The session in the main ATS service. | ||
139 | */ | ||
140 | struct GNUNET_ATS_Session *session; | ||
141 | |||
142 | /** | ||
143 | * Current performance data for this @e session | ||
144 | */ | ||
145 | const struct GNUNET_ATS_SessionData *data; | ||
146 | |||
147 | /** | ||
148 | * Hello matching this session, or NULL for none. | ||
149 | */ | ||
150 | struct Hello *hello; | ||
151 | |||
152 | /** | ||
153 | * Peer this session is for. | ||
154 | */ | ||
155 | struct Peer *peer; | ||
156 | |||
157 | /** | ||
158 | * Address used by this session (largely for debugging). | ||
159 | */ | ||
160 | const char *address; | ||
161 | |||
162 | /** | ||
163 | * When did we last update transport about the allocation? | ||
164 | * Used to dampen the frequency of updates. | ||
165 | */ | ||
166 | struct GNUNET_TIME_Absolute last_allocation; | ||
167 | |||
168 | /** | ||
169 | * Last BW-in allocation given to the transport service. | ||
170 | */ | ||
171 | struct GNUNET_BANDWIDTH_Value32NBO bw_in; | ||
172 | |||
173 | /** | ||
174 | * Last BW-out allocation given to the transport service. | ||
175 | */ | ||
176 | struct GNUNET_BANDWIDTH_Value32NBO bw_out; | ||
177 | |||
178 | /** | ||
179 | * New BW-in allocation given to the transport service. | ||
180 | */ | ||
181 | uint64_t target_in; | ||
182 | |||
183 | /** | ||
184 | * New BW-out allocation given to the transport service. | ||
185 | */ | ||
186 | uint64_t target_out; | ||
187 | }; | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Information about preferences and sessions we track | ||
192 | * per peer. | ||
193 | */ | ||
194 | struct Peer | ||
195 | { | ||
196 | /** | ||
197 | * Kept in DLL per peer. | ||
198 | */ | ||
199 | struct GNUNET_ATS_SessionHandle *sh_head; | ||
200 | |||
201 | /** | ||
202 | * Kept in DLL per peer. | ||
203 | */ | ||
204 | struct GNUNET_ATS_SessionHandle *sh_tail; | ||
205 | |||
206 | /** | ||
207 | * Kept in a DLL. | ||
208 | */ | ||
209 | struct Hello *h_head; | ||
210 | |||
211 | /** | ||
212 | * Kept in a DLL. | ||
213 | */ | ||
214 | struct Hello *h_tail; | ||
215 | |||
216 | /** | ||
217 | * The handle for the proportional solver | ||
218 | */ | ||
219 | struct SimpleHandle *h; | ||
220 | |||
221 | /** | ||
222 | * Watch context where we are currently looking for HELLOs for | ||
223 | * this peer. | ||
224 | */ | ||
225 | struct GNUNET_PEERSTORE_WatchContext *wc; | ||
226 | |||
227 | /** | ||
228 | * Task used to try again to suggest an address for this peer. | ||
229 | */ | ||
230 | struct GNUNET_SCHEDULER_Task *task; | ||
231 | |||
232 | /** | ||
233 | * Which peer is this for? | ||
234 | */ | ||
235 | struct GNUNET_PeerIdentity pid; | ||
236 | |||
237 | /** | ||
238 | * When did we last suggest an address to connect to for this peer? | ||
239 | */ | ||
240 | struct GNUNET_TIME_Absolute last_suggestion; | ||
241 | |||
242 | /** | ||
243 | * Array where we sum up the bandwidth requests received indexed | ||
244 | * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) | ||
245 | */ | ||
246 | uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT]; | ||
247 | }; | ||
248 | |||
249 | |||
250 | /** | ||
251 | * Representation of a network (to be expanded...) | ||
252 | */ | ||
253 | struct Network | ||
254 | { | ||
255 | /** | ||
256 | * Total inbound quota | ||
257 | */ | ||
258 | unsigned long long total_quota_in; | ||
259 | |||
260 | /** | ||
261 | * Total outbound quota | ||
262 | */ | ||
263 | unsigned long long total_quota_out; | ||
264 | |||
265 | /** | ||
266 | * ATS network type | ||
267 | */ | ||
268 | enum GNUNET_NetworkType type; | ||
269 | }; | ||
270 | |||
271 | |||
272 | /** | ||
273 | * A handle for the proportional solver | ||
274 | */ | ||
275 | struct SimpleHandle | ||
276 | { | ||
277 | /** | ||
278 | * Our execution environment. | ||
279 | */ | ||
280 | struct GNUNET_ATS_PluginEnvironment *env; | ||
281 | |||
282 | /** | ||
283 | * Information we track for each peer. | ||
284 | */ | ||
285 | struct GNUNET_CONTAINER_MultiPeerMap *peers; | ||
286 | |||
287 | /** | ||
288 | * Handle to the peerstore service. | ||
289 | */ | ||
290 | struct GNUNET_PEERSTORE_Handle *ps; | ||
291 | |||
292 | /** | ||
293 | * Array where we sum up the bandwidth requests received indexed | ||
294 | * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums | ||
295 | * over all peers). | ||
296 | */ | ||
297 | uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT]; | ||
298 | |||
299 | /** | ||
300 | * Information we track per network type (quotas). | ||
301 | */ | ||
302 | struct Network networks[GNUNET_NT_COUNT]; | ||
303 | }; | ||
304 | |||
305 | |||
306 | /** | ||
307 | * Lookup peer in the peers map. | ||
308 | * | ||
309 | * @param h handle to look up in | ||
310 | * @param pid peer identity to look up by | ||
311 | * @return NULL for not found | ||
312 | */ | ||
313 | struct Peer * | ||
314 | lookup_peer (struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid) | ||
315 | { | ||
316 | return GNUNET_CONTAINER_multipeermap_get (h->peers, pid); | ||
317 | } | ||
318 | |||
319 | |||
320 | /** | ||
321 | * Check if there is _any_ interesting information left we | ||
322 | * store about the peer in @a p. | ||
323 | * | ||
324 | * @param p peer to test if we can drop the data structure | ||
325 | * @return #GNUNET_YES if no information is left in @a p | ||
326 | */ | ||
327 | static int | ||
328 | peer_test_dead (struct Peer *p) | ||
329 | { | ||
330 | for (enum GNUNET_MQ_PreferenceKind pk = 0; pk < GNUNET_MQ_PREFERENCE_COUNT; | ||
331 | pk++) | ||
332 | if (0 != p->bw_by_pk[pk]) | ||
333 | return GNUNET_NO; | ||
334 | if (NULL != p->sh_head) | ||
335 | return GNUNET_NO; | ||
336 | return GNUNET_YES; | ||
337 | } | ||
338 | |||
339 | |||
340 | /** | ||
341 | * Contact the transport service and suggest to it to | ||
342 | * try connecting to the address of @a hello. Updates | ||
343 | * backoff and timestamp values in the @a hello. | ||
344 | * | ||
345 | * @param hello[in,out] address suggestion to make | ||
346 | */ | ||
347 | static void | ||
348 | suggest_hello (struct Hello *hello) | ||
349 | { | ||
350 | struct Peer *p = hello->peer; | ||
351 | struct SimpleHandle *h = p->h; | ||
352 | |||
353 | p->last_suggestion = hello->last_attempt = GNUNET_TIME_absolute_get (); | ||
354 | hello->backoff = | ||
355 | GNUNET_TIME_randomized_backoff (hello->backoff, | ||
356 | GNUNET_TIME_absolute_get_remaining ( | ||
357 | hello->expiration)); | ||
358 | h->env->suggest_cb (h->env->cls, &p->pid, hello->address); | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * Consider suggesting a HELLO (without a session) to transport. | ||
364 | * We look at how many active sessions we have for the peer, and | ||
365 | * if there are many, reduce the frequency of trying new addresses. | ||
366 | * Also, for each address we consider when we last tried it, and | ||
367 | * its exponential backoff if the attempt failed. Note that it | ||
368 | * is possible that this function is called when no suggestion | ||
369 | * is to be made. | ||
370 | * | ||
371 | * In this case, we only calculate the time until we make the next | ||
372 | * suggestion. | ||
373 | * | ||
374 | * @param cls a `struct Peer` | ||
375 | */ | ||
376 | static void | ||
377 | suggest_start_cb (void *cls) | ||
378 | { | ||
379 | struct Peer *p = cls; | ||
380 | struct GNUNET_TIME_Relative delay = GNUNET_TIME_UNIT_ZERO; | ||
381 | struct Hello *hello = NULL; | ||
382 | struct GNUNET_TIME_Absolute hpt = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
383 | struct GNUNET_TIME_Relative xdelay; | ||
384 | struct GNUNET_TIME_Absolute xnext; | ||
385 | unsigned int num_sessions = 0; | ||
386 | uint32_t sq; | ||
387 | |||
388 | /* count number of active sessions */ | ||
389 | for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head; NULL != sh; | ||
390 | sh = sh->next) | ||
391 | num_sessions++; | ||
392 | /* calculate square of number of sessions */ | ||
393 | num_sessions++; /* start with 1, even if we have zero sessions */ | ||
394 | if (num_sessions < UINT16_MAX) | ||
395 | sq = num_sessions * (uint32_t) num_sessions; | ||
396 | else | ||
397 | sq = UINT32_MAX; | ||
398 | xdelay = | ||
399 | GNUNET_TIME_randomized_backoff (GNUNET_TIME_relative_multiply (SUGGEST_FREQ, | ||
400 | sq), | ||
401 | GNUNET_TIME_UNIT_FOREVER_REL); | ||
402 | xnext = GNUNET_TIME_relative_to_absolute (xdelay); | ||
403 | |||
404 | p->task = NULL; | ||
405 | while (0 == delay.rel_value_us) | ||
406 | { | ||
407 | struct Hello *next; | ||
408 | struct GNUNET_TIME_Absolute xmax; | ||
409 | |||
410 | if (NULL != hello) | ||
411 | { | ||
412 | /* We went through the loop already once and found | ||
413 | a HELLO that is due *now*, so make a suggestion! */ | ||
414 | GNUNET_break (NULL == hello->sh); | ||
415 | suggest_hello (hello); | ||
416 | hello = NULL; | ||
417 | hpt = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
418 | } | ||
419 | for (struct Hello *pos = p->h_head; NULL != pos; pos = next) | ||
420 | { | ||
421 | struct GNUNET_TIME_Absolute pt; | ||
422 | |||
423 | next = pos->next; | ||
424 | if (NULL != pos->sh) | ||
425 | continue; | ||
426 | if (0 == | ||
427 | GNUNET_TIME_absolute_get_remaining (pos->expiration).rel_value_us) | ||
428 | { | ||
429 | /* expired, remove! */ | ||
430 | GNUNET_CONTAINER_DLL_remove (p->h_head, p->h_tail, pos); | ||
431 | GNUNET_free (pos); | ||
432 | continue; | ||
433 | } | ||
434 | pt = GNUNET_TIME_absolute_add (pos->last_attempt, pos->backoff); | ||
435 | if ((NULL == hello) || (pt.abs_value_us < hpt.abs_value_us)) | ||
436 | { | ||
437 | hello = pos; | ||
438 | hpt = pt; | ||
439 | } | ||
440 | } | ||
441 | if (NULL == hello) | ||
442 | return; /* no HELLOs that could still be tried */ | ||
443 | |||
444 | /* hpt is now the *earliest* possible time for any HELLO | ||
445 | but we might not want to go for as early as possible for | ||
446 | this peer. So the actual time is the max of the earliest | ||
447 | HELLO and the 'xnext' */ | ||
448 | xmax = GNUNET_TIME_absolute_max (hpt, xnext); | ||
449 | delay = GNUNET_TIME_absolute_get_remaining (xmax); | ||
450 | } | ||
451 | p->task = GNUNET_SCHEDULER_add_delayed (delay, &suggest_start_cb, p); | ||
452 | } | ||
453 | |||
454 | |||
455 | /** | ||
456 | * Function called by PEERSTORE for each matching record. | ||
457 | * | ||
458 | * @param cls closure with a `struct Peer` | ||
459 | * @param record peerstore record information | ||
460 | * @param emsg error message, or NULL if no errors | ||
461 | */ | ||
462 | static void | ||
463 | watch_cb (void *cls, | ||
464 | const struct GNUNET_PEERSTORE_Record *record, | ||
465 | const char *emsg) | ||
466 | { | ||
467 | struct Peer *p = cls; | ||
468 | char *addr; | ||
469 | size_t alen; | ||
470 | enum GNUNET_NetworkType nt; | ||
471 | struct GNUNET_TIME_Absolute expiration; | ||
472 | struct Hello *hello; | ||
473 | |||
474 | if (0 != GNUNET_memcmp (&p->pid, &record->peer)) | ||
475 | { | ||
476 | GNUNET_break (0); | ||
477 | return; | ||
478 | } | ||
479 | if (0 != strcmp (record->key, GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY)) | ||
480 | { | ||
481 | GNUNET_break (0); | ||
482 | return; | ||
483 | } | ||
484 | addr = GNUNET_HELLO_extract_address (record->value, | ||
485 | record->value_size, | ||
486 | &p->pid, | ||
487 | &nt, | ||
488 | &expiration); | ||
489 | if (NULL == addr) | ||
490 | return; /* invalid hello, bad signature, other problem */ | ||
491 | if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) | ||
492 | { | ||
493 | /* expired, ignore */ | ||
494 | GNUNET_free (addr); | ||
495 | return; | ||
496 | } | ||
497 | /* check if addr is already known */ | ||
498 | for (struct Hello *he = p->h_head; NULL != he; he = he->next) | ||
499 | { | ||
500 | if (0 != strcmp (he->address, addr)) | ||
501 | continue; | ||
502 | if (he->expiration.abs_value_us < expiration.abs_value_us) | ||
503 | { | ||
504 | he->expiration = expiration; | ||
505 | he->nt = nt; | ||
506 | } | ||
507 | GNUNET_free (addr); | ||
508 | return; | ||
509 | } | ||
510 | /* create new HELLO */ | ||
511 | alen = strlen (addr) + 1; | ||
512 | hello = GNUNET_malloc (sizeof(struct Hello) + alen); | ||
513 | hello->address = (const char *) &hello[1]; | ||
514 | hello->expiration = expiration; | ||
515 | hello->nt = nt; | ||
516 | hello->peer = p; | ||
517 | memcpy (&hello[1], addr, alen); | ||
518 | GNUNET_CONTAINER_DLL_insert (p->h_head, p->h_tail, hello); | ||
519 | /* check if sh for this HELLO already exists */ | ||
520 | for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head; NULL != sh; | ||
521 | sh = sh->next) | ||
522 | { | ||
523 | if ((NULL == sh->address) || (0 != strcmp (sh->address, addr))) | ||
524 | continue; | ||
525 | GNUNET_assert (NULL == sh->hello); | ||
526 | sh->hello = hello; | ||
527 | hello->sh = sh; | ||
528 | break; | ||
529 | } | ||
530 | GNUNET_free (addr); | ||
531 | if (NULL == p->task) | ||
532 | p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb, p); | ||
533 | } | ||
534 | |||
535 | |||
536 | /** | ||
537 | * Find or add peer if necessary. | ||
538 | * | ||
539 | * @param h our plugin handle | ||
540 | * @param pid the peer identity to add/look for | ||
541 | * @return a peer handle | ||
542 | */ | ||
543 | static struct Peer * | ||
544 | peer_add (struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid) | ||
545 | { | ||
546 | struct Peer *p = lookup_peer (h, pid); | ||
547 | |||
548 | if (NULL != p) | ||
549 | return p; | ||
550 | p = GNUNET_new (struct Peer); | ||
551 | p->h = h; | ||
552 | p->pid = *pid; | ||
553 | p->wc = GNUNET_PEERSTORE_watch (h->ps, | ||
554 | "transport", | ||
555 | &p->pid, | ||
556 | GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY, | ||
557 | &watch_cb, | ||
558 | p); | ||
559 | GNUNET_assert (GNUNET_YES == | ||
560 | GNUNET_CONTAINER_multipeermap_put ( | ||
561 | h->peers, | ||
562 | &p->pid, | ||
563 | p, | ||
564 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
565 | |||
566 | return p; | ||
567 | } | ||
568 | |||
569 | |||
570 | /** | ||
571 | * Free the entry (and associated tasks) of peer @a p. | ||
572 | * Note that @a p must be dead already (see #peer_test_dead()). | ||
573 | * | ||
574 | * @param p the peer to free | ||
575 | */ | ||
576 | static void | ||
577 | peer_free (struct Peer *p) | ||
578 | { | ||
579 | struct SimpleHandle *h = p->h; | ||
580 | struct Hello *hello; | ||
581 | |||
582 | GNUNET_assert (NULL == p->sh_head); | ||
583 | while (NULL != (hello = p->h_head)) | ||
584 | { | ||
585 | GNUNET_CONTAINER_DLL_remove (p->h_head, p->h_tail, hello); | ||
586 | GNUNET_assert (NULL == hello->sh); | ||
587 | GNUNET_free (hello); | ||
588 | } | ||
589 | if (NULL != p->task) | ||
590 | { | ||
591 | GNUNET_SCHEDULER_cancel (p->task); | ||
592 | p->task = NULL; | ||
593 | } | ||
594 | if (NULL != p->wc) | ||
595 | { | ||
596 | GNUNET_PEERSTORE_watch_cancel (p->wc); | ||
597 | p->wc = NULL; | ||
598 | } | ||
599 | GNUNET_assert (GNUNET_YES == | ||
600 | GNUNET_CONTAINER_multipeermap_remove (h->peers, &p->pid, p)); | ||
601 | GNUNET_free (p); | ||
602 | } | ||
603 | |||
604 | |||
605 | /** | ||
606 | * Check if the new allocation for @a sh is significantly different | ||
607 | * from the last one, and if so, tell transport. | ||
608 | * | ||
609 | * @param sh session handle to consider updating transport for | ||
610 | */ | ||
611 | static void | ||
612 | consider_notify_transport (struct GNUNET_ATS_SessionHandle *sh) | ||
613 | { | ||
614 | struct Peer *peer = sh->peer; | ||
615 | struct SimpleHandle *h = peer->h; | ||
616 | enum GNUNET_NetworkType nt = sh->data->prop.nt; | ||
617 | struct GNUNET_TIME_Relative delay; | ||
618 | uint64_t sig_in; | ||
619 | uint64_t sig_out; | ||
620 | int64_t delta_in; | ||
621 | int64_t delta_out; | ||
622 | |||
623 | delay = GNUNET_TIME_absolute_get_duration (sh->last_allocation); | ||
624 | /* A significant change is more than 10% of the quota, | ||
625 | which is given in bytes/second */ | ||
626 | sig_in = h->networks[nt].total_quota_in * (delay.rel_value_us / 1000LL) | ||
627 | / 1000LL / 10; | ||
628 | sig_out = h->networks[nt].total_quota_out * (delay.rel_value_us / 1000LL) | ||
629 | / 1000LL / 10; | ||
630 | delta_in = ((int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in); | ||
631 | delta_out = ((int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in); | ||
632 | /* we want the absolute values */ | ||
633 | if (delta_in < 0) | ||
634 | delta_in = -delta_in; | ||
635 | if (INT64_MIN == delta_in) | ||
636 | delta_in = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */ | ||
637 | if (delta_out < 0) | ||
638 | delta_out = -delta_out; | ||
639 | if (INT64_MIN == delta_out) | ||
640 | delta_out = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */ | ||
641 | if ((sig_in > delta_in) && (sig_out > delta_out)) | ||
642 | return; /* insignificant change */ | ||
643 | /* change is significant, tell transport! */ | ||
644 | if (sh->target_in > UINT32_MAX) | ||
645 | sh->target_in = UINT32_MAX; | ||
646 | sh->bw_in.value__ = htonl ((uint32_t) sh->target_in); | ||
647 | if (sh->target_out > UINT32_MAX) | ||
648 | sh->target_out = UINT32_MAX; | ||
649 | sh->bw_out.value__ = htonl ((uint32_t) sh->target_out); | ||
650 | sh->last_allocation = GNUNET_TIME_absolute_get (); | ||
651 | h->env->allocate_cb (h->env->cls, | ||
652 | sh->session, | ||
653 | &peer->pid, | ||
654 | sh->bw_in, | ||
655 | sh->bw_out); | ||
656 | } | ||
657 | |||
658 | |||
659 | /** | ||
660 | * Closure for #update_counters and #update_allocation. | ||
661 | */ | ||
662 | struct Counters | ||
663 | { | ||
664 | /** | ||
665 | * Plugin's state. | ||
666 | */ | ||
667 | struct SimpleHandle *h; | ||
668 | |||
669 | /** | ||
670 | * Bandwidth that applications would prefer to allocate in this | ||
671 | * network type. We initially add all requested allocations to the | ||
672 | * respective network type where the given preference is best | ||
673 | * satisfied. Later we may rebalance. | ||
674 | */ | ||
675 | uint64_t bw_out_by_nt[GNUNET_NT_COUNT]; | ||
676 | |||
677 | /** | ||
678 | * Current bandwidth utilization for this network type. We simply | ||
679 | * add the current goodput up (with some fairness considerations). | ||
680 | */ | ||
681 | uint64_t bw_in_by_nt[GNUNET_NT_COUNT]; | ||
682 | |||
683 | /** | ||
684 | * By how much do we have to scale (up or down) our expectations | ||
685 | * for outbound bandwidth? | ||
686 | */ | ||
687 | double scale_out[GNUNET_NT_COUNT]; | ||
688 | |||
689 | /** | ||
690 | * By how much do we have to scale (up or down) our expectations | ||
691 | * for inbound bandwidth? | ||
692 | */ | ||
693 | double scale_in[GNUNET_NT_COUNT]; | ||
694 | }; | ||
695 | |||
696 | |||
697 | /** | ||
698 | * Function used to iterate over all peers and collect | ||
699 | * counter data. | ||
700 | * | ||
701 | * @param cls a `struct Counters *` | ||
702 | * @param pid identity of the peer we process, unused | ||
703 | * @param value a `struct Peer *` | ||
704 | * @return #GNUNET_YES (continue to iterate) | ||
705 | */ | ||
706 | static int | ||
707 | update_counters (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) | ||
708 | { | ||
709 | struct Counters *c = cls; | ||
710 | struct Peer *peer = value; | ||
711 | struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT]; | ||
712 | |||
713 | (void) pid; | ||
714 | if (NULL == peer->sh_head) | ||
715 | return GNUNET_YES; /* no available session, cannot allocate bandwidth */ | ||
716 | memset (best, 0, sizeof(best)); | ||
717 | for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head; NULL != sh; | ||
718 | sh = sh->next) | ||
719 | { | ||
720 | enum GNUNET_NetworkType nt = sh->data->prop.nt; | ||
721 | |||
722 | sh->target_out = MIN_BANDWIDTH_PER_SESSION; | ||
723 | c->bw_out_by_nt[nt] += MIN_BANDWIDTH_PER_SESSION; | ||
724 | c->bw_in_by_nt[nt] += | ||
725 | GNUNET_MAX (MIN_BANDWIDTH_PER_SESSION, sh->data->prop.goodput_in); | ||
726 | for (enum GNUNET_MQ_PreferenceKind pk = 0; pk < GNUNET_MQ_PREFERENCE_COUNT; | ||
727 | pk++) | ||
728 | { | ||
729 | /* General rule: always prefer smaller distance if possible, | ||
730 | otherwise decide by pk: */ | ||
731 | switch (pk) | ||
732 | { | ||
733 | case GNUNET_MQ_PREFERENCE_NONE: | ||
734 | break; | ||
735 | |||
736 | case GNUNET_MQ_PREFERENCE_BANDWIDTH: | ||
737 | /* For bandwidth, we compare the sum of transmitted bytes and | ||
738 | confirmed transmitted bytes, so confirmed data counts twice */ | ||
739 | if ((NULL == best[pk]) || | ||
740 | (sh->data->prop.distance < best[pk]->data->prop.distance) || | ||
741 | (sh->data->prop.utilization_out + sh->data->prop.goodput_out > | ||
742 | best[pk]->data->prop.utilization_out | ||
743 | + best[pk]->data->prop.goodput_out)) | ||
744 | best[pk] = sh; | ||
745 | /* If both are equal (i.e. usually this happens if there is a zero), use | ||
746 | latency as a yardstick */ | ||
747 | if ((sh->data->prop.utilization_out + sh->data->prop.goodput_out == | ||
748 | best[pk]->data->prop.utilization_out | ||
749 | + best[pk]->data->prop.goodput_out) && | ||
750 | (sh->data->prop.distance == best[pk]->data->prop.distance) && | ||
751 | (sh->data->prop.delay.rel_value_us < | ||
752 | best[pk]->data->prop.delay.rel_value_us)) | ||
753 | best[pk] = sh; | ||
754 | break; | ||
755 | |||
756 | case GNUNET_MQ_PREFERENCE_LATENCY: | ||
757 | if ((NULL == best[pk]) || | ||
758 | (sh->data->prop.distance < best[pk]->data->prop.distance) || | ||
759 | ((sh->data->prop.distance == best[pk]->data->prop.distance) && | ||
760 | (sh->data->prop.delay.rel_value_us < | ||
761 | best[pk]->data->prop.delay.rel_value_us))) | ||
762 | best[pk] = sh; | ||
763 | break; | ||
764 | |||
765 | case GNUNET_MQ_PREFERENCE_RELIABILITY: | ||
766 | /* For reliability, we consider the ratio of goodput to utilization | ||
767 | (but use multiplicative formultations to avoid division by zero) */ | ||
768 | if ((NULL == best[pk]) || (1ULL * sh->data->prop.goodput_out | ||
769 | * best[pk]->data->prop.utilization_out > | ||
770 | 1ULL * sh->data->prop.utilization_out | ||
771 | * best[pk]->data->prop.goodput_out)) | ||
772 | best[pk] = sh; | ||
773 | /* If both are equal (i.e. usually this happens if there is a zero), use | ||
774 | latency as a yardstick */ | ||
775 | if ((1ULL * sh->data->prop.goodput_out | ||
776 | * best[pk]->data->prop.utilization_out == | ||
777 | 1ULL * sh->data->prop.utilization_out | ||
778 | * best[pk]->data->prop.goodput_out) && | ||
779 | (sh->data->prop.distance == best[pk]->data->prop.distance) && | ||
780 | (sh->data->prop.delay.rel_value_us < | ||
781 | best[pk]->data->prop.delay.rel_value_us)) | ||
782 | best[pk] = sh; | ||
783 | break; | ||
784 | } | ||
785 | } | ||
786 | } | ||
787 | /* for first round, assign target bandwidth simply to sum of | ||
788 | requested bandwidth */ | ||
789 | for (enum GNUNET_MQ_PreferenceKind pk = | ||
790 | 1 /* skip GNUNET_MQ_PREFERENCE_NONE */; | ||
791 | pk < GNUNET_MQ_PREFERENCE_COUNT; | ||
792 | pk++) | ||
793 | { | ||
794 | const struct GNUNET_ATS_SessionData *data = best[pk]->data; | ||
795 | enum GNUNET_NetworkType nt; | ||
796 | |||
797 | GNUNET_assert (NULL != data); | ||
798 | nt = data->prop.nt; | ||
799 | best[pk]->target_out = | ||
800 | GNUNET_MIN (peer->bw_by_pk[pk], MIN_BANDWIDTH_PER_SESSION); | ||
801 | c->bw_out_by_nt[nt] += | ||
802 | (uint64_t) (best[pk]->target_out - MIN_BANDWIDTH_PER_SESSION); | ||
803 | } | ||
804 | return GNUNET_YES; | ||
805 | } | ||
806 | |||
807 | |||
808 | /** | ||
809 | * Function used to iterate over all peers and collect | ||
810 | * counter data. | ||
811 | * | ||
812 | * @param cls a `struct Counters *` | ||
813 | * @param pid identity of the peer we process, unused | ||
814 | * @param value a `struct Peer *` | ||
815 | * @return #GNUNET_YES (continue to iterate) | ||
816 | */ | ||
817 | static int | ||
818 | update_allocation (void *cls, | ||
819 | const struct GNUNET_PeerIdentity *pid, | ||
820 | void *value) | ||
821 | { | ||
822 | struct Counters *c = cls; | ||
823 | struct Peer *peer = value; | ||
824 | |||
825 | (void) pid; | ||
826 | for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head; NULL != sh; | ||
827 | sh = sh->next) | ||
828 | { | ||
829 | enum GNUNET_NetworkType nt = sh->data->prop.nt; | ||
830 | |||
831 | sh->target_out = (uint64_t) (c->scale_out[nt] * sh->target_out); | ||
832 | sh->target_in = (uint64_t) (c->scale_in[nt] * sh->target_in); | ||
833 | consider_notify_transport (sh); | ||
834 | } | ||
835 | return GNUNET_YES; | ||
836 | } | ||
837 | |||
838 | |||
839 | /** | ||
840 | * The world changed, recalculate our allocations. | ||
841 | */ | ||
842 | static void | ||
843 | update (struct SimpleHandle *h) | ||
844 | { | ||
845 | struct Counters cnt = { .h = h }; | ||
846 | |||
847 | GNUNET_CONTAINER_multipeermap_iterate (h->peers, &update_counters, &cnt); | ||
848 | /* calculate how badly the missmatch between requested | ||
849 | allocations and available bandwidth is per network type */ | ||
850 | for (enum GNUNET_NetworkType nt = 0; nt < GNUNET_NT_COUNT; nt++) | ||
851 | { | ||
852 | cnt.scale_out[nt] = | ||
853 | 1.0 * cnt.bw_out_by_nt[nt] / h->networks[nt].total_quota_out; | ||
854 | cnt.scale_in[nt] = | ||
855 | 1.0 * cnt.bw_in_by_nt[nt] / h->networks[nt].total_quota_in; | ||
856 | } | ||
857 | /* recalculate allocations, considering scaling factor, and | ||
858 | update transport if the change is significant */ | ||
859 | GNUNET_CONTAINER_multipeermap_iterate (h->peers, &update_allocation, &cnt); | ||
860 | } | ||
861 | |||
862 | |||
863 | /** | ||
864 | * The plugin should begin to respect a new preference. | ||
865 | * | ||
866 | * @param cls the closure | ||
867 | * @param pref the preference to add | ||
868 | * @return plugin's internal representation, or NULL | ||
869 | */ | ||
870 | static struct GNUNET_ATS_PreferenceHandle * | ||
871 | simple_preference_add (void *cls, const struct GNUNET_ATS_Preference *pref) | ||
872 | { | ||
873 | struct SimpleHandle *h = cls; | ||
874 | struct Peer *p = peer_add (h, &pref->peer); | ||
875 | |||
876 | GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT); | ||
877 | p->bw_by_pk[pref->pk] += ntohl (pref->bw.value__); | ||
878 | h->bw_by_pk[pref->pk] += ntohl (pref->bw.value__); | ||
879 | update (h); | ||
880 | return NULL; | ||
881 | } | ||
882 | |||
883 | |||
884 | /** | ||
885 | * The plugin should end respecting a preference. | ||
886 | * | ||
887 | * @param cls the closure | ||
888 | * @param ph whatever @e preference_add returned | ||
889 | * @param pref the preference to delete | ||
890 | * @return plugin's internal representation, or NULL | ||
891 | */ | ||
892 | static void | ||
893 | simple_preference_del (void *cls, | ||
894 | struct GNUNET_ATS_PreferenceHandle *ph, | ||
895 | const struct GNUNET_ATS_Preference *pref) | ||
896 | { | ||
897 | struct SimpleHandle *h = cls; | ||
898 | struct Peer *p = lookup_peer (h, &pref->peer); | ||
899 | |||
900 | GNUNET_assert (NULL != p); | ||
901 | GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT); | ||
902 | p->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__); | ||
903 | h->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__); | ||
904 | if ((0 == p->bw_by_pk[pref->pk]) && (GNUNET_YES == peer_test_dead (p))) | ||
905 | peer_free (p); | ||
906 | update (h); | ||
907 | } | ||
908 | |||
909 | |||
910 | /** | ||
911 | * Transport established a new session with performance | ||
912 | * characteristics given in @a data. | ||
913 | * | ||
914 | * @param cls closure | ||
915 | * @param data performance characteristics of @a sh | ||
916 | * @param address address information (for debugging) | ||
917 | * @return handle by which the plugin will identify this session | ||
918 | */ | ||
919 | static struct GNUNET_ATS_SessionHandle * | ||
920 | simple_session_add (void *cls, | ||
921 | const struct GNUNET_ATS_SessionData *data, | ||
922 | const char *address) | ||
923 | { | ||
924 | struct SimpleHandle *h = cls; | ||
925 | struct Peer *p = peer_add (h, &data->peer); | ||
926 | struct Hello *hello; | ||
927 | size_t alen; | ||
928 | struct GNUNET_ATS_SessionHandle *sh; | ||
929 | |||
930 | /* setup session handle */ | ||
931 | GNUNET_assert (NULL != data); | ||
932 | if (NULL == address) | ||
933 | alen = 0; | ||
934 | else | ||
935 | alen = strlen (address) + 1; | ||
936 | sh = GNUNET_malloc (sizeof(struct GNUNET_ATS_SessionHandle) + alen); | ||
937 | sh->peer = p; | ||
938 | sh->session = data->session; | ||
939 | sh->data = data; | ||
940 | if (NULL == address) | ||
941 | { | ||
942 | sh->address = NULL; | ||
943 | } | ||
944 | else | ||
945 | { | ||
946 | memcpy (&sh[1], address, alen); | ||
947 | sh->address = (const char *) &sh[1]; | ||
948 | } | ||
949 | GNUNET_CONTAINER_DLL_insert (p->sh_head, p->sh_tail, sh); | ||
950 | if (NULL != address) | ||
951 | { | ||
952 | /* match HELLO */ | ||
953 | hello = p->h_head; | ||
954 | while ((NULL != hello) && (0 != strcmp (address, hello->address))) | ||
955 | hello = hello->next; | ||
956 | if (NULL != hello) | ||
957 | { | ||
958 | hello->sh = sh; | ||
959 | hello->backoff = GNUNET_TIME_UNIT_ZERO; | ||
960 | sh->hello = hello; | ||
961 | } | ||
962 | } | ||
963 | update (h); | ||
964 | return sh; | ||
965 | } | ||
966 | |||
967 | |||
968 | /** | ||
969 | * @a data changed for a given @a sh, solver should consider | ||
970 | * the updated performance characteristics. | ||
971 | * | ||
972 | * @param cls closure | ||
973 | * @param sh session this is about | ||
974 | * @param data performance characteristics of @a sh | ||
975 | */ | ||
976 | static void | ||
977 | simple_session_update (void *cls, | ||
978 | struct GNUNET_ATS_SessionHandle *sh, | ||
979 | const struct GNUNET_ATS_SessionData *data) | ||
980 | { | ||
981 | struct SimpleHandle *h = cls; | ||
982 | |||
983 | GNUNET_assert (NULL != data); | ||
984 | sh->data = data; /* this statement should not really do anything... */ | ||
985 | update (h); | ||
986 | } | ||
987 | |||
988 | |||
989 | /** | ||
990 | * A session went away. Solver should update accordingly. | ||
991 | * | ||
992 | * @param cls closure | ||
993 | * @param sh session this is about | ||
994 | * @param data (last) performance characteristics of @a sh | ||
995 | */ | ||
996 | static void | ||
997 | simple_session_del (void *cls, | ||
998 | struct GNUNET_ATS_SessionHandle *sh, | ||
999 | const struct GNUNET_ATS_SessionData *data) | ||
1000 | { | ||
1001 | struct SimpleHandle *h = cls; | ||
1002 | struct Peer *p = sh->peer; | ||
1003 | struct Hello *hello = sh->hello; | ||
1004 | |||
1005 | /* clean up sh */ | ||
1006 | GNUNET_CONTAINER_DLL_remove (p->sh_head, p->sh_tail, sh); | ||
1007 | if (NULL != hello) | ||
1008 | { | ||
1009 | GNUNET_assert (sh == hello->sh); | ||
1010 | hello->sh = NULL; | ||
1011 | /* session went down, if necessary restart suggesting | ||
1012 | addresses */ | ||
1013 | if (NULL == p->task) | ||
1014 | p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb, p); | ||
1015 | } | ||
1016 | GNUNET_free (sh); | ||
1017 | /* del peer if otherwise dead */ | ||
1018 | if ((NULL == p->sh_head) && (GNUNET_YES == peer_test_dead (p))) | ||
1019 | peer_free (p); | ||
1020 | update (h); | ||
1021 | } | ||
1022 | |||
1023 | |||
1024 | #include "plugin_ats2_common.c" | ||
1025 | |||
1026 | |||
1027 | /** | ||
1028 | * Function invoked when the plugin is loaded. | ||
1029 | * | ||
1030 | * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use; | ||
1031 | * modified to return the API functions (ugh). | ||
1032 | * @return the `struct SimpleHandle` to pass as a closure | ||
1033 | */ | ||
1034 | void * | ||
1035 | libgnunet_plugin_ats2_simple_init (void *cls) | ||
1036 | { | ||
1037 | static struct GNUNET_ATS_SolverFunctions sf; | ||
1038 | struct GNUNET_ATS_PluginEnvironment *env = cls; | ||
1039 | struct SimpleHandle *s; | ||
1040 | |||
1041 | s = GNUNET_new (struct SimpleHandle); | ||
1042 | s->env = env; | ||
1043 | s->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); | ||
1044 | s->ps = GNUNET_PEERSTORE_connect (env->cfg); | ||
1045 | sf.cls = s; | ||
1046 | sf.preference_add = &simple_preference_add; | ||
1047 | sf.preference_del = &simple_preference_del; | ||
1048 | sf.session_add = &simple_session_add; | ||
1049 | sf.session_update = &simple_session_update; | ||
1050 | sf.session_del = &simple_session_del; | ||
1051 | for (enum GNUNET_NetworkType nt = 0; nt < GNUNET_NT_COUNT; nt++) | ||
1052 | { | ||
1053 | const char *name = GNUNET_NT_to_string (nt); | ||
1054 | |||
1055 | if (NULL == name) | ||
1056 | { | ||
1057 | GNUNET_break (0); | ||
1058 | break; | ||
1059 | } | ||
1060 | get_quota (env->cfg, name, "IN", &s->networks[nt].total_quota_in); | ||
1061 | get_quota (env->cfg, name, "OUT", &s->networks[nt].total_quota_out); | ||
1062 | s->networks[nt].type = nt; | ||
1063 | } | ||
1064 | return &sf; | ||
1065 | } | ||
1066 | |||
1067 | |||
1068 | /** | ||
1069 | * Function used to unload the plugin. | ||
1070 | * | ||
1071 | * @param cls return value from #libgnunet_plugin_ats_proportional_init() | ||
1072 | */ | ||
1073 | void * | ||
1074 | libgnunet_plugin_ats2_simple_done (void *cls) | ||
1075 | { | ||
1076 | struct GNUNET_ATS_SolverFunctions *sf = cls; | ||
1077 | struct SimpleHandle *s = sf->cls; | ||
1078 | |||
1079 | GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (s->peers)); | ||
1080 | GNUNET_CONTAINER_multipeermap_destroy (s->peers); | ||
1081 | GNUNET_PEERSTORE_disconnect (s->ps, GNUNET_NO); | ||
1082 | GNUNET_free (s); | ||
1083 | return NULL; | ||
1084 | } | ||
1085 | |||
1086 | |||
1087 | /* end of plugin_ats2_simple.c */ | ||