90566c56117f4eef212a817a4d8b7e133de59281
[oweals/gnunet.git] / src / ats / plugin_ats2_simple.c
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    * Kept in a DLL.
74    */
75   struct Hello *next;
76
77   /**
78    * Kept in a DLL.
79    */
80   struct Hello *prev;
81
82   /**
83    * Peer this hello belongs to.
84    */
85   struct Peer *peer;
86
87   /**
88    * The address we could try.
89    */
90   const char *address;
91
92   /**
93    * Is a session with this address already up?
94    * If not, set to NULL.
95    */
96   struct GNUNET_ATS_SessionHandle *sh;
97
98   /**
99    * When does the HELLO expire?
100    */
101   struct GNUNET_TIME_Absolute expiration;
102
103   /**
104    * When did we try it last?
105    */
106   struct GNUNET_TIME_Absolute last_attempt;
107
108   /**
109    * Current exponential backoff value.
110    */
111   struct GNUNET_TIME_Relative backoff;
112
113   /**
114    * Type of the network for this HELLO.
115    */
116   enum GNUNET_NetworkType nt;
117 };
118
119
120 /**
121  * Internal representation of a session by the plugin.
122  * (If desired, plugin may just use NULL.)
123  */
124 struct GNUNET_ATS_SessionHandle {
125   /**
126    * Kept in DLL per peer.
127    */
128   struct GNUNET_ATS_SessionHandle *next;
129
130   /**
131    * Kept in DLL per peer.
132    */
133   struct GNUNET_ATS_SessionHandle *prev;
134
135   /**
136    * The session in the main ATS service.
137    */
138   struct GNUNET_ATS_Session *session;
139
140   /**
141    * Current performance data for this @e session
142    */
143   const struct GNUNET_ATS_SessionData *data;
144
145   /**
146    * Hello matching this session, or NULL for none.
147    */
148   struct Hello *hello;
149
150   /**
151    * Peer this session is for.
152    */
153   struct Peer *peer;
154
155   /**
156    * Address used by this session (largely for debugging).
157    */
158   const char *address;
159
160   /**
161    * When did we last update transport about the allocation?
162    * Used to dampen the frequency of updates.
163    */
164   struct GNUNET_TIME_Absolute last_allocation;
165
166   /**
167    * Last BW-in allocation given to the transport service.
168    */
169   struct GNUNET_BANDWIDTH_Value32NBO bw_in;
170
171   /**
172    * Last BW-out allocation given to the transport service.
173    */
174   struct GNUNET_BANDWIDTH_Value32NBO bw_out;
175
176   /**
177    * New BW-in allocation given to the transport service.
178    */
179   uint64_t target_in;
180
181   /**
182    * New BW-out allocation given to the transport service.
183    */
184   uint64_t target_out;
185 };
186
187
188 /**
189  * Information about preferences and sessions we track
190  * per peer.
191  */
192 struct Peer {
193   /**
194    * Kept in DLL per peer.
195    */
196   struct GNUNET_ATS_SessionHandle *sh_head;
197
198   /**
199    * Kept in DLL per peer.
200    */
201   struct GNUNET_ATS_SessionHandle *sh_tail;
202
203   /**
204    * Kept in a DLL.
205    */
206   struct Hello *h_head;
207
208   /**
209    * Kept in a DLL.
210    */
211   struct Hello *h_tail;
212
213   /**
214    * The handle for the proportional solver
215    */
216   struct SimpleHandle *h;
217
218   /**
219    * Watch context where we are currently looking for HELLOs for
220    * this peer.
221    */
222   struct GNUNET_PEERSTORE_WatchContext *wc;
223
224   /**
225    * Task used to try again to suggest an address for this peer.
226    */
227   struct GNUNET_SCHEDULER_Task *task;
228
229   /**
230    * Which peer is this for?
231    */
232   struct GNUNET_PeerIdentity pid;
233
234   /**
235    * When did we last suggest an address to connect to for this peer?
236    */
237   struct GNUNET_TIME_Absolute last_suggestion;
238
239   /**
240    * Array where we sum up the bandwidth requests received indexed
241    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
242    */
243   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
244 };
245
246
247 /**
248  * Representation of a network (to be expanded...)
249  */
250 struct Network {
251   /**
252    * Total inbound quota
253    */
254   unsigned long long total_quota_in;
255
256   /**
257    * Total outbound quota
258    */
259   unsigned long long total_quota_out;
260
261   /**
262    * ATS network type
263    */
264   enum GNUNET_NetworkType type;
265 };
266
267
268 /**
269  * A handle for the proportional solver
270  */
271 struct SimpleHandle {
272   /**
273    * Our execution environment.
274    */
275   struct GNUNET_ATS_PluginEnvironment *env;
276
277   /**
278    * Information we track for each peer.
279    */
280   struct GNUNET_CONTAINER_MultiPeerMap *peers;
281
282   /**
283    * Handle to the peerstore service.
284    */
285   struct GNUNET_PEERSTORE_Handle *ps;
286
287   /**
288    * Array where we sum up the bandwidth requests received indexed
289    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums
290    * over all peers).
291    */
292   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
293
294   /**
295    * Information we track per network type (quotas).
296    */
297   struct Network networks[GNUNET_NT_COUNT];
298 };
299
300
301 /**
302  * Lookup peer in the peers map.
303  *
304  * @param h handle to look up in
305  * @param pid peer identity to look up by
306  * @return NULL for not found
307  */
308 struct Peer *
309 lookup_peer(struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid)
310 {
311   return GNUNET_CONTAINER_multipeermap_get(h->peers, pid);
312 }
313
314
315 /**
316  * Check if there is _any_ interesting information left we
317  * store about the peer in @a p.
318  *
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
321  */
322 static int
323 peer_test_dead(struct Peer *p)
324 {
325   for (enum GNUNET_MQ_PreferenceKind pk = 0; pk < GNUNET_MQ_PREFERENCE_COUNT;
326        pk++)
327     if (0 != p->bw_by_pk[pk])
328       return GNUNET_NO;
329   if (NULL != p->sh_head)
330     return GNUNET_NO;
331   return GNUNET_YES;
332 }
333
334
335 /**
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.
339  *
340  * @param hello[in,out] address suggestion to make
341  */
342 static void
343 suggest_hello(struct Hello *hello)
344 {
345   struct Peer *p = hello->peer;
346   struct SimpleHandle *h = p->h;
347
348   p->last_suggestion = hello->last_attempt = GNUNET_TIME_absolute_get();
349   hello->backoff =
350     GNUNET_TIME_randomized_backoff(hello->backoff,
351                                    GNUNET_TIME_absolute_get_remaining(
352                                      hello->expiration));
353   h->env->suggest_cb(h->env->cls, &p->pid, hello->address);
354 }
355
356
357 /**
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
364  * is to be made.
365  *
366  * In this case, we only calculate the time until we make the next
367  * suggestion.
368  *
369  * @param cls a `struct Peer`
370  */
371 static void
372 suggest_start_cb(void *cls)
373 {
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;
381   uint32_t sq;
382
383   /* count number of active sessions */
384   for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head; NULL != sh;
385        sh = sh->next)
386     num_sessions++;
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;
391   else
392     sq = UINT32_MAX;
393   xdelay =
394     GNUNET_TIME_randomized_backoff(GNUNET_TIME_relative_multiply(SUGGEST_FREQ,
395                                                                  sq),
396                                    GNUNET_TIME_UNIT_FOREVER_REL);
397   xnext = GNUNET_TIME_relative_to_absolute(xdelay);
398
399   p->task = NULL;
400   while (0 == delay.rel_value_us)
401     {
402       struct Hello *next;
403       struct GNUNET_TIME_Absolute xmax;
404
405       if (NULL != hello)
406         {
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);
411           hello = NULL;
412           hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
413         }
414       for (struct Hello *pos = p->h_head; NULL != pos; pos = next)
415         {
416           struct GNUNET_TIME_Absolute pt;
417
418           next = pos->next;
419           if (NULL != pos->sh)
420             continue;
421           if (0 ==
422               GNUNET_TIME_absolute_get_remaining(pos->expiration).rel_value_us)
423             {
424               /* expired, remove! */
425               GNUNET_CONTAINER_DLL_remove(p->h_head, p->h_tail, pos);
426               GNUNET_free(pos);
427               continue;
428             }
429           pt = GNUNET_TIME_absolute_add(pos->last_attempt, pos->backoff);
430           if ((NULL == hello) || (pt.abs_value_us < hpt.abs_value_us))
431             {
432               hello = pos;
433               hpt = pt;
434             }
435         }
436       if (NULL == hello)
437         return; /* no HELLOs that could still be tried */
438
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);
445     }
446   p->task = GNUNET_SCHEDULER_add_delayed(delay, &suggest_start_cb, p);
447 }
448
449
450 /**
451  * Function called by PEERSTORE for each matching record.
452  *
453  * @param cls closure with a `struct Peer`
454  * @param record peerstore record information
455  * @param emsg error message, or NULL if no errors
456  */
457 static void
458 watch_cb(void *cls,
459          const struct GNUNET_PEERSTORE_Record *record,
460          const char *emsg)
461 {
462   struct Peer *p = cls;
463   char *addr;
464   size_t alen;
465   enum GNUNET_NetworkType nt;
466   struct GNUNET_TIME_Absolute expiration;
467   struct Hello *hello;
468
469   if (0 != GNUNET_memcmp(&p->pid, &record->peer))
470     {
471       GNUNET_break(0);
472       return;
473     }
474   if (0 != strcmp(record->key, GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY))
475     {
476       GNUNET_break(0);
477       return;
478     }
479   addr = GNUNET_HELLO_extract_address(record->value,
480                                       record->value_size,
481                                       &p->pid,
482                                       &nt,
483                                       &expiration);
484   if (NULL == addr)
485     return; /* invalid hello, bad signature, other problem */
486   if (0 == GNUNET_TIME_absolute_get_remaining(expiration).rel_value_us)
487     {
488       /* expired, ignore */
489       GNUNET_free(addr);
490       return;
491     }
492   /* check if addr is already known */
493   for (struct Hello *he = p->h_head; NULL != he; he = he->next)
494     {
495       if (0 != strcmp(he->address, addr))
496         continue;
497       if (he->expiration.abs_value_us < expiration.abs_value_us)
498         {
499           he->expiration = expiration;
500           he->nt = nt;
501         }
502       GNUNET_free(addr);
503       return;
504     }
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;
510   hello->nt = nt;
511   hello->peer = p;
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;
516        sh = sh->next)
517     {
518       if ((NULL == sh->address) || (0 != strcmp(sh->address, addr)))
519         continue;
520       GNUNET_assert(NULL == sh->hello);
521       sh->hello = hello;
522       hello->sh = sh;
523       break;
524     }
525   GNUNET_free(addr);
526   if (NULL == p->task)
527     p->task = GNUNET_SCHEDULER_add_now(&suggest_start_cb, p);
528 }
529
530
531 /**
532  * Find or add peer if necessary.
533  *
534  * @param h our plugin handle
535  * @param pid the peer identity to add/look for
536  * @return a peer handle
537  */
538 static struct Peer *
539 peer_add(struct SimpleHandle *h, const struct GNUNET_PeerIdentity *pid)
540 {
541   struct Peer *p = lookup_peer(h, pid);
542
543   if (NULL != p)
544     return p;
545   p = GNUNET_new(struct Peer);
546   p->h = h;
547   p->pid = *pid;
548   p->wc = GNUNET_PEERSTORE_watch(h->ps,
549                                  "transport",
550                                  &p->pid,
551                                  GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY,
552                                  &watch_cb,
553                                  p);
554   GNUNET_assert(GNUNET_YES ==
555                 GNUNET_CONTAINER_multipeermap_put(
556                   h->peers,
557                   &p->pid,
558                   p,
559                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
560
561   return p;
562 }
563
564
565 /**
566  * Free the entry (and associated tasks) of peer @a p.
567  * Note that @a p must be dead already (see #peer_test_dead()).
568  *
569  * @param p the peer to free
570  */
571 static void
572 peer_free(struct Peer *p)
573 {
574   struct SimpleHandle *h = p->h;
575   struct Hello *hello;
576
577   GNUNET_assert(NULL == p->sh_head);
578   while (NULL != (hello = p->h_head))
579     {
580       GNUNET_CONTAINER_DLL_remove(p->h_head, p->h_tail, hello);
581       GNUNET_assert(NULL == hello->sh);
582       GNUNET_free(hello);
583     }
584   if (NULL != p->task)
585     {
586       GNUNET_SCHEDULER_cancel(p->task);
587       p->task = NULL;
588     }
589   if (NULL != p->wc)
590     {
591       GNUNET_PEERSTORE_watch_cancel(p->wc);
592       p->wc = NULL;
593     }
594   GNUNET_assert(GNUNET_YES ==
595                 GNUNET_CONTAINER_multipeermap_remove(h->peers, &p->pid, p));
596   GNUNET_free(p);
597 }
598
599
600 /**
601  * Check if the new allocation for @a sh is significantly different
602  * from the last one, and if so, tell transport.
603  *
604  * @param sh session handle to consider updating transport for
605  */
606 static void
607 consider_notify_transport(struct GNUNET_ATS_SessionHandle *sh)
608 {
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;
613   uint64_t sig_in;
614   uint64_t sig_out;
615   int64_t delta_in;
616   int64_t delta_out;
617
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) /
622            1000LL / 10;
623   sig_out = h->networks[nt].total_quota_out * (delay.rel_value_us / 1000LL) /
624             1000LL / 10;
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 */
628   if (delta_in < 0)
629     delta_in = -delta_in;
630   if (INT64_MIN == delta_in)
631     delta_in = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
632   if (delta_out < 0)
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,
647                       sh->session,
648                       &peer->pid,
649                       sh->bw_in,
650                       sh->bw_out);
651 }
652
653
654 /**
655  * Closure for #update_counters and #update_allocation.
656  */
657 struct Counters {
658   /**
659    * Plugin's state.
660    */
661   struct SimpleHandle *h;
662
663   /**
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.
668    */
669   uint64_t bw_out_by_nt[GNUNET_NT_COUNT];
670
671   /**
672    * Current bandwidth utilization for this network type.  We simply
673    * add the current goodput up (with some fairness considerations).
674    */
675   uint64_t bw_in_by_nt[GNUNET_NT_COUNT];
676
677   /**
678    * By how much do we have to scale (up or down) our expectations
679    * for outbound bandwidth?
680    */
681   double scale_out[GNUNET_NT_COUNT];
682
683   /**
684    * By how much do we have to scale (up or down) our expectations
685    * for inbound bandwidth?
686    */
687   double scale_in[GNUNET_NT_COUNT];
688 };
689
690
691 /**
692  * Function used to iterate over all peers and collect
693  * counter data.
694  *
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)
699  */
700 static int
701 update_counters(void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
702 {
703   struct Counters *c = cls;
704   struct Peer *peer = value;
705   struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT];
706
707   (void)pid;
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;
712        sh = sh->next)
713     {
714       enum GNUNET_NetworkType nt = sh->data->prop.nt;
715
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;
721            pk++)
722         {
723           /* General rule: always prefer smaller distance if possible,
724              otherwise decide by pk: */
725           switch (pk)
726             {
727             case GNUNET_MQ_PREFERENCE_NONE:
728               break;
729
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))
738                 best[pk] = sh;
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))
747                 best[pk] = sh;
748               break;
749
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)))
756                 best[pk] = sh;
757               break;
758
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))
766                 best[pk] = sh;
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))
776                 best[pk] = sh;
777               break;
778             }
779         }
780     }
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;
786        pk++)
787     {
788       const struct GNUNET_ATS_SessionData *data = best[pk]->data;
789       enum GNUNET_NetworkType nt;
790
791       GNUNET_assert(NULL != data);
792       nt = data->prop.nt;
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);
797     }
798   return GNUNET_YES;
799 }
800
801
802 /**
803  * Function used to iterate over all peers and collect
804  * counter data.
805  *
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)
810  */
811 static int
812 update_allocation(void *cls,
813                   const struct GNUNET_PeerIdentity *pid,
814                   void *value)
815 {
816   struct Counters *c = cls;
817   struct Peer *peer = value;
818
819   (void)pid;
820   for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head; NULL != sh;
821        sh = sh->next)
822     {
823       enum GNUNET_NetworkType nt = sh->data->prop.nt;
824
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);
828     }
829   return GNUNET_YES;
830 }
831
832
833 /**
834  * The world changed, recalculate our allocations.
835  */
836 static void
837 update(struct SimpleHandle *h)
838 {
839   struct Counters cnt = { .h = h };
840
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++)
845     {
846       cnt.scale_out[nt] =
847         1.0 * cnt.bw_out_by_nt[nt] / h->networks[nt].total_quota_out;
848       cnt.scale_in[nt] =
849         1.0 * cnt.bw_in_by_nt[nt] / h->networks[nt].total_quota_in;
850     }
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);
854 }
855
856
857 /**
858  * The plugin should begin to respect a new preference.
859  *
860  * @param cls the closure
861  * @param pref the preference to add
862  * @return plugin's internal representation, or NULL
863  */
864 static struct GNUNET_ATS_PreferenceHandle *
865 simple_preference_add(void *cls, const struct GNUNET_ATS_Preference *pref)
866 {
867   struct SimpleHandle *h = cls;
868   struct Peer *p = peer_add(h, &pref->peer);
869
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__);
873   update(h);
874   return NULL;
875 }
876
877
878 /**
879  * The plugin should end respecting a preference.
880  *
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
885  */
886 static void
887 simple_preference_del(void *cls,
888                       struct GNUNET_ATS_PreferenceHandle *ph,
889                       const struct GNUNET_ATS_Preference *pref)
890 {
891   struct SimpleHandle *h = cls;
892   struct Peer *p = lookup_peer(h, &pref->peer);
893
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)))
899     peer_free(p);
900   update(h);
901 }
902
903
904 /**
905  * Transport established a new session with performance
906  * characteristics given in @a data.
907  *
908  * @param cls closure
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
912  */
913 static struct GNUNET_ATS_SessionHandle *
914 simple_session_add(void *cls,
915                    const struct GNUNET_ATS_SessionData *data,
916                    const char *address)
917 {
918   struct SimpleHandle *h = cls;
919   struct Peer *p = peer_add(h, &data->peer);
920   struct Hello *hello;
921   size_t alen;
922   struct GNUNET_ATS_SessionHandle *sh;
923
924   /* setup session handle */
925   GNUNET_assert(NULL != data);
926   if (NULL == address)
927     alen = 0;
928   else
929     alen = strlen(address) + 1;
930   sh = GNUNET_malloc(sizeof(struct GNUNET_ATS_SessionHandle) + alen);
931   sh->peer = p;
932   sh->session = data->session;
933   sh->data = data;
934   if (NULL == address)
935     {
936       sh->address = NULL;
937     }
938   else
939     {
940       memcpy(&sh[1], address, alen);
941       sh->address = (const char *)&sh[1];
942     }
943   GNUNET_CONTAINER_DLL_insert(p->sh_head, p->sh_tail, sh);
944   if (NULL != address)
945     {
946       /* match HELLO */
947       hello = p->h_head;
948       while ((NULL != hello) && (0 != strcmp(address, hello->address)))
949         hello = hello->next;
950       if (NULL != hello)
951         {
952           hello->sh = sh;
953           hello->backoff = GNUNET_TIME_UNIT_ZERO;
954           sh->hello = hello;
955         }
956     }
957   update(h);
958   return sh;
959 }
960
961
962 /**
963  * @a data changed for a given @a sh, solver should consider
964  * the updated performance characteristics.
965  *
966  * @param cls closure
967  * @param sh session this is about
968  * @param data performance characteristics of @a sh
969  */
970 static void
971 simple_session_update(void *cls,
972                       struct GNUNET_ATS_SessionHandle *sh,
973                       const struct GNUNET_ATS_SessionData *data)
974 {
975   struct SimpleHandle *h = cls;
976
977   GNUNET_assert(NULL != data);
978   sh->data = data; /* this statement should not really do anything... */
979   update(h);
980 }
981
982
983 /**
984  * A session went away. Solver should update accordingly.
985  *
986  * @param cls closure
987  * @param sh session this is about
988  * @param data (last) performance characteristics of @a sh
989  */
990 static void
991 simple_session_del(void *cls,
992                    struct GNUNET_ATS_SessionHandle *sh,
993                    const struct GNUNET_ATS_SessionData *data)
994 {
995   struct SimpleHandle *h = cls;
996   struct Peer *p = sh->peer;
997   struct Hello *hello = sh->hello;
998
999   /* clean up sh */
1000   GNUNET_CONTAINER_DLL_remove(p->sh_head, p->sh_tail, sh);
1001   if (NULL != hello)
1002     {
1003       GNUNET_assert(sh == hello->sh);
1004       hello->sh = NULL;
1005       /* session went down, if necessary restart suggesting
1006          addresses */
1007       if (NULL == p->task)
1008         p->task = GNUNET_SCHEDULER_add_now(&suggest_start_cb, p);
1009     }
1010   GNUNET_free(sh);
1011   /* del peer if otherwise dead */
1012   if ((NULL == p->sh_head) && (GNUNET_YES == peer_test_dead(p)))
1013     peer_free(p);
1014   update(h);
1015 }
1016
1017
1018 #include "plugin_ats2_common.c"
1019
1020
1021 /**
1022  * Function invoked when the plugin is loaded.
1023  *
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
1027  */
1028 void *
1029 libgnunet_plugin_ats2_simple_init(void *cls)
1030 {
1031   static struct GNUNET_ATS_SolverFunctions sf;
1032   struct GNUNET_ATS_PluginEnvironment *env = cls;
1033   struct SimpleHandle *s;
1034
1035   s = GNUNET_new(struct SimpleHandle);
1036   s->env = env;
1037   s->peers = GNUNET_CONTAINER_multipeermap_create(128, GNUNET_YES);
1038   s->ps = GNUNET_PEERSTORE_connect(env->cfg);
1039   sf.cls = s;
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++)
1046     {
1047       const char *name = GNUNET_NT_to_string(nt);
1048
1049       if (NULL == name)
1050         {
1051           GNUNET_break(0);
1052           break;
1053         }
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;
1057     }
1058   return &sf;
1059 }
1060
1061
1062 /**
1063  * Function used to unload the plugin.
1064  *
1065  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1066  */
1067 void *
1068 libgnunet_plugin_ats2_simple_done(void *cls)
1069 {
1070   struct GNUNET_ATS_SolverFunctions *sf = cls;
1071   struct SimpleHandle *s = sf->cls;
1072
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);
1076   GNUNET_free(s);
1077   return NULL;
1078 }
1079
1080
1081 /* end of plugin_ats2_simple.c */