rename and cleanup due to writing docu
[oweals/gnunet.git] / src / ats / gnunet-service-ats-solver_proportional.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file ats/gnunet-service-ats-solver_proportional.c
23  * @brief ATS proportional solver
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet_statistics_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "ats-simplistic",__VA_ARGS__)
33
34 /**
35  *
36  * NOTE: Do not change this documentation. This documentation is based
37  * on gnunet.org:/vcs/fsnsg/ats-paper.git/tech-doku/ats-tech-guide.tex
38  * use build_txt.sh to generate plaintext output
39  *
40  * ATS addresses : simplistic solver
41  *
42  *    The simplistic solver ("simplistic") distributes the available
43  *    bandwidth fair over all the addresses influenced by the
44  *    preference values. For each available network type an in- and
45  *    outbound quota is configured and the bandwidth available in
46  *    these networks is distributed over the addresses.  The solver
47  *    first assigns every addresses the minimum amount of bandwidth
48  *    GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT and then distributes the
49  *    remaining bandwidth available according to the preference
50  *    values. For each peer only a single address gets bandwidth
51  *    assigned and only one address marked as active.  The most
52  *    important functionality for the solver is implemented in: *
53  *    find_address_it is an hashmap iterator returning the prefered
54  *    address for an peer * update_quota_per_network distributes
55  *    available bandwidth for a network over active addresses
56  *
57  *    Changes to addresses automatically have an impact on the the
58  *    bandwidth assigned to other addresses in the same network since
59  *    the solver distributes the remaining bandwidth over the
60  *    addresses in the network.  When changes to the addresses occur,
61  *    the solver first performs the changes, like adding or deleting
62  *    addresses, and then updates bandwidth assignment for the
63  *    affected network. Bandwidth assignment is only recalculated on
64  *    demand when an address is requested by a client for a peer or
65  *    when the addresses available have changed or an address changed
66  *    the network it is located in. When the bandwidth assignment has
67  *    changed the callback is called with the new bandwidth
68  *    assignments. The bandwidth distribution for a network is
69  *    recalculated due to: * address suggestion requests * address
70  *    deletions * address switching networks during address update *
71  *    preference changes
72  *
73  *     3.1 Data structures used
74  *
75  *    For each ATS network (e.g. WAN, LAN, loopback) a struct Network
76  *    is used to specify network related information as total adresses
77  *    and active addresses in this network and the configured in- and
78  *    outbound quota. Each network also contains a list of addresses
79  *    added to the solver located in this network. The simplistic
80  *    solver uses the addresses' solver_information field to store the
81  *    simplistic network it belongs to for each address.
82  *
83  *     3.2 Initializing
84  *
85  *    When the simplistic solver is initialized the solver creates a
86  *    new solver handle and initializes the network structures with
87  *    the quotas passed from addresses and returns the handle solver.
88  *
89  *     3.3 Adding an address
90  *
91  *    When a new address is added to the solver using s_add, a lookup
92  *    for the network for this address is done and the address is
93  *    enqueued in in the linked list of the network.
94  *
95  *     3.4 Updating an address
96  *
97  *    The main purpose of address updates is to update the ATS
98  *    information for addresse selection. Important for the simplistic
99  *    solver is when an address switches network it is located
100  *    in. This is common because addresses added by transport's
101  *    validation mechanism are commonly located in
102  *    GNUNET_ATS_NET_UNSPECIFIED. Addresses in validation are located
103  *    in this network type and only if a connection is successful on
104  *    return of payload data transport switches to the real network
105  *    the address is located in.  When an address changes networks it
106  *    is first of all removed from the old network using the solver
107  *    API function GAS_simplistic_address_delete and the network in
108  *    the address struct is updated. A lookup for the respective new
109  *    simplistic network is done and stored in the addresse's
110  *    solver_information field. Next the address is re-added to the
111  *    solver using the solver API function
112  *    GAS_simplistic_address_add. If the address was marked as in
113  *    active, the solver checks if bandwidth is available in the
114  *    network and if yes sets the address to active and updates the
115  *    bandwidth distribution in this network. If no bandwidth is
116  *    available it sets the bandwidth for this address to 0 and tries
117  *    to suggest an alternative address. If an alternative address was
118  *    found, addresses' callback is called for this address.
119  *
120  *     3.5 Deleting an address
121  *
122  *    When an address is removed from the solver, it removes the
123  *    respective address from the network and if the address was
124  *    marked as active, it updates the bandwidth distribution for this
125  *    network.
126  *
127  *     3.6 Requesting addresses
128  *
129  *    When an address is requested for a peer the solver performs a
130  *    lookup for the peer entry in addresses address hashmap and
131  *    selects the best address.  The selection of the most suitable
132  *    address is done in the find_address_it hashmap iterator
133  *    described in detail in section 3.7. If no address is returned,
134  *    no address can be suggested at the moment. If the address
135  *    returned is marked as active, the solver can return this
136  *    address. If the address is not marked as active, the solver
137  *    checks if another address belongign to this peer is marked as
138  *    active and marks the address as inactive, updates the bandwidth
139  *    for this address to 0, call the bandwidth changed callback for
140  *    this address due to the change and updates quota assignment for
141  *    the addresse's network. the now in-active address is belonging
142  *    to. The solver marks the new address as active and updates the
143  *    bandwidth assignment for this network.
144  *
145  *     3.7 Choosing addresses
146  *
147  *    Choosing the best possible address for suggestion is done by
148  *    iterating over all addresses of a peer stored in addresses'
149  *    hashmap and using the hashmap iterator find_address_it to select
150  *    the best available address.  Several checks are done when an
151  *    address is selected. First if this address is currently blocked
152  *    by addresses from being suggested. An address is blocked for the
153  *    duration of ATS_BLOCKING_DELTA when it is suggested to
154  *    transport. Next it is checked if at least
155  *    GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT bytes bandwidth is available
156  *    in the addresse's network, because suggesting an address without
157  *    bandwidth does not make sense. This also ensures that all active
158  *    addresses in this network get at least the minimum amount of
159  *    bandwidth assigned. In the next step the solver ensures that for
160  *    tcp connections inbound connections are prefered over outbound
161  *    connections. In the next stet the solver ensures that
162  *    connections are prefered in the following order: * connections
163  *    are already established and have bandwidth assigned *
164  *    connections with a shorter distance * connectes have a shorter
165  *    latency
166  *
167  *     3.8 Changing preferences
168  *
169  *     3.9 Shutdown
170  *
171  *    During shutdown all network entries and aging processes are
172  *    destroyed and freed.
173  *
174  *
175  * OLD DOCUMENTATION
176  *
177  * This solver assigns in and outbound bandwidth equally for all
178  * addresses in specific network type (WAN, LAN) based on configured
179  * in and outbound quota for this network.
180  *
181  * The solver is notified by addresses about changes to the addresses
182  * and recalculates the bandwith assigned if required. The solver
183  * notifies addresses by calling the GAS_bandwidth_changed_cb
184  * callback.
185  *
186  * - Initialization
187  *
188  *
189  *
190  *
191  * For each peer only a single is selected and marked as "active" in the address
192  * struct.
193  *
194  * E.g.:
195  *
196  * You have the networks WAN and LAN and quotas
197  * WAN_TOTAL_IN, WAN_TOTAL_OUT
198  * LAN_TOTAL_IN, LAN_TOTAL_OUT
199  *
200  * If you have x addresses in the network segment LAN, the quotas are
201  * QUOTA_PER_ADDRESS = LAN_TOTAL_OUT / x
202  *
203  * Quotas are automatically recalculated and reported back when addresses are
204  * - requested
205  *
206  */
207
208 #define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
209 #define PREF_AGING_FACTOR 0.95
210
211 #define DEFAULT_PREFERENCE 1.0
212 #define MIN_UPDATE_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
213
214 /**
215  * A handle for the simplistic solver
216  */
217 struct GAS_SIMPLISTIC_Handle
218 {
219   /**
220    * Statistics handle
221    */
222
223   struct GNUNET_STATISTICS_Handle *stats;
224
225   /**
226    * Total number of addresses for solver
227    */
228   unsigned int total_addresses;
229
230   /**
231    * Number of active addresses for solver
232    */
233   unsigned int active_addresses;
234
235   /**
236    * Networks array
237    */
238   struct Network *network_entries;
239
240   /**
241    * Number of networks
242    */
243   unsigned int networks;
244
245   /**
246    * Callback
247    */
248   GAS_bandwidth_changed_cb bw_changed;
249
250   /**
251    * Callback cls
252    */
253   void *bw_changed_cls;
254
255   struct GNUNET_CONTAINER_MultiHashMap *prefs;
256
257   struct PreferenceClient *pc_head;
258   struct PreferenceClient *pc_tail;
259 };
260
261 struct Network
262 {
263   /**
264    * ATS network type
265    */
266   unsigned int type;
267
268   /**
269    * Network description
270    */
271   char *desc;
272
273   /**
274    * Total inbound quota
275    *
276    */
277   unsigned long long total_quota_in;
278
279   /**
280    * Total outbound quota
281    *
282    */
283   unsigned long long total_quota_out;
284
285   /**
286    * Number of active addresses for this network
287    */
288   unsigned int active_addresses;
289
290   /**
291    * Number of total addresses for this network
292    */
293   unsigned int total_addresses;
294
295   /**
296    * String for statistics total addresses
297    */
298   char *stat_total;
299
300   /**
301    * String for statistics active addresses
302    */
303   char *stat_active;
304
305   struct AddressWrapper *head;
306   struct AddressWrapper *tail;
307 };
308
309 struct AddressWrapper
310 {
311   struct AddressWrapper *next;
312   struct AddressWrapper *prev;
313
314   struct ATS_Address *addr;
315 };
316
317
318 struct PreferenceClient
319 {
320   struct PreferenceClient *prev;
321   struct PreferenceClient *next;
322   void *client;
323
324   double f_total[GNUNET_ATS_PreferenceCount];
325
326   struct PreferencePeer *p_head;
327   struct PreferencePeer *p_tail;
328 };
329
330
331 struct PreferencePeer
332 {
333   struct PreferencePeer *next;
334   struct PreferencePeer *prev;
335   struct PreferenceClient *client;
336   struct GAS_SIMPLISTIC_Handle *s;
337   struct GNUNET_PeerIdentity id;
338
339   double f[GNUNET_ATS_PreferenceCount];
340   double f_rel[GNUNET_ATS_PreferenceCount];
341   double f_rel_total;
342
343   GNUNET_SCHEDULER_TaskIdentifier aging_task;
344 };
345
346 /**
347  * Get the prefered address for a specific peer
348  *
349  * @param solver the solver handle
350  * @param addresses the address hashmap containing all addresses
351  * @param peer the identity of the peer
352  */
353 const struct ATS_Address *
354 GAS_simplistic_get_preferred_address (void *solver,
355                                struct GNUNET_CONTAINER_MultiHashMap * addresses,
356                                const struct GNUNET_PeerIdentity *peer);
357
358
359
360 /**
361  *  Important solver functions
362  *  ---------------------------
363  */
364
365
366
367
368 /**
369  *  Helper functions
370  *  ---------------------------
371  */
372
373 static int
374 free_pref (void *cls,
375            const struct GNUNET_HashCode * key,
376            void *value)
377 {
378   float *v = value;
379   GNUNET_free (v);
380   return GNUNET_OK;
381 }
382
383
384
385
386 /**
387  * Test if bandwidth is available in this network
388  *
389  * @param net the network type to update
390  * @return GNUNET_YES or GNUNET_NO
391  */
392
393 static int
394 bw_available_in_network (struct Network *net)
395 {
396   unsigned int na = net->active_addresses + 1;
397   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
398   if (((net->total_quota_in / na) > min_bw) &&
399       ((net->total_quota_out / na) > min_bw))
400   {    
401     LOG (GNUNET_ERROR_TYPE_DEBUG,
402          "Enough bandwidth available for %u active addresses in network `%s'\n",
403          na,
404          net->desc);
405                                                                       
406     return GNUNET_YES;
407   }
408     LOG (GNUNET_ERROR_TYPE_DEBUG,
409          "Not enough bandwidth available for %u active addresses in network `%s'\n",
410          na,
411          net->desc);  
412   return GNUNET_NO;
413 }
414
415
416 /**
417  * Update the quotas for a network type
418  *
419  * @param s the solver handle
420  * @param net the network type to update
421  * @param address_except address excluded from notifcation, since we suggest
422  * this address
423  */
424 static void
425 update_quota_per_network (struct GAS_SIMPLISTIC_Handle *s,
426                           struct Network *net,
427                           struct ATS_Address *address_except)
428 {
429   unsigned long long remaining_quota_in = 0;
430   unsigned long long quota_out_used = 0;
431
432   unsigned long long remaining_quota_out = 0;
433   unsigned long long quota_in_used = 0;
434   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
435   double total_prefs; /* Important: has to be double not float due to precision */
436   double cur_pref; /* Important: has to be double not float due to precision */
437   double *t = NULL; /* Important: has to be double not float due to precision */
438
439   unsigned long long assigned_quota_in = 0;
440   unsigned long long assigned_quota_out = 0;
441   struct AddressWrapper *cur;
442
443   LOG (GNUNET_ERROR_TYPE_DEBUG,
444               "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
445               net->desc, net->active_addresses, net->total_quota_in, net->total_quota_in);
446
447   if (net->active_addresses == 0)
448     return; /* no addresses to update */
449
450   /* Idea
451    * Assign every peer in network minimum Bandwidth
452    * Distribute bandwidth left according to preference
453    */
454
455   if ((net->active_addresses * min_bw) > net->total_quota_in)
456   {
457     GNUNET_break (0);
458     return;
459   }
460   if ((net->active_addresses * min_bw) > net->total_quota_out)
461   {
462     GNUNET_break (0);
463     return;
464   }
465
466   remaining_quota_in = net->total_quota_in - (net->active_addresses * min_bw);
467   remaining_quota_out = net->total_quota_out - (net->active_addresses * min_bw);
468   LOG (GNUNET_ERROR_TYPE_DEBUG, "Remaining bandwidth : (in/out): %llu/%llu \n",
469               remaining_quota_in, remaining_quota_out);
470   total_prefs = 0.0;
471   for (cur = net->head; NULL != cur; cur = cur->next)
472   {
473       if (GNUNET_YES == cur->addr->active)
474       {
475         t = GNUNET_CONTAINER_multihashmap_get (s->prefs, &cur->addr->peer.hashPubKey);
476         if (NULL == t)
477                 total_prefs += DEFAULT_PREFERENCE;
478         else
479          {
480                         total_prefs += (*t);
481          }
482       }
483   }
484   for (cur = net->head; NULL != cur; cur = cur->next)
485   {
486      if (GNUNET_YES == cur->addr->active)
487      {
488        cur_pref = 0.0;
489        t = GNUNET_CONTAINER_multihashmap_get (s->prefs, &cur->addr->peer.hashPubKey);
490        if (NULL == t)
491          cur_pref = DEFAULT_PREFERENCE;
492        else
493          cur_pref = (*t);
494        assigned_quota_in = min_bw + ((cur_pref / total_prefs) * remaining_quota_in);
495        assigned_quota_out = min_bw + ((cur_pref / total_prefs) * remaining_quota_out);
496
497        LOG (GNUNET_ERROR_TYPE_DEBUG,
498                    "New quota for peer `%s' with preference (cur/total) %.3f/%.3f (in/out): %llu / %llu\n",
499                    GNUNET_i2s (&cur->addr->peer),
500                    cur_pref, total_prefs,
501                    assigned_quota_in, assigned_quota_out);
502      }
503      else
504      {
505        assigned_quota_in = 0;
506        assigned_quota_out = 0;
507      }
508
509      quota_in_used += assigned_quota_in;
510      quota_out_used += assigned_quota_out;
511      /* Prevent overflow due to rounding errors */
512      if (assigned_quota_in > UINT32_MAX)
513        assigned_quota_in = UINT32_MAX;
514      if (assigned_quota_out > UINT32_MAX)
515        assigned_quota_out = UINT32_MAX;
516
517      /* Compare to current bandwidth assigned */
518      if ((assigned_quota_in != ntohl(cur->addr->assigned_bw_in.value__)) ||
519          (assigned_quota_out != ntohl(cur->addr->assigned_bw_out.value__)))
520      {
521        cur->addr->assigned_bw_in.value__ = htonl (assigned_quota_in);
522        cur->addr->assigned_bw_out.value__ = htonl (assigned_quota_out);
523        /* Notify on change */
524        if ((GNUNET_YES == cur->addr->active) && (cur->addr != address_except))
525          s->bw_changed (s->bw_changed_cls, cur->addr);
526      }
527
528   }
529   LOG (GNUNET_ERROR_TYPE_DEBUG,
530                           "Total bandwidth assigned is (in/out): %llu /%llu\n",
531                           quota_in_used,
532                           quota_out_used);
533   if (quota_out_used > net->total_quota_out + 1) /* +1 is required due to rounding errors */
534   {
535       LOG (GNUNET_ERROR_TYPE_ERROR,
536                             "Total outbound bandwidth assigned is larger than allowed (used/allowed) for %u active addresses: %llu / %llu\n",
537                             net->active_addresses,
538                             quota_out_used,
539                             net->total_quota_out);
540   }
541   if (quota_in_used > net->total_quota_in + 1) /* +1 is required due to rounding errors */
542   {
543       LOG (GNUNET_ERROR_TYPE_ERROR,
544                             "Total inbound bandwidth assigned is larger than allowed (used/allowed) for %u active addresses: %llu / %llu\n",
545                             net->active_addresses,
546                             quota_in_used,
547                             net->total_quota_in);
548   }
549 }
550
551 static void
552 update_all_networks (struct GAS_SIMPLISTIC_Handle *s)
553 {
554         int i;
555         for (i = 0; i < s->networks; i++)
556                 update_quota_per_network (s, &s->network_entries[i], NULL);
557
558 }
559
560 static void
561 addresse_increment (struct GAS_SIMPLISTIC_Handle *s,
562                                 struct Network *net,
563                                 int total,
564                                 int active)
565 {
566   if (GNUNET_YES == total)
567   {
568       s->total_addresses ++;
569       net->total_addresses ++;
570       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", 1, GNUNET_NO);
571       GNUNET_STATISTICS_update (s->stats, net->stat_total, 1, GNUNET_NO);
572   }
573   if (GNUNET_YES == active)
574   {
575     net->active_addresses ++;
576     s->active_addresses ++;
577     GNUNET_STATISTICS_update (s->stats, "# ATS active addresses total", 1, GNUNET_NO);
578     GNUNET_STATISTICS_update (s->stats, net->stat_active, 1, GNUNET_NO);
579   }
580
581 }
582
583 static int
584 addresse_decrement (struct GAS_SIMPLISTIC_Handle *s,
585                     struct Network *net,
586                     int total,
587                     int active)
588 {
589   int res = GNUNET_OK;
590   if (GNUNET_YES == total)
591   {
592     if (s->total_addresses < 1)
593     {
594       GNUNET_break (0);
595       res = GNUNET_SYSERR;
596     }
597     else
598     {
599       s->total_addresses --;
600       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", -1, GNUNET_NO);
601     }
602     if (net->total_addresses < 1)
603     {
604       GNUNET_break (0);
605       res = GNUNET_SYSERR;
606     }
607     else
608     {
609       net->total_addresses --;
610       GNUNET_STATISTICS_update (s->stats, net->stat_total, -1, GNUNET_NO);
611     }
612   }
613
614   if (GNUNET_YES == active)
615   {
616     if (net->active_addresses < 1)
617     {
618       GNUNET_break (0);
619       res = GNUNET_SYSERR;
620     }
621     else
622     {
623       net->active_addresses --;
624       GNUNET_STATISTICS_update (s->stats, net->stat_active, -1, GNUNET_NO);
625     }
626     if (s->active_addresses < 1)
627     {
628       GNUNET_break (0);
629       res = GNUNET_SYSERR;
630     }
631     else
632     {
633       s->active_addresses --;
634       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", -1, GNUNET_NO);
635     }
636   }
637   return res;
638 }
639
640 /**
641  * Extract an ATS performance info from an address
642  *
643  * @param address the address
644  * @param type the type to extract in HBO
645  * @return the value in HBO or GNUNET_ATS_VALUE_UNDEFINED in HBO if value does not exist
646  */
647 static int
648 get_performance_info (struct ATS_Address *address, uint32_t type)
649 {
650         int c1;
651         GNUNET_assert (NULL != address);
652
653         if ((NULL == address->atsi) || (0 == address->atsi_count))
654                         return GNUNET_ATS_VALUE_UNDEFINED;
655
656         for (c1 = 0; c1 < address->atsi_count; c1++)
657         {
658                         if (ntohl(address->atsi[c1].type) == type)
659                                 return ntohl(address->atsi[c1].value);
660         }
661         return GNUNET_ATS_VALUE_UNDEFINED;
662 }
663
664
665 /**
666  * Add a single address within a network to the solver
667  *
668  * @param solver the solver Handle
669  * @param addresses the address hashmap containing all addresses
670  * @param address the address to add
671  * @param network network type of this address
672  */
673 void
674 GAS_simplistic_address_add (void *solver,
675                                                                                                                 struct GNUNET_CONTAINER_MultiHashMap *addresses,
676                                                                                                                 struct ATS_Address *address,
677                                                                                                                 uint32_t network)
678 {
679   struct GAS_SIMPLISTIC_Handle *s = solver;
680   struct Network *net = NULL;
681   struct AddressWrapper *aw = NULL;
682   int c;
683
684   GNUNET_assert (NULL != s);
685   for (c = 0; c < s->networks; c++)
686   {
687       net = &s->network_entries[c];
688       if (network == net->type)
689           break;
690   }
691   if (NULL == net)
692   {
693     GNUNET_break (0);
694     return;
695   }
696
697   aw = GNUNET_malloc (sizeof (struct AddressWrapper));
698   aw->addr = address;
699   GNUNET_CONTAINER_DLL_insert (net->head, net->tail, aw);
700   addresse_increment (s, net, GNUNET_YES, GNUNET_NO);
701   aw->addr->solver_information = net;
702
703   LOG (GNUNET_ERROR_TYPE_DEBUG, "After adding address now total %u and active %u addresses in network `%s'\n",
704       net->total_addresses,
705       net->active_addresses,
706       net->desc);
707 }
708
709 /**
710  * Remove an address from the solver
711  *
712  * @param solver the solver handle
713  * @param addresses the address hashmap containing all addresses
714  * @param address the address to remove
715  * @param session_only delete only session not whole address
716  */
717 void
718 GAS_simplistic_address_delete (void *solver,
719     struct GNUNET_CONTAINER_MultiHashMap * addresses,
720     struct ATS_Address *address, int session_only)
721 {
722   struct GAS_SIMPLISTIC_Handle *s = solver;
723   struct Network *net;
724   struct AddressWrapper *aw;
725
726   /* Remove an adress completely, we have to:
727    * - Remove from specific network
728    * - Decrease number of total addresses
729    * - If active:
730    *   - decrease number of active addreses
731    *   - update quotas
732    */
733
734   net = (struct Network *) address->solver_information;
735
736   if (GNUNET_NO == session_only)
737   {
738     LOG (GNUNET_ERROR_TYPE_DEBUG, "Deleting %s address %p for peer `%s' from network `%s' (total: %u/ active: %u)\n",
739         (GNUNET_NO == address->active) ? "inactive" : "active",
740         address, GNUNET_i2s (&address->peer),
741         net->desc, net->total_addresses, net->active_addresses);
742
743     /* Remove address */
744     addresse_decrement (s, net, GNUNET_YES, GNUNET_NO);
745     for (aw = net->head; NULL != aw; aw = aw->next)
746     {
747         if (aw->addr == address)
748           break;
749     }
750     if (NULL == aw )
751     {
752         GNUNET_break (0);
753         return;
754     }
755     GNUNET_CONTAINER_DLL_remove (net->head, net->tail, aw);
756     GNUNET_free (aw);
757   }
758   else
759   {
760       /* Remove session only: remove if active and update */
761       LOG (GNUNET_ERROR_TYPE_DEBUG, "Deleting %s session %p for peer `%s' from network `%s' (total: %u/ active: %u)\n",
762           (GNUNET_NO == address->active) ? "inactive" : "active",
763           address, GNUNET_i2s (&address->peer),
764           net->desc, net->total_addresses, net->active_addresses);
765   }
766
767   if (GNUNET_YES == address->active)
768   {
769       /* Address was active, remove from network and update quotas*/
770       address->active = GNUNET_NO;
771       if (GNUNET_SYSERR == addresse_decrement (s, net, GNUNET_NO, GNUNET_YES))
772         GNUNET_break (0);
773       update_quota_per_network (s, net, NULL);
774   }
775   LOG (GNUNET_ERROR_TYPE_DEBUG, "After deleting address now total %u and active %u addresses in network `%s'\n",
776       net->total_addresses,
777       net->active_addresses,
778       net->desc);
779
780 }
781
782 static struct Network *
783 find_network (struct GAS_SIMPLISTIC_Handle *s, uint32_t type)
784 {
785   int c;
786   for (c = 0 ; c < s->networks; c++)
787   {
788       if (s->network_entries[c].type == type)
789         return &s->network_entries[c];
790   }
791   return NULL;
792 }
793
794 /**
795  * Updates a single address in the solver
796  *
797  * If ATS information was updated, the previous values are passed
798  *
799  * @param solver the solver Handle
800  * @param addresses the address hashmap containing all addresses
801  * @param address the update address
802  * @param session the new session (if changed otherwise current)
803  * @param in_use the new address in use state (if changed otherwise current)
804  * @param prev_ats  ATS information
805  * @param prev_atsi_count the atsi count
806  */
807 void
808 GAS_simplistic_address_update (void *solver,
809                               struct GNUNET_CONTAINER_MultiHashMap *addresses,
810                               struct ATS_Address *address,
811                               uint32_t session,
812                               int in_use,
813                               const struct GNUNET_ATS_Information *prev_ats,
814                               uint32_t prev_atsi_count)
815 {
816   struct ATS_Address *new;
817   struct GAS_SIMPLISTIC_Handle *s = (struct GAS_SIMPLISTIC_Handle *) solver;
818   int i;
819   uint32_t prev_value;
820   uint32_t prev_type;
821   uint32_t addr_net;
822   int save_active = GNUNET_NO;
823   struct Network *new_net = NULL;
824   for (i = 0; i < prev_atsi_count; i++)
825   {
826     prev_type = ntohl (prev_ats[i].type);
827     prev_value = ntohl (prev_ats[i].value);
828     switch (prev_type)
829     {
830     case GNUNET_ATS_UTILIZATION_UP:
831     case GNUNET_ATS_UTILIZATION_DOWN:
832     case GNUNET_ATS_QUALITY_NET_DELAY:
833     case GNUNET_ATS_QUALITY_NET_DISTANCE:
834     case GNUNET_ATS_COST_WAN:
835     case GNUNET_ATS_COST_LAN:
836     case GNUNET_ATS_COST_WLAN:
837         /* No actions required here*/
838         break;
839     case GNUNET_ATS_NETWORK_TYPE:
840
841       addr_net = get_performance_info (address, GNUNET_ATS_NETWORK_TYPE);
842       if (GNUNET_ATS_VALUE_UNDEFINED == addr_net)
843       {
844         GNUNET_break (0);
845         addr_net = GNUNET_ATS_NET_UNSPECIFIED;
846       }
847       if (addr_net != prev_value)
848       {
849         /* Network changed */
850         LOG (GNUNET_ERROR_TYPE_DEBUG, "Network type changed, moving %s address from `%s' to `%s'\n",
851             (GNUNET_YES == address->active) ? "active" : "inactive",
852              GNUNET_ATS_print_network_type(prev_value),
853              GNUNET_ATS_print_network_type(addr_net));
854
855         save_active = address->active;
856         /* remove from old network */
857         GAS_simplistic_address_delete (solver, addresses, address, GNUNET_NO);
858
859         /* set new network type */
860         new_net = find_network (solver, addr_net);
861         address->solver_information = new_net;
862
863         /* Add to new network and update*/
864         GAS_simplistic_address_add (solver, addresses, address, addr_net);
865         if (GNUNET_YES == save_active)
866         {
867           /* check if bandwidth available in new network */
868           if (GNUNET_YES == (bw_available_in_network (new_net)))
869           {
870               /* Suggest updated address */
871               address->active = GNUNET_YES;
872               addresse_increment (s, new_net, GNUNET_NO, GNUNET_YES);
873               update_quota_per_network (solver, new_net, NULL);
874           }
875           else
876           {
877             LOG (GNUNET_ERROR_TYPE_DEBUG, "Not enough bandwidth in new network, suggesting alternative address ..\n");
878
879             /* Set old address to zero bw */
880             address->assigned_bw_in = GNUNET_BANDWIDTH_value_init (0);
881             address->assigned_bw_out = GNUNET_BANDWIDTH_value_init (0);
882             s->bw_changed  (s->bw_changed_cls, address);
883
884             /* Find new address to suggest since no bandwidth in network*/
885             new = (struct ATS_Address *) GAS_simplistic_get_preferred_address (s, addresses, &address->peer);
886             if (NULL != new)
887             {
888                 /* Have an alternative address to suggest */
889                 s->bw_changed  (s->bw_changed_cls, new);
890             }
891
892           }
893         }
894       }
895
896       break;
897     case GNUNET_ATS_ARRAY_TERMINATOR:
898       break;
899     default:
900       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
901                   "Received unsupported ATS type %u\n", prev_type);
902       GNUNET_break (0);
903       break;
904
905     }
906
907   }
908   if (address->session_id != session)
909   {
910       LOG (GNUNET_ERROR_TYPE_DEBUG,
911                   "Session changed from %u to %u\n", address->session_id, session);
912       address->session_id = session;
913   }
914   if (address->used != in_use)
915   {
916       LOG (GNUNET_ERROR_TYPE_DEBUG,
917                   "Usage changed from %u to %u\n", address->used, in_use);
918       address->used = in_use;
919   }
920
921 }
922
923
924
925 /**
926  * Find a "good" address to use for a peer.  If we already have an existing
927  * address, we stick to it.  Otherwise, we pick by lowest distance and then
928  * by lowest latency.
929  *
930  * @param cls the 'struct ATS_Address**' where we store the result
931  * @param key unused
932  * @param value another 'struct ATS_Address*' to consider using
933  * @return GNUNET_OK (continue to iterate)
934  */
935 static int
936 find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
937 {
938   struct ATS_Address **previous_p = cls;
939   struct ATS_Address *current = (struct ATS_Address *) value;
940   struct ATS_Address *previous = *previous_p;
941   struct GNUNET_TIME_Absolute now;
942   struct Network *net = (struct Network *) current->solver_information;
943   uint32_t p_distance_cur;
944   uint32_t p_distance_prev;
945   uint32_t p_delay_cur;
946   uint32_t p_delay_prev;
947
948   now = GNUNET_TIME_absolute_get();
949
950   if (current->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, current->blocked_until).abs_value)
951   {
952     /* This address is blocked for suggestion */
953     LOG (GNUNET_ERROR_TYPE_DEBUG,
954                 "Address %p blocked for suggestion for %llu ms \n",
955                 current,
956                 GNUNET_TIME_absolute_get_difference(now, current->blocked_until).rel_value);
957     return GNUNET_OK;
958   }
959
960   if (GNUNET_NO == bw_available_in_network (net))
961     return GNUNET_OK; /* There's no bandwidth available in this network */
962
963   if (NULL != previous)
964   {
965     if ((0 == strcmp (previous->plugin, "tcp")) &&
966         (0 == strcmp (current->plugin, "tcp")))
967     {
968       if ((0 != previous->addr_len) &&
969           (0 == current->addr_len))
970       {
971         /* saved address was an outbound address, but we have an inbound address */
972         *previous_p = current;
973         return GNUNET_OK;
974       }
975       if (0 == previous->addr_len)
976       {
977         /* saved address was an inbound address, so do not overwrite */
978         return GNUNET_OK;
979       }
980     }
981   }
982
983   if (NULL == previous)
984   {
985     *previous_p = current;
986     return GNUNET_OK;
987   }
988   if ((ntohl (previous->assigned_bw_in.value__) == 0) &&
989       (ntohl (current->assigned_bw_in.value__) > 0))
990   {
991     /* stick to existing connection */
992     *previous_p = current;
993     return GNUNET_OK;
994   }
995
996   p_distance_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DISTANCE);
997   p_distance_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DISTANCE);
998   if ((p_distance_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_distance_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
999                 (p_distance_prev > p_distance_cur))
1000   {
1001     /* user shorter distance */
1002     *previous_p = current;
1003     return GNUNET_OK;
1004   }
1005
1006   p_delay_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DELAY);
1007   p_delay_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DELAY);
1008   if ((p_delay_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_delay_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
1009                 (p_delay_prev > p_delay_cur))
1010   {
1011     /* user lower latency */
1012     *previous_p = current;
1013     return GNUNET_OK;
1014   }
1015
1016   /* don't care */
1017   return GNUNET_OK;
1018 }
1019
1020 static int
1021 find_active_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1022 {
1023   struct ATS_Address * dest = (struct ATS_Address *) (*(struct ATS_Address **)cls);
1024   struct ATS_Address * aa = (struct ATS_Address *) value;
1025
1026   if (GNUNET_YES == aa->active)
1027   {
1028       if (dest != NULL)
1029       {
1030           /* should never happen */
1031           LOG (GNUNET_ERROR_TYPE_ERROR, "Multiple active addresses for peer `%s'\n", GNUNET_i2s (&aa->peer));
1032           GNUNET_break (0);
1033           return GNUNET_NO;
1034       }
1035       dest = aa;
1036   }
1037   return GNUNET_OK;
1038 }
1039
1040 static struct ATS_Address *
1041 find_active_address (void *solver,
1042                      struct GNUNET_CONTAINER_MultiHashMap * addresses,
1043                      const struct GNUNET_PeerIdentity *peer)
1044 {
1045   struct ATS_Address * dest = NULL;
1046
1047   GNUNET_CONTAINER_multihashmap_get_multiple(addresses,
1048        &peer->hashPubKey,
1049        &find_active_address_it, &dest);
1050   return dest;
1051 }
1052
1053 /**
1054  * Get the prefered address for a specific peer
1055  *
1056  * @param solver the solver handle
1057  * @param addresses the address hashmap containing all addresses
1058  * @param peer the identity of the peer
1059  */
1060 const struct ATS_Address *
1061 GAS_simplistic_get_preferred_address (void *solver,
1062                                struct GNUNET_CONTAINER_MultiHashMap * addresses,
1063                                const struct GNUNET_PeerIdentity *peer)
1064 {
1065   struct GAS_SIMPLISTIC_Handle *s = solver;
1066   struct Network *net_prev;
1067   struct Network *net_cur;
1068   struct ATS_Address *cur;
1069   struct ATS_Address *prev;
1070
1071   GNUNET_assert (s != NULL);
1072   cur = NULL;
1073   /* Get address with: stick to current address, lower distance, lower latency */
1074   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
1075                                               &find_address_it, &cur);
1076   if (NULL == cur)
1077   {
1078     LOG (GNUNET_ERROR_TYPE_DEBUG, "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
1079     return NULL;
1080   }
1081
1082   LOG (GNUNET_ERROR_TYPE_DEBUG, "Suggesting %s address %p for peer `%s'\n",
1083       (GNUNET_NO == cur->active) ? "inactive" : "active",
1084       cur, GNUNET_i2s (peer));
1085   net_cur = (struct Network *) cur->solver_information;
1086   if (GNUNET_YES == cur->active)
1087   {
1088       /* This address was selected previously, so no need to update quotas */
1089       return cur;
1090   }
1091
1092   /* This address was not active, so we have to:
1093    *
1094    * - mark previous active address as not active
1095    * - update quota for previous address network
1096    * - update quota for this address network
1097    */
1098
1099   prev = find_active_address (s, addresses, peer);
1100   if (NULL != prev)
1101   {
1102       net_prev = (struct Network *) prev->solver_information;
1103       prev->active = GNUNET_NO; /* No active any longer */
1104       prev->assigned_bw_in = GNUNET_BANDWIDTH_value_init (0); /* no bw assigned */
1105       prev->assigned_bw_out = GNUNET_BANDWIDTH_value_init (0); /* no bw assigned */
1106       s->bw_changed (s->bw_changed_cls, prev); /* notify about bw change, REQUIRED? */
1107       if (GNUNET_SYSERR == addresse_decrement (s, net_prev, GNUNET_NO, GNUNET_YES))
1108         GNUNET_break (0);
1109       update_quota_per_network (s, net_prev, NULL);
1110   }
1111
1112   if (GNUNET_NO == (bw_available_in_network (cur->solver_information)))
1113   {
1114     GNUNET_break (0); /* This should never happen*/
1115     return NULL;
1116   }
1117
1118   cur->active = GNUNET_YES;
1119   addresse_increment(s, net_cur, GNUNET_NO, GNUNET_YES);
1120   update_quota_per_network (s, net_cur, cur);
1121
1122   return cur;
1123 }
1124
1125
1126 /**
1127  * Stop notifying about address and bandwidth changes for this peer
1128  *
1129  * @param solver the MLP handle
1130  * @param addresses address hashmap
1131  * @param peer the peer
1132  */
1133 void
1134 GAS_simplistic_stop_get_preferred_address (void *solver,
1135                                      struct GNUNET_CONTAINER_MultiHashMap *addresses,
1136                                      const struct GNUNET_PeerIdentity *peer)
1137 {
1138         return;
1139 }
1140
1141
1142 /**
1143  *  Preference calculation
1144  *  ---------------------------
1145  */
1146
1147
1148 static void
1149 recalculate_preferences (struct PreferencePeer *p)
1150 {
1151         struct GAS_SIMPLISTIC_Handle *s = p->s;
1152         struct PreferencePeer *p_cur;
1153         struct PreferenceClient *c_cur = p->client;
1154         double p_rel_global;
1155   double *dest;
1156   int kind;
1157   int rkind;
1158   int clients;
1159
1160   /**
1161    * Idea:
1162    *
1163    * We have:
1164    * Set of clients c
1165    * Set of peers p_i in P
1166    * Set of preference kinds k
1167    * A preference value f_k_p_i with an unknown range
1168    *
1169    * We get:
1170    * A client specific relative preference f_p_i_rel [1..2] for all peers
1171    *
1172    * For every client c
1173    * {
1174    *   For every preference kind k:
1175    *   {
1176    *     We remember for the preference f_p_i for each peer p_i.
1177    *     We have a default preference value f_p_i = 0
1178    *     We have a sum of all preferences f_t = sum (f_p_i)
1179    *     So we can calculate a relative preference value fr_p_i:
1180    *
1181    *     f_k_p_i_ *  / f_t
1182    *     f_k_p_i_rel = [1..2], default 1.0
1183    *    }
1184    *    f_p_i_rel = sum (f_k_p_i_rel) / count(k)
1185    * }
1186    *
1187    **/
1188
1189   /* For this client: for all preferences, except TERMINATOR */
1190   for (kind = GNUNET_ATS_PREFERENCE_END + 1 ; kind < GNUNET_ATS_PreferenceCount; kind ++)
1191   {
1192           /* Recalcalculate total preference for this quality kind over all peers*/
1193           c_cur->f_total[kind] = 0;
1194           for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
1195                 c_cur->f_total[kind] += p_cur->f[kind];
1196
1197           LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p has total preference for %s of %.3f\n",
1198                         c_cur->client,
1199               GNUNET_ATS_print_preference_type (kind),
1200               c_cur->f_total[kind]);
1201
1202           /* Recalcalculate relative preference for all peers */
1203           for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
1204           {
1205             /* Calculate relative preference for specific kind */
1206                 if (0.0 == c_cur->f_total[kind])
1207                 {
1208                                 /* No one has preference, so set default preference */
1209                                 p_cur->f_rel[kind] = DEFAULT_PREFERENCE;
1210                 }
1211                 else
1212                 {
1213                                 p_cur->f_rel[kind] = (c_cur->f_total[kind] + p_cur->f[kind]) / c_cur->f_total[kind];
1214                 }
1215             LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p: peer `%s' has relative preference for %s of %.3f\n",
1216                         c_cur->client,
1217                 GNUNET_i2s (&p_cur->id),
1218                 GNUNET_ATS_print_preference_type (kind),
1219                 p_cur->f_rel[kind]);
1220
1221             /* Calculate peer relative preference */
1222             /* Start with kind = 1 to exclude terminator */
1223             p_cur->f_rel_total = 0;
1224             for (rkind = GNUNET_ATS_PREFERENCE_END + 1; rkind < GNUNET_ATS_PreferenceCount; rkind ++)
1225             {
1226                 p_cur->f_rel_total += p_cur->f_rel[rkind];
1227             }
1228             p_cur->f_rel_total /=  (GNUNET_ATS_PreferenceCount - 1.0); /* -1 due to terminator */
1229             LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p: peer `%s' has total relative preference of %.3f\n",
1230                         c_cur->client,
1231                 GNUNET_i2s (&p_cur->id),
1232                 p_cur->f_rel_total);
1233           }
1234   }
1235
1236   /* Calculcate global total relative peer preference over all clients */
1237   p_rel_global = 0.0;
1238   clients = 0;
1239   for (c_cur = s->pc_head; NULL != c_cur; c_cur = c_cur->next)
1240   {
1241       for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
1242           if (0 == memcmp (&p_cur->id, &p->id, sizeof (p_cur->id)))
1243               break;
1244       if (NULL != p_cur)
1245       {
1246           clients++;
1247           p_rel_global += p_cur->f_rel_total;
1248       }
1249   }
1250   p_rel_global /= clients;
1251   LOG (GNUNET_ERROR_TYPE_DEBUG, "Global preference value for peer `%s': %.3f\n",
1252       GNUNET_i2s (&p->id), p_rel_global);
1253
1254   /* Update global map */
1255   if (NULL != (dest = GNUNET_CONTAINER_multihashmap_get(s->prefs, &p->id.hashPubKey)))
1256       (*dest) = p_rel_global;
1257   else
1258   {
1259       dest = GNUNET_malloc (sizeof (double));
1260       (*dest) = p_rel_global;
1261       GNUNET_CONTAINER_multihashmap_put(s->prefs,
1262           &p->id.hashPubKey,
1263           dest,
1264           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1265   }
1266 }
1267
1268 static void
1269 update_preference (struct PreferencePeer *p,
1270                                                                          enum GNUNET_ATS_PreferenceKind kind,
1271                                                          float score_f)
1272 {
1273         double score = score_f;
1274
1275   /* Update preference value according to type */
1276   switch (kind) {
1277     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1278     case GNUNET_ATS_PREFERENCE_LATENCY:
1279       p->f[kind] = (p->f[kind] + score) / 2;
1280       break;
1281     case GNUNET_ATS_PREFERENCE_END:
1282       break;
1283     default:
1284       break;
1285   }
1286   recalculate_preferences(p);
1287   update_all_networks (p->s);
1288 }
1289
1290
1291 static void
1292 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1293 {
1294         int i;
1295         double *t = NULL;
1296         double backup;
1297         struct PreferencePeer *p = cls;
1298         GNUNET_assert (NULL != p);
1299
1300
1301         p->aging_task = GNUNET_SCHEDULER_NO_TASK;
1302
1303   LOG (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
1304                 GNUNET_i2s (&p->id));
1305
1306   /* Issue for aging :
1307    *
1308    * Not for every peer preference values are set by default, so reducing the
1309    * absolute preference value does not help for aging because it does not have
1310    * influence on the relative values.
1311    *
1312    * So we have to reduce the relative value to have an immediate impact on
1313    * quota calculation. In addition we cannot call recalculate_preferences here
1314    * but instead reduce the absolute value to have an aging impact on future
1315    * calls to change_preference where recalculate_preferences is called
1316    *
1317    */
1318   /* Aging absolute values: */
1319   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
1320   {
1321                 if (p->f[i] > 1.0)
1322                 {
1323                         backup = p->f[i];
1324                         p->f[i] *= PREF_AGING_FACTOR;
1325                         LOG (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
1326                         GNUNET_i2s (&p->id), backup, p->f[i]);
1327                 }
1328   }
1329   /* Updating relative value */
1330   t = GNUNET_CONTAINER_multihashmap_get (p->s->prefs, &p->id.hashPubKey);
1331   if (NULL == t)
1332   {
1333         GNUNET_break (0);
1334   }
1335   else
1336   {
1337                 if ((*t) > 1.0)
1338                         (*t) = (*t) * PREF_AGING_FACTOR;
1339                 else
1340                         (*t) = 1.0;
1341                 update_all_networks (p->s);
1342   }
1343   p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
1344                 &preference_aging, p);
1345 }
1346
1347
1348 /**
1349  * Changes the preferences for a peer in the problem
1350  *
1351  * @param solver the solver handle
1352  * @param client the client with this preference
1353  * @param peer the peer to change the preference for
1354  * @param kind the kind to change the preference
1355  * @param score the score
1356  */
1357 void
1358 GAS_simplistic_address_change_preference (void *solver,
1359                                    void *client,
1360                                    const struct GNUNET_PeerIdentity *peer,
1361                                    enum GNUNET_ATS_PreferenceKind kind,
1362                                    float score)
1363 {
1364   static struct GNUNET_TIME_Absolute next_update;
1365   struct GAS_SIMPLISTIC_Handle *s = solver;
1366   struct PreferenceClient *c_cur;
1367   struct PreferencePeer *p_cur;
1368   int i;
1369
1370   GNUNET_assert (NULL != solver);
1371   GNUNET_assert (NULL != client);
1372   GNUNET_assert (NULL != peer);
1373
1374   LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p changes preference for peer `%s' %s %f\n",
1375                                 client,
1376                                 GNUNET_i2s (peer),
1377                                 GNUNET_ATS_print_preference_type (kind),
1378                                 score);
1379
1380   if (kind >= GNUNET_ATS_PreferenceCount)
1381   {
1382       GNUNET_break (0);
1383       return;
1384   }
1385
1386   /* Find preference client */
1387   for (c_cur = s->pc_head; NULL != c_cur; c_cur = c_cur->next)
1388   {
1389       if (client == c_cur->client)
1390         break;
1391   }
1392   /* Not found: create new preference client */
1393   if (NULL == c_cur)
1394   {
1395     c_cur = GNUNET_malloc (sizeof (struct PreferenceClient));
1396     c_cur->client = client;
1397     GNUNET_CONTAINER_DLL_insert (s->pc_head, s->pc_tail, c_cur);
1398   }
1399
1400   /* Find entry for peer */
1401   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
1402     if (0 == memcmp (&p_cur->id, peer, sizeof (p_cur->id)))
1403         break;
1404
1405   /* Not found: create new peer entry */
1406   if (NULL == p_cur)
1407   {
1408       p_cur = GNUNET_malloc (sizeof (struct PreferencePeer));
1409       p_cur->s = s;
1410       p_cur->client = c_cur;
1411       p_cur->id = (*peer);
1412       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
1413       {
1414         /* Default value per peer absolut preference for a quality:
1415          * No value set, so absolute preference 0 */
1416         p_cur->f[i] = 0.0;
1417         /* Default value per peer relative preference for a quality: 1.0 */
1418         p_cur->f_rel[i] = DEFAULT_PREFERENCE;
1419       }
1420       p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL, &preference_aging, p_cur);
1421       GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
1422   }
1423
1424   update_preference (p_cur, kind, score);
1425
1426   /* FIXME: We should update quotas if UPDATE_INTERVAL is reached */
1427   if (GNUNET_TIME_absolute_get().abs_value > next_update.abs_value)
1428   {
1429       /* update quotas*/
1430       next_update = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
1431                                               MIN_UPDATE_INTERVAL);
1432   }
1433 }
1434
1435
1436 /**
1437  *  Solver API functions
1438  *  ---------------------------
1439  */
1440
1441
1442 /**
1443  * Shutdown the proportional problem solver
1444  *
1445  * @param solver the respective handle to shutdown
1446  */
1447 void
1448 GAS_proportional_done (void *solver)
1449 {
1450   struct GAS_SIMPLISTIC_Handle *s = solver;
1451   struct PreferenceClient *pc;
1452   struct PreferenceClient *next_pc;
1453   struct PreferencePeer *p;
1454   struct PreferencePeer *next_p;
1455   struct AddressWrapper *cur;
1456   struct AddressWrapper *next;
1457   int c;
1458   GNUNET_assert (s != NULL);
1459
1460   for (c = 0; c < s->networks; c++)
1461   {
1462       if (s->network_entries[c].total_addresses > 0)
1463       {
1464         LOG (GNUNET_ERROR_TYPE_ERROR,
1465                     "Had %u addresses for network `%s' not deleted during shutdown\n",
1466                     s->network_entries[c].total_addresses,
1467                     s->network_entries[c].desc);
1468         GNUNET_break (0);
1469       }
1470
1471       if (s->network_entries[c].active_addresses > 0)
1472       {
1473         LOG (GNUNET_ERROR_TYPE_ERROR,
1474                     "Had %u active addresses for network `%s' not deleted during shutdown\n",
1475                     s->network_entries[c].active_addresses,
1476                     s->network_entries[c].desc);
1477         GNUNET_break (0);
1478       }
1479
1480       next = s->network_entries[c].head;
1481       while (NULL != (cur = next))
1482       {
1483           next = cur->next;
1484           GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
1485                                        s->network_entries[c].tail,
1486                                        cur);
1487           GNUNET_free (cur);
1488       }
1489       GNUNET_free (s->network_entries[c].stat_total);
1490       GNUNET_free (s->network_entries[c].stat_active);
1491   }
1492   if (s->total_addresses > 0)
1493   {
1494     LOG (GNUNET_ERROR_TYPE_ERROR,
1495                 "Had %u addresses not deleted during shutdown\n",
1496                 s->total_addresses);
1497     GNUNET_break (0);
1498   }
1499   if (s->active_addresses > 0)
1500   {
1501     LOG (GNUNET_ERROR_TYPE_ERROR,
1502                 "Had %u active addresses not deleted during shutdown\n",
1503                 s->active_addresses);
1504     GNUNET_break (0);
1505   }
1506   GNUNET_free (s->network_entries);
1507
1508   next_pc = s->pc_head;
1509   while (NULL != (pc = next_pc))
1510   {
1511       next_pc = pc->next;
1512       GNUNET_CONTAINER_DLL_remove (s->pc_head, s->pc_tail, pc);
1513       next_p = pc->p_head;
1514       while (NULL != (p = next_p))
1515       {
1516           next_p = p->next;
1517           if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
1518           {
1519                 GNUNET_SCHEDULER_cancel(p->aging_task);
1520                 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
1521           }
1522           GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
1523           GNUNET_free (p);
1524       }
1525       GNUNET_free (pc);
1526   }
1527
1528   GNUNET_CONTAINER_multihashmap_iterate (s->prefs, &free_pref, NULL);
1529   GNUNET_CONTAINER_multihashmap_destroy (s->prefs);
1530   GNUNET_free (s);
1531 }
1532
1533
1534 /**
1535  * Init the proportional problem solver
1536  *
1537  * Quotas:
1538  * network[i] contains the network type as type GNUNET_ATS_NetworkType[i]
1539  * out_quota[i] contains outbound quota for network type i
1540  * in_quota[i] contains inbound quota for network type i
1541  *
1542  * Example
1543  * network = {GNUNET_ATS_NET_UNSPECIFIED, GNUNET_ATS_NET_LOOPBACK, GNUNET_ATS_NET_LAN, GNUNET_ATS_NET_WAN, GNUNET_ATS_NET_WLAN}
1544  * network[2]   == GNUNET_ATS_NET_LAN
1545  * out_quota[2] == 65353
1546  * in_quota[2]  == 65353
1547  *
1548  * @param cfg configuration handle
1549  * @param stats the GNUNET_STATISTICS handle
1550  * @param network array of GNUNET_ATS_NetworkType with length dest_length
1551  * @param out_quota array of outbound quotas
1552  * @param in_quota array of outbound quota
1553  * @param dest_length array length for quota arrays
1554  * @param bw_changed_cb callback for changed bandwidth amounts
1555  * @param bw_changed_cb_cls cls for callback
1556  * @return handle for the solver on success, NULL on fail
1557  */
1558 void *
1559 GAS_proportional_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1560                      const struct GNUNET_STATISTICS_Handle *stats,
1561                      int *network,
1562                      unsigned long long *out_quota,
1563                      unsigned long long *in_quota,
1564                      int dest_length,
1565                      GAS_bandwidth_changed_cb bw_changed_cb,
1566                      void *bw_changed_cb_cls)
1567 {
1568   int c;
1569   struct GAS_SIMPLISTIC_Handle *s = GNUNET_malloc (sizeof (struct GAS_SIMPLISTIC_Handle));
1570   struct Network * cur;
1571   char * net_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
1572
1573
1574   s->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1575   s->bw_changed = bw_changed_cb;
1576   s->bw_changed_cls = bw_changed_cb_cls;
1577   s->networks = dest_length;
1578   s->network_entries = GNUNET_malloc (dest_length * sizeof (struct Network));
1579   s->active_addresses = 0;
1580   s->total_addresses = 0;
1581   s->prefs = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
1582
1583   for (c = 0; c < dest_length; c++)
1584   {
1585       cur = &s->network_entries[c];
1586       cur->total_addresses = 0;
1587       cur->active_addresses = 0;
1588       cur->type = network[c];
1589       cur->total_quota_in = in_quota[c];
1590       cur->total_quota_out = out_quota[c];
1591       cur->desc = net_str[c];
1592       GNUNET_asprintf (&cur->stat_total, "# ATS addresses %s total", cur->desc);
1593       GNUNET_asprintf (&cur->stat_active, "# ATS active addresses %s total", cur->desc);
1594   }
1595   return s;
1596 }
1597
1598 /* end of gnunet-service-ats-solver_proportional.c */