aboutsummaryrefslogtreecommitdiff
path: root/src/ats/gnunet-service-ats_preferences.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ats/gnunet-service-ats_preferences.c')
-rw-r--r--src/ats/gnunet-service-ats_preferences.c776
1 files changed, 0 insertions, 776 deletions
diff --git a/src/ats/gnunet-service-ats_preferences.c b/src/ats/gnunet-service-ats_preferences.c
deleted file mode 100644
index 0869c65dc..000000000
--- a/src/ats/gnunet-service-ats_preferences.c
+++ /dev/null
@@ -1,776 +0,0 @@
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 preference 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 */
60struct 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 */
81static struct PeerRelative defvalues;
82
83
84/**
85 * Preference information per peer and client.
86 */
87struct 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 */
119struct 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 */
155static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
156
157/**
158 * Clients in DLL: head
159 */
160static struct PreferenceClient *pc_head;
161
162/**
163 * Clients in DLL: tail
164 */
165static struct PreferenceClient *pc_tail;
166
167/**
168 * Handle for task we run periodically to age preferences over time.
169 */
170static struct GNUNET_SCHEDULER_Task *aging_task;
171
172
173/**
174 * Closure for #sum_relative_preferences().
175 */
176struct 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 */
200static int
201sum_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 relative 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 */
221static void
222update_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 */
262static int
263free_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 */
286static int
287free_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 */
318struct 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 */
341static int
342age_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 */
388static void
389preference_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 */
423struct 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 */
446static int
447update_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 */
468static int
469update_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 */
493static void
494recalculate_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 */
530static int
531update_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 */
552static void
553update_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 */
649void
650GAS_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 */
679void
680GAS_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 */
694void
695GAS_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 */
734const double *
735GAS_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 */
755void
756GAS_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 */