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