add changelog
[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 Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file ats/plugin_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  * Representation of a network
102  */
103 struct Network
104 {
105   /**
106    * Network description
107    */
108   const char *desc;
109
110   /**
111    * String for statistics total addresses
112    */
113   char *stat_total;
114
115   /**
116    * String for statistics active addresses
117    */
118   char *stat_active;
119
120   /**
121    * Linked list of addresses in this network: head
122    */
123   struct AddressWrapper *head;
124
125   /**
126    * Linked list of addresses in this network: tail
127    */
128   struct AddressWrapper *tail;
129
130   /**
131    * Total inbound quota
132    */
133   unsigned long long total_quota_in;
134
135   /**
136    * Total outbound quota
137    */
138   unsigned long long total_quota_out;
139
140   /**
141    * ATS network type
142    */
143   enum GNUNET_NetworkType type;
144
145   /**
146    * Number of active addresses for this network
147    */
148   unsigned int active_addresses;
149
150   /**
151    * Number of total addresses for this network
152    */
153   unsigned int total_addresses;
154 };
155
156
157 /**
158  * A handle for the proportional solver
159  */
160 struct GAS_PROPORTIONAL_Handle
161 {
162   /**
163    * Our execution environment.
164    */
165   struct GNUNET_ATS_PluginEnvironment *env;
166
167   /**
168    * Networks array
169    */
170   struct Network *network_entries;
171
172   /**
173    * Proportionality factor
174    */
175   double prop_factor;
176
177   /**
178    * Stability factor
179    */
180   double stability_factor;
181
182   /**
183    * Bulk lock counter. If zero, we are not locked.
184    */
185   unsigned int bulk_lock;
186
187   /**
188    * Number of changes made while solver was locked.  We really only
189    * use 0/non-zero to check on unlock if we have to run the update.
190    */
191   unsigned int bulk_requests;
192
193   /**
194    * Number of active addresses for solver
195    */
196   unsigned int active_addresses;
197 };
198
199
200 /**
201  * Test if bandwidth is available in this network to add an additional address.
202  *
203  * @param net the network type to check
204  * @param extra for how many extra addresses do we check?
205  * @return #GNUNET_YES or #GNUNET_NO
206  */
207 static int
208 is_bandwidth_available_in_network (struct Network *net,
209                                    int extra)
210 {
211   unsigned int na;
212   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
213
214   GNUNET_assert (((int) net->active_addresses) + extra >= 0);
215   na = net->active_addresses + extra;
216   if (0 == na)
217     return GNUNET_YES;
218   if (((net->total_quota_in / na) > min_bw) &&
219       ((net->total_quota_out / na) > min_bw))
220     return GNUNET_YES;
221   return GNUNET_NO;
222 }
223
224
225 /**
226  * Test if all peers in this network require connectivity at level at
227  * least @a con.
228  *
229  * @param s the solver handle
230  * @param net the network type to check
231  * @param con connection return value threshold to check
232  * @return #GNUNET_YES or #GNUNET_NO
233  */
234 static int
235 all_require_connectivity (struct GAS_PROPORTIONAL_Handle *s,
236                           struct Network *net,
237                           unsigned int con)
238 {
239   struct AddressWrapper *aw;
240
241   for (aw = net->head; NULL != aw; aw = aw->next)
242     if (con >
243         s->env->get_connectivity (s->env->cls,
244                                   &aw->addr->peer))
245       return GNUNET_NO;
246   return GNUNET_YES;
247 }
248
249
250 /**
251  * Update bandwidth assigned to peers in this network.  The basic idea
252  * is to assign every peer in the network the minimum bandwidth, and
253  * then distribute the remaining bandwidth proportional to application
254  * preferences.
255  *
256  * @param s the solver handle
257  * @param net the network type to update
258  */
259 static void
260 distribute_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
261                       struct Network *net)
262 {
263   const uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
264   struct AddressWrapper *aw;
265   unsigned long long remaining_quota_in;
266   unsigned long long quota_out_used;
267   unsigned long long remaining_quota_out;
268   unsigned long long quota_in_used;
269   unsigned int count_addresses;
270   double sum_relative_peer_prefences;
271   double peer_weight;
272   double total_weight;
273   const double *peer_relative_prefs;
274
275   LOG (GNUNET_ERROR_TYPE_INFO,
276        "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
277        net->desc,
278        net->active_addresses,
279        net->total_quota_in,
280        net->total_quota_in);
281
282   if (0 == net->active_addresses)
283     return; /* no addresses to update */
284
285   /* sanity checks */
286   if ((net->active_addresses * min_bw) > net->total_quota_in)
287   {
288     GNUNET_break (0);
289     return;
290   }
291   if ((net->active_addresses * min_bw) > net->total_quota_out)
292   {
293     GNUNET_break (0);
294     return;
295   }
296
297   /* Calculate sum of relative preference for active addresses in this
298      network */
299   sum_relative_peer_prefences = 0.0;
300   count_addresses = 0;
301   for (aw = net->head; NULL != aw; aw = aw->next)
302   {
303     if (GNUNET_YES != aw->addr->active)
304       continue;
305     peer_relative_prefs = s->env->get_preferences (s->env->cls,
306                                                    &aw->addr->peer);
307     sum_relative_peer_prefences
308       += peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
309     count_addresses++;
310   }
311   if (count_addresses != net->active_addresses)
312   {
313     GNUNET_break (0);
314     LOG (GNUNET_ERROR_TYPE_WARNING,
315          "%s: Counted %u active addresses, expected %u active addresses\n",
316          net->desc,
317          count_addresses,
318          net->active_addresses);
319     /* try to fix... */
320     net->active_addresses = count_addresses;
321   }
322   LOG (GNUNET_ERROR_TYPE_INFO,
323        "Total relative preference %.3f for %u addresses in network %s\n",
324        sum_relative_peer_prefences,
325        net->active_addresses,
326        net->desc);
327
328   /* check how much we have to distribute */
329   remaining_quota_in = net->total_quota_in - (net->active_addresses * min_bw);
330   remaining_quota_out = net->total_quota_out - (net->active_addresses * min_bw);
331   LOG (GNUNET_ERROR_TYPE_DEBUG,
332        "Proportionally distributable bandwidth (in/out): %llu/%llu\n",
333        remaining_quota_in,
334        remaining_quota_out);
335
336   /* distribute remaining quota; we do not do it exactly proportional,
337      but balance "even" distribution ("net->active_addresses") with
338      the preference sum using the "prop_factor". */
339   total_weight = net->active_addresses
340                  + s->prop_factor * sum_relative_peer_prefences;
341   quota_out_used = 0;
342   quota_in_used = 0;
343   for (aw = net->head; NULL != aw; aw = aw->next)
344   {
345     if (GNUNET_YES != aw->addr->active)
346     {
347       /* set to 0, just to be sure */
348       aw->calculated_quota_in = 0;
349       aw->calculated_quota_out = 0;
350       continue;
351     }
352     peer_relative_prefs = s->env->get_preferences (s->env->cls,
353                                                    &aw->addr->peer);
354     peer_weight = 1.0
355                   + s->prop_factor
356                   * peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
357
358     aw->calculated_quota_in = min_bw
359                               + (peer_weight / total_weight)
360                               * remaining_quota_in;
361     aw->calculated_quota_out = min_bw
362                                + (peer_weight / total_weight)
363                                * remaining_quota_out;
364
365     LOG (GNUNET_ERROR_TYPE_INFO,
366          "New quotas for peer `%s' with weight (cur/total) %.3f/%.3f (in/out) are: %u/%u\n",
367          GNUNET_i2s (&aw->addr->peer),
368          peer_weight,
369          total_weight,
370          (unsigned int) aw->calculated_quota_in,
371          (unsigned int) aw->calculated_quota_out);
372     quota_in_used += aw->calculated_quota_in;
373     quota_out_used += aw->calculated_quota_out;
374   }
375   LOG (GNUNET_ERROR_TYPE_DEBUG,
376        "Total bandwidth assigned is (in/out): %llu /%llu\n",
377        quota_in_used,
378        quota_out_used);
379   /* +1 due to possible rounding errors */
380   GNUNET_break (quota_out_used <= net->total_quota_out + 1);
381   GNUNET_break (quota_in_used <= net->total_quota_in + 1);
382 }
383
384
385 /**
386  * Notify ATS service of bandwidth changes to addresses.
387  *
388  * @param s solver handle
389  * @param net the network to propagate changes in
390  */
391 static void
392 propagate_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
393                      struct Network *net)
394 {
395   struct AddressWrapper *cur;
396
397   for (cur = net->head; NULL != cur; cur = cur->next)
398   {
399     if ((cur->addr->assigned_bw_in == cur->calculated_quota_in) &&
400         (cur->addr->assigned_bw_out == cur->calculated_quota_out))
401       continue;
402     cur->addr->assigned_bw_in = cur->calculated_quota_in;
403     cur->addr->assigned_bw_out = cur->calculated_quota_out;
404     if (GNUNET_YES == cur->addr->active)
405       s->env->bandwidth_changed_cb (s->env->cls,
406                                     cur->addr);
407   }
408 }
409
410
411 /**
412  * Distribute bandwidth.  The addresses have already been selected,
413  * this is merely distributed the bandwidth among the addresses.
414  *
415  * @param s the solver handle
416  * @param n the network, can be NULL for all networks
417  */
418 static void
419 distribute_bandwidth_in_network (struct GAS_PROPORTIONAL_Handle *s,
420                                  struct Network *n)
421 {
422   unsigned int i;
423
424   if (0 != s->bulk_lock)
425   {
426     s->bulk_requests++;
427     return;
428   }
429   if (NULL != n)
430   {
431     LOG (GNUNET_ERROR_TYPE_DEBUG,
432          "Redistributing bandwidth in network %s with %u active and %u total addresses\n",
433          GNUNET_NT_to_string (n->type),
434          n->active_addresses,
435          n->total_addresses);
436     s->env->info_cb (s->env->cls,
437                      GAS_OP_SOLVE_START,
438                      GAS_STAT_SUCCESS,
439                      GAS_INFO_PROP_SINGLE);
440     distribute_bandwidth (s,
441                           n);
442     s->env->info_cb (s->env->cls,
443                      GAS_OP_SOLVE_STOP,
444                      GAS_STAT_SUCCESS,
445                      GAS_INFO_PROP_SINGLE);
446     s->env->info_cb (s->env->cls,
447                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
448                      GAS_STAT_SUCCESS,
449                      GAS_INFO_PROP_SINGLE);
450     propagate_bandwidth (s,
451                          n);
452
453     s->env->info_cb (s->env->cls,
454                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
455                      GAS_STAT_SUCCESS,
456                      GAS_INFO_PROP_SINGLE);
457   }
458   else
459   {
460     LOG (GNUNET_ERROR_TYPE_DEBUG,
461          "Redistributing bandwidth in all %u networks\n",
462          s->env->network_count);
463     s->env->info_cb (s->env->cls,
464                      GAS_OP_SOLVE_START,
465                      GAS_STAT_SUCCESS,
466                      GAS_INFO_PROP_ALL);
467     for (i = 0; i < s->env->network_count; i++)
468       distribute_bandwidth (s,
469                             &s->network_entries[i]);
470     s->env->info_cb (s->env->cls,
471                      GAS_OP_SOLVE_STOP,
472                      GAS_STAT_SUCCESS,
473                      GAS_INFO_PROP_ALL);
474     s->env->info_cb (s->env->cls,
475                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
476                      GAS_STAT_SUCCESS,
477                      GAS_INFO_PROP_ALL);
478     for (i = 0; i < s->env->network_count; i++)
479       propagate_bandwidth (s,
480                            &s->network_entries[i]);
481     s->env->info_cb (s->env->cls,
482                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
483                      GAS_STAT_SUCCESS,
484                      GAS_INFO_PROP_ALL);
485   }
486 }
487
488
489 /**
490  * Context for finding the best address* Linked list of addresses in this network: head
491  */
492 struct FindBestAddressCtx
493 {
494   /**
495    * The solver handle
496    */
497   struct GAS_PROPORTIONAL_Handle *s;
498
499   /**
500    * The currently best address
501    */
502   struct ATS_Address *best;
503 };
504
505
506 /**
507  * Find a "good" address to use for a peer by iterating over the
508  * addresses for this peer.  If we already have an existing address,
509  * we stick to it.  Otherwise, we pick by lowest distance and then by
510  * lowest latency.
511  *
512  * @param cls the `struct FindBestAddressCtx *' where we store the result
513  * @param key the peer we are trying to find the best address for
514  * @param value another `struct ATS_Address*` to consider using
515  * @return #GNUNET_OK (continue to iterate)
516  */
517 static int
518 find_best_address_it (void *cls,
519                       const struct GNUNET_PeerIdentity *key,
520                       void *value)
521 {
522   struct FindBestAddressCtx *ctx = cls;
523   struct ATS_Address *current = value;
524   struct AddressWrapper *asi = current->solver_information;
525   struct GNUNET_TIME_Relative active_time;
526   double best_delay;
527   double best_distance;
528   double cur_delay;
529   double cur_distance;
530   unsigned int con;
531   int bw_available;
532   int need;
533
534   /* we need +1 slot if 'current' is not yet active */
535   need = (GNUNET_YES == current->active) ? 0 : 1;
536   /* we save -1 slot if 'best' is active and belongs
537      to the same network (as we would replace it) */
538   if ((NULL != ctx->best) &&
539       (GNUNET_YES == ctx->best->active) &&
540       (((struct AddressWrapper *) ctx->best->solver_information)->network ==
541        asi->network))
542     need--;
543   /* we can gain -1 slot if this peers connectivity
544      requirement is higher than that of another peer
545      in that network scope */
546   con = ctx->s->env->get_connectivity (ctx->s->env->cls,
547                                        key);
548   if (GNUNET_YES !=
549       all_require_connectivity (ctx->s,
550                                 asi->network,
551                                 con))
552     need--;
553   /* test if minimum bandwidth for 'current' would be available */
554   bw_available
555     = is_bandwidth_available_in_network (asi->network,
556                                          need);
557   if (! bw_available)
558   {
559     /* Bandwidth for this address is unavailable, so we cannot use
560        it. */
561     return GNUNET_OK;
562   }
563   if (GNUNET_YES == current->active)
564   {
565     active_time = GNUNET_TIME_absolute_get_duration (asi->activated);
566     if (active_time.rel_value_us <=
567         ((double) GNUNET_TIME_UNIT_SECONDS.rel_value_us)
568         * 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. */LOG (GNUNET_ERROR_TYPE_DEBUG,
777            "Disconnecting peer `%s'.\n",
778            GNUNET_i2s (peer));
779       s->env->bandwidth_changed_cb (s->env->cls,
780                                     current_address);
781       return;
782     }
783   }
784   if (NULL == best_address)
785   {
786     /* We do not have a new address, so we are done. */
787     LOG (GNUNET_ERROR_TYPE_DEBUG,
788          "Cannot suggest address for peer `%s'\n",
789          GNUNET_i2s (peer));
790     return;
791   }
792   /* We do have a new address, activate it */
793   LOG (GNUNET_ERROR_TYPE_DEBUG,
794        "Selecting new address %p for peer `%s'\n",
795        best_address,
796        GNUNET_i2s (peer));
797   /* Mark address as active */
798   best_address->active = GNUNET_YES;
799   asi_best->activated = GNUNET_TIME_absolute_get ();
800   asi_best->network->active_addresses++;
801   s->active_addresses++;
802   GNUNET_STATISTICS_update (s->env->stats,
803                             "# ATS active addresses total",
804                             1,
805                             GNUNET_NO);
806   GNUNET_STATISTICS_update (s->env->stats,
807                             asi_best->network->stat_active,
808                             1,
809                             GNUNET_NO);
810   LOG (GNUNET_ERROR_TYPE_INFO,
811        "Address %p for peer `%s' is now active\n",
812        best_address,
813        GNUNET_i2s (peer));
814
815   if (GNUNET_NO ==
816       is_bandwidth_available_in_network (asi_best->network,
817                                          0))
818   {
819     /* we went over the maximum number of addresses for
820        this scope; remove the address with the smallest
821        connectivity requirement */
822     con_min = UINT32_MAX;
823     aw_min = NULL;
824     for (aw = asi_best->network->head; NULL != aw; aw = aw->next)
825     {
826       if ((con_min >
827            (a_con = s->env->get_connectivity (s->env->cls,
828                                               &aw->addr->peer))) &&
829           (GNUNET_YES == aw->addr->active))
830       {
831         aw_min = aw;
832         con_min = a_con;
833         if (0 == con_min)
834           break;
835       }
836     }
837     update_active_address (s,
838                            aw_min->addr,
839                            &aw_min->addr->peer);
840   }
841   distribute_bandwidth_in_network (s,
842                                    asi_best->network);
843 }
844
845
846 /**
847  * The preferences for a peer in the problem changed.
848  *
849  * @param solver the solver handle
850  * @param peer the peer to change the preference for
851  * @param kind the kind to change the preference
852  * @param pref_rel the normalized preference value for this kind over all clients
853  */
854 static void
855 GAS_proportional_change_preference (void *solver,
856                                     const struct GNUNET_PeerIdentity *peer,
857                                     enum GNUNET_ATS_PreferenceKind kind,
858                                     double pref_rel)
859 {
860   struct GAS_PROPORTIONAL_Handle *s = solver;
861
862   if (GNUNET_ATS_PREFERENCE_BANDWIDTH != kind)
863     return; /* we do not care */
864   distribute_bandwidth_in_network (s,
865                                    NULL);
866 }
867
868
869 /**
870  * Get application feedback for a peer
871  *
872  * @param solver the solver handle
873  * @param application the application
874  * @param peer the peer to change the preference for
875  * @param scope the time interval for this feedback: [now - scope .. now]
876  * @param kind the kind to change the preference
877  * @param score the score
878  */
879 static void
880 GAS_proportional_feedback (void *solver,
881                            struct GNUNET_SERVICE_Client *application,
882                            const struct GNUNET_PeerIdentity *peer,
883                            const struct GNUNET_TIME_Relative scope,
884                            enum GNUNET_ATS_PreferenceKind kind,
885                            double score)
886 {
887   /* Proportional does not care about feedback */
888 }
889
890
891 /**
892  * Get the preferred address for a specific peer
893  *
894  * @param solver the solver handle
895  * @param peer the identity of the peer
896  */
897 static void
898 GAS_proportional_start_get_address (void *solver,
899                                     const struct GNUNET_PeerIdentity *peer)
900 {
901   struct GAS_PROPORTIONAL_Handle *s = solver;
902
903   update_active_address (s,
904                          get_active_address (s,
905                                              peer),
906                          peer);
907 }
908
909
910 /**
911  * Stop notifying about address and bandwidth changes for this peer
912  *
913  * @param solver the solver handle
914  * @param peer the peer
915  */
916 static void
917 GAS_proportional_stop_get_address (void *solver,
918                                    const struct GNUNET_PeerIdentity *peer)
919 {
920   struct GAS_PROPORTIONAL_Handle *s = solver;
921   struct ATS_Address *cur;
922   struct AddressWrapper *asi;
923
924   cur = get_active_address (s,
925                             peer);
926   if (NULL == cur)
927     return;
928   asi = cur->solver_information;
929   distribute_bandwidth_in_network (s,
930                                    asi->network);
931 }
932
933
934 /**
935  * Start a bulk operation
936  *
937  * @param solver the solver
938  */
939 static void
940 GAS_proportional_bulk_start (void *solver)
941 {
942   struct GAS_PROPORTIONAL_Handle *s = solver;
943
944   LOG (GNUNET_ERROR_TYPE_DEBUG,
945        "Locking solver for bulk operation ...\n");
946   GNUNET_assert (NULL != solver);
947   s->bulk_lock++;
948 }
949
950
951 /**
952  * Bulk operation done.
953  *
954  * @param solver our `struct GAS_PROPORTIONAL_Handle *`
955  */
956 static void
957 GAS_proportional_bulk_stop (void *solver)
958 {
959   struct GAS_PROPORTIONAL_Handle *s = solver;
960
961   LOG (GNUNET_ERROR_TYPE_DEBUG,
962        "Unlocking solver from bulk operation ...\n");
963   if (s->bulk_lock < 1)
964   {
965     GNUNET_break (0);
966     return;
967   }
968   s->bulk_lock--;
969   if ((0 == s->bulk_lock) &&
970       (0 < s->bulk_requests))
971   {
972     LOG (GNUNET_ERROR_TYPE_INFO,
973          "No lock pending, recalculating\n");
974     distribute_bandwidth_in_network (s,
975                                      NULL);
976     s->bulk_requests = 0;
977   }
978 }
979
980
981 /**
982  * Transport properties for this address have changed
983  *
984  * @param solver solver handle
985  * @param address the address
986  */
987 static void
988 GAS_proportional_address_property_changed (void *solver,
989                                            struct ATS_Address *address)
990 {
991   struct GAS_PROPORTIONAL_Handle *s = solver;
992   struct AddressWrapper *asi = address->solver_information;
993
994   distribute_bandwidth_in_network (s,
995                                    asi->network);
996 }
997
998
999 /**
1000  * Add a new single address to a network
1001  *
1002  * @param solver the solver Handle
1003  * @param address the address to add
1004  * @param network network type of this address
1005  */
1006 static void
1007 GAS_proportional_address_add (void *solver,
1008                               struct ATS_Address *address,
1009                               uint32_t network)
1010 {
1011   struct GAS_PROPORTIONAL_Handle *s = solver;
1012   struct Network *net;
1013   struct AddressWrapper *aw;
1014
1015   GNUNET_assert (network < s->env->network_count);
1016   net = &s->network_entries[network];
1017   net->total_addresses++;
1018
1019   aw = GNUNET_new (struct AddressWrapper);
1020   aw->addr = address;
1021   aw->network = net;
1022   address->solver_information = aw;
1023   GNUNET_CONTAINER_DLL_insert (net->head,
1024                                net->tail,
1025                                aw);
1026   GNUNET_STATISTICS_update (s->env->stats,
1027                             "# ATS addresses total",
1028                             1,
1029                             GNUNET_NO);
1030   GNUNET_STATISTICS_update (s->env->stats,
1031                             net->stat_total,
1032                             1,
1033                             GNUNET_NO);
1034   update_active_address (s,
1035                          get_active_address (s,
1036                                              &address->peer),
1037                          &address->peer);
1038   LOG (GNUNET_ERROR_TYPE_INFO,
1039        "Added new address for `%s', now total %u and active %u addresses in network `%s'\n",
1040        GNUNET_i2s (&address->peer),
1041        net->total_addresses,
1042        net->active_addresses,
1043        net->desc);
1044 }
1045
1046
1047 /**
1048  * Remove an address from the solver. To do so, we:
1049  * - Removed it from specific network
1050  * - Decrease the number of total addresses
1051  * - If active:
1052  *   - decrease number of active addreses
1053  *   - update quotas
1054  *
1055  * @param solver the solver handle
1056  * @param address the address to remove
1057  */
1058 static void
1059 GAS_proportional_address_delete (void *solver,
1060                                  struct ATS_Address *address)
1061 {
1062   struct GAS_PROPORTIONAL_Handle *s = solver;
1063   struct AddressWrapper *aw = address->solver_information;
1064   struct Network *net = aw->network;
1065
1066   LOG (GNUNET_ERROR_TYPE_DEBUG,
1067        "Deleting %s address for peer `%s' from network `%s' (total: %u/active: %u)\n",
1068        (GNUNET_NO == address->active) ? "inactive" : "active",
1069        GNUNET_i2s (&address->peer),
1070        net->desc,
1071        net->total_addresses,
1072        net->active_addresses);
1073
1074   GNUNET_CONTAINER_DLL_remove (net->head,
1075                                net->tail,
1076                                aw);
1077   GNUNET_assert (net->total_addresses > 0);
1078   net->total_addresses--;
1079   GNUNET_STATISTICS_update (s->env->stats,
1080                             net->stat_total,
1081                             -1,
1082                             GNUNET_NO);
1083   if (GNUNET_YES == address->active)
1084   {
1085     /* Address was active, remove from network and update quotas */
1086     update_active_address (s,
1087                            address,
1088                            &address->peer);
1089     distribute_bandwidth_in_network (s, net);
1090   }
1091   GNUNET_free (aw);
1092   address->solver_information = NULL;
1093   LOG (GNUNET_ERROR_TYPE_DEBUG,
1094        "After deleting address now total %u and active %u addresses in network `%s'\n",
1095        net->total_addresses,
1096        net->active_addresses,
1097        net->desc);
1098 }
1099
1100
1101 /**
1102  * Function invoked when the plugin is loaded.
1103  *
1104  * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1105  *            modified to return the API functions (ugh).
1106  * @return the `struct GAS_PROPORTIONAL_Handle` to pass as a closure
1107  */
1108 void *
1109 libgnunet_plugin_ats_proportional_init (void *cls)
1110 {
1111   static struct GNUNET_ATS_SolverFunctions sf;
1112   struct GNUNET_ATS_PluginEnvironment *env = cls;
1113   struct GAS_PROPORTIONAL_Handle *s;
1114   struct Network *cur;
1115   float f_tmp;
1116   unsigned int c;
1117
1118   s = GNUNET_new (struct GAS_PROPORTIONAL_Handle);
1119   s->env = env;
1120   sf.cls = s;
1121   sf.s_add = &GAS_proportional_address_add;
1122   sf.s_address_update_property = &GAS_proportional_address_property_changed;
1123   sf.s_get = &GAS_proportional_start_get_address;
1124   sf.s_get_stop = &GAS_proportional_stop_get_address;
1125   sf.s_pref = &GAS_proportional_change_preference;
1126   sf.s_feedback = &GAS_proportional_feedback;
1127   sf.s_del = &GAS_proportional_address_delete;
1128   sf.s_bulk_start = &GAS_proportional_bulk_start;
1129   sf.s_bulk_stop = &GAS_proportional_bulk_stop;
1130   s->stability_factor = PROP_STABILITY_FACTOR;
1131   if (GNUNET_SYSERR !=
1132       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1133                                             "ats",
1134                                             "PROP_STABILITY_FACTOR",
1135                                             &f_tmp))
1136   {
1137     if ((f_tmp < 1.0) || (f_tmp > 2.0))
1138     {
1139       LOG (GNUNET_ERROR_TYPE_ERROR,
1140            _ ("Invalid %s configuration %f \n"),
1141            "PROP_STABILITY_FACTOR",
1142            f_tmp);
1143     }
1144     else
1145     {
1146       s->stability_factor = f_tmp;
1147       LOG (GNUNET_ERROR_TYPE_INFO,
1148            "Using %s of %.3f\n",
1149            "PROP_STABILITY_FACTOR",
1150            f_tmp);
1151     }
1152   }
1153   s->prop_factor = PROPORTIONALITY_FACTOR;
1154   if (GNUNET_SYSERR !=
1155       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1156                                             "ats",
1157                                             "PROP_PROPORTIONALITY_FACTOR",
1158                                             &f_tmp))
1159   {
1160     if (f_tmp < 1.0)
1161     {
1162       LOG (GNUNET_ERROR_TYPE_ERROR,
1163            _ ("Invalid %s configuration %f\n"),
1164            "PROP_PROPORTIONALITY_FACTOR",
1165            f_tmp);
1166     }
1167     else
1168     {
1169       s->prop_factor = f_tmp;
1170       LOG (GNUNET_ERROR_TYPE_INFO,
1171            "Using %s of %.3f\n",
1172            "PROP_PROPORTIONALITY_FACTOR",
1173            f_tmp);
1174     }
1175   }
1176
1177   s->network_entries = GNUNET_malloc (env->network_count
1178                                       * sizeof(struct Network));
1179   for (c = 0; c < env->network_count; c++)
1180   {
1181     cur = &s->network_entries[c];
1182     cur->type = c;
1183     cur->total_quota_in = env->in_quota[c];
1184     cur->total_quota_out = env->out_quota[c];
1185     cur->desc = GNUNET_NT_to_string (c);
1186     GNUNET_asprintf (&cur->stat_total,
1187                      "# ATS addresses %s total",
1188                      cur->desc);
1189     GNUNET_asprintf (&cur->stat_active,
1190                      "# ATS active addresses %s total",
1191                      cur->desc);
1192     LOG (GNUNET_ERROR_TYPE_INFO,
1193          "Added network %u `%s' (%llu/%llu)\n",
1194          c,
1195          cur->desc,
1196          cur->total_quota_in,
1197          cur->total_quota_out);
1198   }
1199   return &sf;
1200 }
1201
1202
1203 /**
1204  * Function used to unload the plugin.
1205  *
1206  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1207  */
1208 void *
1209 libgnunet_plugin_ats_proportional_done (void *cls)
1210 {
1211   struct GNUNET_ATS_SolverFunctions *sf = cls;
1212   struct GAS_PROPORTIONAL_Handle *s = sf->cls;
1213   struct AddressWrapper *cur;
1214   struct AddressWrapper *next;
1215   unsigned int c;
1216
1217   for (c = 0; c < s->env->network_count; c++)
1218   {
1219     GNUNET_break (0 == s->network_entries[c].total_addresses);
1220     GNUNET_break (0 == s->network_entries[c].active_addresses);
1221     next = s->network_entries[c].head;
1222     while (NULL != (cur = next))
1223     {
1224       next = cur->next;
1225       GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
1226                                    s->network_entries[c].tail,
1227                                    cur);
1228       GNUNET_free_non_null (cur->addr->solver_information);
1229       GNUNET_free (cur);
1230     }
1231     GNUNET_free (s->network_entries[c].stat_total);
1232     GNUNET_free (s->network_entries[c].stat_active);
1233   }
1234   GNUNET_break (0 == s->active_addresses);
1235   GNUNET_free (s->network_entries);
1236   GNUNET_free (s);
1237   return NULL;
1238 }
1239
1240
1241 /* end of plugin_ats_proportional.c */