2 This file is part of GNUnet.
3 (C) 2011 Christian Grothoff (and other contributing authors)
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file ats/gnunet-service-ats_normalization.c
23 * @brief ats service address: management of ATS properties and preferences normalization
24 * @author Matthias Wachs
25 * @author Christian Grothoff
28 #include "gnunet_ats_service.h"
29 #include "gnunet-service-ats_normalization.h"
36 struct PreferenceClient
41 struct PreferenceClient *prev;
47 struct PreferenceClient *next;
55 * Total preference for this peer
57 double f_abs_sum[GNUNET_ATS_PreferenceCount];
60 * List of peer preferences for this client
66 struct PreferencePeer *p_head;
71 struct PreferencePeer *p_tail;
83 struct PreferencePeer *next;
88 struct PreferencePeer *prev;
93 struct PreferenceClient *client;
98 struct GNUNET_PeerIdentity id;
101 * Absolute preference values
103 double f_abs[GNUNET_ATS_PreferenceCount];
106 * Relative preference values
108 double f_rel[GNUNET_ATS_PreferenceCount];
113 GNUNET_SCHEDULER_TaskIdentifier aging_task;
117 * Relative preferences for a peer
122 * Relative preference values
124 double f_rel[GNUNET_ATS_PreferenceCount];
129 struct GNUNET_PeerIdentity id;
132 GAS_Normalization_preference_changed_cb pref_changed_cb;
133 void *pref_changed_cb_cls;
134 struct GNUNET_CONTAINER_MultiHashMap *peers;
135 struct PreferenceClient *pc_head;
136 struct PreferenceClient *pc_tail;
137 struct PeerRelative defvalues;
141 update_peers (struct GNUNET_PeerIdentity *id,
142 enum GNUNET_ATS_PreferenceKind kind)
144 struct PreferenceClient *c_cur;
145 struct PreferencePeer *p_cur;
146 struct PeerRelative *rp;
154 /* For all clients */
155 for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
157 /* Find peer with id */
158 for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
160 if (0 == memcmp (id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
165 /* Found peer with id */
166 f_rel_total += p_cur->f_rel[kind];
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%u clients have a total relative preference for peer `%s''s `%s' of %.3f\n",
175 GNUNET_ATS_print_preference_type (kind),
177 if (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (peers, &id->hashPubKey)))
179 backup = rp->f_rel[kind];
182 rp->f_rel[kind] = f_rel_total / count;
186 rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
191 return DEFAULT_REL_PREFERENCE;
194 if ((backup != rp->f_rel[kind]) && (NULL != pref_changed_cb))
196 pref_changed_cb (pref_changed_cb_cls, &rp->id, kind, rp->f_rel[kind]);
199 return rp->f_rel[kind];
203 * Recalculate preference for a specific ATS property
206 * @param kind the preference kind
209 recalculate_rel_preferences (struct PreferenceClient *c,
210 struct PreferencePeer *p,
211 enum GNUNET_ATS_PreferenceKind kind)
213 struct PreferencePeer *p_cur;
214 struct PeerRelative *rp;
219 /* For this client: sum preferences to total preference */
220 c->f_abs_sum[kind] = 0;
221 for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
222 c->f_abs_sum[kind] += p_cur->f_abs[kind];
223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p has total preference for %s of %.3f\n",
225 GNUNET_ATS_print_preference_type (kind),
228 ret = DEFAULT_REL_PREFERENCE;
229 /* For all peers: calculate relative preference */
230 for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
232 /* Calculate relative preference for specific kind */
233 backup = p_cur->f_rel[kind];
234 if (DEFAULT_ABS_PREFERENCE == c->f_abs_sum[kind])
235 /* No peer has a preference for this property, so set default preference */
236 p_cur->f_rel[kind] = DEFAULT_REL_PREFERENCE;
238 p_cur->f_rel[kind] = (c->f_abs_sum[kind] + p_cur->f_abs[kind]) / c->f_abs_sum[kind];
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p: peer `%s' has relative preference for %s of %.3f\n",
242 GNUNET_i2s (&p_cur->id),
243 GNUNET_ATS_print_preference_type (kind),
247 if (p_cur->f_rel[kind] != backup)
249 /* Value changed, recalculate */
250 res = update_peers (&p_cur->id,kind);
251 if (0 == memcmp (&p->id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
256 /* Value did not chang, return old value*/
257 GNUNET_assert (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (peers, &p->id.hashPubKey)));
258 ret = rp->f_rel[kind];
265 update_preference (struct PreferenceClient *c,
266 struct PreferencePeer *p,
267 enum GNUNET_ATS_PreferenceKind kind,
270 double score = score_abs;
272 /* Update preference value according to type */
274 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
275 case GNUNET_ATS_PREFERENCE_LATENCY:
276 p->f_abs[kind] = (p->f_abs[kind] + score) / 2;
278 case GNUNET_ATS_PREFERENCE_END:
283 return recalculate_rel_preferences (c, p, kind);
287 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
292 struct PreferencePeer *p = cls;
293 GNUNET_assert (NULL != p);
295 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
298 GNUNET_i2s (&p->id));
302 * Not for every peer preference values are set by default, so reducing the
303 * absolute preference value does not help for aging because it does not have
304 * influence on the relative values.
306 * So we have to reduce the relative value to have an immediate impact on
307 * quota calculation. In addition we cannot call recalculate_preferences here
308 * but instead reduce the absolute value to have an aging impact on future
309 * calls to change_preference where recalculate_preferences is called
312 /* Aging absolute values: */
313 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
315 backup = p->f_abs[i];
316 if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
317 p->f_abs[i] *= PREF_AGING_FACTOR;
318 if (backup != p->f_abs[i])
320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
321 GNUNET_i2s (&p->id), backup, p->f_abs[i]);
322 recalculate_rel_preferences (p->client, p, i);
325 p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
326 &preference_aging, p);
330 * Changes the preferences for a peer in the problem
332 * @param solver the solver handle
333 * @param client the client with this preference
334 * @param peer the peer to change the preference for
335 * @param kind the kind to change the preference
336 * @param score the normalized score
339 GAS_normalization_change_preference (void *src,
340 const struct GNUNET_PeerIdentity *peer,
341 enum GNUNET_ATS_PreferenceKind kind,
345 struct PreferenceClient *c_cur;
346 struct PreferencePeer *p_cur;
347 struct PeerRelative *r_cur;
351 GNUNET_assert (NULL != src);
352 GNUNET_assert (NULL != peer);
354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p changes preference for peer `%s' for `%s' to %.2f\n",
357 GNUNET_ATS_print_preference_type (kind),
360 if (kind >= GNUNET_ATS_PreferenceCount)
366 /* Find preference client */
367 for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
369 if (src == c_cur->client)
372 /* Not found: create new preference client */
375 c_cur = GNUNET_malloc (sizeof (struct PreferenceClient));
377 GNUNET_CONTAINER_DLL_insert (pc_head, pc_tail, c_cur);
380 /* Find entry for peer */
381 for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
382 if (0 == memcmp (&p_cur->id, peer, sizeof (p_cur->id)))
385 /* Not found: create new peer entry */
388 p_cur = GNUNET_malloc (sizeof (struct PreferencePeer));
389 p_cur->client = c_cur;
391 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
393 /* Default value per peer absolut preference for a quality:
394 * No value set, so absolute preference 0 */
395 p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
396 /* Default value per peer relative preference for a quality: 1.0 */
397 p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
399 p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL, &preference_aging, p_cur);
400 GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
403 if (NULL == (r_cur = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey)))
405 r_cur = GNUNET_malloc (sizeof (struct PeerRelative));
407 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
408 r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
409 GNUNET_CONTAINER_multihashmap_put (peers, &r_cur->id.hashPubKey, r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
412 score_rel = update_preference (c_cur, p_cur, kind, score_abs);
417 * Get the normalized preference values for a specific peer
420 * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind, default preferences if peer does not exist
423 GAS_normalization_get_preferences (struct GNUNET_PeerIdentity *id)
425 GNUNET_assert (NULL != peers);
426 GNUNET_assert (NULL != id);
428 struct PeerRelative *rp;
429 if (NULL == (rp = GNUNET_CONTAINER_multihashmap_get (peers, &id->hashPubKey)))
431 return defvalues.f_rel;
438 GAS_normalization_start (GAS_Normalization_preference_changed_cb pref_ch_cb, void *pref_ch_cb_cls)
441 peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
442 pref_changed_cb = pref_ch_cb;
443 pref_changed_cb_cls = pref_ch_cb_cls;
444 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
445 defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
450 free_peer (void *cls,
451 const struct GNUNET_HashCode * key,
454 struct PeerRelative *rp = value;
455 GNUNET_CONTAINER_multihashmap_remove (peers, key, value);
461 GAS_normalization_stop ()
463 struct PreferenceClient *pc;
464 struct PreferenceClient *next_pc;
465 struct PreferencePeer *p;
466 struct PreferencePeer *next_p;
469 while (NULL != (pc = next_pc))
472 GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
474 while (NULL != (p = next_p))
477 if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
479 GNUNET_SCHEDULER_cancel(p->aging_task);
480 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
482 GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
487 GNUNET_CONTAINER_multihashmap_iterate (peers, &free_peer, NULL);
488 GNUNET_CONTAINER_multihashmap_destroy (peers);
492 /* end of gnunet-service-ats_normalization.c */