aboutsummaryrefslogtreecommitdiff
path: root/src/ats/plugin_ats2_simple.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-10-06 21:03:33 +0200
committerChristian Grothoff <christian@grothoff.org>2019-10-06 21:03:33 +0200
commit622fd12c7101c53c1fa0f9563a831f3196732dc5 (patch)
treed86047807232d9921341b8ed84558dea2b9df929 /src/ats/plugin_ats2_simple.c
parent6c38429f96ff9128ebd983a85aa6a2024112ed25 (diff)
downloadgnunet-622fd12c7101c53c1fa0f9563a831f3196732dc5.tar.gz
gnunet-622fd12c7101c53c1fa0f9563a831f3196732dc5.zip
remove dead, obsolete or never-to-become ATS logic (DCE)
Diffstat (limited to 'src/ats/plugin_ats2_simple.c')
-rw-r--r--src/ats/plugin_ats2_simple.c1087
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 */
58struct SimpleHandle;
59
60
61/**
62 * Information about preferences and sessions we track
63 * per peer.
64 */
65struct Peer;
66
67
68/**
69 * Entry in list of addresses we could try per peer.
70 */
71struct 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 */
125struct 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 */
194struct 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 */
253struct 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 */
275struct 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 */
313struct Peer *
314lookup_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 */
327static int
328peer_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 */
347static void
348suggest_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 */
376static void
377suggest_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 */
462static void
463watch_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 */
543static struct Peer *
544peer_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 */
576static void
577peer_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 */
611static void
612consider_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 */
662struct 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 */
706static int
707update_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 */
817static int
818update_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 */
842static void
843update (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 */
870static struct GNUNET_ATS_PreferenceHandle *
871simple_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 */
892static void
893simple_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 */
919static struct GNUNET_ATS_SessionHandle *
920simple_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 */
976static void
977simple_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 */
996static void
997simple_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 */
1034void *
1035libgnunet_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 */
1073void *
1074libgnunet_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 */