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_addresses.h"
30 #include "gnunet-service-ats_normalization.h"
37 struct PreferenceClient
42 struct PreferenceClient *prev;
48 struct PreferenceClient *next;
56 * Total preference for this peer
58 double f_abs_sum[GNUNET_ATS_PreferenceCount];
61 * List of peer preferences for this client
67 struct PreferencePeer *p_head;
72 struct PreferencePeer *p_tail;
84 struct PreferencePeer *next;
89 struct PreferencePeer *prev;
94 struct PreferenceClient *client;
99 struct GNUNET_PeerIdentity id;
102 * Absolute preference values
104 double f_abs[GNUNET_ATS_PreferenceCount];
107 * Relative preference values
109 double f_rel[GNUNET_ATS_PreferenceCount];
114 GNUNET_SCHEDULER_TaskIdentifier aging_task;
118 * Relative preferences for a peer
123 * Relative preference values
125 double f_rel[GNUNET_ATS_PreferenceCount];
130 struct GNUNET_PeerIdentity id;
135 * Callback to call on changing preference values
137 static GAS_Normalization_preference_changed_cb pref_changed_cb;
141 * Closure for callback to call on changing preference values
143 static void *pref_changed_cb_cls;
147 * Hashmap to store peer information for preference normalization
149 static struct GNUNET_CONTAINER_MultiHashMap *preference_peers;
154 * Hashmap to store peer information for property normalization
156 static struct GNUNET_CONTAINER_MultiHashMap *property_peers;
161 * Clients in DLL: head
163 static struct PreferenceClient *pc_head;
167 * Clients in DLL: tail
169 static struct PreferenceClient *pc_tail;
175 static struct PeerRelative defvalues;
178 * Application Preference Normalization
185 * @param kind the kind
186 * @return the new relative preference
189 update_peers (struct GNUNET_PeerIdentity *id,
190 enum GNUNET_ATS_PreferenceKind kind)
192 struct PreferenceClient *c_cur;
193 struct PreferencePeer *p_cur;
194 struct PeerRelative *rp;
202 /* For all clients */
203 for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
205 /* Find peer with id */
206 for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
208 if (0 == memcmp (id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
213 /* Found peer with id */
214 f_rel_total += p_cur->f_rel[kind];
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "%u clients have a total relative preference for peer `%s''s `%s' of %.3f\n",
224 GNUNET_ATS_print_preference_type (kind),
226 if (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers, &id->hashPubKey)))
228 backup = rp->f_rel[kind];
231 rp->f_rel[kind] = f_rel_total / count;
235 rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
240 return DEFAULT_REL_PREFERENCE;
243 if ((backup != rp->f_rel[kind]) && (NULL != pref_changed_cb))
245 pref_changed_cb (pref_changed_cb_cls, &rp->id, kind, rp->f_rel[kind]);
248 return rp->f_rel[kind];
253 * Recalculate preference for a specific ATS property
255 * @param c the preference client
257 * @param kind the preference kind
261 recalculate_rel_preferences (struct PreferenceClient *c,
262 struct PreferencePeer *p,
263 enum GNUNET_ATS_PreferenceKind kind)
265 struct PreferencePeer *p_cur;
266 struct PeerRelative *rp;
271 /* For this client: sum preferences to total preference */
272 c->f_abs_sum[kind] = 0;
273 for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
274 c->f_abs_sum[kind] += p_cur->f_abs[kind];
275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276 "Client %p has total preference for %s of %.3f\n",
278 GNUNET_ATS_print_preference_type (kind),
281 ret = DEFAULT_REL_PREFERENCE;
282 /* For all peers: calculate relative preference */
283 for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
285 /* Calculate relative preference for specific kind */
286 backup = p_cur->f_rel[kind];
287 if (DEFAULT_ABS_PREFERENCE == c->f_abs_sum[kind])
288 /* No peer has a preference for this property,
289 * so set default preference */
290 p_cur->f_rel[kind] = DEFAULT_REL_PREFERENCE;
292 p_cur->f_rel[kind] = (c->f_abs_sum[kind] + p_cur->f_abs[kind]) /
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296 "Client %p: peer `%s' has relative preference for %s of %.3f\n",
298 GNUNET_i2s (&p_cur->id),
299 GNUNET_ATS_print_preference_type (kind),
303 if (p_cur->f_rel[kind] != backup)
305 /* Value changed, recalculate */
306 res = update_peers (&p_cur->id,kind);
307 if (0 == memcmp (&p->id, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
312 /* Value did not chang, return old value*/
313 GNUNET_assert (NULL != (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers,
314 &p->id.hashPubKey)));
315 ret = rp->f_rel[kind];
323 * Update the absolute preference value for a peer
324 * @param c the client
326 * @param kind the preference kind
327 * @param score_abs the absolute value
328 * @return the new relative preference value
331 update_preference (struct PreferenceClient *c,
332 struct PreferencePeer *p,
333 enum GNUNET_ATS_PreferenceKind kind,
336 double score = score_abs;
338 /* Update preference value according to type */
340 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
341 case GNUNET_ATS_PREFERENCE_LATENCY:
342 p->f_abs[kind] = (p->f_abs[kind] + score) / 2;
344 case GNUNET_ATS_PREFERENCE_END:
349 return recalculate_rel_preferences (c, p, kind);
354 * Reduce absolute preferences since they got old
356 * @param cls the PreferencePeer
360 preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
364 struct PreferencePeer *p = cls;
365 GNUNET_assert (NULL != p);
367 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
370 GNUNET_i2s (&p->id));
372 /* Aging absolute values: */
373 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
375 backup = p->f_abs[i];
376 if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
377 p->f_abs[i] *= PREF_AGING_FACTOR;
378 if (backup != p->f_abs[i])
380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
381 GNUNET_i2s (&p->id), backup, p->f_abs[i]);
382 recalculate_rel_preferences (p->client, p, i);
385 p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
386 &preference_aging, p);
391 * Normalize an updated preference value
393 * @param src the client with this preference
394 * @param peer the peer to change the preference for
395 * @param kind the kind to change the preference
396 * @param score_abs the normalized score
399 GAS_normalization_normalize_preference (void *src,
400 const struct GNUNET_PeerIdentity *peer,
401 enum GNUNET_ATS_PreferenceKind kind,
405 struct PreferenceClient *c_cur;
406 struct PreferencePeer *p_cur;
407 struct PeerRelative *r_cur;
411 GNUNET_assert (NULL != src);
412 GNUNET_assert (NULL != peer);
414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415 "Client %p changes preference for peer `%s' for `%s' to %.2f\n",
418 GNUNET_ATS_print_preference_type (kind),
421 if (kind >= GNUNET_ATS_PreferenceCount)
427 /* Find preference client */
428 for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
430 if (src == c_cur->client)
433 /* Not found: create new preference client */
436 c_cur = GNUNET_malloc (sizeof (struct PreferenceClient));
438 GNUNET_CONTAINER_DLL_insert (pc_head, pc_tail, c_cur);
441 /* Find entry for peer */
442 for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
443 if (0 == memcmp (&p_cur->id, peer, sizeof (p_cur->id)))
446 /* Not found: create new peer entry */
449 p_cur = GNUNET_malloc (sizeof (struct PreferencePeer));
450 p_cur->client = c_cur;
452 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
454 /* Default value per peer absolut preference for a quality:
455 * No value set, so absolute preference 0 */
456 p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
457 /* Default value per peer relative preference for a quality: 1.0 */
458 p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
460 p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
461 &preference_aging, p_cur);
462 GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
465 if (NULL == (r_cur = GNUNET_CONTAINER_multihashmap_get (preference_peers,
468 r_cur = GNUNET_malloc (sizeof (struct PeerRelative));
470 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
471 r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
472 GNUNET_CONTAINER_multihashmap_put (preference_peers, &r_cur->id.hashPubKey,
473 r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
476 score_rel = update_preference (c_cur, p_cur, kind, score_abs);
482 * Get the normalized preference values for a specific peer or
483 * the default values if
486 * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
487 * default preferences if peer does not exist
490 GAS_normalization_get_preferences (const struct GNUNET_PeerIdentity *id)
492 GNUNET_assert (NULL != preference_peers);
493 GNUNET_assert (NULL != id);
495 struct PeerRelative *rp;
496 if (NULL == (rp = GNUNET_CONTAINER_multihashmap_get (preference_peers, &id->hashPubKey)))
498 return defvalues.f_rel;
504 * Quality Normalization
509 int have_min; /* Do we have min and max */
515 struct Property properties[GNUNET_ATS_QualityPropertiesCount];
518 * Normalize a specific ATS type with the values in queue
519 * @param address the address
520 * @param atsi the ats information
521 * @return the new average or GNUNET_ATS_VALUE_UNDEFINED
524 uint32_t property_average (struct ATS_Address *address,
525 const struct GNUNET_ATS_Information *atsi)
527 struct GAS_NormalizationInfo *ni;
528 uint32_t current_type;
529 uint32_t current_val;
535 unsigned int props[] = GNUNET_ATS_QualityProperties;
537 /* Average the values of this property */
538 current_type = ntohl (atsi->type);
539 current_val = ntohl (atsi->value);
541 for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
543 if (current_type == props[c1])
546 if (c1 == GNUNET_ATS_QualityPropertiesCount)
549 return GNUNET_ATS_VALUE_UNDEFINED;
553 ni = &address->atsin[index];
554 ni->atsi_abs[ni->index] = current_val;
556 if (GAS_normalization_queue_length == ni->index)
560 for (c1 = 0; c1 < GAS_normalization_queue_length; c1++)
562 if (GNUNET_ATS_VALUE_UNDEFINED != ni->atsi_abs[c1])
565 if (GNUNET_ATS_VALUE_UNDEFINED > (sum + ni->atsi_abs[c1]))
566 sum += ni->atsi_abs[c1];
569 sum = GNUNET_ATS_VALUE_UNDEFINED - 1;
574 GNUNET_assert (0 != count);
575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New average from %u elements: %u\n", count, sum / count);
579 double property_normalize (struct Property *p,
580 struct ATS_Address *address,
586 /* Normalize the values of this property */
587 if (p->max < avg_value)
590 if (GNUNET_NO == p->have_max)
591 p->have_max = GNUNET_YES;
592 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
593 "New maximum of %u for property %u\n",
596 if (p->min > avg_value)
599 if (GNUNET_NO == p->have_min)
600 p->have_min = GNUNET_YES;
601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602 "New minimum of %u for property %u\n",
606 if ((GNUNET_YES == p->have_max) && (GNUNET_YES == p->have_min))
608 delta = p->max - p->min;
609 res = (delta + avg_value) / (delta);
610 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611 "New normalized value of %f for property %u\n",
616 return DEFAULT_REL_QUALITY;
622 GAS_normalization_normalize_property (struct ATS_Address *address,
623 const struct GNUNET_ATS_Information *atsi,
626 struct Property *cur_prop;
629 uint32_t current_type;
630 uint32_t current_val;
631 unsigned int existing_properties[] = GNUNET_ATS_QualityProperties;
633 GNUNET_assert (NULL != address);
634 GNUNET_assert (NULL != atsi);
636 for (c1 = 0; c1 < atsi_count; c1++)
638 current_type = ntohl (atsi[c1].type);
639 current_val = ntohl (atsi[c1].value);
640 for (c2 = 0; c2 < GNUNET_ATS_QualityPropertiesCount; c2++)
642 /* Check if type is valid */
643 if (current_type == existing_properties[c2])
646 if (GNUNET_ATS_QualityPropertiesCount == c2)
648 /* Invalid property, continue with next element */
653 current_val = property_average (address, &atsi[c1]);
654 if (GNUNET_ATS_VALUE_UNDEFINED == current_val)
662 cur_prop = &properties[c2];
663 property_normalize (cur_prop, address, ntohl(atsi[c1].type), current_val);
672 * Start the normalization component
674 * @param pref_ch_cb callback to call on relative preference changing
675 * @param pref_ch_cb_cls cls for the callback
678 GAS_normalization_start (GAS_Normalization_preference_changed_cb pref_ch_cb,
679 void *pref_ch_cb_cls)
683 preference_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
684 property_peers = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
686 for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1++)
688 properties[c1].min = 0;
689 properties[c1].max = 0;
690 properties[c1].have_max = GNUNET_NO;
691 properties[c1].have_min = GNUNET_NO;
694 pref_changed_cb = pref_ch_cb;
695 pref_changed_cb_cls = pref_ch_cb_cls;
696 for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
697 defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
707 * @param value RelativePeer
708 * @return GNUNET_OK to continue
711 free_peer (void *cls,
712 const struct GNUNET_HashCode * key,
715 struct PeerRelative *rp = value;
716 GNUNET_CONTAINER_multihashmap_remove (preference_peers, key, value);
723 * Stop the normalization component and free all items
726 GAS_normalization_stop ()
728 struct PreferenceClient *pc;
729 struct PreferenceClient *next_pc;
730 struct PreferencePeer *p;
731 struct PreferencePeer *next_p;
734 while (NULL != (pc = next_pc))
737 GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
739 while (NULL != (p = next_p))
742 if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
744 GNUNET_SCHEDULER_cancel(p->aging_task);
745 p->aging_task = GNUNET_SCHEDULER_NO_TASK;
747 GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
752 GNUNET_CONTAINER_multihashmap_iterate (preference_peers, &free_peer, NULL);
753 GNUNET_CONTAINER_multihashmap_destroy (preference_peers);
754 GNUNET_CONTAINER_multihashmap_destroy (property_peers);
758 /* end of gnunet-service-ats_normalization.c */