misc tng related cleanup
[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
74   /**
75    * Kept in a DLL.
76    */
77   struct Hello *next;
78
79   /**
80    * Kept in a DLL.
81    */
82   struct Hello *prev;
83
84   /**
85    * Peer this hello belongs to.
86    */
87   struct Peer *peer;
88
89   /**
90    * The address we could try.
91    */
92   const char *address;
93
94   /**
95    * Is a session with this address already up?
96    * If not, set to NULL.
97    */
98   struct GNUNET_ATS_SessionHandle *sh;
99
100   /**
101    * When does the HELLO expire?
102    */
103   struct GNUNET_TIME_Absolute expiration;
104
105   /**
106    * When did we try it last?
107    */
108   struct GNUNET_TIME_Absolute last_attempt;
109
110   /**
111    * Current exponential backoff value.
112    */
113   struct GNUNET_TIME_Relative backoff;
114
115   /**
116    * Type of the network for this HELLO.
117    */
118   enum GNUNET_NetworkType nt;
119
120 };
121
122
123 /**
124  * Internal representation of a session by the plugin.
125  * (If desired, plugin may just use NULL.)
126  */
127 struct GNUNET_ATS_SessionHandle
128 {
129
130   /**
131    * Kept in DLL per peer.
132    */
133   struct GNUNET_ATS_SessionHandle *next;
134
135   /**
136    * Kept in DLL per peer.
137    */
138   struct GNUNET_ATS_SessionHandle *prev;
139
140   /**
141    * The session in the main ATS service.
142    */
143   struct GNUNET_ATS_Session *session;
144
145   /**
146    * Current performance data for this @e session
147    */
148   const struct GNUNET_ATS_SessionData *data;
149
150   /**
151    * Hello matching this session, or NULL for none.
152    */
153   struct Hello *hello;
154
155   /**
156    * Peer this session is for.
157    */
158   struct Peer *peer;
159
160   /**
161    * Address used by this session (largely for debugging).
162    */
163   const char *address;
164
165   /**
166    * When did we last update transport about the allocation?
167    * Used to dampen the frequency of updates.
168    */
169   struct GNUNET_TIME_Absolute last_allocation;
170
171   /**
172    * Last BW-in allocation given to the transport service.
173    */
174   struct GNUNET_BANDWIDTH_Value32NBO bw_in;
175
176   /**
177    * Last BW-out allocation given to the transport service.
178    */
179   struct GNUNET_BANDWIDTH_Value32NBO bw_out;
180
181   /**
182    * New BW-in allocation given to the transport service.
183    */
184   uint64_t target_in;
185
186   /**
187    * New BW-out allocation given to the transport service.
188    */
189   uint64_t target_out;
190
191 };
192
193
194 /**
195  * Information about preferences and sessions we track
196  * per peer.
197  */
198 struct Peer
199 {
200
201   /**
202    * Kept in DLL per peer.
203    */
204   struct GNUNET_ATS_SessionHandle *sh_head;
205
206   /**
207    * Kept in DLL per peer.
208    */
209   struct GNUNET_ATS_SessionHandle *sh_tail;
210
211   /**
212    * Kept in a DLL.
213    */
214   struct Hello *h_head;
215
216   /**
217    * Kept in a DLL.
218    */
219   struct Hello *h_tail;
220
221   /**
222    * The handle for the proportional solver
223    */
224   struct SimpleHandle *h;
225
226   /**
227    * Watch context where we are currently looking for HELLOs for
228    * this peer.
229    */
230   struct GNUNET_PEERSTORE_WatchContext *wc;
231
232   /**
233    * Task used to try again to suggest an address for this peer.
234    */
235   struct GNUNET_SCHEDULER_Task *task;
236
237   /**
238    * Which peer is this for?
239    */
240   struct GNUNET_PeerIdentity pid;
241
242   /**
243    * When did we last suggest an address to connect to for this peer?
244    */
245   struct GNUNET_TIME_Absolute last_suggestion;
246
247   /**
248    * Array where we sum up the bandwidth requests received indexed
249    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
250    */
251   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
252
253 };
254
255
256 /**
257  * Representation of a network (to be expanded...)
258  */
259 struct Network
260 {
261
262   /**
263    * Total inbound quota
264    */
265   unsigned long long total_quota_in;
266
267   /**
268    * Total outbound quota
269    */
270   unsigned long long total_quota_out;
271
272   /**
273    * ATS network type
274    */
275   enum GNUNET_NetworkType type;
276
277 };
278
279
280 /**
281  * A handle for the proportional solver
282  */
283 struct SimpleHandle
284 {
285
286   /**
287    * Our execution environment.
288    */
289   struct GNUNET_ATS_PluginEnvironment *env;
290
291   /**
292    * Information we track for each peer.
293    */
294   struct GNUNET_CONTAINER_MultiPeerMap *peers;
295
296   /**
297    * Handle to the peerstore service.
298    */
299   struct GNUNET_PEERSTORE_Handle *ps;
300
301   /**
302    * Array where we sum up the bandwidth requests received indexed
303    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums
304    * over all peers).
305    */
306   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
307
308   /**
309    * Information we track per network type (quotas).
310    */
311   struct Network networks[GNUNET_NT_COUNT];
312
313 };
314
315
316 /**
317  * Lookup peer in the peers map.
318  *
319  * @param h handle to look up in
320  * @param pid peer identity to look up by
321  * @return NULL for not found
322  */
323 struct Peer *
324 lookup_peer (struct SimpleHandle *h,
325              const struct GNUNET_PeerIdentity *pid)
326 {
327   return GNUNET_CONTAINER_multipeermap_get (h->peers,
328                                             pid);
329 }
330
331
332 /**
333  * Check if there is _any_ interesting information left we
334  * store about the peer in @a p.
335  *
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
338  */
339 static int
340 peer_test_dead (struct Peer *p)
341 {
342   for (enum GNUNET_MQ_PreferenceKind pk = 0;
343        pk < GNUNET_MQ_PREFERENCE_COUNT;
344        pk++)
345     if (0 != p->bw_by_pk[pk])
346       return GNUNET_NO;
347   if (NULL != p->sh_head)
348     return GNUNET_NO;
349   return GNUNET_YES;
350 }
351
352
353 /**
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.
357  *
358  * @param hello[in,out] address suggestion to make
359  */
360 static void
361 suggest_hello (struct Hello *hello)
362 {
363   struct Peer *p = hello->peer;
364   struct SimpleHandle *h = p->h;
365
366   p->last_suggestion
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,
372                       &p->pid,
373                       hello->address);
374 }
375
376
377 /**
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
384  * is to be made.
385  *
386  * In this case, we only calculate the time until we make the next
387  * suggestion.
388  *
389  * @param cls a `struct Peer`
390  */
391 static void
392 suggest_start_cb (void *cls)
393 {
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;
401   uint32_t sq;
402
403   /* count number of active sessions */
404   for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
405        NULL != sh;
406        sh = sh->next)
407     num_sessions++;
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;
412   else
413     sq = UINT32_MAX;
414   xdelay = GNUNET_TIME_randomized_backoff (GNUNET_TIME_relative_multiply (SUGGEST_FREQ,
415                                                                           sq),
416                                            GNUNET_TIME_UNIT_FOREVER_REL);
417   xnext = GNUNET_TIME_relative_to_absolute (xdelay);
418
419   p->task = NULL;
420   while (0 == delay.rel_value_us)
421   {
422     struct Hello *next;
423     struct GNUNET_TIME_Absolute xmax;
424
425     if (NULL != hello)
426     {
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);
431       hello = NULL;
432       hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
433     }
434     for (struct Hello *pos = p->h_head; NULL != pos; pos = next)
435     {
436       struct GNUNET_TIME_Absolute pt;
437
438       next = pos->next;
439       if (NULL != pos->sh)
440         continue;
441       if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).rel_value_us)
442       {
443         /* expired, remove! */
444         GNUNET_CONTAINER_DLL_remove (p->h_head,
445                                      p->h_tail,
446                                      pos);
447         GNUNET_free (pos);
448         continue;
449       }
450       pt = GNUNET_TIME_absolute_add (pos->last_attempt,
451                                      pos->backoff);
452       if ( (NULL == hello) ||
453            (pt.abs_value_us < hpt.abs_value_us) )
454       {
455         hello = pos;
456         hpt = pt;
457       }
458     }
459     if (NULL == hello)
460       return; /* no HELLOs that could still be tried */
461
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,
467                                      xnext);
468     delay = GNUNET_TIME_absolute_get_remaining (xmax);
469   }
470   p->task = GNUNET_SCHEDULER_add_delayed (delay,
471                                           &suggest_start_cb,
472                                           p);
473 }
474
475
476 /**
477  * Function called by PEERSTORE for each matching record.
478  *
479  * @param cls closure with a `struct Peer`
480  * @param record peerstore record information
481  * @param emsg error message, or NULL if no errors
482  */
483 static void
484 watch_cb (void *cls,
485           const struct GNUNET_PEERSTORE_Record *record,
486           const char *emsg)
487 {
488   struct Peer *p = cls;
489   char *addr;
490   size_t alen;
491   enum GNUNET_NetworkType nt;
492   struct GNUNET_TIME_Absolute expiration;
493   struct Hello *hello;
494
495   if (0 != memcmp (&p->pid,
496                    &record->peer,
497                    sizeof (struct GNUNET_PeerIdentity)))
498   {
499     GNUNET_break (0);
500     return;
501   }
502   if (0 != strcmp (record->key,
503                    GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY))
504   {
505     GNUNET_break (0);
506     return;
507   }
508   addr = GNUNET_HELLO_extract_address (record->value,
509                                        record->value_size,
510                                        &p->pid,
511                                        &nt,
512                                        &expiration);
513   if (NULL == addr)
514     return; /* invalid hello, bad signature, other problem */
515   if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
516   {
517     /* expired, ignore */
518     GNUNET_free (addr);
519     return;
520   }
521   /* check if addr is already known */
522   for (struct Hello *he = p->h_head;
523        NULL != he;
524        he = he->next)
525   {
526     if (0 != strcmp (he->address,
527                      addr))
528       continue;
529     if (he->expiration.abs_value_us < expiration.abs_value_us)
530     {
531       he->expiration = expiration;
532       he->nt = nt;
533     }
534     GNUNET_free (addr);
535     return;
536   }
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;
542   hello->nt = nt;
543   hello->peer = p;
544   memcpy (&hello[1],
545           addr,
546           alen);
547   GNUNET_free (addr);
548   GNUNET_CONTAINER_DLL_insert (p->h_head,
549                                p->h_tail,
550                                hello);
551   /* check if sh for this HELLO already exists */
552   for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
553        NULL != sh;
554        sh = sh->next)
555   {
556     if ( (NULL == sh->address) ||
557          (0 != strcmp (sh->address,
558                        addr)) )
559       continue;
560     GNUNET_assert (NULL == sh->hello);
561     sh->hello = hello;
562     hello->sh = sh;
563     break;
564   }
565   if (NULL == p->task)
566     p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
567                                         p);
568 }
569
570
571 /**
572  * Find or add peer if necessary.
573  *
574  * @param h our plugin handle
575  * @param pid the peer identity to add/look for
576  * @return a peer handle
577  */
578 static struct Peer *
579 peer_add (struct SimpleHandle *h,
580           const struct GNUNET_PeerIdentity *pid)
581 {
582   struct Peer *p = lookup_peer (h,
583                                 pid);
584
585   if (NULL != p)
586     return p;
587   p = GNUNET_new (struct Peer);
588   p->h = h;
589   p->pid = *pid;
590   p->wc = GNUNET_PEERSTORE_watch (h->ps,
591                                   "transport",
592                                   &p->pid,
593                                   GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY,
594                                   &watch_cb,
595                                   p);
596   GNUNET_assert (GNUNET_YES ==
597                  GNUNET_CONTAINER_multipeermap_put (h->peers,
598                                                     &p->pid,
599                                                     p,
600                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
601
602   return p;
603 }
604
605
606 /**
607  * Free the entry (and associated tasks) of peer @a p.
608  * Note that @a p must be dead already (see #peer_test_dead()).
609  *
610  * @param p the peer to free
611  */
612 static void
613 peer_free (struct Peer *p)
614 {
615   struct SimpleHandle *h = p->h;
616   struct Hello *hello;
617
618   GNUNET_assert (NULL == p->sh_head);
619   while (NULL != (hello = p->h_head))
620   {
621     GNUNET_CONTAINER_DLL_remove (p->h_head,
622                                  p->h_tail,
623                                  hello);
624     GNUNET_assert (NULL == hello->sh);
625     GNUNET_free (hello);
626   }
627   if (NULL != p->task)
628   {
629     GNUNET_SCHEDULER_cancel (p->task);
630     p->task = NULL;
631   }
632   if (NULL != p->wc)
633   {
634     GNUNET_PEERSTORE_watch_cancel (p->wc);
635     p->wc = NULL;
636   }
637   GNUNET_assert (GNUNET_YES ==
638                  GNUNET_CONTAINER_multipeermap_remove (h->peers,
639                                                        &p->pid,
640                                                        p));
641   GNUNET_free (p);
642 }
643
644
645 /**
646  * Check if the new allocation for @a sh is significantly different
647  * from the last one, and if so, tell transport.
648  *
649  * @param sh session handle to consider updating transport for
650  */
651 static void
652 consider_notify_transport (struct GNUNET_ATS_SessionHandle *sh)
653 {
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;
658   uint64_t sig_in;
659   uint64_t sig_out;
660   int64_t delta_in;
661   int64_t delta_out;
662
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 */
666   sig_in
667     = h->networks[nt].total_quota_in * (delay.rel_value_us / 1000LL) / 1000LL / 10;
668   sig_out
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 */
673   if (delta_in < 0)
674     delta_in = - delta_in;
675   if (INT64_MIN == delta_in)
676     delta_in = INT64_MAX;  /* Handle corner case: INT_MIN == - INT_MIN */
677   if (delta_out < 0)
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,
693                        sh->session,
694                        &peer->pid,
695                        sh->bw_in,
696                        sh->bw_out);
697 }
698
699
700 /**
701  * Closure for #update_counters and #update_allocation.
702  */
703 struct Counters
704 {
705   /**
706    * Plugin's state.
707    */
708   struct SimpleHandle *h;
709
710   /**
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.
715    */
716   uint64_t bw_out_by_nt[GNUNET_NT_COUNT];
717
718   /**
719    * Current bandwidth utilization for this network type.  We simply
720    * add the current goodput up (with some fairness considerations).
721    */
722   uint64_t bw_in_by_nt[GNUNET_NT_COUNT];
723
724   /**
725    * By how much do we have to scale (up or down) our expectations
726    * for outbound bandwidth?
727    */
728   double scale_out[GNUNET_NT_COUNT];
729
730   /**
731    * By how much do we have to scale (up or down) our expectations
732    * for inbound bandwidth?
733    */
734   double scale_in[GNUNET_NT_COUNT];
735
736 };
737
738
739 /**
740  * Function used to iterate over all peers and collect
741  * counter data.
742  *
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)
747  */
748 static int
749 update_counters (void *cls,
750                  const struct GNUNET_PeerIdentity *pid,
751                  void *value)
752 {
753   struct Counters *c = cls;
754   struct Peer *peer = value;
755   struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT];
756
757   (void) pid;
758   if (NULL == peer->sh_head)
759     return GNUNET_YES; /* no available session, cannot allocate bandwidth */
760   memset (best,
761           0,
762           sizeof (best));
763   for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
764        NULL != sh;
765        sh = sh->next)
766   {
767     enum GNUNET_NetworkType nt = sh->data->prop.nt;
768
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;
775          pk++)
776     {
777       /* General rule: always prefer smaller distance if possible,
778          otherwise decide by pk: */
779       switch (pk) {
780       case GNUNET_MQ_PREFERENCE_NONE:
781         break;
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) )
789           best[pk] = sh;
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) )
797           best[pk] = sh;
798         break;
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) ) )
805           best[pk] = sh;
806         break;
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) )
813           best[pk] = sh;
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) )
821           best[pk] = sh;
822         break;
823       }
824     }
825   }
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;
830        pk++)
831   {
832     const struct GNUNET_ATS_SessionData *data = best[pk]->data;
833     enum GNUNET_NetworkType nt;
834
835     GNUNET_assert (NULL != data);
836     nt = data->prop.nt;
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);
840   }
841   return GNUNET_YES;
842 }
843
844
845 /**
846  * Function used to iterate over all peers and collect
847  * counter data.
848  *
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)
853  */
854 static int
855 update_allocation (void *cls,
856                    const struct GNUNET_PeerIdentity *pid,
857                    void *value)
858 {
859   struct Counters *c = cls;
860   struct Peer *peer = value;
861
862   (void) pid;
863   for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
864        NULL != sh;
865        sh = sh->next)
866   {
867     enum GNUNET_NetworkType nt = sh->data->prop.nt;
868
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);
872   }
873   return GNUNET_YES;
874 }
875
876
877 /**
878  * The world changed, recalculate our allocations.
879  */
880 static void
881 update (struct SimpleHandle *h)
882 {
883   struct Counters cnt = {
884     .h = h
885   };
886
887   GNUNET_CONTAINER_multipeermap_iterate (h->peers,
888                                          &update_counters,
889                                          &cnt);
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;
894        nt++)
895   {
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;
898   }
899   /* recalculate allocations, considering scaling factor, and
900      update transport if the change is significant */
901   GNUNET_CONTAINER_multipeermap_iterate (h->peers,
902                                          &update_allocation,
903                                          &cnt);
904 }
905
906
907 /**
908  * The plugin should begin to respect a new preference.
909  *
910  * @param cls the closure
911  * @param pref the preference to add
912  * @return plugin's internal representation, or NULL
913  */
914 static struct GNUNET_ATS_PreferenceHandle *
915 simple_preference_add (void *cls,
916                        const struct GNUNET_ATS_Preference *pref)
917 {
918   struct SimpleHandle *h = cls;
919   struct Peer *p = peer_add (h,
920                              &pref->peer);
921
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__);
925   update (h);
926   return NULL;
927 }
928
929
930 /**
931  * The plugin should end respecting a preference.
932  *
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
937  */
938 static void
939 simple_preference_del (void *cls,
940                        struct GNUNET_ATS_PreferenceHandle *ph,
941                        const struct GNUNET_ATS_Preference *pref)
942 {
943   struct SimpleHandle *h = cls;
944   struct Peer *p = lookup_peer (h,
945                                 &pref->peer);
946
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)) )
953     peer_free (p);
954   update (h);
955 }
956
957
958 /**
959  * Transport established a new session with performance
960  * characteristics given in @a data.
961  *
962  * @param cls closure
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
966  */
967 static struct GNUNET_ATS_SessionHandle *
968 simple_session_add (void *cls,
969                     const struct GNUNET_ATS_SessionData *data,
970                     const char *address)
971 {
972   struct SimpleHandle *h = cls;
973   struct Peer *p = peer_add (h,
974                              &data->peer);
975   struct Hello *hello;
976   size_t alen;
977   struct GNUNET_ATS_SessionHandle *sh;
978
979   /* setup session handle */
980   GNUNET_assert (NULL != data);
981   if (NULL == address)
982     alen = 0;
983   else
984     alen = strlen (address) + 1;
985   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionHandle) + alen);
986   sh->peer = p;
987   sh->session = data->session;
988   sh->data = data;
989   if (NULL == address)
990   {
991     sh->address = NULL;
992   }
993   else
994   {
995     memcpy (&sh[1],
996             address,
997             alen);
998     sh->address = (const char *) &sh[1];
999   }
1000   GNUNET_CONTAINER_DLL_insert (p->sh_head,
1001                                p->sh_tail,
1002                                sh);
1003   /* match HELLO */
1004   hello = p->h_head;
1005   while ( (NULL != hello) &&
1006           (0 != strcmp (address,
1007                         hello->address)) )
1008     hello = hello->next;
1009   if (NULL != hello)
1010   {
1011     hello->sh = sh;
1012     hello->backoff = GNUNET_TIME_UNIT_ZERO;
1013     sh->hello = hello;
1014   }
1015   update (h);
1016   return sh;
1017 }
1018
1019
1020 /**
1021  * @a data changed for a given @a sh, solver should consider
1022  * the updated performance characteristics.
1023  *
1024  * @param cls closure
1025  * @param sh session this is about
1026  * @param data performance characteristics of @a sh
1027  */
1028 static void
1029 simple_session_update (void *cls,
1030                        struct GNUNET_ATS_SessionHandle *sh,
1031                        const struct GNUNET_ATS_SessionData *data)
1032 {
1033   struct SimpleHandle *h = cls;
1034
1035   GNUNET_assert (NULL != data);
1036   sh->data = data; /* this statement should not really do anything... */
1037   update (h);
1038 }
1039
1040
1041 /**
1042  * A session went away. Solver should update accordingly.
1043  *
1044  * @param cls closure
1045  * @param sh session this is about
1046  * @param data (last) performance characteristics of @a sh
1047  */
1048 static void
1049 simple_session_del (void *cls,
1050                     struct GNUNET_ATS_SessionHandle *sh,
1051                     const struct GNUNET_ATS_SessionData *data)
1052 {
1053   struct SimpleHandle *h = cls;
1054   struct Peer *p = sh->peer;
1055   struct Hello *hello = sh->hello;
1056
1057   /* clean up sh */
1058   GNUNET_CONTAINER_DLL_remove (p->sh_head,
1059                                p->sh_tail,
1060                                sh);
1061   if (NULL != hello)
1062   {
1063     GNUNET_assert (sh == hello->sh);
1064     hello->sh = NULL;
1065     /* session went down, if necessary restart suggesting
1066        addresses */
1067     if (NULL == p->task)
1068       p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
1069                                           p);
1070   }
1071   GNUNET_free (sh);
1072   /* del peer if otherwise dead */
1073   if ( (NULL == p->sh_head) &&
1074        (GNUNET_YES == peer_test_dead (p)) )
1075     peer_free (p);
1076   update (h);
1077 }
1078
1079
1080 #include "plugin_ats2_common.c"
1081
1082
1083 /**
1084  * Function invoked when the plugin is loaded.
1085  *
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
1089  */
1090 void *
1091 libgnunet_plugin_ats2_simple_init (void *cls)
1092 {
1093   static struct GNUNET_ATS_SolverFunctions sf;
1094   struct GNUNET_ATS_PluginEnvironment *env = cls;
1095   struct SimpleHandle *s;
1096
1097   s = GNUNET_new (struct SimpleHandle);
1098   s->env = env;
1099   s->peers = GNUNET_CONTAINER_multipeermap_create (128,
1100                                                    GNUNET_YES);
1101   s->ps = GNUNET_PEERSTORE_connect (env->cfg);
1102   sf.cls = s;
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;
1110        nt++)
1111   {
1112     const char *name = GNUNET_NT_to_string (nt);
1113
1114     if (NULL == name)
1115     {
1116       GNUNET_break (0);
1117       break;
1118     }
1119     get_quota (env->cfg,
1120                name,
1121                "IN",
1122                &s->networks[nt].total_quota_in);
1123     get_quota (env->cfg,
1124                name,
1125                "OUT",
1126                &s->networks[nt].total_quota_out);
1127     s->networks[nt].type = nt;
1128   }
1129   return &sf;
1130 }
1131
1132
1133 /**
1134  * Function used to unload the plugin.
1135  *
1136  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1137  */
1138 void *
1139 libgnunet_plugin_ats2_simple_done (void *cls)
1140 {
1141   struct GNUNET_ATS_SolverFunctions *sf = cls;
1142   struct SimpleHandle *s = sf->cls;
1143
1144   GNUNET_break (0 ==
1145                 GNUNET_CONTAINER_multipeermap_size (s->peers));
1146   GNUNET_CONTAINER_multipeermap_destroy (s->peers);
1147   GNUNET_PEERSTORE_disconnect (s->ps,
1148                                GNUNET_NO);
1149   GNUNET_free (s);
1150   return NULL;
1151 }
1152
1153
1154 /* end of plugin_ats2_simple.c */