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