update ignore file
[oweals/gnunet.git] / src / ats / gnunet-service-ats_preferences.c
index 762caf8ea16f8269dea54bf00477b065c95611fb..9f11dc6a8dd180aa1c8f6522b6057724fb19d77e 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2011-2015 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2011-2015 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -14,8 +14,8 @@
 
      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.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 /**
  * @file ats/gnunet-service-ats_preferences.c
 #include "gnunet-service-ats_reservations.h"
 #include "ats.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "ats-preferencesx",__VA_ARGS__)
+#define LOG(kind,...) GNUNET_log_from (kind, "ats-preferences",__VA_ARGS__)
 
+/**
+ * How frequently do we age preference values?
+ */
 #define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
 
+/**
+ * By which factor do we age preferences expressed during
+ * each #PREF_AGING_INTERVAL?
+ */
 #define PREF_AGING_FACTOR 0.95
 
+/**
+ * What is the lowest threshold up to which prefernce values
+ * are aged, and below which we consider them zero and thus
+ * no longer subject to aging?
+ */
 #define PREF_EPSILON 0.01
 
 
 /**
- * Relative preferences for a peer
+ * Relative preferences for a peer.
  */
 struct PeerRelative
 {
   /**
-   * Relative preference values
+   * Array of relative preference values, to be indexed by
+   * an `enum GNUNET_ATS_PreferenceKind`.
    */
-  double f_rel[GNUNET_ATS_PreferenceCount];
+  double f_rel[GNUNET_ATS_PREFERENCE_END];
 
   /**
-   * Peer identity for which we have these preferences.
+   * Number of clients that are expressing a preference for
+   * this peer. When this counter reaches zero, this entry
+   * is freed.
    */
-  struct GNUNET_PeerIdentity id;
+  unsigned int num_clients;
 };
 
 
 /**
- * Default values
+ * Default values, returned as our preferences if we do not
+ * have any preferences expressed for a peer.
  */
 static struct PeerRelative defvalues;
 
 
 /**
- * Preference client, information we keep track of per client.
+ * Preference information per peer and client.
  */
-struct PreferenceClient
+struct PreferencePeer
 {
   /**
-   * Previous in DLL
-   */
-  struct PreferenceClient *prev;
-
-  /**
-   * Next in DLL
-   */
-  struct PreferenceClient *next;
-
-  /**
-   * Head of peer list
+   * Next in DLL of preference entries for the same client.
    */
-  struct PreferencePeer *p_head;
+  struct PreferencePeer *next;
 
   /**
-   * Tail of peer list
+   * Previous in DLL of preference entries for the same client.
    */
-  struct PreferencePeer *p_tail;
+  struct PreferencePeer *prev;
 
   /**
-   * Client handle
+   * Absolute preference values for all preference types
+   * as expressed by this client for this peer.
    */
-  struct GNUNET_SERVER_Client *client;
+  double f_abs[GNUNET_ATS_PREFERENCE_END];
 
   /**
-   * Array of sum of absolute preferences for this client
+   * Relative preference values for all preference types,
+   * normalized in [0..1] based on how the respective
+   * client scored other peers.
    */
-  double f_abs_sum[GNUNET_ATS_PreferenceCount];
+  double f_rel[GNUNET_ATS_PREFERENCE_END];
 
-  /**
-   * Array of sum of relative preferences for this client
-   */
-  double f_rel_sum[GNUNET_ATS_PreferenceCount];
 };
 
 
 /**
- * Preference information per peer and client.
+ * Preference client, as in a client that expressed preferences
+ * for peers.  This is the information we keep track of for each
+ * such client.
  */
-struct PreferencePeer
+struct PreferenceClient
 {
-  /**
-   * Next in DLL of preference entries for the same client.
-   */
-  struct PreferencePeer *next;
 
   /**
-   * Previous in DLL of preference entries for the same client.
+   * Next in client list
    */
-  struct PreferencePeer *prev;
+  struct PreferenceClient *next;
 
   /**
-   * Client that expressed this preferences.
+   * Previous in client peer list
    */
-  struct PreferenceClient *client;
+  struct PreferenceClient *prev;
 
   /**
-   * Peer identity for which the preference was expressed.
+   * Client handle
    */
-  struct GNUNET_PeerIdentity id;
+  struct GNUNET_SERVICE_Client *client;
 
   /**
-   * Absolute preference values for all preference types
+   * Mapping peer identities to `struct PreferencePeer` entry
+   * for the respective peer.
    */
-  double f_abs[GNUNET_ATS_PreferenceCount];
+  struct GNUNET_CONTAINER_MultiPeerMap *peer2pref;
 
   /**
-   * Relative preference values for all preference types
+   * Array of sums of absolute preferences for all
+   * peers as expressed by this client.
    */
-  double f_rel[GNUNET_ATS_PreferenceCount];
+  double f_abs_sum[GNUNET_ATS_PREFERENCE_END];
 
-  /**
-   * 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
+ * Hashmap to store peer information for preference normalization.
+ * Maps the identity of a peer to a `struct PeerRelative` containing
+ * the current relative preference values for that peer.
  */
 static struct GNUNET_CONTAINER_MultiPeerMap *preference_peers;
 
@@ -170,546 +173,554 @@ static struct GNUNET_SCHEDULER_Task *aging_task;
 
 
 /**
- * Update a peer
+ * Closure for #sum_relative_preferences().
+ */
+struct SumContext
+{
+  /**
+   * Where to accumulate the result.
+   */
+  double f_rel_total;
+
+  /**
+   * Which kind of preference value are we adding up?
+   */
+  enum GNUNET_ATS_PreferenceKind kind;
+};
+
+
+/**
+ * Add the relative preference for the kind given to the
+ * closure.
+ *
+ * @param cls the `struct SumContext` with the kind and place
+ *                to store the result
+ * @param peer ignored
+ * @param value the `struct PreferencePeer` for getting the rel pref.
+ * @return #GNUNET_OK
+ */
+static int
+sum_relative_preferences (void *cls,
+                          const struct GNUNET_PeerIdentity *peer,
+                          void *value)
+{
+  struct SumContext *sum_ctx = cls;
+  struct PreferencePeer *p_cur = value;
+
+  sum_ctx->f_rel_total += p_cur->f_rel[sum_ctx->kind];
+  return GNUNET_OK;
+}
+
+
+/**
+ * Update the total releative preference for a peer by summing
+ * up the relative preferences all clients have for this peer.
  *
- * @param id peer id
- * @param kind the kind
- * @param rp the relative peer struct
+ * @param id peer id of the peer for which we should do the update
+ * @param kind the kind of preference value to update
  * @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)
+                                enum GNUNET_ATS_PreferenceKind kind)
 {
   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;
+  struct SumContext sum_ctx;
+  struct PeerRelative *rp;
 
-  /* For all clients */
+  sum_ctx.f_rel_total = 0.0;
+  sum_ctx.kind = kind;
   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];
-      }
-
-    }
-  }
-
+    GNUNET_CONTAINER_multipeermap_get_multiple (c_cur->peer2pref,
+                                                id,
+                                                &sum_relative_preferences,
+                                                &sum_ctx);
   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,
+       "Total relative preference for peer `%s' for `%s' is %.3f\n",
        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)
+       sum_ctx.f_rel_total);
+  rp = GNUNET_CONTAINER_multipeermap_get (preference_peers,
+                                          id);
+  GNUNET_assert (NULL != rp);
+  if (rp->f_rel[kind] != sum_ctx.f_rel_total)
   {
-    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]);
+    rp->f_rel[kind] = sum_ctx.f_rel_total;
+    GAS_plugin_notify_preference_changed (id,
+                                          kind,
+                                          rp->f_rel[kind]);
   }
 }
 
 
 /**
- * FIXME
+ * Free a peer's `struct PeerRelative`.
+ *
+ * @param cls unused
+ * @param key the key
+ * @param value the `struct PeerRelative` to free.
+ * @return #GNUNET_OK to continue
  */
 static int
-update_iterator (void *cls,
-                 const struct GNUNET_PeerIdentity *key,
-                 void *value)
+free_peer (void *cls,
+           const struct GNUNET_PeerIdentity *key,
+           void *value)
 {
-  enum GNUNET_ATS_PreferenceKind *kind = cls;
-  struct PeerRelative *pr = value;
+  struct PeerRelative *rp = value;
 
-  update_relative_values_for_peer (key,
-                                   *kind,
-                                   pr);
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multipeermap_remove (preference_peers,
+                                                       key,
+                                                       value));
+  GNUNET_free (rp);
   return GNUNET_OK;
 }
 
 
 /**
- * Recalculate preference for a specific ATS property
+ * Free `struct PreferencePeer` entry in map.
  *
- * @param c the preference client
- * @param kind the preference kind
- * @return the result
+ * @param cls the `struct PreferenceClient` with the map
+ * @param key the peer the entry is for
+ * @param value the `struct PreferencePeer` entry to free
+ * @return #GNUNET_OK (continue to iterate)
  */
-static void
-recalculate_relative_preferences (struct PreferenceClient *c,
-                                  enum GNUNET_ATS_PreferenceKind kind)
+static int
+free_preference (void *cls,
+                 const struct GNUNET_PeerIdentity *key,
+                 void *value)
 {
-  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)
+  struct PreferenceClient *pc = cls;
+  struct PreferencePeer *p = value;
+  struct PeerRelative *pr;
+
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multipeermap_remove (pc->peer2pref,
+                                                       key,
+                                                       p));
+  GNUNET_free (p);
+  pr = GNUNET_CONTAINER_multipeermap_get (preference_peers,
+                                          key);
+  GNUNET_assert (NULL != pr);
+  GNUNET_assert (pr->num_clients > 0);
+  pr->num_clients--;
+  if (0 == pr->num_clients)
   {
-    /* 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]);
+    free_peer (NULL,
+               key,
+               pr);
   }
-
+  return GNUNET_OK;
 }
 
 
 /**
- * FIXME.
- *
+ * Closure for #age_values().
  */
-static void
-run_preference_update (struct PreferenceClient *c_cur,
-                       struct PreferencePeer *p_cur,
-                       enum GNUNET_ATS_PreferenceKind kind,
-                       float score_abs)
+struct AgeContext
 {
-  double old_value;
+  /**
+   * Counter of values remaining to update, incremented for each value
+   * changed (to a new non-zero value).
+   */
+  unsigned int values_to_update;
 
-  /* 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;
+  /**
+   * Client we are currently aging values for.
+   */
+  struct PreferenceClient *cur_client;
 
-  /* Relative preference value changed, recalculate for all peers */
-  GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
-                                        &update_iterator,
-                                        &kind);
-}
+};
 
 
 /**
- * Reduce absolute preferences since they got old
+ * Age preference values of the given peer.
  *
- * @param cls the PreferencePeer
- * @param tc context
+ * @param cls a `
+ * @param peer peer being aged
+ * @param value the `struct PreferencePeer` for the peer
+ * @return #GNUNET_OK (continue to iterate)
  */
-static void
-preference_aging (void *cls,
-                  const struct GNUNET_SCHEDULER_TaskContext *tc)
+static int
+age_values (void *cls,
+            const struct GNUNET_PeerIdentity *peer,
+            void *value)
 {
-  struct PreferencePeer *p;
-  struct PreferenceClient *cur_client;
+  struct AgeContext *ac = cls;
+  struct PreferencePeer *p = value;
   unsigned int i;
-  unsigned int values_to_update;
-  double backup;
-
-  aging_task = NULL;
-  values_to_update = 0;
-  cur_client = NULL;
+  int dead;
 
-  for (cur_client = pc_head; NULL != cur_client; cur_client = cur_client->next)
+  dead = GNUNET_YES;
+  for (i = 0; i < GNUNET_ATS_PREFERENCE_END; i++)
   {
-    for (p = cur_client->p_head; NULL != p; p = p->next)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Aging preference for peer `%s'\n",
+                GNUNET_i2s (peer));
+    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)
     {
-      /* 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++;
-          }
-        }
-      }
+      p->f_abs[i] = DEFAULT_ABS_PREFERENCE;
+      p->f_rel[i] = DEFAULT_REL_PREFERENCE;
+      update_relative_values_for_peer (peer,
+                                       i);
+    }
+    else
+    {
+      ac->values_to_update++;
+      dead = GNUNET_NO;
     }
   }
-
-  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
+  if (GNUNET_YES == dead)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "No values to age left, not rescheduling aging task\n");
+    /* all preferences are zero, remove this entry */
+    free_preference (ac->cur_client,
+                     peer,
+                     p);
   }
+  return GNUNET_OK;
 }
 
 
 /**
- * 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
+ * Reduce absolute preferences since they got old.
+ *
+ * @param cls unused
  */
 static void
-update_abs_preference (struct PreferenceClient *c,
-                       struct PreferencePeer *p,
-                       enum GNUNET_ATS_PreferenceKind kind,
-                       float score_abs)
+preference_aging (void *cls)
 {
-  double score = score_abs;
+  struct AgeContext ac;
 
-  /* Update preference value according to type */
-  switch (kind)
+  aging_task = NULL;
+  GAS_plugin_solver_lock ();
+  ac.values_to_update = 0;
+  for (ac.cur_client = pc_head; NULL != ac.cur_client; ac.cur_client = ac.cur_client->next)
+    GNUNET_CONTAINER_multipeermap_iterate (ac.cur_client->peer2pref,
+                                           &age_values,
+                                           &ac);
+  GAS_plugin_solver_unlock ();
+  if (ac.values_to_update > 0)
   {
-  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;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Rescheduling aging task due to %u elements remaining to age\n",
+                ac.values_to_update);
+    if (NULL == aging_task)
+      aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
+                                                 &preference_aging,
+                                                 NULL);
   }
-}
-
-
-
-/**
- * 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
- */
-static void
-preference_change (struct GNUNET_SERVER_Client *client,
-                    const struct GNUNET_PeerIdentity *peer,
-                    enum GNUNET_ATS_PreferenceKind kind,
-                    float score_abs)
-{
-  if (GNUNET_NO ==
-      GNUNET_CONTAINER_multipeermap_contains (GSA_addresses,
-                                             peer))
+  else
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Received CHANGE_PREFERENCE for unknown peer `%s'\n",
-                GNUNET_i2s (peer));
-    return;
+                "No values to age left, not rescheduling aging task\n");
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received CHANGE_PREFERENCE for peer `%s'\n",
-              GNUNET_i2s (peer));
-  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
+ * Closure for #update_rel_sum() and #update_abs_sum().
  */
-void
-GAS_handle_preference_change (void *cls,
-                              struct GNUNET_SERVER_Client *client,
-                              const struct GNUNET_MessageHeader *message)
+struct UpdateContext
 {
-  const struct ChangePreferenceMessage *msg;
-  const struct PreferenceInformation *pi;
-  uint16_t msize;
-  uint32_t nump;
-  uint32_t i;
+  /**
+   * Preference client with the sum of all absolute scores.
+   */
+  struct PreferenceClient *pc;
 
-  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++)
-    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);
-}
+  /**
+   * Which kind are we updating?
+   */
+  enum GNUNET_ATS_PreferenceKind kind;
+
+};
 
 
 /**
- * Initialize preferences subsystem.
+ * Compute updated absolute score for the client based on the
+ * current absolute scores for each peer.
+ *
+ * @param cls a `struct UpdateContext`
+ * @param peer peer being updated
+ * @param value the `struct PreferencePeer` for the peer
+ * @return #GNUNET_OK (continue to iterate)
  */
-void
-GAS_preference_init ()
+static int
+update_abs_sum (void *cls,
+                const struct GNUNET_PeerIdentity *peer,
+                void *value)
 {
-  int i;
+  struct UpdateContext *uc = cls;
+  struct PreferencePeer *p_cur = value;
 
-  preference_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
-  for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
-    defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
+  uc->pc->f_abs_sum[uc->kind] += p_cur->f_abs[uc->kind];
+  return GNUNET_OK;
 }
 
 
 /**
- * Free a peer
+ * Compute updated relative score for each peer based on the
+ * current absolute score given by this client.
  *
- * @param cls unused
- * @param key the key
- * @param value RelativePeer
- * @return #GNUNET_OK to continue
+ * @param cls a `struct UpdateContext`
+ * @param peer peer being updated
+ * @param value the `struct PreferencePeer` for the peer (updated)
+ * @return #GNUNET_OK (continue to iterate)
  */
 static int
-free_peer (void *cls,
-           const struct GNUNET_PeerIdentity *key,
-           void *value)
+update_rel_sum (void *cls,
+                const struct GNUNET_PeerIdentity *peer,
+                void *value)
 {
-  struct PeerRelative *rp = value;
+  struct UpdateContext *uc = cls;
+  struct PreferencePeer *p_cur = value;
 
-  if (GNUNET_YES ==
-      GNUNET_CONTAINER_multipeermap_remove (preference_peers,
-                                            key,
-                                            value))
-    GNUNET_free (rp);
-  else
-    GNUNET_break (0);
+  p_cur->f_rel[uc->kind] = p_cur->f_abs[uc->kind] / uc->pc->f_abs_sum[uc->kind];
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Client has relative preference for %s for peer `%s' of %.3f\n",
+       GNUNET_ATS_print_preference_type (uc->kind),
+       GNUNET_i2s (peer),
+       p_cur->f_rel[uc->kind]);
   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
-free_client (struct PreferenceClient *pc)
+recalculate_relative_preferences (struct PreferenceClient *c,
+                                  enum GNUNET_ATS_PreferenceKind kind)
 {
-  struct PreferencePeer *next_p;
-  struct PreferencePeer *p;
+  struct UpdateContext uc;
 
-  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);
+  /* For this client: sum of absolute preference values for this preference */
+  uc.kind = kind;
+  uc.pc = c;
+  c->f_abs_sum[kind] = 0.0;
+
+  /* For all peers: calculate sum of absolute preferences */
+  GNUNET_CONTAINER_multipeermap_iterate (c->peer2pref,
+                                         &update_abs_sum,
+                                         &uc);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Client has sum of total preferences for %s of %.3f\n",
+       GNUNET_ATS_print_preference_type (kind),
+       c->f_abs_sum[kind]);
+
+  /* For all peers: calculate relative preference */
+  GNUNET_CONTAINER_multipeermap_iterate (c->peer2pref,
+                                         &update_rel_sum,
+                                         &uc);
 }
 
 
 /**
- * Shutdown preferences subsystem.
+ * The relative preferences of one of the clients have
+ * changed, update the global preferences for the given
+ * peer and notify the plugin.
+ *
+ * @param cls the kind of preference to calculate the
+ *        new global relative preference values for
+ * @param key the peer to update relative preference values for
+ * @param value a `struct PeerRelative`, unused
  */
-void
-GAS_preference_done ()
+static int
+update_iterator (void *cls,
+                 const struct GNUNET_PeerIdentity *key,
+                 void *value)
 {
-  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);
+  enum GNUNET_ATS_PreferenceKind *kind = cls;
 
+  update_relative_values_for_peer (key,
+                                   *kind);
+  return GNUNET_OK;
 }
 
 
 /**
- * Normalize an updated preference value
+ * Update the absolute preference and calculate the
+ * new relative 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 (struct GNUNET_SERVER_Client *client,
-                                        const struct GNUNET_PeerIdentity *peer,
-                                        enum GNUNET_ATS_PreferenceKind kind,
-                                        float score_abs)
+static void
+update_preference (struct GNUNET_SERVICE_Client *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;
+  unsigned int i;
 
+  if (kind >= GNUNET_ATS_PREFERENCE_END)
+  {
+    GNUNET_break(0);
+    return;
+  }
   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->peer2pref = GNUNET_CONTAINER_multipeermap_create (16,
+                                                             GNUNET_NO);
+    for (i = 0; i < GNUNET_ATS_PREFERENCE_END; 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);
+  }
 
-    GNUNET_CONTAINER_DLL_insert(pc_head, pc_tail, c_cur);
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding new client %p \n", c_cur);
+  /* check global peer entry exists */
+  if (NULL ==
+      (r_cur = GNUNET_CONTAINER_multipeermap_get (preference_peers,
+                                                  peer)))
+  {
+    /* Create struct for peer */
+    r_cur = GNUNET_new (struct PeerRelative);
+    for (i = 0; i < GNUNET_ATS_PREFERENCE_END; i++)
+      r_cur->f_rel[i] = DEFAULT_REL_PREFERENCE;
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_CONTAINER_multipeermap_put (preference_peers,
+                                                      peer,
+                                                      r_cur,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   }
 
   /* 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 */
+  p_cur = GNUNET_CONTAINER_multipeermap_get (c_cur->peer2pref,
+                                             peer);
   if (NULL == p_cur)
   {
+    /* Not found: create new peer entry */
     p_cur = GNUNET_new (struct PreferencePeer);
-    p_cur->client = c_cur;
-    p_cur->id = (*peer);
-    for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
+    for (i = 0; i < GNUNET_ATS_PREFERENCE_END; i++)
     {
-      /* Default value per peer absolute preference for a preference: 0 */
+      /* Default value per peer absolute preference for a preference*/
       p_cur->f_abs[i] = DEFAULT_ABS_PREFERENCE;
-      /* Default value per peer relative preference for a quality: 1.0 */
+      /* Default value per peer relative preference for a quality */
       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);
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multipeermap_put (c_cur->peer2pref,
+                                                      peer,
+                                                      p_cur,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    r_cur->num_clients++;
   }
 
-  /* 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);
+  p_cur->f_abs[kind] += score_abs;
+  recalculate_relative_preferences (c_cur, kind);
+  GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
+                                        &update_iterator,
+                                        &kind);
 
-  /* Start aging task */
   if (NULL == aging_task)
     aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
                                                &preference_aging,
                                                NULL);
+}
+
+
+/**
+ * Handle 'preference change' messages from clients.
+ *
+ * @param client the client that sent the request
+ * @param msg the request message
+ */
+void
+GAS_handle_preference_change (struct GNUNET_SERVICE_Client *client,
+                             const struct ChangePreferenceMessage *msg)
+{
+  const struct PreferenceInformation *pi;
+  uint32_t nump;
+
+  nump = ntohl (msg->num_preferences);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received PREFERENCE_CHANGE message for peer `%s'\n",
+              GNUNET_i2s (&msg->peer));
+  GNUNET_STATISTICS_update (GSA_stats,
+                            "# preference change requests processed",
+                            1,
+                            GNUNET_NO);
+  pi = (const struct PreferenceInformation *) &msg[1];
+  GAS_plugin_solver_lock ();
+  for (uint32_t i = 0; i < nump; i++)
+    update_preference (client,
+                       &msg->peer,
+                       (enum GNUNET_ATS_PreferenceKind) ntohl (pi[i].preference_kind),
+                       pi[i].preference_value);
+  GAS_plugin_solver_unlock ();
+}
+
+
+/**
+ * Initialize preferences subsystem.
+ */
+void
+GAS_preference_init ()
+{
+  unsigned int i;
+
+  preference_peers = GNUNET_CONTAINER_multipeermap_create (16,
+                                                           GNUNET_NO);
+  for (i = 0; i < GNUNET_ATS_PREFERENCE_END; i++)
+    defvalues.f_rel[i] = DEFAULT_REL_PREFERENCE;
+}
+
+
+/**
+ * Shutdown preferences subsystem.
+ */
+void
+GAS_preference_done ()
+{
+  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);
+    GNUNET_CONTAINER_multipeermap_iterate (pc->peer2pref,
+                                           &free_preference,
+                                           pc);
+    GNUNET_CONTAINER_multipeermap_destroy (pc->peer2pref);
+    GNUNET_free (pc);
+  }
+  GNUNET_CONTAINER_multipeermap_iterate (preference_peers,
+                                        &free_peer,
+                                         NULL);
+  GNUNET_CONTAINER_multipeermap_destroy (preference_peers);
 
 }
 
@@ -724,8 +735,8 @@ GAS_normalization_normalize_preference (struct GNUNET_SERVER_Client *client,
  * default preferences if peer does not exist
  */
 const double *
-GAS_normalization_get_preferences_by_peer (void *cls,
-                                          const struct GNUNET_PeerIdentity *id)
+GAS_preference_get_by_peer (void *cls,
+                            const struct GNUNET_PeerIdentity *id)
 {
   struct PeerRelative *rp;
 
@@ -745,19 +756,24 @@ GAS_normalization_get_preferences_by_peer (void *cls,
  * @param client the client
  */
 void
-GAS_normalization_preference_client_disconnect (struct GNUNET_SERVER_Client *client)
+GAS_preference_client_disconnect (struct GNUNET_SERVICE_Client *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);
+  GNUNET_CONTAINER_DLL_remove (pc_head,
+                               pc_tail,
+                               c_cur);
+  GNUNET_CONTAINER_multipeermap_iterate (c_cur->peer2pref,
+                                         &free_preference,
+                                         c_cur);
+  GNUNET_CONTAINER_multipeermap_destroy (c_cur->peer2pref);
+  GNUNET_free (c_cur);
 }
+
+
+/* end of gnunet-service-ats_preferences.c */