f5dbf04c902c1cb9bf0e978bac6c874033920921
[oweals/gnunet.git] / src / ats / gnunet-service-ats_normalization.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_normalization.c
23  * @brief ats service address: management of ATS properties and preferences normalization
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet-service-ats_normalization.h"
31
32
33
34 /**
35  * Preference client
36  */
37 struct PreferenceClient
38 {
39   /**
40    * Next in DLL
41    */
42   struct PreferenceClient *prev;
43
44   /**
45    * Next in DLL
46    */
47
48   struct PreferenceClient *next;
49
50   /**
51    * Client handle
52    */
53   void *client;
54
55   /**
56    * Total preference for this peer
57    */
58   double f_abs_sum[GNUNET_ATS_PreferenceCount];
59
60   /**
61    * List of peer preferences for this client
62    */
63
64   /**
65    * Head of peer list
66    */
67   struct PreferencePeer *p_head;
68
69   /**
70    * Tail of peer list
71    */
72   struct PreferencePeer *p_tail;
73 };
74
75
76 /**
77  * Preference peer
78  */
79 struct PreferencePeer
80 {
81   /**
82    * Next in DLL
83    */
84   struct PreferencePeer *next;
85
86   /**
87    * Previous in DLL
88    */
89   struct PreferencePeer *prev;
90
91   /**
92    * Client
93    */
94   struct PreferenceClient *client;
95
96   /**
97    * Peer id
98    */
99   struct GNUNET_PeerIdentity id;
100
101   /**
102    * Absolute preference values
103    */
104   double f_abs[GNUNET_ATS_PreferenceCount];
105
106   /**
107    * Relative preference values
108    */
109   double f_rel[GNUNET_ATS_PreferenceCount];
110
111   /**
112    * Aging Task
113    */
114   GNUNET_SCHEDULER_TaskIdentifier aging_task;
115 };
116
117 /**
118  * Relative preferences for a peer
119  */
120 struct PeerRelative
121 {
122   /**
123    * Relative preference values
124    */
125   double f_rel[GNUNET_ATS_PreferenceCount];
126
127   /**
128    * Peer id
129    */
130   struct GNUNET_PeerIdentity id;
131 };
132
133
134 /**
135  * Callback to call on changing preference values
136  */
137 static GAS_Normalization_preference_changed_cb pref_changed_cb;
138
139
140 /**
141  * Closure for callback to call on changing preference values
142  */
143 static void *pref_changed_cb_cls;
144
145
146 /**
147  * Hashmap to store peer information for preference normalization
148  */
149 static struct GNUNET_CONTAINER_MultiHashMap *preference_peers;
150
151
152
153 /**
154  * Hashmap to store peer information for property normalization
155  */
156 static struct GNUNET_CONTAINER_MultiHashMap *property_peers;
157
158
159
160 /**
161  * Clients in DLL: head
162  */
163 static struct PreferenceClient *pc_head;
164
165
166 /**
167  * Clients in DLL: tail
168  */
169 static struct PreferenceClient *pc_tail;
170
171
172 /**
173  * Default values
174  */
175 static struct PeerRelative defvalues;
176
177 /**
178  * Application Preference Normalization
179  */
180
181
182 /**
183  * Update a peer
184  * @param id peer id
185  * @param kind the kind
186  * @return the new relative preference
187  */
188 static double
189 update_peers (struct GNUNET_PeerIdentity *id,
190                                                         enum GNUNET_ATS_PreferenceKind kind)
191 {
192         struct PreferenceClient *c_cur;
193         struct PreferencePeer *p_cur;
194         struct PeerRelative *rp;
195         double f_rel_total;
196         double backup;
197         unsigned int count;
198
199         f_rel_total = 0.0;
200         count = 0;
201
202         /* For all clients */
203         for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
204         {
205                 /* Find peer with id */
206                 for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
207                 {
208                         if (0 == memcmp (id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
209                                 break;
210                 }
211                 if (NULL != p_cur)
212                 {
213                         /* Found peer with id */
214                         f_rel_total +=  p_cur->f_rel[kind];
215                         count ++;
216                 }
217         }
218
219         /* Find a client */
220         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221                         "%u clients have a total relative preference for peer `%s''s `%s' of %.3f\n",
222                         count,
223                         GNUNET_i2s (id),
224                         GNUNET_ATS_print_preference_type (kind),
225                         f_rel_total);
226         if (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers, &id->hashPubKey)))
227         {
228                 backup = rp->f_rel[kind];
229                 if (0 < count)
230                 {
231                         rp->f_rel[kind] = f_rel_total / count;
232                 }
233                 else
234                 {
235                         rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
236                 }
237         }
238         else
239         {
240                 return DEFAULT_REL_PREFERENCE;
241         }
242
243         if ((backup != rp->f_rel[kind]) && (NULL != pref_changed_cb))
244         {
245                 pref_changed_cb (pref_changed_cb_cls, &rp->id, kind, rp->f_rel[kind]);
246         }
247
248         return rp->f_rel[kind];
249 }
250
251
252 /**
253  * Recalculate preference for a specific ATS property
254  *
255  * @param c the preference client
256  * @param p the peer
257  * @param kind the preference kind
258  * @return the result
259  */
260 static double
261 recalculate_rel_preferences (struct PreferenceClient *c,
262                                                          struct PreferencePeer *p,
263                                                          enum GNUNET_ATS_PreferenceKind kind)
264 {
265         struct PreferencePeer *p_cur;
266         struct PeerRelative *rp;
267         double backup;
268         double res;
269         double ret;
270
271         /* For this client: sum preferences to total preference */
272         c->f_abs_sum[kind] = 0;
273         for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
274                 c->f_abs_sum[kind] += p_cur->f_abs[kind];
275         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276                         "Client %p has total preference for %s of %.3f\n",
277                         c->client,
278                         GNUNET_ATS_print_preference_type (kind),
279                         c->f_abs_sum[kind]);
280
281         ret = DEFAULT_REL_PREFERENCE;
282         /* For all peers: calculate relative preference */
283         for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
284         {
285                 /* Calculate relative preference for specific kind */
286                 backup = p_cur->f_rel[kind];
287                 if (DEFAULT_ABS_PREFERENCE == c->f_abs_sum[kind])
288                                 /* No peer has a preference for this property,
289                                  * so set default preference */
290                                 p_cur->f_rel[kind] = DEFAULT_REL_PREFERENCE;
291                 else
292                                 p_cur->f_rel[kind] = (c->f_abs_sum[kind] + p_cur->f_abs[kind]) /
293                                 c->f_abs_sum[kind];
294
295                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296                                 "Client %p: peer `%s' has relative preference for %s of %.3f\n",
297                                 c->client,
298                                 GNUNET_i2s (&p_cur->id),
299                                 GNUNET_ATS_print_preference_type (kind),
300                                 p_cur->f_rel[kind]);
301
302                 res = 0.0;
303                 if (p_cur->f_rel[kind] != backup)
304                 {
305                         /* Value changed, recalculate */
306                         res = update_peers (&p_cur->id,kind);
307                         if (0 == memcmp (&p->id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
308                                 ret = res;
309                 }
310                 else
311           {
312                         /* Value did not chang, return old value*/
313                         GNUNET_assert (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers,
314                                         &p->id.hashPubKey)));
315                         ret = rp->f_rel[kind];
316           }
317         }
318         return ret;
319 }
320
321
322 /**
323  * Update the absolute preference value for a peer
324  * @param c the client
325  * @param p the peer
326  * @param kind the preference kind
327  * @param score_abs the absolute value
328  * @return the new relative preference value
329  */
330 static double
331 update_preference (struct PreferenceClient *c,
332                                                                          struct PreferencePeer *p,
333                                                                          enum GNUNET_ATS_PreferenceKind kind,
334                                                          float score_abs)
335 {
336         double score = score_abs;
337
338   /* Update preference value according to type */
339   switch (kind) {
340     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
341     case GNUNET_ATS_PREFERENCE_LATENCY:
342       p->f_abs[kind] = (p->f_abs[kind] + score) / 2;
343       break;
344     case GNUNET_ATS_PREFERENCE_END:
345       break;
346     default:
347       break;
348   }
349   return recalculate_rel_preferences (c, p, kind);
350 }
351
352
353 /**
354  * Reduce absolute preferences since they got old
355  *
356  * @param cls the PreferencePeer
357  * @param tc context
358  */
359 static void
360 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
361 {
362         int i;
363         double backup;
364         struct PreferencePeer *p = cls;
365         GNUNET_assert (NULL != p);
366
367         p->aging_task = GNUNET_SCHEDULER_NO_TASK;
368
369         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
370                 GNUNET_i2s (&p->id));
371
372   /* Aging absolute values: */
373   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
374   {
375                         backup = p->f_abs[i];
376                 if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
377                         p->f_abs[i] *= PREF_AGING_FACTOR;
378                 if (backup != p->f_abs[i])
379                 {
380                         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
381                         GNUNET_i2s (&p->id), backup, p->f_abs[i]);
382                         recalculate_rel_preferences (p->client, p, i);
383                 }
384   }
385   p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
386                 &preference_aging, p);
387 }
388
389
390 /**
391  * Normalize an updated preference value
392  *
393  * @param src the client with this preference
394  * @param peer the peer to change the preference for
395  * @param kind the kind to change the preference
396  * @param score_abs the normalized score
397  */
398 float
399 GAS_normalization_normalize_preference (void *src,
400                                          const struct GNUNET_PeerIdentity *peer,
401                                          enum GNUNET_ATS_PreferenceKind kind,
402                                          float score_abs)
403 {
404   float score_rel;
405   struct PreferenceClient *c_cur;
406   struct PreferencePeer *p_cur;
407   struct PeerRelative *r_cur;
408   int i;
409
410
411   GNUNET_assert (NULL != src);
412   GNUNET_assert (NULL != peer);
413
414   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415                 "Client %p changes preference for peer `%s' for `%s' to %.2f\n",
416                         src,
417                         GNUNET_i2s (peer),
418                         GNUNET_ATS_print_preference_type (kind),
419                         score_abs);
420
421   if (kind >= GNUNET_ATS_PreferenceCount)
422   {
423       GNUNET_break (0);
424       return 0.0;
425   }
426
427   /* Find preference client */
428   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
429   {
430       if (src == c_cur->client)
431         break;
432   }
433   /* Not found: create new preference client */
434   if (NULL == c_cur)
435   {
436     c_cur = GNUNET_malloc (sizeof (struct PreferenceClient));
437     c_cur->client = src;
438     GNUNET_CONTAINER_DLL_insert (pc_head, pc_tail, c_cur);
439   }
440
441   /* Find entry for peer */
442   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
443     if (0 == memcmp (&p_cur->id, peer, sizeof (p_cur->id)))
444         break;
445
446   /* Not found: create new peer entry */
447   if (NULL == p_cur)
448   {
449       p_cur = GNUNET_malloc (sizeof (struct PreferencePeer));
450       p_cur->client = c_cur;
451       p_cur->id = (*peer);
452       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
453       {
454         /* Default value per peer absolut preference for a quality:
455          * No value set, so absolute preference 0 */
456         p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
457         /* Default value per peer relative preference for a quality: 1.0 */
458         p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
459       }
460       p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
461                 &preference_aging, p_cur);
462       GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
463   }
464
465   if (NULL == (r_cur = GNUNET_CONTAINER_multihashmap_get (preference_peers,
466                 &peer->hashPubKey)))
467   {
468         r_cur = GNUNET_malloc (sizeof (struct PeerRelative));
469         r_cur->id = (*peer);
470         for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
471                 r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
472         GNUNET_CONTAINER_multihashmap_put (preference_peers, &r_cur->id.hashPubKey,
473                         r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
474   }
475
476   score_rel = update_preference (c_cur, p_cur, kind, score_abs);
477   return score_rel;
478 }
479
480
481 /**
482  * Get the normalized preference values for a specific peer or
483  * the default values if
484  *
485  * @param id the peer
486  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
487  * default preferences if peer does not exist
488  */
489 const double *
490 GAS_normalization_get_preferences (const struct GNUNET_PeerIdentity *id)
491 {
492         GNUNET_assert (NULL != preference_peers);
493         GNUNET_assert (NULL != id);
494
495         struct PeerRelative *rp;
496         if (NULL == (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers, &id->hashPubKey)))
497         {
498                 return defvalues.f_rel;
499         }
500         return rp->f_rel;
501 }
502
503 /**
504  * Quality Normalization
505  */
506
507 struct Property
508 {
509         int have_min; /* Do we have min and max */
510         int have_max;
511         uint32_t min;
512         uint32_t max;
513 };
514
515 struct Property properties[GNUNET_ATS_QualityPropertiesCount];
516
517 /**
518  * Normalize a specific ATS type with the values in queue
519  * @param address the address
520  * @param atsi the ats information
521  * @return the new average or GNUNET_ATS_VALUE_UNDEFINED
522  */
523
524 uint32_t property_average (struct ATS_Address *address,
525                                                                                          const struct GNUNET_ATS_Information *atsi)
526 {
527         struct GAS_NormalizationInfo *ni;
528         uint32_t current_type;
529         uint32_t current_val;
530
531         uint32_t sum;
532         uint32_t count;
533         unsigned int c1;
534         unsigned int index;
535         unsigned int props[] = GNUNET_ATS_QualityProperties;
536
537         /* Average the values of this property */
538         current_type = ntohl (atsi->type);
539         current_val = ntohl (atsi->value);
540
541         for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
542         {
543                 if (current_type == props[c1])
544                         break;
545         }
546         if (c1 == GNUNET_ATS_QualityPropertiesCount)
547         {
548                 GNUNET_break (0);
549                 return GNUNET_ATS_VALUE_UNDEFINED;
550         }
551         index = c1;
552
553         ni = &address->atsin[index];
554         ni->atsi_abs[ni->index] = current_val;
555         ni->index ++;
556         if (GAS_normalization_queue_length == ni->index)
557                 ni->index = 0;
558
559         count = 0;
560         for (c1 = 0; c1 < GAS_normalization_queue_length; c1++)
561         {
562                 if (GNUNET_ATS_VALUE_UNDEFINED != ni->atsi_abs[c1])
563                 {
564                         count++;
565                         if (GNUNET_ATS_VALUE_UNDEFINED > (sum + ni->atsi_abs[c1]))
566                                 sum += ni->atsi_abs[c1];
567                         else
568                         {
569                                 sum = GNUNET_ATS_VALUE_UNDEFINED - 1;
570                                 GNUNET_break (0);
571                         }
572                 }
573         }
574         GNUNET_assert (0 != count);
575         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New average from %u elements: %u\n", count, sum / count);
576         return 0;
577 }
578
579 double property_normalize (struct Property *p,
580                                                                                                  struct ATS_Address *address,
581                                                                                                  uint32_t type,
582                                                                                                  uint32_t avg_value)
583 {
584         double res;
585         double delta;
586         /* Normalize the values of this property */
587         if (p->max < avg_value)
588         {
589                 p->max = avg_value;
590                 if (GNUNET_NO == p->have_max)
591                         p->have_max = GNUNET_YES;
592                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
593                                 "New maximum of %u for property %u\n",
594                                 p->max, avg_value);
595         }
596         if (p->min > avg_value)
597         {
598                 p->min = avg_value;
599                 if (GNUNET_NO == p->have_min)
600                         p->have_min = GNUNET_YES;
601                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602                                 "New minimum of %u for property %u\n",
603                                 p->min, avg_value);
604         }
605
606         if ((GNUNET_YES == p->have_max) && (GNUNET_YES == p->have_min))
607         {
608                 delta = p->max - p->min;
609                 res = (delta + avg_value) / (delta);
610                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611                                 "New normalized value of %f for property %u\n",
612                                 res, type);
613                 return res;
614         }
615
616         return DEFAULT_REL_QUALITY;
617 }
618
619
620
621 void
622 GAS_normalization_normalize_property (struct ATS_Address *address,
623                                                                                                                                                         const struct GNUNET_ATS_Information *atsi,
624                                                                                                                                                         uint32_t atsi_count)
625 {
626         struct Property *cur_prop;
627         int c1;
628         int c2;
629         uint32_t current_type;
630         uint32_t current_val;
631         unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
632
633         GNUNET_assert (NULL != address);
634         GNUNET_assert (NULL != atsi);
635
636         for (c1 = 0; c1 < atsi_count; c1++)
637         {
638                 current_type = ntohl (atsi[c1].type);
639                 current_val = ntohl (atsi[c1].value);
640                 for (c2 = 0; c2 < GNUNET_ATS_QualityPropertiesCount; c2++)
641                 {
642                         /* Check if type is valid */
643                         if (current_type == existing_properties[c2])
644                                 break;
645                 }
646                 if (GNUNET_ATS_QualityPropertiesCount == c2)
647                 {
648                         /* Invalid property, continue with next element */
649                         continue;
650                 }
651
652                 /* Averaging */
653                 current_val = property_average (address, &atsi[c1]);
654                 if (GNUNET_ATS_VALUE_UNDEFINED == current_val)
655                 {
656                         GNUNET_break (0);
657                         continue;
658                 }
659
660                 /* Normalizing */
661                 /* Check min, max */
662                 cur_prop = &properties[c2];
663                 property_normalize (cur_prop, address, ntohl(atsi[c1].type), current_val);
664         }
665
666 }
667
668
669
670
671 /**
672  * Start the normalization component
673  *
674  * @param pref_ch_cb callback to call on relative preference changing
675  * @param pref_ch_cb_cls cls for the callback
676  */
677 void
678 GAS_normalization_start (GAS_Normalization_preference_changed_cb pref_ch_cb,
679                 void *pref_ch_cb_cls)
680 {
681         int c1;
682         int i;
683         preference_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
684         property_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
685
686         for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
687         {
688                 properties[c1].min = 0;
689                 properties[c1].max = 0;
690                 properties[c1].have_max = GNUNET_NO;
691                 properties[c1].have_min = GNUNET_NO;
692         }
693
694         pref_changed_cb = pref_ch_cb;
695         pref_changed_cb_cls = pref_ch_cb_cls;
696         for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
697                 defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
698         return;
699 }
700
701
702 /**
703  * Free a peer
704  *
705  * @param cls unused
706  * @param key the key
707  * @param value RelativePeer
708  * @return GNUNET_OK to continue
709  */
710 static int
711 free_peer (void *cls,
712                          const struct GNUNET_HashCode * key,
713                          void *value)
714 {
715         struct PeerRelative *rp = value;
716         GNUNET_CONTAINER_multihashmap_remove (preference_peers, key, value);
717         GNUNET_free (rp);
718         return GNUNET_OK;
719 }
720
721
722 /**
723  * Stop the normalization component and free all items
724  */
725 void
726 GAS_normalization_stop ()
727 {
728   struct PreferenceClient *pc;
729   struct PreferenceClient *next_pc;
730   struct PreferencePeer *p;
731   struct PreferencePeer *next_p;
732
733   next_pc = pc_head;
734   while (NULL != (pc = next_pc))
735   {
736       next_pc = pc->next;
737       GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
738       next_p = pc->p_head;
739       while (NULL != (p = next_p))
740       {
741           next_p = p->next;
742           if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
743           {
744                 GNUNET_SCHEDULER_cancel(p->aging_task);
745                 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
746           }
747           GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
748           GNUNET_free (p);
749       }
750       GNUNET_free (pc);
751   }
752   GNUNET_CONTAINER_multihashmap_iterate (preference_peers, &free_peer, NULL);
753   GNUNET_CONTAINER_multihashmap_destroy (preference_peers);
754   GNUNET_CONTAINER_multihashmap_destroy (property_peers);
755         return;
756 }
757
758 /* end of gnunet-service-ats_normalization.c */