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