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