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.c1004
1 files changed, 1004 insertions, 0 deletions
diff --git a/src/ats/gnunet-service-ats_preferences.c b/src/ats/gnunet-service-ats_preferences.c
new file mode 100644
index 0000000000..cf0082f9d0
--- /dev/null
+++ b/src/ats/gnunet-service-ats_preferences.c
@@ -0,0 +1,1004 @@
+/*
+ This file is part of GNUnet.
+ (C) 2011-2015 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file ats/gnunet-service-ats_performance.c
+ * @brief ats service, interaction with 'performance' API
+ * @author Matthias Wachs
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet-service-ats.h"
+#include "gnunet-service-ats_addresses.h"
+#include "gnunet-service-ats_performance.h"
+#include "gnunet-service-ats_plugins.h"
+#include "gnunet-service-ats_preferences.h"
+#include "gnunet-service-ats_reservations.h"
+#include "ats.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "ats-preferencesx",__VA_ARGS__)
+
+#define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
+#define PREF_AGING_FACTOR 0.95
+#define PREF_EPSILON 0.01
+
+
+/**
+ * Relative preferences for a peer
+ */
+struct PeerRelative
+{
+ /**
+ * Relative preference values
+ */
+ double f_rel[GNUNET_ATS_PreferenceCount];
+
+ /**
+ * Peer id
+ */
+ struct GNUNET_PeerIdentity id;
+};
+
+
+/**
+ * FIXME
+ */
+struct GAS_Addresses_Preference_Clients
+{
+ /**
+ * Next in DLL
+ */
+ struct GAS_Addresses_Preference_Clients *next;
+
+ /**
+ * Previous in DLL
+ */
+ struct GAS_Addresses_Preference_Clients *prev;
+
+ /**
+ * Peer ID
+ */
+ void *client;
+};
+
+
+/**
+ * Preference requests DLL head
+ */
+static struct GAS_Addresses_Preference_Clients *preference_clients_head;
+
+/**
+ * Preference requests DLL head
+ */
+static struct GAS_Addresses_Preference_Clients *preference_clients_tail;
+
+/**
+ * Preferences clients
+ */
+static int pref_clients;
+
+/**
+ * Default values
+ */
+static struct PeerRelative defvalues;
+
+
+
+/**
+ * Preference client
+ */
+struct PreferenceClient
+{
+ /**
+ * Next in DLL
+ */
+ struct PreferenceClient *prev;
+
+ /**
+ * Next in DLL
+ */
+
+ struct PreferenceClient *next;
+
+ /**
+ * Client handle
+ */
+ void *client;
+
+ /**
+ * Array of sum of absolute preferences for this client
+ */
+ double f_abs_sum[GNUNET_ATS_PreferenceCount];
+
+ /**
+ * Array of sum of relative preferences for this client
+ */
+ double f_rel_sum[GNUNET_ATS_PreferenceCount];
+
+ /**
+ * List of peer preferences for this client
+ */
+
+ /**
+ * Head of peer list
+ */
+ struct PreferencePeer *p_head;
+
+ /**
+ * Tail of peer list
+ */
+ struct PreferencePeer *p_tail;
+};
+
+/**
+ * Preference peer
+ */
+struct PreferencePeer
+{
+ /**
+ * Next in DLL
+ */
+ struct PreferencePeer *next;
+
+ /**
+ * Previous in DLL
+ */
+ struct PreferencePeer *prev;
+
+ /**
+ * Client
+ */
+ struct PreferenceClient *client;
+
+ /**
+ * Peer id
+ */
+ struct GNUNET_PeerIdentity id;
+
+ /**
+ * Absolute preference values for all preference types
+ */
+ double f_abs[GNUNET_ATS_PreferenceCount];
+
+ /**
+ * Relative preference values for all preference types
+ */
+ double f_rel[GNUNET_ATS_PreferenceCount];
+
+ /**
+ * Absolute point of time of next aging process
+ */
+ struct GNUNET_TIME_Absolute next_aging[GNUNET_ATS_PreferenceCount];
+};
+
+
+/**
+ * Hashmap to store peer information for preference normalization
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
+
+
+/**
+ * Clients in DLL: head
+ */
+static struct PreferenceClient *pc_head;
+
+/**
+ * Clients in DLL: tail
+ */
+static struct PreferenceClient *pc_tail;
+
+
+
+static struct GNUNET_SCHEDULER_Task * aging_task;
+
+
+
+
+static struct GAS_Addresses_Preference_Clients *
+find_preference_client (void *client)
+{
+ struct GAS_Addresses_Preference_Clients *cur;
+
+ for (cur = preference_clients_head; NULL != cur; cur = cur->next)
+ if (cur->client == client)
+ return cur;
+ return NULL;
+}
+
+
+/**
+ * Update a peer
+ *
+ * @param id peer id
+ * @param kind the kind
+ * @param rp the relative peer struct
+ * @return the new relative preference
+ */
+static void
+update_relative_values_for_peer (const struct GNUNET_PeerIdentity *id,
+ enum GNUNET_ATS_PreferenceKind kind,
+ struct PeerRelative *rp)
+{
+ struct PreferenceClient *c_cur;
+ struct PreferencePeer *p_cur;
+ double f_rel_total;
+ double f_rel_sum;
+ double backup;
+ unsigned int peer_count;
+
+ f_rel_sum = 0.0;
+ f_rel_total = 0.0;
+ peer_count = 0;
+
+ /* For all clients */
+ for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
+ {
+ /* For peer entries with this id */
+ for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
+ {
+ f_rel_sum += p_cur->f_rel[kind];
+ if (0 == memcmp (id, &p_cur->id, sizeof(struct GNUNET_PeerIdentity)))
+ {
+ peer_count ++;
+ f_rel_total += p_cur->f_rel[kind];
+ }
+
+ }
+ }
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%u clients have a total relative preference for peer `%s' `%s' of %.3f and for %s in total %.3f\n",
+ peer_count, GNUNET_i2s (id),
+ GNUNET_ATS_print_preference_type (kind),
+ f_rel_total,
+ GNUNET_ATS_print_preference_type (kind),
+ f_rel_sum);
+
+ /* Find entry for the peer containing relative values in the hashmap */
+ if (NULL != rp)
+ {
+ backup = rp->f_rel[kind];
+ if (f_rel_sum > 0)
+ rp->f_rel[kind] = f_rel_total / f_rel_sum;
+ else
+ {
+ /* No client had any preferences for this type and any peer */
+ rp->f_rel[kind] = DEFAULT_REL_PREFERENCE;
+ }
+ if (backup != rp->f_rel[kind])
+ GAS_normalized_preference_changed (&rp->id, kind, rp->f_rel[kind]);
+ }
+}
+
+
+static int
+update_iterator (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ enum GNUNET_ATS_PreferenceKind *kind = cls;
+ struct PeerRelative *pr = value;
+
+ update_relative_values_for_peer (key,
+ *kind,
+ pr);
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * Recalculate preference for a specific ATS property
+ *
+ * @param c the preference client
+ * @param kind the preference kind
+ * @return the result
+ */
+static void
+recalculate_relative_preferences (struct PreferenceClient *c,
+ enum GNUNET_ATS_PreferenceKind kind)
+{
+ struct PreferencePeer *p_cur;
+
+ /* For this client: sum of absolute preference values for this preference */
+ c->f_abs_sum[kind] = 0.0;
+ /* For this client: sum of relative preference values for this preference
+ *
+ * Note: this value should also be 1.0, but:
+ * if no preferences exist due to aging, this value can be 0.0
+ * and the client can be removed */
+ c->f_rel_sum[kind] = 0.0;
+
+ for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
+ c->f_abs_sum[kind] += p_cur->f_abs[kind];
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Client %p has sum of total preferences for %s of %.3f\n",
+ c->client, GNUNET_ATS_print_preference_type (kind), c->f_abs_sum[kind]);
+
+ /* For all peers: calculate relative preference */
+ for (p_cur = c->p_head; NULL != p_cur; p_cur = p_cur->next)
+ {
+ /* Calculate relative preference for specific kind */
+
+ /* Every application has a preference for each peer between
+ * [0 .. 1] in relative values
+ * and [0 .. inf] in absolute values */
+ p_cur->f_rel[kind] = p_cur->f_abs[kind] / c->f_abs_sum[kind];
+ c->f_rel_sum[kind] += p_cur->f_rel[kind];
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Client %p has relative preference for %s for peer `%s' of %.3f\n",
+ c->client,
+ GNUNET_ATS_print_preference_type (kind),
+ GNUNET_i2s (&p_cur->id),
+ p_cur->f_rel[kind]);
+ }
+
+}
+
+
+
+static void
+run_preference_update (struct PreferenceClient *c_cur,
+ struct PreferencePeer *p_cur,
+ enum GNUNET_ATS_PreferenceKind kind,
+ float score_abs)
+{
+ double old_value;
+
+ /* Update relative value */
+ old_value = p_cur->f_rel[kind];
+ recalculate_relative_preferences (c_cur, kind);
+ if (p_cur->f_rel[kind] == old_value)
+ return;
+
+ /* Relative preference value changed, recalculate for all peers */
+ GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
+ &update_iterator,
+ &kind);
+}
+
+
+
+
+/**
+ * Reduce absolute preferences since they got old
+ *
+ * @param cls the PreferencePeer
+ * @param tc context
+ */
+static void
+preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct PreferencePeer *p;
+ struct PreferenceClient *cur_client;
+ int i;
+ int values_to_update;
+ double backup;
+
+ aging_task = NULL;
+ values_to_update = 0;
+ cur_client = NULL;
+
+ for (cur_client = pc_head; NULL != cur_client; cur_client = cur_client->next)
+ {
+ for (p = cur_client->p_head; NULL != p; p = p->next)
+ {
+ /* Aging absolute values: */
+ for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+ {
+ if (0
+ == GNUNET_TIME_absolute_get_remaining (p->next_aging[i]).rel_value_us)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Aging preference for peer `%s'\n", GNUNET_i2s (&p->id));
+ backup = p->f_abs[i];
+ if (p->f_abs[i] > DEFAULT_ABS_PREFERENCE)
+ p->f_abs[i] *= PREF_AGING_FACTOR;
+
+ if (p->f_abs[i] <= DEFAULT_ABS_PREFERENCE + PREF_EPSILON)
+ p->f_abs[i] = DEFAULT_ABS_PREFERENCE;
+
+ if ( (p->f_abs[i] != DEFAULT_ABS_PREFERENCE) &&
+ (backup != p->f_abs[i]) )
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Aged preference for peer `%s' from %.3f to %.3f\n",
+ GNUNET_i2s (&p->id), backup, p->f_abs[i]);
+
+ run_preference_update(cur_client, p, i, p->f_abs[i]);
+
+ p->next_aging[i] = GNUNET_TIME_absolute_add (
+ GNUNET_TIME_absolute_get (), PREF_AGING_INTERVAL);
+ values_to_update++;
+ }
+ }
+ }
+ }
+ }
+
+ if (values_to_update > 0)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Rescheduling aging task due to %u elements to age\n",
+ values_to_update);
+ aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
+ &preference_aging, NULL );
+ }
+ else
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "No values to age left, not rescheduling aging task\n");
+
+}
+
+
+/**
+ * Update the absolute preference value for a peer
+ * @param c the client
+ * @param p the peer
+ * @param kind the preference kind
+ * @param score_abs the absolute value
+ * @return the new relative preference value
+ */
+static void
+update_abs_preference (struct PreferenceClient *c,
+ struct PreferencePeer *p,
+ enum GNUNET_ATS_PreferenceKind kind,
+ float score_abs)
+{
+ double score = score_abs;
+
+ /* Update preference value according to type */
+ switch (kind)
+ {
+ case GNUNET_ATS_PREFERENCE_BANDWIDTH:
+ case GNUNET_ATS_PREFERENCE_LATENCY:
+ p->f_abs[kind] = score;
+ /* p->f_abs[kind] = (p->f_abs[kind] + score) / 2; */
+ p->next_aging[kind] = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ PREF_AGING_INTERVAL);
+ break;
+ case GNUNET_ATS_PREFERENCE_END:
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+
+
+/**
+ * A performance client disconnected
+ *
+ * @param client the client
+ */
+void
+GAS_addresses_preference_client_disconnect (void *client)
+{
+ struct GAS_Addresses_Preference_Clients *pc;
+
+ if (NULL != (pc = find_preference_client (client)))
+ {
+ GNUNET_CONTAINER_DLL_remove (preference_clients_head,
+ preference_clients_tail,
+ pc);
+ GNUNET_free (pc);
+ GNUNET_assert (pref_clients > 0);
+ pref_clients --;
+ GNUNET_STATISTICS_set (GSA_stats,
+ "# active performance clients",
+ pref_clients,
+ GNUNET_NO);
+ }
+}
+
+
+
+/**
+ * Change the preference for a peer
+ *
+ * @param client the client sending this request
+ * @param peer the peer id
+ * @param kind the preference kind to change
+ * @param score_abs the new preference score
+ */
+void
+GAS_addresses_preference_change (void *client,
+ const struct GNUNET_PeerIdentity *peer,
+ enum GNUNET_ATS_PreferenceKind kind,
+ float score_abs)
+{
+ struct GAS_Addresses_Preference_Clients *pc;
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' for peer `%s' for client %p\n", "CHANGE PREFERENCE",
+ GNUNET_i2s (peer), client);
+
+ if (GNUNET_NO ==
+ GNUNET_CONTAINER_multipeermap_contains (GSA_addresses,
+ peer))
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' for unknown peer `%s' from client %p\n",
+ "CHANGE PREFERENCE", GNUNET_i2s (peer), client);
+ return;
+ }
+
+ if (NULL == find_preference_client (client))
+ {
+ pc = GNUNET_new (struct GAS_Addresses_Preference_Clients);
+ pc->client = client;
+ GNUNET_CONTAINER_DLL_insert (preference_clients_head,
+ preference_clients_tail,
+ pc);
+ pref_clients ++;
+ GNUNET_STATISTICS_set (GSA_stats,
+ "# active performance clients",
+ pref_clients,
+ GNUNET_NO);
+ }
+ GAS_plugin_update_preferences (client, peer, kind, score_abs);
+}
+
+
+/**
+ * Handle 'preference change' messages from clients.
+ *
+ * @param cls unused, NULL
+ * @param client client that sent the request
+ * @param message the request message
+ */
+void
+GAS_handle_preference_change (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct ChangePreferenceMessage *msg;
+ const struct PreferenceInformation *pi;
+ uint16_t msize;
+ uint32_t nump;
+ uint32_t i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' message\n",
+ "PREFERENCE_CHANGE");
+ msize = ntohs (message->size);
+ if (msize < sizeof (struct ChangePreferenceMessage))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ msg = (const struct ChangePreferenceMessage *) message;
+ nump = ntohl (msg->num_preferences);
+ if (msize !=
+ sizeof (struct ChangePreferenceMessage) +
+ nump * sizeof (struct PreferenceInformation))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ GNUNET_STATISTICS_update (GSA_stats,
+ "# preference change requests processed",
+ 1, GNUNET_NO);
+ pi = (const struct PreferenceInformation *) &msg[1];
+ for (i = 0; i < nump; i++)
+ GAS_addresses_preference_change (client,
+ &msg->peer,
+ (enum GNUNET_ATS_PreferenceKind)
+ ntohl (pi[i].preference_kind),
+ pi[i].preference_value);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+
+
+/**
+ * Change the preference for a peer
+ *
+ * @param application the client sending this request
+ * @param peer the peer id
+ * @param scope the time interval for this feedback: [now - scope .. now]
+ * @param kind the preference kind to change
+ * @param score_abs the new preference score
+ */
+void
+GAS_addresses_preference_feedback (void *application,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_TIME_Relative scope,
+ enum GNUNET_ATS_PreferenceKind kind,
+ float score_abs)
+{
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' for peer `%s' for client %p\n",
+ "PREFERENCE FEEDBACK",
+ GNUNET_i2s (peer),
+ application);
+
+ if (GNUNET_NO ==
+ GNUNET_CONTAINER_multipeermap_contains (GSA_addresses,
+ peer))
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' for unknown peer `%s' from client %p\n",
+ "PREFERENCE FEEDBACK",
+ GNUNET_i2s (peer),
+ application);
+ return;
+ }
+
+ GAS_plugin_preference_feedback (application,
+ peer,
+ scope,
+ kind,
+ score_abs);
+}
+
+
+/**
+ * Handle 'preference feedback' messages from clients.
+ *
+ * @param cls unused, NULL
+ * @param client client that sent the request
+ * @param message the request message
+ */
+void
+GAS_handle_preference_feedback (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct FeedbackPreferenceMessage *msg;
+ const struct PreferenceInformation *pi;
+ uint16_t msize;
+ uint32_t nump;
+ uint32_t i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' message\n",
+ "PREFERENCE_FEEDBACK");
+ msize = ntohs (message->size);
+ if (msize < sizeof (struct FeedbackPreferenceMessage))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ msg = (const struct FeedbackPreferenceMessage *) message;
+ nump = ntohl (msg->num_feedback);
+ if (msize !=
+ sizeof (struct FeedbackPreferenceMessage) +
+ nump * sizeof (struct PreferenceInformation))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ GNUNET_STATISTICS_update (GSA_stats,
+ "# preference feedbacks requests processed",
+ 1, GNUNET_NO);
+ pi = (const struct PreferenceInformation *) &msg[1];
+ for (i = 0; i < nump; i++)
+ GAS_addresses_preference_feedback (client,
+ &msg->peer,
+ GNUNET_TIME_relative_ntoh(msg->scope),
+ (enum GNUNET_ATS_PreferenceKind)
+ ntohl (pi[i].preference_kind),
+ pi[i].preference_value);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Shutdown preferences subsystem.
+ */
+void
+GAS_preference_init ()
+{
+ int i;
+
+ preference_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
+ for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+ defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
+
+}
+
+
+/**
+ * Free a peer
+ *
+ * @param cls unused
+ * @param key the key
+ * @param value RelativePeer
+ * @return #GNUNET_OK to continue
+ */
+static int
+free_peer (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
+{
+ struct PeerRelative *rp = value;
+ if (GNUNET_YES
+ == GNUNET_CONTAINER_multipeermap_remove (preference_peers, key, value))
+ GNUNET_free(rp);
+ else
+ GNUNET_break(0);
+ return GNUNET_OK;
+}
+
+
+static void
+free_client (struct PreferenceClient *pc)
+{
+ struct PreferencePeer *next_p;
+ struct PreferencePeer *p;
+ next_p = pc->p_head;
+ while (NULL != (p = next_p))
+ {
+ next_p = p->next;
+ GNUNET_CONTAINER_DLL_remove(pc->p_head, pc->p_tail, p);
+ GNUNET_free(p);
+ }
+ GNUNET_free(pc);
+}
+
+
+
+
+
+/**
+ * Shutdown preferences subsystem.
+ */
+void
+GAS_preference_done ()
+{
+ struct GAS_Addresses_Preference_Clients *pcur;
+ struct PreferenceClient *pc;
+ struct PreferenceClient *next_pc;
+
+ if (NULL != aging_task)
+ {
+ GNUNET_SCHEDULER_cancel (aging_task);
+ aging_task = NULL;
+ }
+
+ next_pc = pc_head;
+ while (NULL != (pc = next_pc))
+ {
+ next_pc = pc->next;
+ GNUNET_CONTAINER_DLL_remove(pc_head, pc_tail, pc);
+ free_client (pc);
+ }
+
+ GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
+ &free_peer, NULL);
+ GNUNET_CONTAINER_multipeermap_destroy (preference_peers);
+
+ while (NULL != (pcur = preference_clients_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (preference_clients_head,
+ preference_clients_tail,
+ pcur);
+ GNUNET_assert (pref_clients > 0);
+ pref_clients --;
+ GNUNET_STATISTICS_set (GSA_stats,
+ "# active performance clients",
+ pref_clients,
+ GNUNET_NO);
+ GNUNET_free (pcur);
+ }
+}
+
+
+
+
+/**
+ * Normalize an updated preference value
+ *
+ * @param client the client with this preference
+ * @param peer the peer to change the preference for
+ * @param kind the kind to change the preference
+ * @param score_abs the normalized score
+ */
+void
+GAS_normalization_normalize_preference (void *client,
+ const struct GNUNET_PeerIdentity *peer,
+ enum GNUNET_ATS_PreferenceKind kind,
+ float score_abs)
+{
+ struct PreferenceClient *c_cur;
+ struct PreferencePeer *p_cur;
+ struct PeerRelative *r_cur;
+ double old_value;
+ int i;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Client changes preference for peer `%s' for `%s' to %.2f\n",
+ GNUNET_i2s (peer),
+ GNUNET_ATS_print_preference_type (kind),
+ score_abs);
+
+ if (kind >= GNUNET_ATS_PreferenceCount)
+ {
+ GNUNET_break(0);
+ return;
+ }
+
+ /* Find preference client */
+ for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
+ {
+ if (client == c_cur->client)
+ break;
+ }
+ /* Not found: create new preference client */
+ if (NULL == c_cur)
+ {
+ c_cur = GNUNET_new (struct PreferenceClient);
+ c_cur->client = client;
+ for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+ {
+ c_cur->f_abs_sum[i] = DEFAULT_ABS_PREFERENCE;
+ c_cur->f_rel_sum[i] = DEFAULT_REL_PREFERENCE;
+ }
+
+ GNUNET_CONTAINER_DLL_insert(pc_head, pc_tail, c_cur);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding new client %p \n", c_cur);
+ }
+
+ /* Find entry for peer */
+ for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
+ if (0 == memcmp (&p_cur->id, peer, sizeof(p_cur->id)))
+ break;
+
+ /* Not found: create new peer entry */
+ if (NULL == p_cur)
+ {
+ p_cur = GNUNET_new (struct PreferencePeer);
+ p_cur->client = c_cur;
+ p_cur->id = (*peer);
+ for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+ {
+ /* Default value per peer absolute preference for a preference: 0 */
+ p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
+ /* Default value per peer relative preference for a quality: 1.0 */
+ p_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
+ p_cur->next_aging[i] = GNUNET_TIME_UNIT_FOREVER_ABS;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding new peer %p for client %p \n",
+ p_cur, c_cur);
+ GNUNET_CONTAINER_DLL_insert(c_cur->p_head, c_cur->p_tail, p_cur);
+ }
+
+ /* Create struct for peer */
+ if (NULL == GNUNET_CONTAINER_multipeermap_get (preference_peers, peer))
+ {
+ r_cur = GNUNET_new (struct PeerRelative);
+ r_cur->id = (*peer);
+ for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+ r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
+ GNUNET_assert(
+ GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (preference_peers,
+ &r_cur->id, r_cur, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+
+ /* Update absolute value */
+ old_value = p_cur->f_abs[kind];
+ update_abs_preference (c_cur, p_cur, kind, score_abs);
+ if (p_cur->f_abs[kind] == old_value)
+ return;
+
+ run_preference_update (c_cur, p_cur, kind, score_abs);
+
+ /* Start aging task */
+ if (NULL == aging_task)
+ aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
+ &preference_aging, NULL );
+
+}
+
+
+/**
+ * Get the normalized preference values for a specific peer or
+ * the default values if
+ *
+ * @param cls ignored
+ * @param id the peer
+ * @return pointer to the values, can be indexed with GNUNET_ATS_PreferenceKind,
+ * default preferences if peer does not exist
+ */
+const double *
+GAS_normalization_get_preferences_by_peer (void *cls,
+ const struct GNUNET_PeerIdentity *id)
+{
+ GNUNET_assert(NULL != preference_peers);
+ GNUNET_assert(NULL != id);
+
+ struct PeerRelative *rp;
+ if (NULL == (rp = GNUNET_CONTAINER_multipeermap_get (preference_peers, id)))
+ {
+ return defvalues.f_rel;
+ }
+ return rp->f_rel;
+}
+
+
+/**
+ * Get the normalized preference values for a specific client and peer
+ *
+ * @param client client
+ * @param peer the peer
+ * @param pref the preference type
+ * @return the value
+ */
+double
+GAS_normalization_get_preferences_by_client (const void *client,
+ const struct GNUNET_PeerIdentity *peer,
+ enum GNUNET_ATS_PreferenceKind pref)
+{
+ struct PreferenceClient *c_cur;
+ struct PreferencePeer *p_cur;
+
+ /* Find preference client */
+ for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
+ {
+ if (client == c_cur->client)
+ break;
+ }
+ if (NULL == c_cur)
+ return -1.0;
+
+ for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
+ {
+ if (0 == memcmp (peer, &p_cur->id, sizeof (struct GNUNET_PeerIdentity)))
+ break;
+ }
+ if (NULL == p_cur)
+ return DEFAULT_REL_PREFERENCE; /* Not found, return default */
+
+ return p_cur->f_rel[pref];
+}
+
+
+
+/**
+ * A performance client disconnected
+ *
+ * @param client the client
+ */
+void
+GAS_normalization_preference_client_disconnect (void *client)
+{
+ struct PreferenceClient *c_cur;
+ /* Find preference client */
+
+ for (c_cur = pc_head; NULL != c_cur; c_cur = c_cur->next)
+ {
+ if (client == c_cur->client)
+ break;
+ }
+ if (NULL == c_cur)
+ return;
+
+ GNUNET_CONTAINER_DLL_remove(pc_head, pc_tail, c_cur);
+ free_client (c_cur);
+}
+