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.
83 * Peer this hello belongs to.
88 * The address we could try.
93 * Is a session with this address already up?
94 * If not, set to NULL.
96 struct GNUNET_ATS_SessionHandle *sh;
99 * When does the HELLO expire?
101 struct GNUNET_TIME_Absolute expiration;
104 * When did we try it last?
106 struct GNUNET_TIME_Absolute last_attempt;
109 * Current exponential backoff value.
111 struct GNUNET_TIME_Relative backoff;
114 * Type of the network for this HELLO.
116 enum GNUNET_NetworkType nt;
121 * Internal representation of a session by the plugin.
122 * (If desired, plugin may just use NULL.)
124 struct GNUNET_ATS_SessionHandle {
126 * Kept in DLL per peer.
128 struct GNUNET_ATS_SessionHandle *next;
131 * Kept in DLL per peer.
133 struct GNUNET_ATS_SessionHandle *prev;
136 * The session in the main ATS service.
138 struct GNUNET_ATS_Session *session;
141 * Current performance data for this @e session
143 const struct GNUNET_ATS_SessionData *data;
146 * Hello matching this session, or NULL for none.
151 * Peer this session is for.
156 * Address used by this session (largely for debugging).
161 * When did we last update transport about the allocation?
162 * Used to dampen the frequency of updates.
164 struct GNUNET_TIME_Absolute last_allocation;
167 * Last BW-in allocation given to the transport service.
169 struct GNUNET_BANDWIDTH_Value32NBO bw_in;
172 * Last BW-out allocation given to the transport service.
174 struct GNUNET_BANDWIDTH_Value32NBO bw_out;
177 * New BW-in allocation given to the transport service.
182 * New BW-out allocation given to the transport service.
189 * Information about preferences and sessions we track
194 * Kept in DLL per peer.
196 struct GNUNET_ATS_SessionHandle *sh_head;
199 * Kept in DLL per peer.
201 struct GNUNET_ATS_SessionHandle *sh_tail;
206 struct Hello *h_head;
211 struct Hello *h_tail;
214 * The handle for the proportional solver
216 struct SimpleHandle *h;
219 * Watch context where we are currently looking for HELLOs for
222 struct GNUNET_PEERSTORE_WatchContext *wc;
225 * Task used to try again to suggest an address for this peer.
227 struct GNUNET_SCHEDULER_Task *task;
230 * Which peer is this for?
232 struct GNUNET_PeerIdentity pid;
235 * When did we last suggest an address to connect to for this peer?
237 struct GNUNET_TIME_Absolute last_suggestion;
240 * Array where we sum up the bandwidth requests received indexed
241 * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
243 uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
248 * Representation of a network (to be expanded...)
252 * Total inbound quota
254 unsigned long long total_quota_in;
257 * Total outbound quota
259 unsigned long long total_quota_out;
264 enum GNUNET_NetworkType type;
269 * A handle for the proportional solver
271 struct SimpleHandle {
273 * Our execution environment.
275 struct GNUNET_ATS_PluginEnvironment *env;
278 * Information we track for each peer.
280 struct GNUNET_CONTAINER_MultiPeerMap *peers;
283 * Handle to the peerstore service.
285 struct GNUNET_PEERSTORE_Handle *ps;
288 * Array where we sum up the bandwidth requests received indexed
289 * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums
292 uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
295 * Information we track per network type (quotas).
297 struct Network networks[GNUNET_NT_COUNT];
302 * Lookup peer in the peers map.
304 * @param h handle to look up in
305 * @param pid peer identity to look up by
306 * @return NULL for not found
309 lookup_peer(struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid)
311 return GNUNET_CONTAINER_multipeermap_get(h->peers, pid);
316 * Check if there is _any_ interesting information left we
317 * store about the peer in @a p.
319 * @param p peer to test if we can drop the data structure
320 * @return #GNUNET_YES if no information is left in @a p
323 peer_test_dead(struct Peer *p)
325 for (enum GNUNET_MQ_PreferenceKind pk = 0; pk < GNUNET_MQ_PREFERENCE_COUNT;
327 if (0 != p->bw_by_pk[pk])
329 if (NULL != p->sh_head)
336 * Contact the transport service and suggest to it to
337 * try connecting to the address of @a hello. Updates
338 * backoff and timestamp values in the @a hello.
340 * @param hello[in,out] address suggestion to make
343 suggest_hello(struct Hello *hello)
345 struct Peer *p = hello->peer;
346 struct SimpleHandle *h = p->h;
348 p->last_suggestion = hello->last_attempt = GNUNET_TIME_absolute_get();
350 GNUNET_TIME_randomized_backoff(hello->backoff,
351 GNUNET_TIME_absolute_get_remaining(
353 h->env->suggest_cb(h->env->cls, &p->pid, hello->address);
358 * Consider suggesting a HELLO (without a session) to transport.
359 * We look at how many active sessions we have for the peer, and
360 * if there are many, reduce the frequency of trying new addresses.
361 * Also, for each address we consider when we last tried it, and
362 * its exponential backoff if the attempt failed. Note that it
363 * is possible that this function is called when no suggestion
366 * In this case, we only calculate the time until we make the next
369 * @param cls a `struct Peer`
372 suggest_start_cb(void *cls)
374 struct Peer *p = cls;
375 struct GNUNET_TIME_Relative delay = GNUNET_TIME_UNIT_ZERO;
376 struct Hello *hello = NULL;
377 struct GNUNET_TIME_Absolute hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
378 struct GNUNET_TIME_Relative xdelay;
379 struct GNUNET_TIME_Absolute xnext;
380 unsigned int num_sessions = 0;
383 /* count number of active sessions */
384 for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head; NULL != sh;
387 /* calculate square of number of sessions */
388 num_sessions++; /* start with 1, even if we have zero sessions */
389 if (num_sessions < UINT16_MAX)
390 sq = num_sessions * (uint32_t)num_sessions;
394 GNUNET_TIME_randomized_backoff(GNUNET_TIME_relative_multiply(SUGGEST_FREQ,
396 GNUNET_TIME_UNIT_FOREVER_REL);
397 xnext = GNUNET_TIME_relative_to_absolute(xdelay);
400 while (0 == delay.rel_value_us)
403 struct GNUNET_TIME_Absolute xmax;
407 /* We went through the loop already once and found
408 a HELLO that is due *now*, so make a suggestion! */
409 GNUNET_break(NULL == hello->sh);
410 suggest_hello(hello);
412 hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
414 for (struct Hello *pos = p->h_head; NULL != pos; pos = next)
416 struct GNUNET_TIME_Absolute pt;
422 GNUNET_TIME_absolute_get_remaining(pos->expiration).rel_value_us)
424 /* expired, remove! */
425 GNUNET_CONTAINER_DLL_remove(p->h_head, p->h_tail, pos);
429 pt = GNUNET_TIME_absolute_add(pos->last_attempt, pos->backoff);
430 if ((NULL == hello) || (pt.abs_value_us < hpt.abs_value_us))
437 return; /* no HELLOs that could still be tried */
439 /* hpt is now the *earliest* possible time for any HELLO
440 but we might not want to go for as early as possible for
441 this peer. So the actual time is the max of the earliest
442 HELLO and the 'xnext' */
443 xmax = GNUNET_TIME_absolute_max(hpt, xnext);
444 delay = GNUNET_TIME_absolute_get_remaining(xmax);
446 p->task = GNUNET_SCHEDULER_add_delayed(delay, &suggest_start_cb, p);
451 * Function called by PEERSTORE for each matching record.
453 * @param cls closure with a `struct Peer`
454 * @param record peerstore record information
455 * @param emsg error message, or NULL if no errors
459 const struct GNUNET_PEERSTORE_Record *record,
462 struct Peer *p = cls;
465 enum GNUNET_NetworkType nt;
466 struct GNUNET_TIME_Absolute expiration;
469 if (0 != GNUNET_memcmp(&p->pid, &record->peer))
474 if (0 != strcmp(record->key, GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY))
479 addr = GNUNET_HELLO_extract_address(record->value,
485 return; /* invalid hello, bad signature, other problem */
486 if (0 == GNUNET_TIME_absolute_get_remaining(expiration).rel_value_us)
488 /* expired, ignore */
492 /* check if addr is already known */
493 for (struct Hello *he = p->h_head; NULL != he; he = he->next)
495 if (0 != strcmp(he->address, addr))
497 if (he->expiration.abs_value_us < expiration.abs_value_us)
499 he->expiration = expiration;
505 /* create new HELLO */
506 alen = strlen(addr) + 1;
507 hello = GNUNET_malloc(sizeof(struct Hello) + alen);
508 hello->address = (const char *)&hello[1];
509 hello->expiration = expiration;
512 memcpy(&hello[1], addr, alen);
513 GNUNET_CONTAINER_DLL_insert(p->h_head, p->h_tail, hello);
514 /* check if sh for this HELLO already exists */
515 for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head; NULL != sh;
518 if ((NULL == sh->address) || (0 != strcmp(sh->address, addr)))
520 GNUNET_assert(NULL == sh->hello);
527 p->task = GNUNET_SCHEDULER_add_now(&suggest_start_cb, p);
532 * Find or add peer if necessary.
534 * @param h our plugin handle
535 * @param pid the peer identity to add/look for
536 * @return a peer handle
539 peer_add(struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid)
541 struct Peer *p = lookup_peer(h, pid);
545 p = GNUNET_new(struct Peer);
548 p->wc = GNUNET_PEERSTORE_watch(h->ps,
551 GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY,
554 GNUNET_assert(GNUNET_YES ==
555 GNUNET_CONTAINER_multipeermap_put(
559 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
566 * Free the entry (and associated tasks) of peer @a p.
567 * Note that @a p must be dead already (see #peer_test_dead()).
569 * @param p the peer to free
572 peer_free(struct Peer *p)
574 struct SimpleHandle *h = p->h;
577 GNUNET_assert(NULL == p->sh_head);
578 while (NULL != (hello = p->h_head))
580 GNUNET_CONTAINER_DLL_remove(p->h_head, p->h_tail, hello);
581 GNUNET_assert(NULL == hello->sh);
586 GNUNET_SCHEDULER_cancel(p->task);
591 GNUNET_PEERSTORE_watch_cancel(p->wc);
594 GNUNET_assert(GNUNET_YES ==
595 GNUNET_CONTAINER_multipeermap_remove(h->peers, &p->pid, p));
601 * Check if the new allocation for @a sh is significantly different
602 * from the last one, and if so, tell transport.
604 * @param sh session handle to consider updating transport for
607 consider_notify_transport(struct GNUNET_ATS_SessionHandle *sh)
609 struct Peer *peer = sh->peer;
610 struct SimpleHandle *h = peer->h;
611 enum GNUNET_NetworkType nt = sh->data->prop.nt;
612 struct GNUNET_TIME_Relative delay;
618 delay = GNUNET_TIME_absolute_get_duration(sh->last_allocation);
619 /* A significant change is more than 10% of the quota,
620 which is given in bytes/second */
621 sig_in = h->networks[nt].total_quota_in * (delay.rel_value_us / 1000LL) /
623 sig_out = h->networks[nt].total_quota_out * (delay.rel_value_us / 1000LL) /
625 delta_in = ((int64_t)ntohl(sh->bw_in.value__)) - ((int64_t)sh->target_in);
626 delta_out = ((int64_t)ntohl(sh->bw_in.value__)) - ((int64_t)sh->target_in);
627 /* we want the absolute values */
629 delta_in = -delta_in;
630 if (INT64_MIN == delta_in)
631 delta_in = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
633 delta_out = -delta_out;
634 if (INT64_MIN == delta_out)
635 delta_out = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
636 if ((sig_in > delta_in) && (sig_out > delta_out))
637 return; /* insignificant change */
638 /* change is significant, tell transport! */
639 if (sh->target_in > UINT32_MAX)
640 sh->target_in = UINT32_MAX;
641 sh->bw_in.value__ = htonl((uint32_t)sh->target_in);
642 if (sh->target_out > UINT32_MAX)
643 sh->target_out = UINT32_MAX;
644 sh->bw_out.value__ = htonl((uint32_t)sh->target_out);
645 sh->last_allocation = GNUNET_TIME_absolute_get();
646 h->env->allocate_cb(h->env->cls,
655 * Closure for #update_counters and #update_allocation.
661 struct SimpleHandle *h;
664 * Bandwidth that applications would prefer to allocate in this
665 * network type. We initially add all requested allocations to the
666 * respective network type where the given preference is best
667 * satisfied. Later we may rebalance.
669 uint64_t bw_out_by_nt[GNUNET_NT_COUNT];
672 * Current bandwidth utilization for this network type. We simply
673 * add the current goodput up (with some fairness considerations).
675 uint64_t bw_in_by_nt[GNUNET_NT_COUNT];
678 * By how much do we have to scale (up or down) our expectations
679 * for outbound bandwidth?
681 double scale_out[GNUNET_NT_COUNT];
684 * By how much do we have to scale (up or down) our expectations
685 * for inbound bandwidth?
687 double scale_in[GNUNET_NT_COUNT];
692 * Function used to iterate over all peers and collect
695 * @param cls a `struct Counters *`
696 * @param pid identity of the peer we process, unused
697 * @param value a `struct Peer *`
698 * @return #GNUNET_YES (continue to iterate)
701 update_counters(void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
703 struct Counters *c = cls;
704 struct Peer *peer = value;
705 struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT];
708 if (NULL == peer->sh_head)
709 return GNUNET_YES; /* no available session, cannot allocate bandwidth */
710 memset(best, 0, sizeof(best));
711 for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head; NULL != sh;
714 enum GNUNET_NetworkType nt = sh->data->prop.nt;
716 sh->target_out = MIN_BANDWIDTH_PER_SESSION;
717 c->bw_out_by_nt[nt] += MIN_BANDWIDTH_PER_SESSION;
718 c->bw_in_by_nt[nt] +=
719 GNUNET_MAX(MIN_BANDWIDTH_PER_SESSION, sh->data->prop.goodput_in);
720 for (enum GNUNET_MQ_PreferenceKind pk = 0; pk < GNUNET_MQ_PREFERENCE_COUNT;
723 /* General rule: always prefer smaller distance if possible,
724 otherwise decide by pk: */
727 case GNUNET_MQ_PREFERENCE_NONE:
730 case GNUNET_MQ_PREFERENCE_BANDWIDTH:
731 /* For bandwidth, we compare the sum of transmitted bytes and
732 confirmed transmitted bytes, so confirmed data counts twice */
733 if ((NULL == best[pk]) ||
734 (sh->data->prop.distance < best[pk]->data->prop.distance) ||
735 (sh->data->prop.utilization_out + sh->data->prop.goodput_out >
736 best[pk]->data->prop.utilization_out +
737 best[pk]->data->prop.goodput_out))
739 /* If both are equal (i.e. usually this happens if there is a zero), use
740 latency as a yardstick */
741 if ((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 (sh->data->prop.distance == best[pk]->data->prop.distance) &&
745 (sh->data->prop.delay.rel_value_us <
746 best[pk]->data->prop.delay.rel_value_us))
750 case GNUNET_MQ_PREFERENCE_LATENCY:
751 if ((NULL == best[pk]) ||
752 (sh->data->prop.distance < best[pk]->data->prop.distance) ||
753 ((sh->data->prop.distance == best[pk]->data->prop.distance) &&
754 (sh->data->prop.delay.rel_value_us <
755 best[pk]->data->prop.delay.rel_value_us)))
759 case GNUNET_MQ_PREFERENCE_RELIABILITY:
760 /* For reliability, we consider the ratio of goodput to utilization
761 (but use multiplicative formultations to avoid division by zero) */
762 if ((NULL == best[pk]) || (1ULL * sh->data->prop.goodput_out *
763 best[pk]->data->prop.utilization_out >
764 1ULL * sh->data->prop.utilization_out *
765 best[pk]->data->prop.goodput_out))
767 /* If both are equal (i.e. usually this happens if there is a zero), use
768 latency as a yardstick */
769 if ((1ULL * sh->data->prop.goodput_out *
770 best[pk]->data->prop.utilization_out ==
771 1ULL * sh->data->prop.utilization_out *
772 best[pk]->data->prop.goodput_out) &&
773 (sh->data->prop.distance == best[pk]->data->prop.distance) &&
774 (sh->data->prop.delay.rel_value_us <
775 best[pk]->data->prop.delay.rel_value_us))
781 /* for first round, assign target bandwidth simply to sum of
782 requested bandwidth */
783 for (enum GNUNET_MQ_PreferenceKind pk =
784 1 /* skip GNUNET_MQ_PREFERENCE_NONE */;
785 pk < GNUNET_MQ_PREFERENCE_COUNT;
788 const struct GNUNET_ATS_SessionData *data = best[pk]->data;
789 enum GNUNET_NetworkType nt;
791 GNUNET_assert(NULL != data);
793 best[pk]->target_out =
794 GNUNET_MIN(peer->bw_by_pk[pk], MIN_BANDWIDTH_PER_SESSION);
795 c->bw_out_by_nt[nt] +=
796 (uint64_t)(best[pk]->target_out - MIN_BANDWIDTH_PER_SESSION);
803 * Function used to iterate over all peers and collect
806 * @param cls a `struct Counters *`
807 * @param pid identity of the peer we process, unused
808 * @param value a `struct Peer *`
809 * @return #GNUNET_YES (continue to iterate)
812 update_allocation(void *cls,
813 const struct GNUNET_PeerIdentity *pid,
816 struct Counters *c = cls;
817 struct Peer *peer = value;
820 for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head; NULL != sh;
823 enum GNUNET_NetworkType nt = sh->data->prop.nt;
825 sh->target_out = (uint64_t)(c->scale_out[nt] * sh->target_out);
826 sh->target_in = (uint64_t)(c->scale_in[nt] * sh->target_in);
827 consider_notify_transport(sh);
834 * The world changed, recalculate our allocations.
837 update(struct SimpleHandle *h)
839 struct Counters cnt = { .h = h };
841 GNUNET_CONTAINER_multipeermap_iterate(h->peers, &update_counters, &cnt);
842 /* calculate how badly the missmatch between requested
843 allocations and available bandwidth is per network type */
844 for (enum GNUNET_NetworkType nt = 0; nt < GNUNET_NT_COUNT; nt++)
847 1.0 * cnt.bw_out_by_nt[nt] / h->networks[nt].total_quota_out;
849 1.0 * cnt.bw_in_by_nt[nt] / h->networks[nt].total_quota_in;
851 /* recalculate allocations, considering scaling factor, and
852 update transport if the change is significant */
853 GNUNET_CONTAINER_multipeermap_iterate(h->peers, &update_allocation, &cnt);
858 * The plugin should begin to respect a new preference.
860 * @param cls the closure
861 * @param pref the preference to add
862 * @return plugin's internal representation, or NULL
864 static struct GNUNET_ATS_PreferenceHandle *
865 simple_preference_add(void *cls, const struct GNUNET_ATS_Preference *pref)
867 struct SimpleHandle *h = cls;
868 struct Peer *p = peer_add(h, &pref->peer);
870 GNUNET_assert(pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
871 p->bw_by_pk[pref->pk] += ntohl(pref->bw.value__);
872 h->bw_by_pk[pref->pk] += ntohl(pref->bw.value__);
879 * The plugin should end respecting a preference.
881 * @param cls the closure
882 * @param ph whatever @e preference_add returned
883 * @param pref the preference to delete
884 * @return plugin's internal representation, or NULL
887 simple_preference_del(void *cls,
888 struct GNUNET_ATS_PreferenceHandle *ph,
889 const struct GNUNET_ATS_Preference *pref)
891 struct SimpleHandle *h = cls;
892 struct Peer *p = lookup_peer(h, &pref->peer);
894 GNUNET_assert(NULL != p);
895 GNUNET_assert(pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
896 p->bw_by_pk[pref->pk] -= ntohl(pref->bw.value__);
897 h->bw_by_pk[pref->pk] -= ntohl(pref->bw.value__);
898 if ((0 == p->bw_by_pk[pref->pk]) && (GNUNET_YES == peer_test_dead(p)))
905 * Transport established a new session with performance
906 * characteristics given in @a data.
909 * @param data performance characteristics of @a sh
910 * @param address address information (for debugging)
911 * @return handle by which the plugin will identify this session
913 static struct GNUNET_ATS_SessionHandle *
914 simple_session_add(void *cls,
915 const struct GNUNET_ATS_SessionData *data,
918 struct SimpleHandle *h = cls;
919 struct Peer *p = peer_add(h, &data->peer);
922 struct GNUNET_ATS_SessionHandle *sh;
924 /* setup session handle */
925 GNUNET_assert(NULL != data);
929 alen = strlen(address) + 1;
930 sh = GNUNET_malloc(sizeof(struct GNUNET_ATS_SessionHandle) + alen);
932 sh->session = data->session;
940 memcpy(&sh[1], address, alen);
941 sh->address = (const char *)&sh[1];
943 GNUNET_CONTAINER_DLL_insert(p->sh_head, p->sh_tail, sh);
948 while ((NULL != hello) && (0 != strcmp(address, hello->address)))
953 hello->backoff = GNUNET_TIME_UNIT_ZERO;
963 * @a data changed for a given @a sh, solver should consider
964 * the updated performance characteristics.
967 * @param sh session this is about
968 * @param data performance characteristics of @a sh
971 simple_session_update(void *cls,
972 struct GNUNET_ATS_SessionHandle *sh,
973 const struct GNUNET_ATS_SessionData *data)
975 struct SimpleHandle *h = cls;
977 GNUNET_assert(NULL != data);
978 sh->data = data; /* this statement should not really do anything... */
984 * A session went away. Solver should update accordingly.
987 * @param sh session this is about
988 * @param data (last) performance characteristics of @a sh
991 simple_session_del(void *cls,
992 struct GNUNET_ATS_SessionHandle *sh,
993 const struct GNUNET_ATS_SessionData *data)
995 struct SimpleHandle *h = cls;
996 struct Peer *p = sh->peer;
997 struct Hello *hello = sh->hello;
1000 GNUNET_CONTAINER_DLL_remove(p->sh_head, p->sh_tail, sh);
1003 GNUNET_assert(sh == hello->sh);
1005 /* session went down, if necessary restart suggesting
1007 if (NULL == p->task)
1008 p->task = GNUNET_SCHEDULER_add_now(&suggest_start_cb, p);
1011 /* del peer if otherwise dead */
1012 if ((NULL == p->sh_head) && (GNUNET_YES == peer_test_dead(p)))
1018 #include "plugin_ats2_common.c"
1022 * Function invoked when the plugin is loaded.
1024 * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1025 * modified to return the API functions (ugh).
1026 * @return the `struct SimpleHandle` to pass as a closure
1029 libgnunet_plugin_ats2_simple_init(void *cls)
1031 static struct GNUNET_ATS_SolverFunctions sf;
1032 struct GNUNET_ATS_PluginEnvironment *env = cls;
1033 struct SimpleHandle *s;
1035 s = GNUNET_new(struct SimpleHandle);
1037 s->peers = GNUNET_CONTAINER_multipeermap_create(128, GNUNET_YES);
1038 s->ps = GNUNET_PEERSTORE_connect(env->cfg);
1040 sf.preference_add = &simple_preference_add;
1041 sf.preference_del = &simple_preference_del;
1042 sf.session_add = &simple_session_add;
1043 sf.session_update = &simple_session_update;
1044 sf.session_del = &simple_session_del;
1045 for (enum GNUNET_NetworkType nt = 0; nt < GNUNET_NT_COUNT; nt++)
1047 const char *name = GNUNET_NT_to_string(nt);
1054 get_quota(env->cfg, name, "IN", &s->networks[nt].total_quota_in);
1055 get_quota(env->cfg, name, "OUT", &s->networks[nt].total_quota_out);
1056 s->networks[nt].type = nt;
1063 * Function used to unload the plugin.
1065 * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1068 libgnunet_plugin_ats2_simple_done(void *cls)
1070 struct GNUNET_ATS_SolverFunctions *sf = cls;
1071 struct SimpleHandle *s = sf->cls;
1073 GNUNET_break(0 == GNUNET_CONTAINER_multipeermap_size(s->peers));
1074 GNUNET_CONTAINER_multipeermap_destroy(s->peers);
1075 GNUNET_PEERSTORE_disconnect(s->ps, GNUNET_NO);
1081 /* end of plugin_ats2_simple.c */