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