finish first draft of ats2 simple plugin (untested)
[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 /**
19  * @file ats/plugin_ats2_simple.c
20  * @brief ATS simple solver
21  * @author Matthias Wachs
22  * @author Christian Grothoff
23  *
24  * TODO:
25  * - needs testing
26  */
27 #include "platform.h"
28 #include "gnunet_ats_plugin_new.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_peerstore_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "ats-simple",__VA_ARGS__)
33
34
35 /**
36  * Base frequency at which we suggest addresses to transport.
37  * Multiplied by the square of the number of active connections
38  * (and randomized) to calculate the actual frequency at which
39  * we will suggest addresses to the transport.  Furthermore, each
40  * address is also bounded by an exponential back-off.
41  */
42 #define SUGGEST_FREQ GNUNET_TIME_UNIT_SECONDS
43
44 /**
45  * What is the minimum bandwidth we always try to allocate for
46  * any session that is up? (May still be scaled down lower if
47  * the number of sessions is so high that the total bandwidth
48  * is insufficient to allow for this value to be granted.)
49  */
50 #define MIN_BANDWIDTH_PER_SESSION 1024
51
52
53 /**
54  * A handle for the proportional solver
55  */
56 struct SimpleHandle;
57
58
59 /**
60  * Information about preferences and sessions we track
61  * per peer.
62  */
63 struct Peer;
64
65
66 /**
67  * Entry in list of addresses we could try per peer.
68  */
69 struct Hello
70 {
71
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 /**
122  * Internal representation of a session by the plugin.
123  * (If desired, plugin may just use NULL.)
124  */
125 struct GNUNET_ATS_SessionHandle
126 {
127
128   /**
129    * Kept in DLL per peer.
130    */
131   struct GNUNET_ATS_SessionHandle *next;
132
133   /**
134    * Kept in DLL per peer.
135    */
136   struct GNUNET_ATS_SessionHandle *prev;
137
138   /**
139    * The session in the main ATS service.
140    */
141   struct GNUNET_ATS_Session *session;
142
143   /**
144    * Current performance data for this @e session
145    */
146   const struct GNUNET_ATS_SessionData *data;
147
148   /**
149    * Hello matching this session, or NULL for none.
150    */
151   struct Hello *hello;
152
153   /**
154    * Peer this session is for.
155    */
156   struct Peer *peer;
157
158   /**
159    * Address used by this session (largely for debugging).
160    */
161   const char *address;
162
163   /**
164    * When did we last update transport about the allocation?
165    * Used to dampen the frequency of updates.
166    */
167   struct GNUNET_TIME_Absolute last_allocation;
168
169   /**
170    * Last BW-in allocation given to the transport service.
171    */
172   struct GNUNET_BANDWIDTH_Value32NBO bw_in;
173
174   /**
175    * Last BW-out allocation given to the transport service.
176    */
177   struct GNUNET_BANDWIDTH_Value32NBO bw_out;
178
179   /**
180    * New BW-in allocation given to the transport service.
181    */
182   uint64_t target_in;
183
184   /**
185    * New BW-out allocation given to the transport service.
186    */
187   uint64_t target_out;
188
189 };
190
191
192 /**
193  * Information about preferences and sessions we track
194  * per peer.
195  */
196 struct Peer
197 {
198
199   /**
200    * Kept in DLL per peer.
201    */
202   struct GNUNET_ATS_SessionHandle *sh_head;
203
204   /**
205    * Kept in DLL per peer.
206    */
207   struct GNUNET_ATS_SessionHandle *sh_tail;
208
209   /**
210    * Kept in a DLL.
211    */
212   struct Hello *h_head;
213
214   /**
215    * Kept in a DLL.
216    */
217   struct Hello *h_tail;
218
219   /**
220    * The handle for the proportional solver
221    */
222   struct SimpleHandle *h;
223
224   /**
225    * Watch context where we are currently looking for HELLOs for
226    * this peer.
227    */
228   struct GNUNET_PEERSTORE_WatchContext *wc;
229
230   /**
231    * Task used to try again to suggest an address for this peer.
232    */
233   struct GNUNET_SCHEDULER_Task *task;
234
235   /**
236    * Which peer is this for?
237    */
238   struct GNUNET_PeerIdentity pid;
239
240   /**
241    * When did we last suggest an address to connect to for this peer?
242    */
243   struct GNUNET_TIME_Absolute last_suggestion;
244
245   /**
246    * Array where we sum up the bandwidth requests received indexed
247    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
248    */
249   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
250
251 };
252
253
254 /**
255  * Representation of a network (to be expanded...)
256  */
257 struct Network
258 {
259
260   /**
261    * Total inbound quota
262    */
263   unsigned long long total_quota_in;
264
265   /**
266    * Total outbound quota
267    */
268   unsigned long long total_quota_out;
269
270   /**
271    * ATS network type
272    */
273   enum GNUNET_NetworkType type;
274
275 };
276
277
278 /**
279  * A handle for the proportional solver
280  */
281 struct SimpleHandle
282 {
283
284   /**
285    * Our execution environment.
286    */
287   struct GNUNET_ATS_PluginEnvironment *env;
288
289   /**
290    * Information we track for each peer.
291    */
292   struct GNUNET_CONTAINER_MultiPeerMap *peers;
293
294   /**
295    * Handle to the peerstore service.
296    */
297   struct GNUNET_PEERSTORE_Handle *ps;
298
299   /**
300    * Array where we sum up the bandwidth requests received indexed
301    * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) (sums
302    * over all peers).
303    */
304   uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
305
306   /**
307    * Information we track per network type (quotas).
308    */
309   struct Network networks[GNUNET_NT_COUNT];
310
311 };
312
313
314 /**
315  * Lookup peer in the peers map.
316  *
317  * @param h handle to look up in
318  * @param pid peer identity to look up by
319  * @return NULL for not found
320  */
321 struct Peer *
322 lookup_peer (struct SimpleHandle *h,
323              const struct GNUNET_PeerIdentity *pid)
324 {
325   return GNUNET_CONTAINER_multipeermap_get (h->peers,
326                                             pid);
327 }
328
329
330 /**
331  * Check if there is _any_ interesting information left we
332  * store about the peer in @a p.
333  *
334  * @param p peer to test if we can drop the data structure
335  * @return #GNUNET_YES if no information is left in @a p
336  */
337 static int
338 peer_test_dead (struct Peer *p)
339 {
340   for (enum GNUNET_MQ_PreferenceKind pk = 0;
341        pk < GNUNET_MQ_PREFERENCE_COUNT;
342        pk++)
343     if (0 != p->bw_by_pk[pk])
344       return GNUNET_NO;
345   if (NULL != p->sh_head)
346     return GNUNET_NO;
347   return GNUNET_YES;
348 }
349
350
351 /**
352  * Contact the transport service and suggest to it to
353  * try connecting to the address of @a hello.  Updates
354  * backoff and timestamp values in the @a hello.
355  *
356  * @param hello[in,out] address suggestion to make
357  */
358 static void
359 suggest_hello (struct Hello *hello)
360 {
361   struct Peer *p = hello->peer;
362   struct SimpleHandle *h = p->h;
363
364   p->last_suggestion
365     = hello->last_attempt
366     = GNUNET_TIME_absolute_get ();
367   hello->backoff = GNUNET_TIME_randomized_backoff (hello->backoff,
368                                                    GNUNET_TIME_absolute_get_remaining (hello->expiration));
369   h->env->suggest_cb (h->env->cls,
370                       &p->pid,
371                       hello->address);
372 }
373
374
375 /**
376  * Consider suggesting a HELLO (without a session) to transport.
377  * We look at how many active sessions we have for the peer, and
378  * if there are many, reduce the frequency of trying new addresses.
379  * Also, for each address we consider when we last tried it, and
380  * its exponential backoff if the attempt failed.  Note that it
381  * is possible that this function is called when no suggestion
382  * is to be made.
383  *
384  * In this case, we only calculate the time until we make the next
385  * suggestion.
386  *
387  * @param cls a `struct Peer`
388  */
389 static void
390 suggest_start_cb (void *cls)
391 {
392   struct Peer *p = cls;
393   struct GNUNET_TIME_Relative delay = GNUNET_TIME_UNIT_ZERO;
394   struct Hello *hello = NULL;
395   struct GNUNET_TIME_Absolute hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
396   struct GNUNET_TIME_Relative xdelay;
397   struct GNUNET_TIME_Absolute xnext;
398   unsigned int num_sessions = 0;
399   uint32_t sq;
400
401   /* count number of active sessions */
402   for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
403        NULL != sh;
404        sh = sh->next)
405     num_sessions++;
406   /* calculate square of number of sessions */
407   num_sessions++; /* start with 1, even if we have zero sessions */
408   if (num_sessions < UINT16_MAX)
409     sq = num_sessions * (uint32_t) num_sessions;
410   else
411     sq = UINT32_MAX;
412   xdelay = GNUNET_TIME_randomized_backoff (GNUNET_TIME_relative_multiply (SUGGEST_FREQ,
413                                                                           sq),
414                                            GNUNET_TIME_UNIT_FOREVER_REL);
415   xnext = GNUNET_TIME_relative_to_absolute (xdelay);
416
417   p->task = NULL;
418   while (0 == delay.rel_value_us)
419   {
420     struct Hello *next;
421     struct GNUNET_TIME_Absolute xmax;
422
423     if (NULL != hello)
424     {
425       /* We went through the loop already once and found
426          a HELLO that is due *now*, so make a suggestion! */
427       GNUNET_break (NULL == hello->sh);
428       suggest_hello (hello);
429       hello = NULL;
430       hpt = GNUNET_TIME_UNIT_FOREVER_ABS;
431     }
432     for (struct Hello *pos = p->h_head; NULL != pos; pos = next)
433     {
434       struct GNUNET_TIME_Absolute pt;
435
436       next = pos->next;
437       if (NULL != pos->sh)
438         continue;
439       if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).rel_value_us)
440       {
441         /* expired, remove! */
442         GNUNET_CONTAINER_DLL_remove (p->h_head,
443                                      p->h_tail,
444                                      pos);
445         GNUNET_free (pos);
446         continue;
447       }
448       pt = GNUNET_TIME_absolute_add (pos->last_attempt,
449                                      pos->backoff);
450       if ( (NULL == hello) ||
451            (pt.abs_value_us < hpt.abs_value_us) )
452       {
453         hello = pos;
454         hpt = pt;
455       }
456     }
457     if (NULL == hello)
458       return; /* no HELLOs that could still be tried */
459
460     /* hpt is now the *earliest* possible time for any HELLO
461        but we might not want to go for as early as possible for
462        this peer. So the actual time is the max of the earliest
463        HELLO and the 'xnext' */
464     xmax = GNUNET_TIME_absolute_max (hpt,
465                                      xnext);
466     delay = GNUNET_TIME_absolute_get_remaining (xmax);
467   }
468   p->task = GNUNET_SCHEDULER_add_delayed (delay,
469                                           &suggest_start_cb,
470                                           p);
471 }
472
473
474 /**
475  * Function called by PEERSTORE for each matching record.
476  *
477  * @param cls closure with a `struct Peer`
478  * @param record peerstore record information
479  * @param emsg error message, or NULL if no errors
480  */
481 static void
482 watch_cb (void *cls,
483           const struct GNUNET_PEERSTORE_Record *record,
484           const char *emsg)
485 {
486   struct Peer *p = cls;
487   char *addr;
488   size_t alen;
489   enum GNUNET_NetworkType nt;
490   struct GNUNET_TIME_Absolute expiration;
491   struct Hello *hello;
492
493   if (0 != memcmp (&p->pid,
494                    &record->peer,
495                    sizeof (struct GNUNET_PeerIdentity)))
496   {
497     GNUNET_break (0);
498     return;
499   }
500   if (0 != strcmp (record->key,
501                    GNUNET_HELLO_PEERSTORE_KEY))
502   {
503     GNUNET_break (0);
504     return;
505   }
506   addr = GNUNET_HELLO_extract_address (record->value,
507                                        record->value_size,
508                                        &p->pid,
509                                        &nt,
510                                        &expiration);
511   if (NULL == addr)
512     return; /* invalid hello, bad signature, other problem */
513   if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
514   {
515     /* expired, ignore */
516     GNUNET_free (addr);
517     return;
518   }
519   /* check if addr is already known */
520   for (struct Hello *he = p->h_head;
521        NULL != he;
522        he = he->next)
523   {
524     if (0 != strcmp (he->address,
525                      addr))
526       continue;
527     if (he->expiration.abs_value_us < expiration.abs_value_us)
528     {
529       he->expiration = expiration;
530       he->nt = nt;
531     }
532     GNUNET_free (addr);
533     return;
534   }
535   /* create new HELLO */
536   alen = strlen (addr) + 1;
537   hello = GNUNET_malloc (sizeof (struct Hello) + alen);
538   hello->address = (const char *) &hello[1];
539   hello->expiration = expiration;
540   hello->nt = nt;
541   hello->peer = p;
542   memcpy (&hello[1],
543           addr,
544           alen);
545   GNUNET_free (addr);
546   GNUNET_CONTAINER_DLL_insert (p->h_head,
547                                p->h_tail,
548                                hello);
549   /* check if sh for this HELLO already exists */
550   for (struct GNUNET_ATS_SessionHandle *sh = p->sh_head;
551        NULL != sh;
552        sh = sh->next)
553   {
554     if ( (NULL == sh->address) ||
555          (0 != strcmp (sh->address,
556                        addr)) )
557       continue;
558     GNUNET_assert (NULL == sh->hello);
559     sh->hello = hello;
560     hello->sh = sh;
561     break;
562   }
563   if (NULL == p->task)
564     p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
565                                         p);
566 }
567
568
569 /**
570  * Find or add peer if necessary.
571  *
572  * @param h our plugin handle
573  * @param pid the peer identity to add/look for
574  * @return a peer handle
575  */
576 static struct Peer *
577 peer_add (struct SimpleHandle *h,
578           const struct GNUNET_PeerIdentity *pid)
579 {
580   struct Peer *p = lookup_peer (h,
581                                 pid);
582
583   if (NULL != p)
584     return p;
585   p = GNUNET_new (struct Peer);
586   p->h = h;
587   p->pid = *pid;
588   p->wc = GNUNET_PEERSTORE_watch (h->ps,
589                                   "transport",
590                                   &p->pid,
591                                   GNUNET_HELLO_PEERSTORE_KEY,
592                                   &watch_cb,
593                                   p);
594   GNUNET_assert (GNUNET_YES ==
595                  GNUNET_CONTAINER_multipeermap_put (h->peers,
596                                                     &p->pid,
597                                                     p,
598                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
599
600   return p;
601 }
602
603
604 /**
605  * Free the entry (and associated tasks) of peer @a p.
606  * Note that @a p must be dead already (see #peer_test_dead()).
607  *
608  * @param p the peer to free
609  */
610 static void
611 peer_free (struct Peer *p)
612 {
613   struct SimpleHandle *h = p->h;
614   struct Hello *hello;
615
616   GNUNET_assert (NULL == p->sh_head);
617   while (NULL != (hello = p->h_head))
618   {
619     GNUNET_CONTAINER_DLL_remove (p->h_head,
620                                  p->h_tail,
621                                  hello);
622     GNUNET_assert (NULL == hello->sh);
623     GNUNET_free (hello);
624   }
625   if (NULL != p->task)
626   {
627     GNUNET_SCHEDULER_cancel (p->task);
628     p->task = NULL;
629   }
630   if (NULL != p->wc)
631   {
632     GNUNET_PEERSTORE_watch_cancel (p->wc);
633     p->wc = NULL;
634   }
635   GNUNET_assert (GNUNET_YES ==
636                  GNUNET_CONTAINER_multipeermap_remove (h->peers,
637                                                        &p->pid,
638                                                        p));
639   GNUNET_free (p);
640 }
641
642
643 /**
644  * Check if the new allocation for @a sh is significantly different
645  * from the last one, and if so, tell transport.
646  *
647  * @param sh session handle to consider updating transport for
648  */
649 static void
650 consider_notify_transport (struct GNUNET_ATS_SessionHandle *sh)
651 {
652   struct Peer *peer = sh->peer;
653   struct SimpleHandle *h = peer->h;
654   enum GNUNET_NetworkType nt = sh->data->prop.nt;
655   struct GNUNET_TIME_Relative delay;
656   uint64_t sig_in;
657   uint64_t sig_out;
658   int64_t delta_in;
659   int64_t delta_out;
660
661   delay = GNUNET_TIME_absolute_get_duration (sh->last_allocation);
662   /* A significant change is more than 10% of the quota,
663      which is given in bytes/second */
664   sig_in
665     = h->networks[nt].total_quota_in * (delay.rel_value_us / 1000LL) / 1000LL / 10;
666   sig_out
667     = h->networks[nt].total_quota_out * (delay.rel_value_us / 1000LL) / 1000LL / 10;
668   delta_in = ( (int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in);
669   delta_out = ( (int64_t) ntohl (sh->bw_in.value__)) - ((int64_t) sh->target_in);
670   /* we want the absolute values */
671   if (delta_in < 0)
672     delta_in = - delta_in;
673   if (INT64_MIN == delta_in)
674     delta_in = INT64_MAX;  /* Handle corner case: INT_MIN == - INT_MIN */
675   if (delta_out < 0)
676     delta_out = - delta_out;
677   if (INT64_MIN == delta_out)
678     delta_out = INT64_MAX; /* Handle corner case: INT_MIN == - INT_MIN */
679   if ( (sig_in > delta_in) &&
680        (sig_out > delta_out) )
681     return; /* insignificant change */
682   /* change is significant, tell transport! */
683   if (sh->target_in > UINT32_MAX)
684     sh->target_in = UINT32_MAX;
685   sh->bw_in.value__ = htonl ((uint32_t) sh->target_in);
686   if (sh->target_out > UINT32_MAX)
687     sh->target_out = UINT32_MAX;
688   sh->bw_out.value__ = htonl ((uint32_t) sh->target_out);
689   sh->last_allocation = GNUNET_TIME_absolute_get ();
690   h->env->allocate_cb (h->env->cls,
691                        sh->session,
692                        &peer->pid,
693                        sh->bw_in,
694                        sh->bw_out);
695 }
696
697
698 /**
699  * Closure for #update_counters and #update_allocation.
700  */
701 struct Counters
702 {
703   /**
704    * Plugin's state.
705    */
706   struct SimpleHandle *h;
707
708   /**
709    * Bandwidth that applications would prefer to allocate in this
710    * network type.  We initially add all requested allocations to the
711    * respective network type where the given preference is best
712    * satisfied. Later we may rebalance.
713    */
714   uint64_t bw_out_by_nt[GNUNET_NT_COUNT];
715
716   /**
717    * Current bandwidth utilization for this network type.  We simply
718    * add the current goodput up (with some fairness considerations).
719    */
720   uint64_t bw_in_by_nt[GNUNET_NT_COUNT];
721
722   /**
723    * By how much do we have to scale (up or down) our expectations
724    * for outbound bandwidth?
725    */
726   double scale_out[GNUNET_NT_COUNT];
727
728   /**
729    * By how much do we have to scale (up or down) our expectations
730    * for inbound bandwidth?
731    */
732   double scale_in[GNUNET_NT_COUNT];
733
734 };
735
736
737 /**
738  * Function used to iterate over all peers and collect
739  * counter data.
740  *
741  * @param cls a `struct Counters *`
742  * @param pid identity of the peer we process, unused
743  * @param value a `struct Peer *`
744  * @return #GNUNET_YES (continue to iterate)
745  */
746 static int
747 update_counters (void *cls,
748                  const struct GNUNET_PeerIdentity *pid,
749                  void *value)
750 {
751   struct Counters *c = cls;
752   struct Peer *peer = value;
753   struct GNUNET_ATS_SessionHandle *best[GNUNET_MQ_PREFERENCE_COUNT];
754
755   (void) pid;
756   memset (best,
757           0,
758           sizeof (best));
759   for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
760        NULL != sh;
761        sh = sh->next)
762   {
763     enum GNUNET_NetworkType nt = sh->data->prop.nt;
764
765     sh->target_out = MIN_BANDWIDTH_PER_SESSION;
766     c->bw_out_by_nt[nt] += MIN_BANDWIDTH_PER_SESSION;
767     c->bw_in_by_nt[nt] += GNUNET_MAX (MIN_BANDWIDTH_PER_SESSION,
768                                       sh->data->prop.goodput_in);
769     for (enum GNUNET_MQ_PreferenceKind pk = 0;
770          pk < GNUNET_MQ_PREFERENCE_COUNT;
771          pk++)
772     {
773       /* General rule: always prefer smaller distance if possible,
774          otherwise decide by pk: */
775       switch (pk) {
776       case GNUNET_MQ_PREFERENCE_NONE:
777         break;
778       case GNUNET_MQ_PREFERENCE_BANDWIDTH:
779         /* For bandwidth, we compare the sum of transmitted bytes and
780            confirmed transmitted bytes, so confirmed data counts twice */
781         if ( (NULL == best[pk]) ||
782              (sh->data->prop.distance < best[pk]->data->prop.distance) ||
783              (sh->data->prop.utilization_out + sh->data->prop.goodput_out >
784               best[pk]->data->prop.utilization_out + best[pk]->data->prop.goodput_out) )
785           best[pk] = sh;
786         /* If both are equal (i.e. usually this happens if there is a zero), use
787            latency as a yardstick */
788         if ( (sh->data->prop.utilization_out + sh->data->prop.goodput_out ==
789               best[pk]->data->prop.utilization_out + best[pk]->data->prop.goodput_out) &&
790              (sh->data->prop.distance == best[pk]->data->prop.distance) &&
791              (sh->data->prop.delay.rel_value_us <
792               best[pk]->data->prop.delay.rel_value_us) )
793           best[pk] = sh;
794         break;
795       case GNUNET_MQ_PREFERENCE_LATENCY:
796         if ( (NULL == best[pk]) ||
797              (sh->data->prop.distance < best[pk]->data->prop.distance) ||
798              ( (sh->data->prop.distance == best[pk]->data->prop.distance) &&
799                (sh->data->prop.delay.rel_value_us <
800                 best[pk]->data->prop.delay.rel_value_us) ) )
801           best[pk] = sh;
802         break;
803       case GNUNET_MQ_PREFERENCE_RELIABILITY:
804         /* For reliability, we consider the ratio of goodput to utilization
805            (but use multiplicative formultations to avoid division by zero) */
806         if ( (NULL == best[pk]) ||
807              (1ULL * sh->data->prop.goodput_out * best[pk]->data->prop.utilization_out >
808               1ULL * sh->data->prop.utilization_out * best[pk]->data->prop.goodput_out) )
809           best[pk] = sh;
810         /* If both are equal (i.e. usually this happens if there is a zero), use
811            latency as a yardstick */
812         if ( (1ULL * sh->data->prop.goodput_out * best[pk]->data->prop.utilization_out ==
813               1ULL * sh->data->prop.utilization_out * best[pk]->data->prop.goodput_out) &&
814              (sh->data->prop.distance == best[pk]->data->prop.distance) &&
815              (sh->data->prop.delay.rel_value_us <
816               best[pk]->data->prop.delay.rel_value_us) )
817           best[pk] = sh;
818         break;
819       }
820     }
821   }
822   /* for first round, assign target bandwidth simply to sum of
823      requested bandwidth */
824   for (enum GNUNET_MQ_PreferenceKind pk = 0;
825        pk < GNUNET_MQ_PREFERENCE_COUNT;
826        pk++)
827   {
828     enum GNUNET_NetworkType nt = best[pk]->data->prop.nt;
829
830     best[pk]->target_out = GNUNET_MIN (peer->bw_by_pk[pk],
831                                        MIN_BANDWIDTH_PER_SESSION);
832     c->bw_out_by_nt[nt] += (uint64_t) (best[pk]->target_out - MIN_BANDWIDTH_PER_SESSION);
833   }
834   return GNUNET_YES;
835 }
836
837
838 /**
839  * Function used to iterate over all peers and collect
840  * counter data.
841  *
842  * @param cls a `struct Counters *`
843  * @param pid identity of the peer we process, unused
844  * @param value a `struct Peer *`
845  * @return #GNUNET_YES (continue to iterate)
846  */
847 static int
848 update_allocation (void *cls,
849                    const struct GNUNET_PeerIdentity *pid,
850                    void *value)
851 {
852   struct Counters *c = cls;
853   struct Peer *peer = value;
854
855   (void) pid;
856   for (struct GNUNET_ATS_SessionHandle *sh = peer->sh_head;
857        NULL != sh;
858        sh = sh->next)
859   {
860     enum GNUNET_NetworkType nt = sh->data->prop.nt;
861
862     sh->target_out = (uint64_t) (c->scale_out[nt] * sh->target_out);
863     sh->target_in = (uint64_t) (c->scale_in[nt] * sh->target_in);
864     consider_notify_transport (sh);
865   }
866   return GNUNET_YES;
867 }
868
869
870 /**
871  * The world changed, recalculate our allocations.
872  */
873 static void
874 update (struct SimpleHandle *h)
875 {
876   struct Counters cnt = {
877     .h = h
878   };
879
880   GNUNET_CONTAINER_multipeermap_iterate (h->peers,
881                                          &update_counters,
882                                          &cnt);
883   /* calculate how badly the missmatch between requested
884      allocations and available bandwidth is per network type */
885   for (enum GNUNET_NetworkType nt = 0;
886        nt < GNUNET_NT_COUNT;
887        nt++)
888   {
889     cnt.scale_out[nt] = 1.0 * cnt.bw_out_by_nt[nt] / h->networks[nt].total_quota_out;
890     cnt.scale_in[nt] = 1.0 * cnt.bw_in_by_nt[nt] / h->networks[nt].total_quota_in;
891   }
892   /* recalculate allocations, considering scaling factor, and
893      update transport if the change is significant */
894   GNUNET_CONTAINER_multipeermap_iterate (h->peers,
895                                          &update_allocation,
896                                          &cnt);
897 }
898
899
900 /**
901  * The plugin should begin to respect a new preference.
902  *
903  * @param cls the closure
904  * @param pref the preference to add
905  * @return plugin's internal representation, or NULL
906  */
907 static struct GNUNET_ATS_PreferenceHandle *
908 simple_preference_add (void *cls,
909                        const struct GNUNET_ATS_Preference *pref)
910 {
911   struct SimpleHandle *h = cls;
912   struct Peer *p = peer_add (h,
913                              &pref->peer);
914
915   GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
916   p->bw_by_pk[pref->pk] += ntohl (pref->bw.value__);
917   h->bw_by_pk[pref->pk] += ntohl (pref->bw.value__);
918   update (h);
919   return NULL;
920 }
921
922
923 /**
924  * The plugin should end respecting a preference.
925  *
926  * @param cls the closure
927  * @param ph whatever @e preference_add returned
928  * @param pref the preference to delete
929  * @return plugin's internal representation, or NULL
930  */
931 static void
932 simple_preference_del (void *cls,
933                        struct GNUNET_ATS_PreferenceHandle *ph,
934                        const struct GNUNET_ATS_Preference *pref)
935 {
936   struct SimpleHandle *h = cls;
937   struct Peer *p = lookup_peer (h,
938                                 &pref->peer);
939
940   GNUNET_assert (NULL != p);
941   GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
942   p->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__);
943   h->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__);
944   if ( (0 == p->bw_by_pk[pref->pk]) &&
945        (GNUNET_YES == peer_test_dead (p)) )
946     peer_free (p);
947   update (h);
948 }
949
950
951 /**
952  * Transport established a new session with performance
953  * characteristics given in @a data.
954  *
955  * @param cls closure
956  * @param data performance characteristics of @a sh
957  * @param address address information (for debugging)
958  * @return handle by which the plugin will identify this session
959  */
960 static struct GNUNET_ATS_SessionHandle *
961 simple_session_add (void *cls,
962                     const struct GNUNET_ATS_SessionData *data,
963                     const char *address)
964 {
965   struct SimpleHandle *h = cls;
966   struct Peer *p = peer_add (h,
967                              &data->peer);
968   struct Hello *hello;
969   size_t alen;
970   struct GNUNET_ATS_SessionHandle *sh;
971
972   /* setup session handle */
973   if (NULL == address)
974     alen = 0;
975   else
976     alen = strlen (address) + 1;
977   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionHandle) + alen);
978   sh->peer = p;
979   sh->session = data->session;
980   sh->data = data;
981   if (NULL == address)
982   {
983     sh->address = NULL;
984   }
985   else
986   {
987     memcpy (&sh[1],
988             address,
989             alen);
990     sh->address = (const char *) &sh[1];
991   }
992   GNUNET_CONTAINER_DLL_insert (p->sh_head,
993                                p->sh_tail,
994                                sh);
995   /* match HELLO */
996   hello = p->h_head;
997   while ( (NULL != hello) &&
998           (0 != strcmp (address,
999                         hello->address)) )
1000     hello = hello->next;
1001   if (NULL != hello)
1002   {
1003     hello->sh = sh;
1004     hello->backoff = GNUNET_TIME_UNIT_ZERO;
1005     sh->hello = hello;
1006   }
1007   update (h);
1008   return NULL;
1009 }
1010
1011
1012 /**
1013  * @a data changed for a given @a sh, solver should consider
1014  * the updated performance characteristics.
1015  *
1016  * @param cls closure
1017  * @param sh session this is about
1018  * @param data performance characteristics of @a sh
1019  */
1020 static void
1021 simple_session_update (void *cls,
1022                        struct GNUNET_ATS_SessionHandle *sh,
1023                        const struct GNUNET_ATS_SessionData *data)
1024 {
1025   struct SimpleHandle *h = cls;
1026
1027   sh->data = data; /* this statement should not really do anything... */
1028   update (h);
1029 }
1030
1031
1032 /**
1033  * A session went away. Solver should update accordingly.
1034  *
1035  * @param cls closure
1036  * @param sh session this is about
1037  * @param data (last) performance characteristics of @a sh
1038  */
1039 static void
1040 simple_session_del (void *cls,
1041                     struct GNUNET_ATS_SessionHandle *sh,
1042                     const struct GNUNET_ATS_SessionData *data)
1043 {
1044   struct SimpleHandle *h = cls;
1045   struct Peer *p = sh->peer;
1046   struct Hello *hello = sh->hello;
1047
1048   /* clean up sh */
1049   GNUNET_CONTAINER_DLL_remove (p->sh_head,
1050                                p->sh_tail,
1051                                sh);
1052   if (NULL != hello)
1053   {
1054     GNUNET_assert (sh == hello->sh);
1055     hello->sh = NULL;
1056     /* session went down, if necessary restart suggesting
1057        addresses */
1058     if (NULL == p->task)
1059       p->task = GNUNET_SCHEDULER_add_now (&suggest_start_cb,
1060                                           p);
1061   }
1062   GNUNET_free (sh);
1063   /* del peer if otherwise dead */
1064   if ( (NULL == p->sh_head) &&
1065        (GNUNET_YES == peer_test_dead (p)) )
1066     peer_free (p);
1067   update (h);
1068 }
1069
1070
1071 #include "plugin_ats2_common.c"
1072
1073
1074 /**
1075  * Function invoked when the plugin is loaded.
1076  *
1077  * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1078  *            modified to return the API functions (ugh).
1079  * @return the `struct SimpleHandle` to pass as a closure
1080  */
1081 void *
1082 libgnunet_plugin_ats2_simple_init (void *cls)
1083 {
1084   static struct GNUNET_ATS_SolverFunctions sf;
1085   struct GNUNET_ATS_PluginEnvironment *env = cls;
1086   struct SimpleHandle *s;
1087
1088   s = GNUNET_new (struct SimpleHandle);
1089   s->env = env;
1090   s->peers = GNUNET_CONTAINER_multipeermap_create (128,
1091                                                    GNUNET_YES);
1092   s->ps = GNUNET_PEERSTORE_connect (env->cfg);
1093   sf.cls = s;
1094   sf.preference_add = &simple_preference_add;
1095   sf.preference_del = &simple_preference_del;
1096   sf.session_add = &simple_session_add;
1097   sf.session_update = &simple_session_update;
1098   sf.session_del = &simple_session_del;
1099   for (enum GNUNET_NetworkType nt = 0;
1100        nt < GNUNET_NT_COUNT;
1101        nt++)
1102   {
1103     const char *name = GNUNET_NT_to_string (nt);
1104
1105     if (NULL == name)
1106     {
1107       GNUNET_break (0);
1108       break;
1109     }
1110     get_quota (env->cfg,
1111                name,
1112                "IN",
1113                &s->networks[nt].total_quota_in);
1114     get_quota (env->cfg,
1115                name,
1116                "OUT",
1117                &s->networks[nt].total_quota_out);
1118     s->networks[nt].type = nt;
1119   }
1120   return &sf;
1121 }
1122
1123
1124 /**
1125  * Function used to unload the plugin.
1126  *
1127  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1128  */
1129 void *
1130 libgnunet_plugin_ats2_simple_done (void *cls)
1131 {
1132   struct GNUNET_ATS_SolverFunctions *sf = cls;
1133   struct SimpleHandle *s = sf->cls;
1134
1135   GNUNET_break (0 ==
1136                 GNUNET_CONTAINER_multipeermap_size (s->peers));
1137   GNUNET_CONTAINER_multipeermap_destroy (s->peers);
1138   GNUNET_PEERSTORE_disconnect (s->ps,
1139                                GNUNET_NO);
1140   GNUNET_free (s);
1141   return NULL;
1142 }
1143
1144
1145 /* end of plugin_ats2_simple.c */