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