-adding comments, minor cleanup start
[oweals/gnunet.git] / src / ats / gnunet-service-ats_preferences.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2015 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  * @file ats/gnunet-service-ats_preferences.c
22  * @brief manage preferences expressed by clients
23  * @author Matthias Wachs
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-ats.h"
28 #include "gnunet-service-ats_addresses.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_plugins.h"
31 #include "gnunet-service-ats_preferences.h"
32 #include "gnunet-service-ats_reservations.h"
33 #include "ats.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "ats-preferences",__VA_ARGS__)
36
37 /**
38  * How frequently do we age preference values?
39  */
40 #define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
41
42 /**
43  * By which factor do we age preferences expressed during
44  * each #PREF_AGING_INTERVAL?
45  */
46 #define PREF_AGING_FACTOR 0.95
47
48 /**
49  * What is the lowest threshold up to which prefernce values
50  * are aged, and below which we consider them zero and thus
51  * no longer subject to aging?
52  */
53 #define PREF_EPSILON 0.01
54
55
56 /**
57  * Relative preferences for a peer
58  */
59 struct PeerRelative
60 {
61   /**
62    * Relative preference values
63    */
64   double f_rel[GNUNET_ATS_PreferenceCount];
65
66   /**
67    * Peer identity for which we have these preferences.
68    */
69   struct GNUNET_PeerIdentity id;
70 };
71
72
73 /**
74  * Default values
75  */
76 static struct PeerRelative defvalues;
77
78
79 /**
80  * Preference client, information we keep track of per client.
81  */
82 struct PreferenceClient
83 {
84   /**
85    * Previous in DLL
86    */
87   struct PreferenceClient *prev;
88
89   /**
90    * Next in DLL
91    */
92   struct PreferenceClient *next;
93
94   /**
95    * Head of peer list
96    */
97   struct PreferencePeer *p_head;
98
99   /**
100    * Tail of peer list
101    */
102   struct PreferencePeer *p_tail;
103
104   /**
105    * Client handle
106    */
107   struct GNUNET_SERVER_Client *client;
108
109   /**
110    * Array of sum of absolute preferences for this client
111    */
112   double f_abs_sum[GNUNET_ATS_PreferenceCount];
113
114   /**
115    * Array of sum of relative preferences for this client
116    */
117   double f_rel_sum[GNUNET_ATS_PreferenceCount];
118 };
119
120
121 /**
122  * Preference information per peer and client.
123  */
124 struct PreferencePeer
125 {
126   /**
127    * Next in DLL of preference entries for the same client.
128    */
129   struct PreferencePeer *next;
130
131   /**
132    * Previous in DLL of preference entries for the same client.
133    */
134   struct PreferencePeer *prev;
135
136   /**
137    * Client that expressed this preferences.
138    */
139   struct PreferenceClient *client;
140
141   /**
142    * Peer identity for which the preference was expressed.
143    */
144   struct GNUNET_PeerIdentity id;
145
146   /**
147    * Absolute preference values for all preference types
148    */
149   double f_abs[GNUNET_ATS_PreferenceCount];
150
151   /**
152    * Relative preference values for all preference types
153    */
154   double f_rel[GNUNET_ATS_PreferenceCount];
155
156   /**
157    * Absolute point of time of next aging process
158    */
159   struct GNUNET_TIME_Absolute next_aging[GNUNET_ATS_PreferenceCount];
160 };
161
162
163 /**
164  * Hashmap to store peer information for preference normalization.
165  * Maps the identity of a peer to a `struct PeerRelative` containing
166  * the current relative preference values for that peer.
167  */
168 static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
169
170 /**
171  * Clients in DLL: head
172  */
173 static struct PreferenceClient *pc_head;
174
175 /**
176  * Clients in DLL: tail
177  */
178 static struct PreferenceClient *pc_tail;
179
180 /**
181  * Handle for task we run periodically to age preferences over time.
182  */
183 static struct GNUNET_SCHEDULER_Task *aging_task;
184
185
186 /**
187  * Update a peer
188  *
189  * @param id peer id
190  * @param kind the kind
191  * @param rp the relative peer struct
192  * @return the new relative preference
193  */
194 static void
195 update_relative_values_for_peer (const struct GNUNET_PeerIdentity *id,
196                                  enum GNUNET_ATS_PreferenceKind kind,
197                                  struct PeerRelative *rp)
198 {
199   struct PreferenceClient *c_cur;
200   struct PreferencePeer *p_cur;
201   double f_rel_total;
202   double f_rel_sum;
203   double backup;
204   unsigned int peer_count;
205
206   f_rel_sum = 0.0;
207   f_rel_total = 0.0;
208   peer_count = 0;
209
210   /* For all clients */
211   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
212   {
213     /* For peer entries with this id */
214     for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
215     {
216       f_rel_sum += p_cur->f_rel[kind];
217       if (0 == memcmp (id, &p_cur->id, sizeof(struct GNUNET_PeerIdentity)))
218       {
219         peer_count ++;
220         f_rel_total += p_cur->f_rel[kind];
221       }
222     }
223   }
224
225   LOG (GNUNET_ERROR_TYPE_DEBUG,
226        "%u clients have a total relative preference for peer `%s' `%s' of %.3f and for %s in total %.3f\n",
227        peer_count,
228        GNUNET_i2s (id),
229        GNUNET_ATS_print_preference_type (kind),
230        f_rel_total,
231        GNUNET_ATS_print_preference_type (kind),
232        f_rel_sum);
233
234   /* Find entry for the peer containing relative values in the hashmap */
235   if (NULL != rp)
236   {
237     backup = rp->f_rel[kind];
238     if (f_rel_sum > 0)
239       rp->f_rel[kind] = f_rel_total / f_rel_sum;
240     else
241     {
242       /* No client had any preferences for this type and any peer */
243       rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
244     }
245     if (backup != rp->f_rel[kind])
246       GAS_plugin_notify_preference_changed (&rp->id, kind,
247                                          rp->f_rel[kind]);
248   }
249 }
250
251
252 /**
253  * Recalculate preference for a specific ATS property
254  *
255  * @param c the preference client
256  * @param kind the preference kind
257  * @return the result
258  */
259 static void
260 recalculate_relative_preferences (struct PreferenceClient *c,
261                                   enum GNUNET_ATS_PreferenceKind kind)
262 {
263   struct PreferencePeer *p_cur;
264
265   /* For this client: sum of absolute preference values for this preference */
266   c->f_abs_sum[kind] = 0.0;
267   /* For this client: sum of relative preference values for this preference
268    *
269    * Note: this value should also be 1.0, but:
270    * if no preferences exist due to aging, this value can be 0.0
271    * and the client can be removed */
272   c->f_rel_sum[kind] = 0.0;
273
274   for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
275     c->f_abs_sum[kind] += p_cur->f_abs[kind];
276   LOG (GNUNET_ERROR_TYPE_DEBUG,
277        "Client %p has sum of total preferences for %s of %.3f\n",
278        c->client,
279        GNUNET_ATS_print_preference_type (kind),
280        c->f_abs_sum[kind]);
281
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
287     /* Every application has a preference for each peer between
288      * [0 .. 1] in relative values
289      * and [0 .. inf] in absolute values */
290     p_cur->f_rel[kind] =  p_cur->f_abs[kind] / c->f_abs_sum[kind];
291     c->f_rel_sum[kind] += p_cur->f_rel[kind];
292
293     LOG (GNUNET_ERROR_TYPE_DEBUG,
294         "Client %p has relative preference for %s for peer `%s' of %.3f\n",
295         c->client,
296         GNUNET_ATS_print_preference_type (kind),
297         GNUNET_i2s (&p_cur->id),
298         p_cur->f_rel[kind]);
299   }
300 }
301
302
303 /**
304  * FIXME
305  */
306 static int
307 update_iterator (void *cls,
308                  const struct GNUNET_PeerIdentity *key,
309                  void *value)
310 {
311   enum GNUNET_ATS_PreferenceKind *kind = cls;
312   struct PeerRelative *pr = value;
313
314   update_relative_values_for_peer (key,
315                                    *kind,
316                                    pr);
317   return GNUNET_OK;
318 }
319
320
321 /**
322  * FIXME.
323  *
324  */
325 static void
326 run_preference_update (struct PreferenceClient *c_cur,
327                        struct PreferencePeer *p_cur,
328                        enum GNUNET_ATS_PreferenceKind kind,
329                        float score_abs)
330 {
331   double old_value;
332
333   /* Update relative value */
334   old_value = p_cur->f_rel[kind];
335   recalculate_relative_preferences (c_cur, kind);
336   if (p_cur->f_rel[kind] == old_value)
337     return;
338
339   /* Relative preference value changed, recalculate for all peers */
340   GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
341                                          &update_iterator,
342                                          &kind);
343 }
344
345
346 /**
347  * Reduce absolute preferences since they got old
348  *
349  * @param cls the PreferencePeer
350  * @param tc context
351  */
352 static void
353 preference_aging (void *cls,
354                   const struct GNUNET_SCHEDULER_TaskContext *tc)
355 {
356   struct PreferencePeer *p;
357   struct PreferenceClient *cur_client;
358   unsigned int i;
359   unsigned int values_to_update;
360   double backup;
361
362   aging_task = NULL;
363   values_to_update = 0;
364   cur_client = NULL;
365
366   for (cur_client = pc_head; NULL != cur_client; cur_client = cur_client->next)
367   {
368     for (p = cur_client->p_head; NULL != p; p = p->next)
369     {
370       /* Aging absolute values: */
371       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
372       {
373         if (0
374             == GNUNET_TIME_absolute_get_remaining (p->next_aging[i]).rel_value_us)
375         {
376           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
377               "Aging preference for peer `%s'\n", GNUNET_i2s (&p->id));
378           backup = p->f_abs[i];
379           if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
380             p->f_abs[i] *= PREF_AGING_FACTOR;
381
382           if (p->f_abs[i] <= DEFAULT_ABS_PREFERENCE + PREF_EPSILON)
383             p->f_abs[i] = DEFAULT_ABS_PREFERENCE;
384
385           if ( (p->f_abs[i] != DEFAULT_ABS_PREFERENCE) &&
386                (backup != p->f_abs[i]) )
387           {
388             GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
389                 "Aged preference for peer `%s' from %.3f to %.3f\n",
390                 GNUNET_i2s (&p->id), backup, p->f_abs[i]);
391
392             run_preference_update(cur_client, p, i, p->f_abs[i]);
393
394             p->next_aging[i] = GNUNET_TIME_absolute_add (
395                 GNUNET_TIME_absolute_get (), PREF_AGING_INTERVAL);
396             values_to_update++;
397           }
398         }
399       }
400     }
401   }
402
403   if (values_to_update > 0)
404   {
405     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406                 "Rescheduling aging task due to %u elements to age\n",
407                 values_to_update);
408     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
409                                                &preference_aging,
410                                                NULL);
411   }
412   else
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415                 "No values to age left, not rescheduling aging task\n");
416   }
417 }
418
419
420 /**
421  * Update the absolute preference value for a peer
422  * @param c the client
423  * @param p the peer
424  * @param kind the preference kind
425  * @param score_abs the absolute value
426  * @return the new relative preference value
427  */
428 static void
429 update_abs_preference (struct PreferenceClient *c,
430                        struct PreferencePeer *p,
431                        enum GNUNET_ATS_PreferenceKind kind,
432                        float score_abs)
433 {
434   double score = score_abs;
435
436   /* Update preference value according to type */
437   switch (kind)
438   {
439   case GNUNET_ATS_PREFERENCE_BANDWIDTH:
440   case GNUNET_ATS_PREFERENCE_LATENCY:
441     p->f_abs[kind] = score;
442     /* p->f_abs[kind] = (p->f_abs[kind] + score) / 2;  */
443     p->next_aging[kind] = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
444                                                     PREF_AGING_INTERVAL);
445     break;
446   case GNUNET_ATS_PREFERENCE_END:
447     break;
448   default:
449     break;
450   }
451 }
452
453
454 /**
455  * Normalize an updated preference value
456  *
457  * @param client the client with this preference
458  * @param peer the peer to change the preference for
459  * @param kind the kind to change the preference
460  * @param score_abs the normalized score
461  */
462 static void
463 normalize_preference (struct GNUNET_SERVER_Client *client,
464                       const struct GNUNET_PeerIdentity *peer,
465                       enum GNUNET_ATS_PreferenceKind kind,
466                       float score_abs)
467 {
468   struct PreferenceClient *c_cur;
469   struct PreferencePeer *p_cur;
470   struct PeerRelative *r_cur;
471   double old_value;
472   int i;
473
474   LOG (GNUNET_ERROR_TYPE_DEBUG,
475        "Client changes preference for peer `%s' for `%s' to %.2f\n",
476        GNUNET_i2s (peer),
477        GNUNET_ATS_print_preference_type (kind),
478        score_abs);
479
480   if (kind >= GNUNET_ATS_PreferenceCount)
481   {
482     GNUNET_break(0);
483     return;
484   }
485
486   /* Find preference client */
487   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
488     if (client == c_cur->client)
489       break;
490   /* Not found: create new preference client */
491   if (NULL == c_cur)
492   {
493     c_cur = GNUNET_new (struct PreferenceClient);
494     c_cur->client = client;
495     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
496     {
497       c_cur->f_abs_sum[i] = DEFAULT_ABS_PREFERENCE;
498       c_cur->f_rel_sum[i] = DEFAULT_REL_PREFERENCE;
499     }
500     GNUNET_CONTAINER_DLL_insert (pc_head,
501                                  pc_tail,
502                                  c_cur);
503     LOG (GNUNET_ERROR_TYPE_DEBUG,
504          "Adding new client %p\n",
505          c_cur);
506   }
507
508   /* Find entry for peer */
509   for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
510     if (0 == memcmp (&p_cur->id,
511                      peer,
512                      sizeof (p_cur->id)))
513       break;
514
515   /* Not found: create new peer entry */
516   if (NULL == p_cur)
517   {
518     p_cur = GNUNET_new (struct PreferencePeer);
519     p_cur->client = c_cur;
520     p_cur->id = *peer;
521     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
522     {
523       /* Default value per peer absolute preference for a preference: 0 */
524       p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
525       /* Default value per peer relative preference for a quality: 1.0 */
526       p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
527       p_cur->next_aging[i] = GNUNET_TIME_UNIT_FOREVER_ABS;
528     }
529     LOG (GNUNET_ERROR_TYPE_DEBUG,
530          "Adding new peer %p for client %p\n",
531          p_cur,
532          c_cur);
533     GNUNET_CONTAINER_DLL_insert (c_cur->p_head,
534                                  c_cur->p_tail,
535                                  p_cur);
536   }
537
538   /* Create struct for peer */
539   if (NULL ==
540       GNUNET_CONTAINER_multipeermap_get (preference_peers,
541                                          peer))
542   {
543     r_cur = GNUNET_new (struct PeerRelative);
544     r_cur->id = *peer;
545     for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
546       r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
547     GNUNET_assert (GNUNET_OK ==
548                    GNUNET_CONTAINER_multipeermap_put (preference_peers,
549                                                       &r_cur->id,
550                                                       r_cur,
551                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
552   }
553
554   /* Update absolute value */
555   old_value = p_cur->f_abs[kind];
556   update_abs_preference (c_cur, p_cur, kind, score_abs);
557   if (p_cur->f_abs[kind] == old_value)
558     return;
559
560   GAS_plugin_solver_lock ();
561   run_preference_update (c_cur,
562                          p_cur,
563                          kind,
564                          score_abs);
565   GAS_plugin_solver_unlock ();
566
567   if (NULL == aging_task)
568     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
569                                                &preference_aging,
570                                                NULL);
571 }
572
573
574 /**
575  * Handle 'preference change' messages from clients.
576  *
577  * @param cls unused, NULL
578  * @param client client that sent the request
579  * @param message the request message
580  */
581 void
582 GAS_handle_preference_change (void *cls,
583                               struct GNUNET_SERVER_Client *client,
584                               const struct GNUNET_MessageHeader *message)
585 {
586   const struct ChangePreferenceMessage *msg;
587   const struct PreferenceInformation *pi;
588   uint16_t msize;
589   uint32_t nump;
590   uint32_t i;
591
592   msize = ntohs (message->size);
593   if (msize < sizeof (struct ChangePreferenceMessage))
594   {
595     GNUNET_break (0);
596     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
597     return;
598   }
599   msg = (const struct ChangePreferenceMessage *) message;
600   nump = ntohl (msg->num_preferences);
601   if ( (msize !=
602         sizeof (struct ChangePreferenceMessage) +
603         nump * sizeof (struct PreferenceInformation)) ||
604        (UINT16_MAX / sizeof (struct PreferenceInformation) < nump) )
605   {
606     GNUNET_break (0);
607     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
608     return;
609   }
610   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611               "Received PREFERENCE_CHANGE message for peer `%s'\n",
612               GNUNET_i2s (&msg->peer));
613   GNUNET_STATISTICS_update (GSA_stats,
614                             "# preference change requests processed",
615                             1, GNUNET_NO);
616   pi = (const struct PreferenceInformation *) &msg[1];
617   for (i = 0; i < nump; i++)
618     normalize_preference (client,
619                           &msg->peer,
620                           (enum GNUNET_ATS_PreferenceKind)
621                           ntohl (pi[i].preference_kind),
622                           pi[i].preference_value);
623   GNUNET_SERVER_receive_done (client, GNUNET_OK);
624 }
625
626
627 /**
628  * Initialize preferences subsystem.
629  */
630 void
631 GAS_preference_init ()
632 {
633   unsigned int i;
634
635   preference_peers = GNUNET_CONTAINER_multipeermap_create (16,
636                                                            GNUNET_NO);
637   for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
638     defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
639 }
640
641
642 /**
643  * Free a peer's `struct PeerRelative`.
644  *
645  * @param cls unused
646  * @param key the key
647  * @param value the `struct PeerRelative` to free.
648  * @return #GNUNET_OK to continue
649  */
650 static int
651 free_peer (void *cls,
652            const struct GNUNET_PeerIdentity *key,
653            void *value)
654 {
655   struct PeerRelative *rp = value;
656
657   GNUNET_assert (GNUNET_YES ==
658                  GNUNET_CONTAINER_multipeermap_remove (preference_peers,
659                                                        key,
660                                                        value));
661   GNUNET_free (rp);
662   return GNUNET_OK;
663 }
664
665
666 static void
667 free_client (struct PreferenceClient *pc)
668 {
669   struct PreferencePeer *next_p;
670   struct PreferencePeer *p;
671
672   next_p = pc->p_head;
673   while (NULL != (p = next_p))
674   {
675     next_p = p->next;
676     GNUNET_CONTAINER_DLL_remove (pc->p_head,
677                                  pc->p_tail,
678                                  p);
679     GNUNET_free (p);
680   }
681   GNUNET_free (pc);
682 }
683
684
685 /**
686  * Shutdown preferences subsystem.
687  */
688 void
689 GAS_preference_done ()
690 {
691   struct PreferenceClient *pc;
692   struct PreferenceClient *next_pc;
693
694   if (NULL != aging_task)
695   {
696     GNUNET_SCHEDULER_cancel (aging_task);
697     aging_task = NULL;
698   }
699   next_pc = pc_head;
700   while (NULL != (pc = next_pc))
701   {
702     next_pc = pc->next;
703     GNUNET_CONTAINER_DLL_remove (pc_head,
704                                  pc_tail,
705                                  pc);
706     free_client (pc);
707   }
708   GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
709                                          &free_peer,
710                                          NULL);
711   GNUNET_CONTAINER_multipeermap_destroy (preference_peers);
712
713 }
714
715
716 /**
717  * Get the normalized preference values for a specific peer or
718  * the default values if
719  *
720  * @param cls ignored
721  * @param id the peer
722  * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
723  * default preferences if peer does not exist
724  */
725 const double *
726 GAS_preference_get_by_peer (void *cls,
727                             const struct GNUNET_PeerIdentity *id)
728 {
729   struct PeerRelative *rp;
730
731   if (NULL ==
732       (rp = GNUNET_CONTAINER_multipeermap_get (preference_peers,
733                                                id)))
734   {
735     return defvalues.f_rel;
736   }
737   return rp->f_rel;
738 }
739
740
741 /**
742  * A performance client disconnected
743  *
744  * @param client the client
745  */
746 void
747 GAS_preference_client_disconnect (struct GNUNET_SERVER_Client *client)
748 {
749   struct PreferenceClient *c_cur;
750
751   for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
752     if (client == c_cur->client)
753       break;
754   if (NULL == c_cur)
755     return;
756   GNUNET_CONTAINER_DLL_remove (pc_head,
757                                pc_tail,
758                                c_cur);
759   free_client (c_cur);
760 }
761
762
763 /* end of gnunet-service-ats_preferences.c */