2 This file is part of GNUnet.
3 Copyright (C) 2011-2015, 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file ats/plugin_ats2_simple.c
22 * @brief ATS simple solver
23 * @author Matthias Wachs
24 * @author Christian Grothoff
30 #include "gnunet_ats_plugin_new.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_peerstore_service.h"
34 #define LOG(kind,...) GNUNET_log_from (kind, "ats-simple",__VA_ARGS__)
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.
44 #define SUGGEST_FREQ GNUNET_TIME_UNIT_SECONDS
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.)
52 #define MIN_BANDWIDTH_PER_SESSION 1024
56 * A handle for the proportional solver
62 * Information about preferences and sessions we track
69 * Entry in list of addresses we could try per peer.
85 * Peer this hello belongs to.
90 * The address we could try.
95 * Is a session with this address already up?
96 * If not, set to NULL.
98 struct GNUNET_ATS_SessionHandle *sh;
101 * When does the HELLO expire?
103 struct GNUNET_TIME_Absolute expiration;
106 * When did we try it last?
108 struct GNUNET_TIME_Absolute last_attempt;
111 * Current exponential backoff value.
113 struct GNUNET_TIME_Relative backoff;
116 * Type of the network for this HELLO.
118 enum GNUNET_NetworkType nt;
124 * Internal representation of a session by the plugin.
125 * (If desired, plugin may just use NULL.)
127 struct GNUNET_ATS_SessionHandle
131 * Kept in DLL per peer.
133 struct GNUNET_ATS_SessionHandle *next;
136 * Kept in DLL per peer.
138 struct GNUNET_ATS_SessionHandle *prev;
141 * The session in the main ATS service.
143 struct GNUNET_ATS_Session *session;
146 * Current performance data for this @e session
148 const struct GNUNET_ATS_SessionData *data;
151 * Hello matching this session, or NULL for none.
156 * Peer this session is for.
161 * Address used by this session (largely for debugging).
166 * When did we last update transport about the allocation?
167 * Used to dampen the frequency of updates.
169 struct GNUNET_TIME_Absolute last_allocation;
172 * Last BW-in allocation given to the transport service.
174 struct GNUNET_BANDWIDTH_Value32NBO bw_in;
177 * Last BW-out allocation given to the transport service.
179 struct GNUNET_BANDWIDTH_Value32NBO bw_out;
182 * New BW-in allocation given to the transport service.
187 * New BW-out allocation given to the transport service.
195 * Information about preferences and sessions we track
202 * Kept in DLL per peer.
204 struct GNUNET_ATS_SessionHandle *sh_head;
207 * Kept in DLL per peer.
209 struct GNUNET_ATS_SessionHandle *sh_tail;
214 struct Hello *h_head;
219 struct Hello *h_tail;
222 * The handle for the proportional solver
224 struct SimpleHandle *h;
227 * Watch context where we are currently looking for HELLOs for
230 struct GNUNET_PEERSTORE_WatchContext *wc;
233 * Task used to try again to suggest an address for this peer.
235 struct GNUNET_SCHEDULER_Task *task;
238 * Which peer is this for?
240 struct GNUNET_PeerIdentity pid;
243 * When did we last suggest an address to connect to for this peer?
245 struct GNUNET_TIME_Absolute last_suggestion;
248 * Array where we sum up the bandwidth requests received indexed
249 * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
251 uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
257 * Representation of a network (to be expanded...)
263 * Total inbound quota
265 unsigned long long total_quota_in;
268 * Total outbound quota
270 unsigned long long total_quota_out;
275 enum GNUNET_NetworkType type;
281 * A handle for the proportional solver
287 * Our execution environment.
289 struct GNUNET_ATS_PluginEnvironment *env;
292 * Information we track for each peer.
294 struct GNUNET_CONTAINER_MultiPeerMap *peers;
297 * Handle to the peerstore service.
299 struct GNUNET_PEERSTORE_Handle *ps;
302 * Array where we sum up the bandwidth requests received indexed
303 * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums
306 uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
309 * Information we track per network type (quotas).
311 struct Network networks[GNUNET_NT_COUNT];
317 * Lookup peer in the peers map.
319 * @param h handle to look up in
320 * @param pid peer identity to look up by
321 * @return NULL for not found
324 lookup_peer (struct SimpleHandle *h,
325 const struct GNUNET_PeerIdentity *pid)
327 return GNUNET_CONTAINER_multipeermap_get (h->peers,
333 * Check if there is _any_ interesting information left we
334 * store about the peer in @a p.
336 * @param p peer to test if we can drop the data structure
337 * @return #GNUNET_YES if no information is left in @a p
340 peer_test_dead (struct Peer *p)
342 for (enum GNUNET_MQ_PreferenceKind pk = 0;
343 pk < GNUNET_MQ_PREFERENCE_COUNT;
345 if (0 != p->bw_by_pk[pk])
347 if (NULL != p->sh_head)
354 * Contact the transport service and suggest to it to
355 * try connecting to the address of @a hello. Updates
356 * backoff and timestamp values in the @a hello.
358 * @param hello[in,out] address suggestion to make
361 suggest_hello (struct Hello *hello)
363 struct Peer *p = hello->peer;
364 struct SimpleHandle *h = p->h;
367 = hello->last_attempt
368 = GNUNET_TIME_absolute_get ();
369 hello->backoff = GNUNET_TIME_randomized_backoff (hello->backoff,
370 GNUNET_TIME_absolute_get_remaining (hello->expiration));
371 h->env->suggest_cb (h->env->cls,
378 * Consider suggesting a HELLO (without a session) to transport.
379 * We look at how many active sessions we have for the peer, and
380 * if there are many, reduce the frequency of trying new addresses.
381 * Also, for each address we consider when we last tried it, and
382 * its exponential backoff if the attempt failed. Note that it
383 * is possible that this function is called when no suggestion
386 * In this case, we only calculate the time until we make the next
389 * @param cls a `struct Peer`
392 suggest_start_cb (void *cls)
394 struct Peer *p = cls;
395 struct GNUNET_TIME_Relative delay = GNUNET_TIME_UNIT_ZERO;
396 struct Hello *hello = NULL;
397 struct GNUNET_TIME_Absolute hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
398 struct GNUNET_TIME_Relative xdelay;
399 struct GNUNET_TIME_Absolute xnext;
400 unsigned int num_sessions = 0;
403 /* count number of active sessions */
404 for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
408 /* calculate square of number of sessions */
409 num_sessions++; /* start with 1, even if we have zero sessions */
410 if (num_sessions < UINT16_MAX)
411 sq = num_sessions * (uint32_t) num_sessions;
414 xdelay = GNUNET_TIME_randomized_backoff (GNUNET_TIME_relative_multiply (SUGGEST_FREQ,
416 GNUNET_TIME_UNIT_FOREVER_REL);
417 xnext = GNUNET_TIME_relative_to_absolute (xdelay);
420 while (0 == delay.rel_value_us)
423 struct GNUNET_TIME_Absolute xmax;
427 /* We went through the loop already once and found
428 a HELLO that is due *now*, so make a suggestion! */
429 GNUNET_break (NULL == hello->sh);
430 suggest_hello (hello);
432 hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
434 for (struct Hello *pos = p->h_head; NULL != pos; pos = next)
436 struct GNUNET_TIME_Absolute pt;
441 if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).rel_value_us)
443 /* expired, remove! */
444 GNUNET_CONTAINER_DLL_remove (p->h_head,
450 pt = GNUNET_TIME_absolute_add (pos->last_attempt,
452 if ( (NULL == hello) ||
453 (pt.abs_value_us < hpt.abs_value_us) )
460 return; /* no HELLOs that could still be tried */
462 /* hpt is now the *earliest* possible time for any HELLO
463 but we might not want to go for as early as possible for
464 this peer. So the actual time is the max of the earliest
465 HELLO and the 'xnext' */
466 xmax = GNUNET_TIME_absolute_max (hpt,
468 delay = GNUNET_TIME_absolute_get_remaining (xmax);
470 p->task = GNUNET_SCHEDULER_add_delayed (delay,
477 * Function called by PEERSTORE for each matching record.
479 * @param cls closure with a `struct Peer`
480 * @param record peerstore record information
481 * @param emsg error message, or NULL if no errors
485 const struct GNUNET_PEERSTORE_Record *record,
488 struct Peer *p = cls;
491 enum GNUNET_NetworkType nt;
492 struct GNUNET_TIME_Absolute expiration;
495 if (0 != memcmp (&p->pid,
497 sizeof (struct GNUNET_PeerIdentity)))
502 if (0 != strcmp (record->key,
503 GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY))
508 addr = GNUNET_HELLO_extract_address (record->value,
514 return; /* invalid hello, bad signature, other problem */
515 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
517 /* expired, ignore */
521 /* check if addr is already known */
522 for (struct Hello *he = p->h_head;
526 if (0 != strcmp (he->address,
529 if (he->expiration.abs_value_us < expiration.abs_value_us)
531 he->expiration = expiration;
537 /* create new HELLO */
538 alen = strlen (addr) + 1;
539 hello = GNUNET_malloc (sizeof (struct Hello) + alen);
540 hello->address = (const char *) &hello[1];
541 hello->expiration = expiration;
548 GNUNET_CONTAINER_DLL_insert (p->h_head,
551 /* check if sh for this HELLO already exists */
552 for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
556 if ( (NULL == sh->address) ||
557 (0 != strcmp (sh->address,
560 GNUNET_assert (NULL == sh->hello);
566 p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
572 * Find or add peer if necessary.
574 * @param h our plugin handle
575 * @param pid the peer identity to add/look for
576 * @return a peer handle
579 peer_add (struct SimpleHandle *h,
580 const struct GNUNET_PeerIdentity *pid)
582 struct Peer *p = lookup_peer (h,
587 p = GNUNET_new (struct Peer);
590 p->wc = GNUNET_PEERSTORE_watch (h->ps,
593 GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY,
596 GNUNET_assert (GNUNET_YES ==
597 GNUNET_CONTAINER_multipeermap_put (h->peers,
600 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
607 * Free the entry (and associated tasks) of peer @a p.
608 * Note that @a p must be dead already (see #peer_test_dead()).
610 * @param p the peer to free
613 peer_free (struct Peer *p)
615 struct SimpleHandle *h = p->h;
618 GNUNET_assert (NULL == p->sh_head);
619 while (NULL != (hello = p->h_head))
621 GNUNET_CONTAINER_DLL_remove (p->h_head,
624 GNUNET_assert (NULL == hello->sh);
629 GNUNET_SCHEDULER_cancel (p->task);
634 GNUNET_PEERSTORE_watch_cancel (p->wc);
637 GNUNET_assert (GNUNET_YES ==
638 GNUNET_CONTAINER_multipeermap_remove (h->peers,
646 * Check if the new allocation for @a sh is significantly different
647 * from the last one, and if so, tell transport.
649 * @param sh session handle to consider updating transport for
652 consider_notify_transport (struct GNUNET_ATS_SessionHandle *sh)
654 struct Peer *peer = sh->peer;
655 struct SimpleHandle *h = peer->h;
656 enum GNUNET_NetworkType nt = sh->data->prop.nt;
657 struct GNUNET_TIME_Relative delay;
663 delay = GNUNET_TIME_absolute_get_duration (sh->last_allocation);
664 /* A significant change is more than 10% of the quota,
665 which is given in bytes/second */
667 = h->networks[nt].total_quota_in * (delay.rel_value_us / 1000LL) / 1000LL / 10;
669 = h->networks[nt].total_quota_out * (delay.rel_value_us / 1000LL) / 1000LL / 10;
670 delta_in = ( (int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in);
671 delta_out = ( (int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in);
672 /* we want the absolute values */
674 delta_in = - delta_in;
675 if (INT64_MIN == delta_in)
676 delta_in = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
678 delta_out = - delta_out;
679 if (INT64_MIN == delta_out)
680 delta_out = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
681 if ( (sig_in > delta_in) &&
682 (sig_out > delta_out) )
683 return; /* insignificant change */
684 /* change is significant, tell transport! */
685 if (sh->target_in > UINT32_MAX)
686 sh->target_in = UINT32_MAX;
687 sh->bw_in.value__ = htonl ((uint32_t) sh->target_in);
688 if (sh->target_out > UINT32_MAX)
689 sh->target_out = UINT32_MAX;
690 sh->bw_out.value__ = htonl ((uint32_t) sh->target_out);
691 sh->last_allocation = GNUNET_TIME_absolute_get ();
692 h->env->allocate_cb (h->env->cls,
701 * Closure for #update_counters and #update_allocation.
708 struct SimpleHandle *h;
711 * Bandwidth that applications would prefer to allocate in this
712 * network type. We initially add all requested allocations to the
713 * respective network type where the given preference is best
714 * satisfied. Later we may rebalance.
716 uint64_t bw_out_by_nt[GNUNET_NT_COUNT];
719 * Current bandwidth utilization for this network type. We simply
720 * add the current goodput up (with some fairness considerations).
722 uint64_t bw_in_by_nt[GNUNET_NT_COUNT];
725 * By how much do we have to scale (up or down) our expectations
726 * for outbound bandwidth?
728 double scale_out[GNUNET_NT_COUNT];
731 * By how much do we have to scale (up or down) our expectations
732 * for inbound bandwidth?
734 double scale_in[GNUNET_NT_COUNT];
740 * Function used to iterate over all peers and collect
743 * @param cls a `struct Counters *`
744 * @param pid identity of the peer we process, unused
745 * @param value a `struct Peer *`
746 * @return #GNUNET_YES (continue to iterate)
749 update_counters (void *cls,
750 const struct GNUNET_PeerIdentity *pid,
753 struct Counters *c = cls;
754 struct Peer *peer = value;
755 struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT];
758 if (NULL == peer->sh_head)
759 return GNUNET_YES; /* no available session, cannot allocate bandwidth */
763 for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
767 enum GNUNET_NetworkType nt = sh->data->prop.nt;
769 sh->target_out = MIN_BANDWIDTH_PER_SESSION;
770 c->bw_out_by_nt[nt] += MIN_BANDWIDTH_PER_SESSION;
771 c->bw_in_by_nt[nt] += GNUNET_MAX (MIN_BANDWIDTH_PER_SESSION,
772 sh->data->prop.goodput_in);
773 for (enum GNUNET_MQ_PreferenceKind pk = 0;
774 pk < GNUNET_MQ_PREFERENCE_COUNT;
777 /* General rule: always prefer smaller distance if possible,
778 otherwise decide by pk: */
780 case GNUNET_MQ_PREFERENCE_NONE:
782 case GNUNET_MQ_PREFERENCE_BANDWIDTH:
783 /* For bandwidth, we compare the sum of transmitted bytes and
784 confirmed transmitted bytes, so confirmed data counts twice */
785 if ( (NULL == best[pk]) ||
786 (sh->data->prop.distance < best[pk]->data->prop.distance) ||
787 (sh->data->prop.utilization_out + sh->data->prop.goodput_out >
788 best[pk]->data->prop.utilization_out + best[pk]->data->prop.goodput_out) )
790 /* If both are equal (i.e. usually this happens if there is a zero), use
791 latency as a yardstick */
792 if ( (sh->data->prop.utilization_out + sh->data->prop.goodput_out ==
793 best[pk]->data->prop.utilization_out + best[pk]->data->prop.goodput_out) &&
794 (sh->data->prop.distance == best[pk]->data->prop.distance) &&
795 (sh->data->prop.delay.rel_value_us <
796 best[pk]->data->prop.delay.rel_value_us) )
799 case GNUNET_MQ_PREFERENCE_LATENCY:
800 if ( (NULL == best[pk]) ||
801 (sh->data->prop.distance < best[pk]->data->prop.distance) ||
802 ( (sh->data->prop.distance == best[pk]->data->prop.distance) &&
803 (sh->data->prop.delay.rel_value_us <
804 best[pk]->data->prop.delay.rel_value_us) ) )
807 case GNUNET_MQ_PREFERENCE_RELIABILITY:
808 /* For reliability, we consider the ratio of goodput to utilization
809 (but use multiplicative formultations to avoid division by zero) */
810 if ( (NULL == best[pk]) ||
811 (1ULL * sh->data->prop.goodput_out * best[pk]->data->prop.utilization_out >
812 1ULL * sh->data->prop.utilization_out * best[pk]->data->prop.goodput_out) )
814 /* If both are equal (i.e. usually this happens if there is a zero), use
815 latency as a yardstick */
816 if ( (1ULL * sh->data->prop.goodput_out * best[pk]->data->prop.utilization_out ==
817 1ULL * sh->data->prop.utilization_out * best[pk]->data->prop.goodput_out) &&
818 (sh->data->prop.distance == best[pk]->data->prop.distance) &&
819 (sh->data->prop.delay.rel_value_us <
820 best[pk]->data->prop.delay.rel_value_us) )
826 /* for first round, assign target bandwidth simply to sum of
827 requested bandwidth */
828 for (enum GNUNET_MQ_PreferenceKind pk = 1 /* skip GNUNET_MQ_PREFERENCE_NONE */;
829 pk < GNUNET_MQ_PREFERENCE_COUNT;
832 const struct GNUNET_ATS_SessionData *data = best[pk]->data;
833 enum GNUNET_NetworkType nt;
835 GNUNET_assert (NULL != data);
837 best[pk]->target_out = GNUNET_MIN (peer->bw_by_pk[pk],
838 MIN_BANDWIDTH_PER_SESSION);
839 c->bw_out_by_nt[nt] += (uint64_t) (best[pk]->target_out - MIN_BANDWIDTH_PER_SESSION);
846 * Function used to iterate over all peers and collect
849 * @param cls a `struct Counters *`
850 * @param pid identity of the peer we process, unused
851 * @param value a `struct Peer *`
852 * @return #GNUNET_YES (continue to iterate)
855 update_allocation (void *cls,
856 const struct GNUNET_PeerIdentity *pid,
859 struct Counters *c = cls;
860 struct Peer *peer = value;
863 for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
867 enum GNUNET_NetworkType nt = sh->data->prop.nt;
869 sh->target_out = (uint64_t) (c->scale_out[nt] * sh->target_out);
870 sh->target_in = (uint64_t) (c->scale_in[nt] * sh->target_in);
871 consider_notify_transport (sh);
878 * The world changed, recalculate our allocations.
881 update (struct SimpleHandle *h)
883 struct Counters cnt = {
887 GNUNET_CONTAINER_multipeermap_iterate (h->peers,
890 /* calculate how badly the missmatch between requested
891 allocations and available bandwidth is per network type */
892 for (enum GNUNET_NetworkType nt = 0;
893 nt < GNUNET_NT_COUNT;
896 cnt.scale_out[nt] = 1.0 * cnt.bw_out_by_nt[nt] / h->networks[nt].total_quota_out;
897 cnt.scale_in[nt] = 1.0 * cnt.bw_in_by_nt[nt] / h->networks[nt].total_quota_in;
899 /* recalculate allocations, considering scaling factor, and
900 update transport if the change is significant */
901 GNUNET_CONTAINER_multipeermap_iterate (h->peers,
908 * The plugin should begin to respect a new preference.
910 * @param cls the closure
911 * @param pref the preference to add
912 * @return plugin's internal representation, or NULL
914 static struct GNUNET_ATS_PreferenceHandle *
915 simple_preference_add (void *cls,
916 const struct GNUNET_ATS_Preference *pref)
918 struct SimpleHandle *h = cls;
919 struct Peer *p = peer_add (h,
922 GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
923 p->bw_by_pk[pref->pk] += ntohl (pref->bw.value__);
924 h->bw_by_pk[pref->pk] += ntohl (pref->bw.value__);
931 * The plugin should end respecting a preference.
933 * @param cls the closure
934 * @param ph whatever @e preference_add returned
935 * @param pref the preference to delete
936 * @return plugin's internal representation, or NULL
939 simple_preference_del (void *cls,
940 struct GNUNET_ATS_PreferenceHandle *ph,
941 const struct GNUNET_ATS_Preference *pref)
943 struct SimpleHandle *h = cls;
944 struct Peer *p = lookup_peer (h,
947 GNUNET_assert (NULL != p);
948 GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
949 p->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__);
950 h->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__);
951 if ( (0 == p->bw_by_pk[pref->pk]) &&
952 (GNUNET_YES == peer_test_dead (p)) )
959 * Transport established a new session with performance
960 * characteristics given in @a data.
963 * @param data performance characteristics of @a sh
964 * @param address address information (for debugging)
965 * @return handle by which the plugin will identify this session
967 static struct GNUNET_ATS_SessionHandle *
968 simple_session_add (void *cls,
969 const struct GNUNET_ATS_SessionData *data,
972 struct SimpleHandle *h = cls;
973 struct Peer *p = peer_add (h,
977 struct GNUNET_ATS_SessionHandle *sh;
979 /* setup session handle */
980 GNUNET_assert (NULL != data);
984 alen = strlen (address) + 1;
985 sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionHandle) + alen);
987 sh->session = data->session;
998 sh->address = (const char *) &sh[1];
1000 GNUNET_CONTAINER_DLL_insert (p->sh_head,
1005 while ( (NULL != hello) &&
1006 (0 != strcmp (address,
1008 hello = hello->next;
1012 hello->backoff = GNUNET_TIME_UNIT_ZERO;
1021 * @a data changed for a given @a sh, solver should consider
1022 * the updated performance characteristics.
1024 * @param cls closure
1025 * @param sh session this is about
1026 * @param data performance characteristics of @a sh
1029 simple_session_update (void *cls,
1030 struct GNUNET_ATS_SessionHandle *sh,
1031 const struct GNUNET_ATS_SessionData *data)
1033 struct SimpleHandle *h = cls;
1035 GNUNET_assert (NULL != data);
1036 sh->data = data; /* this statement should not really do anything... */
1042 * A session went away. Solver should update accordingly.
1044 * @param cls closure
1045 * @param sh session this is about
1046 * @param data (last) performance characteristics of @a sh
1049 simple_session_del (void *cls,
1050 struct GNUNET_ATS_SessionHandle *sh,
1051 const struct GNUNET_ATS_SessionData *data)
1053 struct SimpleHandle *h = cls;
1054 struct Peer *p = sh->peer;
1055 struct Hello *hello = sh->hello;
1058 GNUNET_CONTAINER_DLL_remove (p->sh_head,
1063 GNUNET_assert (sh == hello->sh);
1065 /* session went down, if necessary restart suggesting
1067 if (NULL == p->task)
1068 p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
1072 /* del peer if otherwise dead */
1073 if ( (NULL == p->sh_head) &&
1074 (GNUNET_YES == peer_test_dead (p)) )
1080 #include "plugin_ats2_common.c"
1084 * Function invoked when the plugin is loaded.
1086 * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1087 * modified to return the API functions (ugh).
1088 * @return the `struct SimpleHandle` to pass as a closure
1091 libgnunet_plugin_ats2_simple_init (void *cls)
1093 static struct GNUNET_ATS_SolverFunctions sf;
1094 struct GNUNET_ATS_PluginEnvironment *env = cls;
1095 struct SimpleHandle *s;
1097 s = GNUNET_new (struct SimpleHandle);
1099 s->peers = GNUNET_CONTAINER_multipeermap_create (128,
1101 s->ps = GNUNET_PEERSTORE_connect (env->cfg);
1103 sf.preference_add = &simple_preference_add;
1104 sf.preference_del = &simple_preference_del;
1105 sf.session_add = &simple_session_add;
1106 sf.session_update = &simple_session_update;
1107 sf.session_del = &simple_session_del;
1108 for (enum GNUNET_NetworkType nt = 0;
1109 nt < GNUNET_NT_COUNT;
1112 const char *name = GNUNET_NT_to_string (nt);
1119 get_quota (env->cfg,
1122 &s->networks[nt].total_quota_in);
1123 get_quota (env->cfg,
1126 &s->networks[nt].total_quota_out);
1127 s->networks[nt].type = nt;
1134 * Function used to unload the plugin.
1136 * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1139 libgnunet_plugin_ats2_simple_done (void *cls)
1141 struct GNUNET_ATS_SolverFunctions *sf = cls;
1142 struct SimpleHandle *s = sf->cls;
1145 GNUNET_CONTAINER_multipeermap_size (s->peers));
1146 GNUNET_CONTAINER_multipeermap_destroy (s->peers);
1147 GNUNET_PEERSTORE_disconnect (s->ps,
1154 /* end of plugin_ats2_simple.c */