first batch of license fixes (boring)
[oweals/gnunet.git] / src / ats / plugin_ats_proportional.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 GNUnet e.V.
4
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU 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 /**
16  * @file ats/plugin_ats_proportional.c
17  * @brief ATS proportional solver
18  * @author Matthias Wachs
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_statistics_service.h"
23 #include "gnunet_ats_service.h"
24 #include "gnunet_ats_plugin.h"
25 #include "gnunet-service-ats_addresses.h"
26
27 #define LOG(kind,...) GNUNET_log_from (kind, "ats-proportional",__VA_ARGS__)
28
29 /**
30  * How much do we value stability over adaptation by default.  A low
31  * value (close to 1.0) means we adapt as soon as possible, a larger
32  * value means that we have to have the respective factor of an
33  * advantage (or delay) before we adapt and sacrifice stability.
34  */
35 #define PROP_STABILITY_FACTOR 1.25
36
37
38 /**
39  * Default value to assume for the proportionality factor, if none is
40  * given in the configuration.  This factor determines how strong the
41  * bandwidth allocation will orient itself on the application
42  * preferences.  A lower factor means a more balanced bandwidth
43  * distribution while a larger number means a distribution more in
44  * line with application (bandwidth) preferences.
45  */
46 #define PROPORTIONALITY_FACTOR 2.0
47
48
49 /**
50  * Address information stored for the proportional solver in the
51  * `solver_information` member of `struct GNUNET_ATS_Address`.
52  *
53  * They are also stored in the respective `struct Network`'s linked
54  * list.
55  */
56 struct AddressWrapper
57 {
58   /**
59    * Next in DLL
60    */
61   struct AddressWrapper *next;
62
63   /**
64    * Previous in DLL
65    */
66   struct AddressWrapper *prev;
67
68   /**
69    * The address
70    */
71   struct ATS_Address *addr;
72
73     /**
74    * Network scope this address is in
75    */
76   struct Network *network;
77
78   /**
79    * Inbound quota
80    */
81   uint32_t calculated_quota_in;
82
83   /**
84    * Outbound quota
85    */
86   uint32_t calculated_quota_out;
87
88   /**
89    * When was this address activated
90    */
91   struct GNUNET_TIME_Absolute activated;
92
93 };
94
95
96 /**
97  * Representation of a network
98  */
99 struct Network
100 {
101   /**
102    * Network description
103    */
104   const char *desc;
105
106   /**
107    * String for statistics total addresses
108    */
109   char *stat_total;
110
111   /**
112    * String for statistics active addresses
113    */
114   char *stat_active;
115
116   /**
117    * Linked list of addresses in this network: head
118    */
119   struct AddressWrapper *head;
120
121   /**
122    * Linked list of addresses in this network: tail
123    */
124   struct AddressWrapper *tail;
125
126   /**
127    * Total inbound quota
128    */
129   unsigned long long total_quota_in;
130
131   /**
132    * Total outbound quota
133    */
134   unsigned long long total_quota_out;
135
136   /**
137    * ATS network type
138    */
139   enum GNUNET_ATS_Network_Type type;
140
141   /**
142    * Number of active addresses for this network
143    */
144   unsigned int active_addresses;
145
146   /**
147    * Number of total addresses for this network
148    */
149   unsigned int total_addresses;
150
151 };
152
153
154 /**
155  * A handle for the proportional solver
156  */
157 struct GAS_PROPORTIONAL_Handle
158 {
159
160   /**
161    * Our execution environment.
162    */
163   struct GNUNET_ATS_PluginEnvironment *env;
164
165   /**
166    * Networks array
167    */
168   struct Network *network_entries;
169
170   /**
171    * Proportionality factor
172    */
173   double prop_factor;
174
175   /**
176    * Stability factor
177    */
178   double stability_factor;
179
180   /**
181    * Bulk lock counter. If zero, we are not locked.
182    */
183   unsigned int bulk_lock;
184
185   /**
186    * Number of changes made while solver was locked.  We really only
187    * use 0/non-zero to check on unlock if we have to run the update.
188    */
189   unsigned int bulk_requests;
190
191   /**
192    * Number of active addresses for solver
193    */
194   unsigned int active_addresses;
195
196 };
197
198
199 /**
200  * Test if bandwidth is available in this network to add an additional address.
201  *
202  * @param net the network type to check
203  * @param extra for how many extra addresses do we check?
204  * @return #GNUNET_YES or #GNUNET_NO
205  */
206 static int
207 is_bandwidth_available_in_network (struct Network *net,
208                                    int extra)
209 {
210   unsigned int na;
211   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
212
213   GNUNET_assert (((int)net->active_addresses) + extra >= 0);
214   na = net->active_addresses + extra;
215   if (0 == na)
216     return GNUNET_YES;
217   if ( ((net->total_quota_in / na) > min_bw) &&
218        ((net->total_quota_out / na) > min_bw) )
219     return GNUNET_YES;
220   return GNUNET_NO;
221 }
222
223
224 /**
225  * Test if all peers in this network require connectivity at level at
226  * least @a con.
227  *
228  * @param s the solver handle
229  * @param net the network type to check
230  * @param con connection return value threshold to check
231  * @return #GNUNET_YES or #GNUNET_NO
232  */
233 static int
234 all_require_connectivity (struct GAS_PROPORTIONAL_Handle *s,
235                           struct Network *net,
236                           unsigned int con)
237 {
238   struct AddressWrapper *aw;
239
240   for (aw = net->head; NULL != aw; aw = aw->next)
241     if (con >
242         s->env->get_connectivity (s->env->cls,
243                                   &aw->addr->peer))
244       return GNUNET_NO;
245   return GNUNET_YES;
246 }
247
248
249 /**
250  * Update bandwidth assigned to peers in this network.  The basic idea
251  * is to assign every peer in the network the minimum bandwidth, and
252  * then distribute the remaining bandwidth proportional to application
253  * preferences.
254  *
255  * @param s the solver handle
256  * @param net the network type to update
257  */
258 static void
259 distribute_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
260                       struct Network *net)
261 {
262   const uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
263   struct AddressWrapper *aw;
264   unsigned long long remaining_quota_in;
265   unsigned long long quota_out_used;
266   unsigned long long remaining_quota_out;
267   unsigned long long quota_in_used;
268   unsigned int count_addresses;
269   double sum_relative_peer_prefences;
270   double peer_weight;
271   double total_weight;
272   const double *peer_relative_prefs;
273
274   LOG (GNUNET_ERROR_TYPE_INFO,
275        "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
276        net->desc,
277        net->active_addresses,
278        net->total_quota_in,
279        net->total_quota_in);
280
281   if (0 == net->active_addresses)
282     return; /* no addresses to update */
283
284   /* sanity checks */
285   if ((net->active_addresses * min_bw) > net->total_quota_in)
286   {
287     GNUNET_break(0);
288     return;
289   }
290   if ((net->active_addresses * min_bw) > net->total_quota_out)
291   {
292     GNUNET_break(0);
293     return;
294   }
295
296   /* Calculate sum of relative preference for active addresses in this
297      network */
298   sum_relative_peer_prefences = 0.0;
299   count_addresses = 0;
300   for (aw = net->head; NULL != aw; aw = aw->next)
301   {
302     if (GNUNET_YES != aw->addr->active)
303       continue;
304     peer_relative_prefs = s->env->get_preferences (s->env->cls,
305                                                    &aw->addr->peer);
306     sum_relative_peer_prefences
307       += peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
308     count_addresses++;
309   }
310   if (count_addresses != net->active_addresses)
311   {
312     GNUNET_break (0);
313     LOG (GNUNET_ERROR_TYPE_WARNING,
314          "%s: Counted %u active addresses, expected %u active addresses\n",
315          net->desc,
316          count_addresses,
317          net->active_addresses);
318     /* try to fix... */
319     net->active_addresses = count_addresses;
320   }
321   LOG (GNUNET_ERROR_TYPE_INFO,
322        "Total relative preference %.3f for %u addresses in network %s\n",
323        sum_relative_peer_prefences,
324        net->active_addresses,
325        net->desc);
326
327   /* check how much we have to distribute */
328   remaining_quota_in = net->total_quota_in - (net->active_addresses * min_bw);
329   remaining_quota_out = net->total_quota_out - (net->active_addresses * min_bw);
330   LOG (GNUNET_ERROR_TYPE_DEBUG,
331        "Proportionally distributable bandwidth (in/out): %llu/%llu\n",
332        remaining_quota_in,
333        remaining_quota_out);
334
335   /* distribute remaining quota; we do not do it exactly proportional,
336      but balance "even" distribution ("net->active_addresses") with
337      the preference sum using the "prop_factor". */
338   total_weight = net->active_addresses +
339     s->prop_factor * sum_relative_peer_prefences;
340   quota_out_used = 0;
341   quota_in_used = 0;
342   for (aw = net->head; NULL != aw; aw = aw->next)
343   {
344     if (GNUNET_YES != aw->addr->active)
345     {
346       /* set to 0, just to be sure */
347       aw->calculated_quota_in = 0;
348       aw->calculated_quota_out = 0;
349       continue;
350     }
351     peer_relative_prefs = s->env->get_preferences (s->env->cls,
352                                                    &aw->addr->peer);
353     peer_weight = 1.0
354       + s->prop_factor * peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
355
356     aw->calculated_quota_in = min_bw
357       + (peer_weight / total_weight) * remaining_quota_in;
358     aw->calculated_quota_out = min_bw
359       + (peer_weight / total_weight) * remaining_quota_out;
360
361     LOG (GNUNET_ERROR_TYPE_INFO,
362          "New quotas for peer `%s' with weight (cur/total) %.3f/%.3f (in/out) are: %u/%u\n",
363          GNUNET_i2s (&aw->addr->peer),
364          peer_weight,
365          total_weight,
366          (unsigned int) aw->calculated_quota_in,
367          (unsigned int) aw->calculated_quota_out);
368     quota_in_used += aw->calculated_quota_in;
369     quota_out_used += aw->calculated_quota_out;
370   }
371   LOG (GNUNET_ERROR_TYPE_DEBUG,
372        "Total bandwidth assigned is (in/out): %llu /%llu\n",
373        quota_in_used,
374        quota_out_used);
375   /* +1 due to possible rounding errors */
376   GNUNET_break (quota_out_used <= net->total_quota_out + 1);
377   GNUNET_break (quota_in_used <= net->total_quota_in + 1);
378 }
379
380
381 /**
382  * Notify ATS service of bandwidth changes to addresses.
383  *
384  * @param s solver handle
385  * @param net the network to propagate changes in
386  */
387 static void
388 propagate_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
389                      struct Network *net)
390 {
391   struct AddressWrapper *cur;
392
393   for (cur = net->head; NULL != cur; cur = cur->next)
394   {
395     if ( (cur->addr->assigned_bw_in == cur->calculated_quota_in) &&
396          (cur->addr->assigned_bw_out == cur->calculated_quota_out) )
397       continue;
398     cur->addr->assigned_bw_in = cur->calculated_quota_in;
399     cur->addr->assigned_bw_out = cur->calculated_quota_out;
400     if (GNUNET_YES == cur->addr->active)
401       s->env->bandwidth_changed_cb (s->env->cls,
402                                     cur->addr);
403   }
404 }
405
406
407 /**
408  * Distribute bandwidth.  The addresses have already been selected,
409  * this is merely distributed the bandwidth among the addresses.
410  *
411  * @param s the solver handle
412  * @param n the network, can be NULL for all networks
413  */
414 static void
415 distribute_bandwidth_in_network (struct GAS_PROPORTIONAL_Handle *s,
416                                  struct Network *n)
417 {
418   unsigned int i;
419
420   if (0 != s->bulk_lock)
421   {
422     s->bulk_requests++;
423     return;
424   }
425   if (NULL != n)
426   {
427     LOG (GNUNET_ERROR_TYPE_DEBUG,
428         "Redistributing bandwidth in network %s with %u active and %u total addresses\n",
429          GNUNET_ATS_print_network_type(n->type),
430          n->active_addresses,
431          n->total_addresses);
432     s->env->info_cb (s->env->cls,
433                      GAS_OP_SOLVE_START,
434                      GAS_STAT_SUCCESS,
435                      GAS_INFO_PROP_SINGLE);
436     distribute_bandwidth(s,
437                          n);
438     s->env->info_cb (s->env->cls,
439                      GAS_OP_SOLVE_STOP,
440                      GAS_STAT_SUCCESS,
441                      GAS_INFO_PROP_SINGLE);
442     s->env->info_cb (s->env->cls,
443                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
444                      GAS_STAT_SUCCESS,
445                      GAS_INFO_PROP_SINGLE);
446     propagate_bandwidth (s,
447                          n);
448
449     s->env->info_cb (s->env->cls,
450                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
451                      GAS_STAT_SUCCESS,
452                      GAS_INFO_PROP_SINGLE);
453   }
454   else
455   {
456     LOG (GNUNET_ERROR_TYPE_DEBUG,
457          "Redistributing bandwidth in all %u networks\n",
458          s->env->network_count);
459     s->env->info_cb (s->env->cls,
460                      GAS_OP_SOLVE_START,
461                      GAS_STAT_SUCCESS,
462                      GAS_INFO_PROP_ALL);
463     for (i = 0; i < s->env->network_count; i++)
464       distribute_bandwidth (s,
465                             &s->network_entries[i]);
466     s->env->info_cb (s->env->cls,
467                      GAS_OP_SOLVE_STOP,
468                      GAS_STAT_SUCCESS,
469                      GAS_INFO_PROP_ALL);
470     s->env->info_cb (s->env->cls,
471                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
472                      GAS_STAT_SUCCESS,
473                      GAS_INFO_PROP_ALL);
474     for (i = 0; i < s->env->network_count; i++)
475       propagate_bandwidth (s,
476                            &s->network_entries[i]);
477     s->env->info_cb (s->env->cls,
478                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
479                      GAS_STAT_SUCCESS,
480                      GAS_INFO_PROP_ALL);
481   }
482 }
483
484
485 /**
486  * Context for finding the best address* Linked list of addresses in this network: head
487  */
488 struct FindBestAddressCtx
489 {
490   /**
491    * The solver handle
492    */
493   struct GAS_PROPORTIONAL_Handle *s;
494
495   /**
496    * The currently best address
497    */
498   struct ATS_Address *best;
499 };
500
501
502 /**
503  * Find a "good" address to use for a peer by iterating over the
504  * addresses for this peer.  If we already have an existing address,
505  * we stick to it.  Otherwise, we pick by lowest distance and then by
506  * lowest latency.
507  *
508  * @param cls the `struct FindBestAddressCtx *' where we store the result
509  * @param key the peer we are trying to find the best address for
510  * @param value another `struct ATS_Address*` to consider using
511  * @return #GNUNET_OK (continue to iterate)
512  */
513 static int
514 find_best_address_it (void *cls,
515                       const struct GNUNET_PeerIdentity *key,
516                       void *value)
517 {
518   struct FindBestAddressCtx *ctx = cls;
519   struct ATS_Address *current = value;
520   struct AddressWrapper *asi = current->solver_information;
521   struct GNUNET_TIME_Relative active_time;
522   double best_delay;
523   double best_distance;
524   double cur_delay;
525   double cur_distance;
526   unsigned int con;
527   int bw_available;
528   int need;
529
530   /* we need +1 slot if 'current' is not yet active */
531   need = (GNUNET_YES == current->active) ? 0 : 1;
532   /* we save -1 slot if 'best' is active and belongs
533      to the same network (as we would replace it) */
534   if ( (NULL != ctx->best) &&
535        (GNUNET_YES == ctx->best->active) &&
536        (((struct AddressWrapper *) ctx->best->solver_information)->network ==
537         asi->network) )
538     need--;
539   /* we can gain -1 slot if this peers connectivity
540      requirement is higher than that of another peer
541      in that network scope */
542   con = ctx->s->env->get_connectivity (ctx->s->env->cls,
543                                        key);
544   if (GNUNET_YES !=
545       all_require_connectivity (ctx->s,
546                                 asi->network,
547                                 con))
548     need--;
549   /* test if minimum bandwidth for 'current' would be available */
550   bw_available
551     = is_bandwidth_available_in_network (asi->network,
552                                          need);
553   if (! bw_available)
554   {
555     /* Bandwidth for this address is unavailable, so we cannot use
556        it. */
557     return GNUNET_OK;
558   }
559   if (GNUNET_YES == current->active)
560   {
561     active_time = GNUNET_TIME_absolute_get_duration (asi->activated);
562     if (active_time.rel_value_us <=
563         ((double) GNUNET_TIME_UNIT_SECONDS.rel_value_us) * ctx->s->stability_factor)
564     {
565       /* Keep active address for stability reasons */
566       ctx->best = current;
567       return GNUNET_NO;
568     }
569   }
570   if (NULL == ctx->best)
571   {
572     /* We so far have nothing else, so go with it! */
573     ctx->best = current;
574     return GNUNET_OK;
575   }
576
577   /* Now compare ATS information */
578   cur_distance = current->norm_distance.norm;
579   best_distance = ctx->best->norm_distance.norm;
580   cur_delay = current->norm_delay.norm;
581   best_delay = ctx->best->norm_delay.norm;
582
583   /* user shorter distance */
584   if (cur_distance < best_distance)
585   {
586     if (GNUNET_NO == ctx->best->active)
587     {
588       /* Activity doesn't influence the equation, use current */
589       ctx->best = current;
590     }
591     else if ((best_distance / cur_distance) > ctx->s->stability_factor)
592     {
593       /* Distance change is significant, switch active address! */
594       ctx->best = current;
595     }
596   }
597
598   /* User connection with less delay */
599   if (cur_delay < best_delay)
600   {
601     if (GNUNET_NO == ctx->best->active)
602     {
603       /* Activity doesn't influence the equation, use current */
604       ctx->best = current;
605     }
606     else if ((best_delay / cur_delay) > ctx->s->stability_factor)
607     {
608       /* Latency change is significant, switch active address! */
609       ctx->best = current;
610     }
611   }
612   return GNUNET_OK;
613 }
614
615
616 /**
617  * Find the currently best address for a peer from the set of
618  * addresses available or return NULL of no address is available.
619  *
620  * @param s the proportional handle
621  * @param addresses the address hashmap
622  * @param id the peer id
623  * @return the address or NULL
624  */
625 struct ATS_Address *
626 get_best_address (struct GAS_PROPORTIONAL_Handle *s,
627                   struct GNUNET_CONTAINER_MultiPeerMap *addresses,
628                   const struct GNUNET_PeerIdentity *id)
629 {
630   struct FindBestAddressCtx fba_ctx;
631
632   fba_ctx.best = NULL;
633   fba_ctx.s = s;
634   GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
635                                               id,
636                                               &find_best_address_it,
637                                               &fba_ctx);
638   return fba_ctx.best;
639 }
640
641
642 /**
643  * Decrease number of active addresses in network.
644  *
645  * @param s the solver handle
646  * @param net the network type
647  */
648 static void
649 address_decrement_active (struct GAS_PROPORTIONAL_Handle *s,
650                           struct Network *net)
651 {
652   GNUNET_assert (net->active_addresses > 0);
653   net->active_addresses--;
654   GNUNET_STATISTICS_update (s->env->stats,
655                             net->stat_active,
656                             -1,
657                             GNUNET_NO);
658   GNUNET_assert (s->active_addresses > 0);
659   s->active_addresses--;
660   GNUNET_STATISTICS_update (s->env->stats,
661                             "# ATS addresses total",
662                             -1,
663                             GNUNET_NO);
664 }
665
666
667 /**
668  * Address map iterator to find current active address for peer.
669  * Asserts that only one address is active per peer.
670  *
671  * @param cls last active address
672  * @param key peer's key
673  * @param value address to check
674  * @return #GNUNET_NO on double active address else #GNUNET_YES;
675  */
676 static int
677 get_active_address_it (void *cls,
678                        const struct GNUNET_PeerIdentity *key,
679                        void *value)
680 {
681   struct ATS_Address **dest = cls;
682   struct ATS_Address *aa = value;
683
684   if (GNUNET_YES != aa->active)
685     return GNUNET_OK;
686   GNUNET_assert (NULL == (*dest));
687   (*dest) = aa;
688   return GNUNET_OK;
689 }
690
691
692 /**
693  * Find current active address for peer
694  *
695  * @param s the solver handle
696  * @param peer the peer
697  * @return active address or NULL
698  */
699 static struct ATS_Address *
700 get_active_address (struct GAS_PROPORTIONAL_Handle *s,
701                     const struct GNUNET_PeerIdentity *peer)
702 {
703   struct ATS_Address *dest;
704
705   dest = NULL;
706   GNUNET_CONTAINER_multipeermap_get_multiple (s->env->addresses,
707                                               peer,
708                                               &get_active_address_it,
709                                               &dest);
710   return dest;
711 }
712
713
714 /**
715  * Update active address for a peer.  Check if active address exists
716  * and what the best address is, if addresses are different switch.
717  * Then reallocate bandwidth within the affected network scopes.
718  *
719  * @param s solver handle
720  * @param current_address the address currently active for the peer,
721  *        NULL for none
722  * @param peer the peer to check
723  */
724 static void
725 update_active_address (struct GAS_PROPORTIONAL_Handle *s,
726                        struct ATS_Address *current_address,
727                        const struct GNUNET_PeerIdentity *peer)
728 {
729   struct ATS_Address *best_address;
730   struct AddressWrapper *asi_cur;
731   struct AddressWrapper *asi_best;
732   struct AddressWrapper *aw;
733   struct AddressWrapper *aw_min;
734   unsigned int a_con;
735   unsigned int con_min;
736
737   best_address = get_best_address (s,
738                                    s->env->addresses,
739                                    peer);
740   if (NULL != best_address)
741     asi_best = best_address->solver_information;
742   else
743     asi_best = NULL;
744   if (current_address == best_address)
745     return; /* no changes */
746   if (NULL != current_address)
747   {
748     /* We switch to a new address (or to none);
749        mark old address as inactive. */
750     asi_cur = current_address->solver_information;
751     GNUNET_assert (GNUNET_YES == current_address->active);
752     LOG (GNUNET_ERROR_TYPE_INFO,
753          "Disabling previous active address for peer `%s'\n",
754          GNUNET_i2s (peer));
755     asi_cur->activated = GNUNET_TIME_UNIT_ZERO_ABS;
756     current_address->active = GNUNET_NO;
757     current_address->assigned_bw_in = 0;
758     current_address->assigned_bw_out = 0;
759     address_decrement_active (s,
760                               asi_cur->network);
761     if ( (NULL == best_address) ||
762          (asi_best->network != asi_cur->network) )
763       distribute_bandwidth_in_network (s,
764                                        asi_cur->network);
765     if (NULL == best_address)
766     {
767       /* We previously had an active address, but now we cannot
768        * suggest one.  Therefore we have to disconnect the peer.
769        * The above call to "distribute_bandwidth_in_network()
770        * does not see 'current_address' so we need to trigger
771        * the update here. */
772       LOG (GNUNET_ERROR_TYPE_DEBUG,
773            "Disconnecting peer `%s'.\n",
774            GNUNET_i2s (peer));
775       s->env->bandwidth_changed_cb (s->env->cls,
776                                     current_address);
777       return;
778     }
779   }
780   if (NULL == best_address)
781   {
782     /* We do not have a new address, so we are done. */
783     LOG (GNUNET_ERROR_TYPE_DEBUG,
784          "Cannot suggest address for peer `%s'\n",
785          GNUNET_i2s (peer));
786     return;
787   }
788   /* We do have a new address, activate it */
789   LOG (GNUNET_ERROR_TYPE_DEBUG,
790        "Selecting new address %p for peer `%s'\n",
791        best_address,
792        GNUNET_i2s (peer));
793   /* Mark address as active */
794   best_address->active = GNUNET_YES;
795   asi_best->activated = GNUNET_TIME_absolute_get ();
796   asi_best->network->active_addresses++;
797   s->active_addresses++;
798   GNUNET_STATISTICS_update (s->env->stats,
799                             "# ATS active addresses total",
800                             1,
801                             GNUNET_NO);
802   GNUNET_STATISTICS_update (s->env->stats,
803                             asi_best->network->stat_active,
804                             1,
805                             GNUNET_NO);
806   LOG (GNUNET_ERROR_TYPE_INFO,
807        "Address %p for peer `%s' is now active\n",
808        best_address,
809        GNUNET_i2s (peer));
810
811   if (GNUNET_NO ==
812       is_bandwidth_available_in_network (asi_best->network,
813                                          0))
814   {
815     /* we went over the maximum number of addresses for
816        this scope; remove the address with the smallest
817        connectivity requirement */
818     con_min = UINT32_MAX;
819     aw_min = NULL;
820     for (aw = asi_best->network->head; NULL != aw; aw = aw->next)
821     {
822       if ( (con_min >
823             (a_con = s->env->get_connectivity (s->env->cls,
824                                                &aw->addr->peer))) &&
825            (GNUNET_YES == aw->addr->active) )
826       {
827         aw_min = aw;
828         con_min = a_con;
829         if (0 == con_min)
830           break;
831       }
832     }
833     update_active_address (s,
834                            aw_min->addr,
835                            &aw_min->addr->peer);
836   }
837   distribute_bandwidth_in_network (s,
838                                    asi_best->network);
839 }
840
841
842 /**
843  * The preferences for a peer in the problem changed.
844  *
845  * @param solver the solver handle
846  * @param peer the peer to change the preference for
847  * @param kind the kind to change the preference
848  * @param pref_rel the normalized preference value for this kind over all clients
849  */
850 static void
851 GAS_proportional_change_preference (void *solver,
852                                     const struct GNUNET_PeerIdentity *peer,
853                                     enum GNUNET_ATS_PreferenceKind kind,
854                                     double pref_rel)
855 {
856   struct GAS_PROPORTIONAL_Handle *s = solver;
857
858   if (GNUNET_ATS_PREFERENCE_BANDWIDTH != kind)
859     return; /* we do not care */
860   distribute_bandwidth_in_network (s,
861                                    NULL);
862 }
863
864
865 /**
866  * Get application feedback for a peer
867  *
868  * @param solver the solver handle
869  * @param application the application
870  * @param peer the peer to change the preference for
871  * @param scope the time interval for this feedback: [now - scope .. now]
872  * @param kind the kind to change the preference
873  * @param score the score
874  */
875 static void
876 GAS_proportional_feedback (void *solver,
877                            struct GNUNET_SERVICE_Client *application,
878                            const struct GNUNET_PeerIdentity *peer,
879                            const struct GNUNET_TIME_Relative scope,
880                            enum GNUNET_ATS_PreferenceKind kind,
881                            double score)
882 {
883   /* Proportional does not care about feedback */
884 }
885
886
887 /**
888  * Get the preferred address for a specific peer
889  *
890  * @param solver the solver handle
891  * @param peer the identity of the peer
892  */
893 static void
894 GAS_proportional_start_get_address (void *solver,
895                                     const struct GNUNET_PeerIdentity *peer)
896 {
897   struct GAS_PROPORTIONAL_Handle *s = solver;
898
899   update_active_address (s,
900                          get_active_address (s,
901                                              peer),
902                          peer);
903 }
904
905
906 /**
907  * Stop notifying about address and bandwidth changes for this peer
908  *
909  * @param solver the solver handle
910  * @param peer the peer
911  */
912 static void
913 GAS_proportional_stop_get_address (void *solver,
914                                    const struct GNUNET_PeerIdentity *peer)
915 {
916   struct GAS_PROPORTIONAL_Handle *s = solver;
917   struct ATS_Address *cur;
918   struct AddressWrapper *asi;
919
920   cur = get_active_address (s,
921                             peer);
922   if (NULL == cur)
923     return;
924   asi = cur->solver_information;
925   distribute_bandwidth_in_network (s,
926                                    asi->network);
927 }
928
929
930 /**
931  * Start a bulk operation
932  *
933  * @param solver the solver
934  */
935 static void
936 GAS_proportional_bulk_start (void *solver)
937 {
938   struct GAS_PROPORTIONAL_Handle *s = solver;
939
940   LOG (GNUNET_ERROR_TYPE_DEBUG,
941        "Locking solver for bulk operation ...\n");
942   GNUNET_assert (NULL != solver);
943   s->bulk_lock++;
944 }
945
946
947 /**
948  * Bulk operation done.
949  *
950  * @param solver our `struct GAS_PROPORTIONAL_Handle *`
951  */
952 static void
953 GAS_proportional_bulk_stop (void *solver)
954 {
955   struct GAS_PROPORTIONAL_Handle *s = solver;
956
957   LOG (GNUNET_ERROR_TYPE_DEBUG,
958        "Unlocking solver from bulk operation ...\n");
959   if (s->bulk_lock < 1)
960   {
961     GNUNET_break(0);
962     return;
963   }
964   s->bulk_lock--;
965   if ( (0 == s->bulk_lock) &&
966        (0 < s->bulk_requests) )
967   {
968     LOG (GNUNET_ERROR_TYPE_INFO,
969          "No lock pending, recalculating\n");
970     distribute_bandwidth_in_network (s,
971                                      NULL);
972     s->bulk_requests = 0;
973   }
974 }
975
976
977 /**
978  * Transport properties for this address have changed
979  *
980  * @param solver solver handle
981  * @param address the address
982  */
983 static void
984 GAS_proportional_address_property_changed (void *solver,
985                                            struct ATS_Address *address)
986 {
987   struct GAS_PROPORTIONAL_Handle *s = solver;
988   struct AddressWrapper *asi = address->solver_information;
989
990   distribute_bandwidth_in_network (s,
991                                    asi->network);
992 }
993
994
995 /**
996  * Add a new single address to a network
997  *
998  * @param solver the solver Handle
999  * @param address the address to add
1000  * @param network network type of this address
1001  */
1002 static void
1003 GAS_proportional_address_add (void *solver,
1004                               struct ATS_Address *address,
1005                               enum GNUNET_ATS_Network_Type network)
1006 {
1007   struct GAS_PROPORTIONAL_Handle *s = solver;
1008   struct Network *net;
1009   struct AddressWrapper *aw;
1010
1011   GNUNET_assert (network < s->env->network_count);
1012   net = &s->network_entries[network];
1013   net->total_addresses++;
1014
1015   aw = GNUNET_new (struct AddressWrapper);
1016   aw->addr = address;
1017   aw->network = net;
1018   address->solver_information = aw;
1019   GNUNET_CONTAINER_DLL_insert (net->head,
1020                                net->tail,
1021                                aw);
1022   GNUNET_STATISTICS_update (s->env->stats,
1023                             "# ATS addresses total",
1024                             1,
1025                             GNUNET_NO);
1026   GNUNET_STATISTICS_update (s->env->stats,
1027                             net->stat_total,
1028                             1,
1029                             GNUNET_NO);
1030   update_active_address (s,
1031                          get_active_address (s,
1032                                              &address->peer),
1033                          &address->peer);
1034   LOG (GNUNET_ERROR_TYPE_INFO,
1035        "Added new address for `%s', now total %u and active %u addresses in network `%s'\n",
1036        GNUNET_i2s (&address->peer),
1037        net->total_addresses,
1038        net->active_addresses,
1039        net->desc);
1040 }
1041
1042
1043 /**
1044  * Remove an address from the solver. To do so, we:
1045  * - Removed it from specific network
1046  * - Decrease the number of total addresses
1047  * - If active:
1048  *   - decrease number of active addreses
1049  *   - update quotas
1050  *
1051  * @param solver the solver handle
1052  * @param address the address to remove
1053  */
1054 static void
1055 GAS_proportional_address_delete (void *solver,
1056                                  struct ATS_Address *address)
1057 {
1058   struct GAS_PROPORTIONAL_Handle *s = solver;
1059   struct AddressWrapper *aw = address->solver_information;
1060   struct Network *net = aw->network;
1061
1062   LOG (GNUNET_ERROR_TYPE_DEBUG,
1063        "Deleting %s address for peer `%s' from network `%s' (total: %u/active: %u)\n",
1064        (GNUNET_NO == address->active) ? "inactive" : "active",
1065        GNUNET_i2s (&address->peer),
1066        net->desc,
1067        net->total_addresses,
1068        net->active_addresses);
1069
1070   GNUNET_CONTAINER_DLL_remove (net->head,
1071                                net->tail,
1072                                aw);
1073   GNUNET_assert (net->total_addresses > 0);
1074   net->total_addresses--;
1075   GNUNET_STATISTICS_update (s->env->stats,
1076                             net->stat_total,
1077                             -1,
1078                             GNUNET_NO);
1079   if (GNUNET_YES == address->active)
1080   {
1081     /* Address was active, remove from network and update quotas */
1082     update_active_address (s,
1083                            address,
1084                            &address->peer);
1085     distribute_bandwidth_in_network (s, net);
1086   }
1087   GNUNET_free (aw);
1088   address->solver_information = NULL;
1089   LOG (GNUNET_ERROR_TYPE_DEBUG,
1090        "After deleting address now total %u and active %u addresses in network `%s'\n",
1091        net->total_addresses,
1092        net->active_addresses,
1093        net->desc);
1094 }
1095
1096
1097 /**
1098  * Function invoked when the plugin is loaded.
1099  *
1100  * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1101  *            modified to return the API functions (ugh).
1102  * @return the `struct GAS_PROPORTIONAL_Handle` to pass as a closure
1103  */
1104 void *
1105 libgnunet_plugin_ats_proportional_init (void *cls)
1106 {
1107   static struct GNUNET_ATS_SolverFunctions sf;
1108   struct GNUNET_ATS_PluginEnvironment *env = cls;
1109   struct GAS_PROPORTIONAL_Handle *s;
1110   struct Network * cur;
1111   float f_tmp;
1112   unsigned int c;
1113
1114   s = GNUNET_new (struct GAS_PROPORTIONAL_Handle);
1115   s->env = env;
1116   sf.cls = s;
1117   sf.s_add = &GAS_proportional_address_add;
1118   sf.s_address_update_property = &GAS_proportional_address_property_changed;
1119   sf.s_get = &GAS_proportional_start_get_address;
1120   sf.s_get_stop = &GAS_proportional_stop_get_address;
1121   sf.s_pref = &GAS_proportional_change_preference;
1122   sf.s_feedback = &GAS_proportional_feedback;
1123   sf.s_del = &GAS_proportional_address_delete;
1124   sf.s_bulk_start = &GAS_proportional_bulk_start;
1125   sf.s_bulk_stop = &GAS_proportional_bulk_stop;
1126   s->stability_factor = PROP_STABILITY_FACTOR;
1127   if (GNUNET_SYSERR !=
1128       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1129                                             "ats",
1130                                             "PROP_STABILITY_FACTOR",
1131                                             &f_tmp))
1132   {
1133     if ((f_tmp < 1.0) || (f_tmp > 2.0))
1134     {
1135       LOG (GNUNET_ERROR_TYPE_ERROR,
1136            _("Invalid %s configuration %f \n"),
1137            "PROP_STABILITY_FACTOR",
1138            f_tmp);
1139     }
1140     else
1141     {
1142       s->stability_factor = f_tmp;
1143       LOG (GNUNET_ERROR_TYPE_INFO,
1144            "Using %s of %.3f\n",
1145            "PROP_STABILITY_FACTOR",
1146            f_tmp);
1147     }
1148   }
1149   s->prop_factor = PROPORTIONALITY_FACTOR;
1150   if (GNUNET_SYSERR !=
1151       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1152                                             "ats",
1153                                             "PROP_PROPORTIONALITY_FACTOR",
1154                                             &f_tmp))
1155   {
1156     if (f_tmp < 1.0)
1157     {
1158       LOG (GNUNET_ERROR_TYPE_ERROR,
1159            _("Invalid %s configuration %f\n"),
1160            "PROP_PROPORTIONALITY_FACTOR",
1161            f_tmp);
1162     }
1163     else
1164     {
1165       s->prop_factor = f_tmp;
1166       LOG (GNUNET_ERROR_TYPE_INFO,
1167            "Using %s of %.3f\n",
1168            "PROP_PROPORTIONALITY_FACTOR",
1169            f_tmp);
1170     }
1171   }
1172
1173   s->network_entries = GNUNET_malloc (env->network_count *
1174                                       sizeof (struct Network));
1175   for (c = 0; c < env->network_count; c++)
1176   {
1177     cur = &s->network_entries[c];
1178     cur->type = c;
1179     cur->total_quota_in = env->in_quota[c];
1180     cur->total_quota_out = env->out_quota[c];
1181     cur->desc = GNUNET_ATS_print_network_type (c);
1182     GNUNET_asprintf (&cur->stat_total,
1183                      "# ATS addresses %s total",
1184                      cur->desc);
1185     GNUNET_asprintf (&cur->stat_active,
1186                      "# ATS active addresses %s total",
1187                      cur->desc);
1188     LOG (GNUNET_ERROR_TYPE_INFO,
1189          "Added network %u `%s' (%llu/%llu)\n",
1190          c,
1191          cur->desc,
1192          cur->total_quota_in,
1193          cur->total_quota_out);
1194   }
1195   return &sf;
1196 }
1197
1198
1199 /**
1200  * Function used to unload the plugin.
1201  *
1202  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1203  */
1204 void *
1205 libgnunet_plugin_ats_proportional_done (void *cls)
1206 {
1207   struct GNUNET_ATS_SolverFunctions *sf = cls;
1208   struct GAS_PROPORTIONAL_Handle *s = sf->cls;
1209   struct AddressWrapper *cur;
1210   struct AddressWrapper *next;
1211   unsigned int c;
1212
1213   for (c = 0; c < s->env->network_count; c++)
1214   {
1215     GNUNET_break (0 == s->network_entries[c].total_addresses);
1216     GNUNET_break (0 == s->network_entries[c].active_addresses);
1217     next = s->network_entries[c].head;
1218     while (NULL != (cur = next))
1219     {
1220       next = cur->next;
1221       GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
1222                                    s->network_entries[c].tail,
1223                                    cur);
1224       GNUNET_free_non_null (cur->addr->solver_information);
1225       GNUNET_free(cur);
1226     }
1227     GNUNET_free (s->network_entries[c].stat_total);
1228     GNUNET_free (s->network_entries[c].stat_active);
1229   }
1230   GNUNET_break (0 == s->active_addresses);
1231   GNUNET_free (s->network_entries);
1232   GNUNET_free (s);
1233   return NULL;
1234 }
1235
1236
1237 /* end of plugin_ats_proportional.c */