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