fixing log level
[oweals/gnunet.git] / src / ats / gnunet-service-ats-solver_proportional.c
index 119c97bf32d49d66dbbb4949bf2820a8d5494717..ddd15eb1d4eae22955cd1cb5254d63b402f402aa 100644 (file)
 #define PREF_AGING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
 #define PREF_AGING_FACTOR 0.95
 
-#define DEFAULT_PREFERENCE 1.0
+#define DEFAULT_REL_PREFERENCE 1.0
+#define DEFAULT_ABS_PREFERENCE 0.0
 #define MIN_UPDATE_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
 
+
 /**
  * A handle for the proportional solver
  */
@@ -219,9 +221,39 @@ struct GAS_PROPORTIONAL_Handle
   /**
    * Statistics handle
    */
-
   struct GNUNET_STATISTICS_Handle *stats;
 
+  /**
+   * Bandwidth changed callback
+   */
+  GAS_bandwidth_changed_cb bw_changed;
+
+  /**
+   * Bandwidth changed callback cls
+   */
+  void *bw_changed_cls;
+
+  /**
+   * ATS function to get preferences
+   */
+  GAS_get_preferences get_preferences;
+
+  /**
+   * Closure for ATS function to get preferences
+   */
+  void *get_preferences_cls;
+
+  /**
+   * Bulk lock
+   */
+  int bulk_lock;
+
+  /**
+   * Number of changes while solver was locked
+   */
+  int bulk_requests;
+
+
   /**
    * Total number of addresses for solver
    */
@@ -242,22 +274,9 @@ struct GAS_PROPORTIONAL_Handle
    */
   unsigned int networks;
 
-  /**
-   * Callback
-   */
-  GAS_bandwidth_changed_cb bw_changed;
-
-  /**
-   * Callback cls
-   */
-  void *bw_changed_cls;
-
-  struct GNUNET_CONTAINER_MultiHashMap *prefs;
-
-  struct PreferenceClient *pc_head;
-  struct PreferenceClient *pc_tail;
 };
 
+
 /**
  * Representation of a network
  */
@@ -310,43 +329,28 @@ struct Network
 };
 
 
+/**
+ * Wrapper for addresses to store them in network's linked list
+ */
 struct AddressWrapper
 {
+  /**
+   * Next in DLL
+   */
   struct AddressWrapper *next;
+
+  /**
+   * Previous in DLL
+   */
   struct AddressWrapper *prev;
 
+  /**
+   * The address
+   */
   struct ATS_Address *addr;
 };
 
 
-struct PreferenceClient
-{
-  struct PreferenceClient *prev;
-  struct PreferenceClient *next;
-  void *client;
-
-  double f_total[GNUNET_ATS_PreferenceCount];
-
-  struct PreferencePeer *p_head;
-  struct PreferencePeer *p_tail;
-};
-
-
-struct PreferencePeer
-{
-  struct PreferencePeer *next;
-  struct PreferencePeer *prev;
-  struct PreferenceClient *client;
-  struct GAS_PROPORTIONAL_Handle *s;
-  struct GNUNET_PeerIdentity id;
-
-  double f[GNUNET_ATS_PreferenceCount];
-  double f_rel[GNUNET_ATS_PreferenceCount];
-  double f_rel_total;
-
-  GNUNET_SCHEDULER_TaskIdentifier aging_task;
-};
-
 
 /**
  *  Important solver functions
@@ -362,6 +366,7 @@ struct PreferencePeer
 static int
 is_bandwidth_available_in_network (struct Network *net)
 {
+       GNUNET_assert (NULL != net);
   unsigned int na = net->active_addresses + 1;
   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
   if (((net->total_quota_in / na) > min_bw) &&
@@ -387,11 +392,11 @@ is_bandwidth_available_in_network (struct Network *net)
  *
  * @param s the solver handle
  * @param net the network type to update
- * @param address_except address excluded from notifcation, since we suggest
+ * @param address_except address excluded from notification, since we suggest
  * this address
  */
 static void
-update_quota_per_network (struct GAS_PROPORTIONAL_Handle *s,
+distribute_bandwidth_in_network (struct GAS_PROPORTIONAL_Handle *s,
                           struct Network *net,
                           struct ATS_Address *address_except)
 {
@@ -401,14 +406,23 @@ update_quota_per_network (struct GAS_PROPORTIONAL_Handle *s,
   unsigned long long remaining_quota_out = 0;
   unsigned long long quota_in_used = 0;
   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
+  double peer_prefs;
   double total_prefs; /* Important: has to be double not float due to precision */
   double cur_pref; /* Important: has to be double not float due to precision */
-  double *t = NULL; /* Important: has to be double not float due to precision */
+  const double *t = NULL; /* Important: has to be double not float due to precision */
+  int c;
 
   unsigned long long assigned_quota_in = 0;
   unsigned long long assigned_quota_out = 0;
   struct AddressWrapper *cur;
 
+
+       if (GNUNET_YES == s->bulk_lock)
+       {
+               s->bulk_requests++;
+               return;
+       }
+
   LOG (GNUNET_ERROR_TYPE_DEBUG,
               "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
               net->desc, net->active_addresses, net->total_quota_in, net->total_quota_in);
@@ -441,13 +455,18 @@ update_quota_per_network (struct GAS_PROPORTIONAL_Handle *s,
   {
       if (GNUNET_YES == cur->addr->active)
       {
-        t = GNUNET_CONTAINER_multihashmap_get (s->prefs, &cur->addr->peer.hashPubKey);
-        if (NULL == t)
-               total_prefs += DEFAULT_PREFERENCE;
-        else
-         {
-                       total_prefs += (*t);
-         }
+        GNUNET_assert (NULL != (t = s->get_preferences (s->get_preferences_cls, &cur->addr->peer)));
+
+                               peer_prefs = 0.0;
+                               for (c = 0; c < GNUNET_ATS_PreferenceCount; c++)
+                               {
+                                       if (c != GNUNET_ATS_PREFERENCE_END)
+                                       {
+                                               //fprintf (stderr, "VALUE[%u] %s %.3f \n", c, GNUNET_i2s (&cur->addr->peer), t[c]);
+                                               peer_prefs += t[c];
+                                       }
+                               }
+                               total_prefs += (peer_prefs / (GNUNET_ATS_PreferenceCount -1));
       }
   }
   for (cur = net->head; NULL != cur; cur = cur->next)
@@ -455,11 +474,15 @@ update_quota_per_network (struct GAS_PROPORTIONAL_Handle *s,
      if (GNUNET_YES == cur->addr->active)
      {
        cur_pref = 0.0;
-       t = GNUNET_CONTAINER_multihashmap_get (s->prefs, &cur->addr->peer.hashPubKey);
-       if (NULL == t)
-         cur_pref = DEFAULT_PREFERENCE;
-       else
-         cur_pref = (*t);
+       GNUNET_assert (NULL != (t = s->get_preferences (s->get_preferences_cls, &cur->addr->peer)));
+
+                        for (c = 0; c < GNUNET_ATS_PreferenceCount; c++)
+                        {
+                                if (c != GNUNET_ATS_PREFERENCE_END)
+                                        cur_pref += t[c];
+                        }
+                        cur_pref /= 2;
+
        assigned_quota_in = min_bw + ((cur_pref / total_prefs) * remaining_quota_in);
        assigned_quota_out = min_bw + ((cur_pref / total_prefs) * remaining_quota_out);
 
@@ -518,24 +541,129 @@ update_quota_per_network (struct GAS_PROPORTIONAL_Handle *s,
 }
 
 
+/**
+ * Extract an ATS performance info from an address
+ *
+ * @param address the address
+ * @param type the type to extract in HBO
+ * @return the value in HBO or GNUNET_ATS_VALUE_UNDEFINED in HBO if value does not exist
+ */
+static int
+get_performance_info (struct ATS_Address *address, uint32_t type);
+
+/**
+ * Find a "good" address to use for a peer by iterating over the addresses for this peer.
+ * If we already have an existing address, we stick to it.
+ * Otherwise, we pick by lowest distance and then by lowest latency.
+ *
+ * @param cls the 'struct ATS_Address**' where we store the result
+ * @param key unused
+ * @param value another 'struct ATS_Address*' to consider using
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+find_best_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
+{
+  struct ATS_Address **previous_p = cls;
+  struct ATS_Address *current = (struct ATS_Address *) value;
+  struct ATS_Address *previous = *previous_p;
+  struct GNUNET_TIME_Absolute now;
+  struct Network *net = (struct Network *) current->solver_information;
+  uint32_t p_distance_cur;
+  uint32_t p_distance_prev;
+  uint32_t p_delay_cur;
+  uint32_t p_delay_prev;
+
+  now = GNUNET_TIME_absolute_get();
+
+  if (current->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, current->blocked_until).abs_value)
+  {
+    /* This address is blocked for suggestion */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+                "Address %p blocked for suggestion for %llu ms \n",
+                current,
+                GNUNET_TIME_absolute_get_difference(now, current->blocked_until).rel_value);
+    return GNUNET_OK;
+  }
+
+  if (GNUNET_NO == is_bandwidth_available_in_network (net))
+    return GNUNET_OK; /* There's no bandwidth available in this network */
+
+  if (NULL != previous)
+  {
+       GNUNET_assert (NULL != previous->plugin);
+       GNUNET_assert (NULL != current->plugin);
+    if (0 == strcmp (previous->plugin, current->plugin))
+    {
+      if ((0 != previous->addr_len) &&
+          (0 == current->addr_len))
+      {
+        /* saved address was an outbound address, but we have an inbound address */
+        *previous_p = current;
+        return GNUNET_OK;
+      }
+      if (0 == previous->addr_len)
+      {
+        /* saved address was an inbound address, so do not overwrite */
+        return GNUNET_OK;
+      }
+    }
+  }
+
+  if (NULL == previous)
+  {
+    *previous_p = current;
+    return GNUNET_OK;
+  }
+  if ((ntohl (previous->assigned_bw_in.value__) == 0) &&
+      (ntohl (current->assigned_bw_in.value__) > 0))
+  {
+    /* stick to existing connection */
+    *previous_p = current;
+    return GNUNET_OK;
+  }
+
+  p_distance_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DISTANCE);
+  p_distance_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DISTANCE);
+  if ((p_distance_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_distance_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
+               (p_distance_prev > p_distance_cur))
+  {
+    /* user shorter distance */
+    *previous_p = current;
+    return GNUNET_OK;
+  }
+
+  p_delay_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DELAY);
+  p_delay_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DELAY);
+  if ((p_delay_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_delay_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
+               (p_delay_prev > p_delay_cur))
+  {
+    /* user lower latency */
+    *previous_p = current;
+    return GNUNET_OK;
+  }
+
+  /* don't care */
+  return GNUNET_OK;
+}
 
 /**
  *  Helper functions
  *  ---------------------------
  */
 
-
 /**
  * Update bandwidth assignment for all networks
  *
  * @param s the solver handle
  */
 static void
-update_all_networks (struct GAS_PROPORTIONAL_Handle *s)
+distribute_bandwidth_in_all_networks (struct GAS_PROPORTIONAL_Handle *s)
 {
        int i;
+
        for (i = 0; i < s->networks; i++)
-               update_quota_per_network (s, &s->network_entries[i], NULL);
+               distribute_bandwidth_in_network (s, &s->network_entries[i], NULL);
 
 }
 
@@ -555,6 +683,7 @@ get_network (struct GAS_PROPORTIONAL_Handle *s, uint32_t type)
   {
       if (s->network_entries[c].type == type)
         return &s->network_entries[c];
+
   }
   return NULL;
 }
@@ -588,6 +717,7 @@ get_active_address_it (void *cls, const struct GNUNET_HashCode * key, void *valu
   return GNUNET_OK;
 }
 
+
 /**
  * Find current active address for peer
  *
@@ -609,22 +739,6 @@ get_active_address (void *solver,
   return dest;
 }
 
-static int
-free_pref (void *cls,
-           const struct GNUNET_HashCode * key,
-           void *value)
-{
-  float *v = value;
-  GNUNET_free (v);
-  return GNUNET_OK;
-}
-
-
-
-
-
-
-
 
 
 static void
@@ -650,6 +764,7 @@ addresse_increment (struct GAS_PROPORTIONAL_Handle *s,
 
 }
 
+
 static int
 addresse_decrement (struct GAS_PROPORTIONAL_Handle *s,
                     struct Network *net,
@@ -707,6 +822,7 @@ addresse_decrement (struct GAS_PROPORTIONAL_Handle *s,
   return res;
 }
 
+
 /**
  * Extract an ATS performance info from an address
  *
@@ -732,411 +848,34 @@ get_performance_info (struct ATS_Address *address, uint32_t type)
 }
 
 
-
-
-
-
-
-
-
 /**
- * Find a "good" address to use for a peer.  If we already have an existing
- * address, we stick to it.  Otherwise, we pick by lowest distance and then
- * by lowest latency.
- *
- * @param cls the 'struct ATS_Address**' where we store the result
- * @param key unused
- * @param value another 'struct ATS_Address*' to consider using
- * @return GNUNET_OK (continue to iterate)
- */
-static int
-find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
-{
-  struct ATS_Address **previous_p = cls;
-  struct ATS_Address *current = (struct ATS_Address *) value;
-  struct ATS_Address *previous = *previous_p;
-  struct GNUNET_TIME_Absolute now;
-  struct Network *net = (struct Network *) current->solver_information;
-  uint32_t p_distance_cur;
-  uint32_t p_distance_prev;
-  uint32_t p_delay_cur;
-  uint32_t p_delay_prev;
-
-  now = GNUNET_TIME_absolute_get();
-
-  if (current->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, current->blocked_until).abs_value)
-  {
-    /* This address is blocked for suggestion */
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-                "Address %p blocked for suggestion for %llu ms \n",
-                current,
-                GNUNET_TIME_absolute_get_difference(now, current->blocked_until).rel_value);
-    return GNUNET_OK;
-  }
-
-  if (GNUNET_NO == is_bandwidth_available_in_network (net))
-    return GNUNET_OK; /* There's no bandwidth available in this network */
-
-  if (NULL != previous)
-  {
-    if ((0 == strcmp (previous->plugin, "tcp")) &&
-        (0 == strcmp (current->plugin, "tcp")))
-    {
-      if ((0 != previous->addr_len) &&
-          (0 == current->addr_len))
-      {
-        /* saved address was an outbound address, but we have an inbound address */
-        *previous_p = current;
-        return GNUNET_OK;
-      }
-      if (0 == previous->addr_len)
-      {
-        /* saved address was an inbound address, so do not overwrite */
-        return GNUNET_OK;
-      }
-    }
-  }
-
-  if (NULL == previous)
-  {
-    *previous_p = current;
-    return GNUNET_OK;
-  }
-  if ((ntohl (previous->assigned_bw_in.value__) == 0) &&
-      (ntohl (current->assigned_bw_in.value__) > 0))
-  {
-    /* stick to existing connection */
-    *previous_p = current;
-    return GNUNET_OK;
-  }
-
-  p_distance_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DISTANCE);
-  p_distance_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DISTANCE);
-  if ((p_distance_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_distance_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
-               (p_distance_prev > p_distance_cur))
-  {
-    /* user shorter distance */
-    *previous_p = current;
-    return GNUNET_OK;
-  }
-
-  p_delay_prev = get_performance_info (previous, GNUNET_ATS_QUALITY_NET_DELAY);
-  p_delay_cur = get_performance_info (current, GNUNET_ATS_QUALITY_NET_DELAY);
-  if ((p_delay_prev != GNUNET_ATS_VALUE_UNDEFINED) && (p_delay_cur != GNUNET_ATS_VALUE_UNDEFINED) &&
-               (p_delay_prev > p_delay_cur))
-  {
-    /* user lower latency */
-    *previous_p = current;
-    return GNUNET_OK;
-  }
-
-  /* don't care */
-  return GNUNET_OK;
-}
-
-
-
-
-
-/**
- *  Preference calculation
+ *  Solver API functions
  *  ---------------------------
  */
 
-
-static void
-recalculate_preferences (struct PreferencePeer *p)
-{
-       struct GAS_PROPORTIONAL_Handle *s = p->s;
-       struct PreferencePeer *p_cur;
-       struct PreferenceClient *c_cur = p->client;
-       double p_rel_global;
-  double *dest;
-  int kind;
-  int rkind;
-  int clients;
-
-  /**
-   * Idea:
-   *
-   * We have:
-   * Set of clients c
-   * Set of peers p_i in P
-   * Set of preference kinds k
-   * A preference value f_k_p_i with an unknown range
-   *
-   * We get:
-   * A client specific relative preference f_p_i_rel [1..2] for all peers
-   *
-   * For every client c
-   * {
-   *   For every preference kind k:
-   *   {
-   *     We remember for the preference f_p_i for each peer p_i.
-   *     We have a default preference value f_p_i = 0
-   *     We have a sum of all preferences f_t = sum (f_p_i)
-   *     So we can calculate a relative preference value fr_p_i:
-   *
-   *     f_k_p_i_ *  / f_t
-   *     f_k_p_i_rel = [1..2], default 1.0
-   *    }
-   *    f_p_i_rel = sum (f_k_p_i_rel) / count(k)
-   * }
-   *
-   **/
-
-  /* For this client: for all preferences, except TERMINATOR */
-  for (kind = GNUNET_ATS_PREFERENCE_END + 1 ; kind < GNUNET_ATS_PreferenceCount; kind ++)
-  {
-         /* Recalcalculate total preference for this quality kind over all peers*/
-         c_cur->f_total[kind] = 0;
-         for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
-               c_cur->f_total[kind] += p_cur->f[kind];
-
-         LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p has total preference for %s of %.3f\n",
-                       c_cur->client,
-             GNUNET_ATS_print_preference_type (kind),
-             c_cur->f_total[kind]);
-
-         /* Recalcalculate relative preference for all peers */
-         for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
-         {
-           /* Calculate relative preference for specific kind */
-               if (0.0 == c_cur->f_total[kind])
-               {
-                               /* No one has preference, so set default preference */
-                               p_cur->f_rel[kind] = DEFAULT_PREFERENCE;
-               }
-               else
-               {
-                               p_cur->f_rel[kind] = (c_cur->f_total[kind] + p_cur->f[kind]) / c_cur->f_total[kind];
-               }
-           LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p: peer `%s' has relative preference for %s of %.3f\n",
-                       c_cur->client,
-               GNUNET_i2s (&p_cur->id),
-               GNUNET_ATS_print_preference_type (kind),
-               p_cur->f_rel[kind]);
-
-           /* Calculate peer relative preference */
-           /* Start with kind = 1 to exclude terminator */
-           p_cur->f_rel_total = 0;
-           for (rkind = GNUNET_ATS_PREFERENCE_END + 1; rkind < GNUNET_ATS_PreferenceCount; rkind ++)
-           {
-               p_cur->f_rel_total += p_cur->f_rel[rkind];
-           }
-           p_cur->f_rel_total /=  (GNUNET_ATS_PreferenceCount - 1.0); /* -1 due to terminator */
-           LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p: peer `%s' has total relative preference of %.3f\n",
-                       c_cur->client,
-               GNUNET_i2s (&p_cur->id),
-               p_cur->f_rel_total);
-         }
-  }
-
-  /* Calculcate global total relative peer preference over all clients */
-  p_rel_global = 0.0;
-  clients = 0;
-  for (c_cur = s->pc_head; NULL != c_cur; c_cur = c_cur->next)
-  {
-      for (p_cur = c_cur->p_head; NULL != p_cur; p_cur = p_cur->next)
-          if (0 == memcmp (&p_cur->id, &p->id, sizeof (p_cur->id)))
-              break;
-      if (NULL != p_cur)
-      {
-          clients++;
-          p_rel_global += p_cur->f_rel_total;
-      }
-  }
-  p_rel_global /= clients;
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Global preference value for peer `%s': %.3f\n",
-      GNUNET_i2s (&p->id), p_rel_global);
-
-  /* Update global map */
-  if (NULL != (dest = GNUNET_CONTAINER_multihashmap_get(s->prefs, &p->id.hashPubKey)))
-      (*dest) = p_rel_global;
-  else
-  {
-      dest = GNUNET_malloc (sizeof (double));
-      (*dest) = p_rel_global;
-      GNUNET_CONTAINER_multihashmap_put(s->prefs,
-          &p->id.hashPubKey,
-          dest,
-          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
-  }
-}
-
-static void
-update_preference (struct PreferencePeer *p,
-                                                                        enum GNUNET_ATS_PreferenceKind kind,
-                                                        float score_f)
-{
-       double score = score_f;
-
-  /* Update preference value according to type */
-  switch (kind) {
-    case GNUNET_ATS_PREFERENCE_BANDWIDTH:
-    case GNUNET_ATS_PREFERENCE_LATENCY:
-      p->f[kind] = (p->f[kind] + score) / 2;
-      break;
-    case GNUNET_ATS_PREFERENCE_END:
-      break;
-    default:
-      break;
-  }
-  recalculate_preferences(p);
-  update_all_networks (p->s);
-}
-
-
-static void
-preference_aging (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-       int i;
-       double *t = NULL;
-       double backup;
-       struct PreferencePeer *p = cls;
-       GNUNET_assert (NULL != p);
-
-
-       p->aging_task = GNUNET_SCHEDULER_NO_TASK;
-
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Aging preferences for peer `%s'\n",
-               GNUNET_i2s (&p->id));
-
-  /* Issue for aging :
-   *
-   * Not for every peer preference values are set by default, so reducing the
-   * absolute preference value does not help for aging because it does not have
-   * influence on the relative values.
-   *
-   * So we have to reduce the relative value to have an immediate impact on
-   * quota calculation. In addition we cannot call recalculate_preferences here
-   * but instead reduce the absolute value to have an aging impact on future
-   * calls to change_preference where recalculate_preferences is called
-   *
-   */
-  /* Aging absolute values: */
-  for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
-  {
-               if (p->f[i] > 1.0)
-               {
-                       backup = p->f[i];
-                       p->f[i] *= PREF_AGING_FACTOR;
-                       LOG (GNUNET_ERROR_TYPE_DEBUG, "Aged preference for peer `%s' from %.3f to %.3f\n",
-                       GNUNET_i2s (&p->id), backup, p->f[i]);
-               }
-  }
-  /* Updating relative value */
-  t = GNUNET_CONTAINER_multihashmap_get (p->s->prefs, &p->id.hashPubKey);
-  if (NULL == t)
-  {
-       GNUNET_break (0);
-  }
-  else
-  {
-               if ((*t) > 1.0)
-                       (*t) = (*t) * PREF_AGING_FACTOR;
-               else
-                       (*t) = 1.0;
-               update_all_networks (p->s);
-  }
-  p->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL,
-               &preference_aging, p);
-}
-
-
 /**
  * Changes the preferences for a peer in the problem
  *
  * @param solver the solver handle
- * @param client the client with this preference
+ * @param addresses the address hashmap
  * @param peer the peer to change the preference for
  * @param kind the kind to change the preference
- * @param score the score
+ * @param pref_rel the normalized preference value for this kind over all clients
  */
 void
-GAS_simplistic_address_change_preference (void *solver,
-                                   void *client,
-                                   const struct GNUNET_PeerIdentity *peer,
-                                   enum GNUNET_ATS_PreferenceKind kind,
-                                   float score)
+GAS_proportional_address_change_preference (void *solver,
+                                                                                       struct GNUNET_CONTAINER_MultiHashMap *addresses,
+                                                                                       const struct GNUNET_PeerIdentity *peer,
+                                                                                       enum GNUNET_ATS_PreferenceKind kind,
+                                                                                       double pref_rel)
 {
-  static struct GNUNET_TIME_Absolute next_update;
   struct GAS_PROPORTIONAL_Handle *s = solver;
-  struct PreferenceClient *c_cur;
-  struct PreferencePeer *p_cur;
-  int i;
-
   GNUNET_assert (NULL != solver);
-  GNUNET_assert (NULL != client);
   GNUNET_assert (NULL != peer);
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p changes preference for peer `%s' %s %f\n",
-                                client,
-                                GNUNET_i2s (peer),
-                                GNUNET_ATS_print_preference_type (kind),
-                                score);
-
-  if (kind >= GNUNET_ATS_PreferenceCount)
-  {
-      GNUNET_break (0);
-      return;
-  }
-
-  /* Find preference client */
-  for (c_cur = s->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_malloc (sizeof (struct PreferenceClient));
-    c_cur->client = client;
-    GNUNET_CONTAINER_DLL_insert (s->pc_head, s->pc_tail, 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_malloc (sizeof (struct PreferencePeer));
-      p_cur->s = s;
-      p_cur->client = c_cur;
-      p_cur->id = (*peer);
-      for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
-      {
-        /* Default value per peer absolut preference for a quality:
-         * No value set, so absolute preference 0 */
-        p_cur->f[i] = 0.0;
-        /* Default value per peer relative preference for a quality: 1.0 */
-        p_cur->f_rel[i] = DEFAULT_PREFERENCE;
-      }
-      p_cur->aging_task = GNUNET_SCHEDULER_add_delayed (PREF_AGING_INTERVAL, &preference_aging, p_cur);
-      GNUNET_CONTAINER_DLL_insert (c_cur->p_head, c_cur->p_tail, p_cur);
-  }
-
-  update_preference (p_cur, kind, score);
-
-  /* FIXME: We should update quotas if UPDATE_INTERVAL is reached */
-  if (GNUNET_TIME_absolute_get().abs_value > next_update.abs_value)
-  {
-      /* update quotas*/
-      next_update = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
-                                              MIN_UPDATE_INTERVAL);
-  }
+       distribute_bandwidth_in_all_networks (s);
 }
 
-
-/**
- *  Solver API functions
- *  ---------------------------
- */
-
 /**
  * Get the preferred address for a specific peer
  *
@@ -1159,7 +898,7 @@ GAS_proportional_get_preferred_address (void *solver,
   cur = NULL;
   /* Get address with: stick to current address, lower distance, lower latency */
   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
-                                              &find_address_it, &cur);
+                                              &find_best_address_it, &cur);
   if (NULL == cur)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
@@ -1170,6 +909,13 @@ GAS_proportional_get_preferred_address (void *solver,
       (GNUNET_NO == cur->active) ? "inactive" : "active",
       cur, GNUNET_i2s (peer));
   net_cur = (struct Network *) cur->solver_information;
+  if (NULL == cur)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Trying to suggesting unknown address peer `%s'\n",
+        GNUNET_i2s (peer));
+    GNUNET_break (0);
+    return NULL;
+  }
   if (GNUNET_YES == cur->active)
   {
       /* This address was selected previously, so no need to update quotas */
@@ -1193,7 +939,7 @@ GAS_proportional_get_preferred_address (void *solver,
       s->bw_changed (s->bw_changed_cls, prev); /* notify about bw change, REQUIRED? */
       if (GNUNET_SYSERR == addresse_decrement (s, net_prev, GNUNET_NO, GNUNET_YES))
         GNUNET_break (0);
-      update_quota_per_network (s, net_prev, NULL);
+       distribute_bandwidth_in_network (s, net_prev, NULL);
   }
 
   if (GNUNET_NO == (is_bandwidth_available_in_network (cur->solver_information)))
@@ -1204,8 +950,7 @@ GAS_proportional_get_preferred_address (void *solver,
 
   cur->active = GNUNET_YES;
   addresse_increment(s, net_cur, GNUNET_NO, GNUNET_YES);
-  update_quota_per_network (s, net_cur, cur);
-
+  distribute_bandwidth_in_network (s, net_cur, cur);
   return cur;
 }
 
@@ -1290,7 +1035,7 @@ GAS_proportional_address_delete (void *solver,
       address->active = GNUNET_NO;
       if (GNUNET_SYSERR == addresse_decrement (s, net, GNUNET_NO, GNUNET_YES))
         GNUNET_break (0);
-      update_quota_per_network (s, net, NULL);
+      distribute_bandwidth_in_network (s, net, NULL);
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG, "After deleting address now total %u and active %u addresses in network `%s'\n",
       net->total_addresses,
@@ -1300,6 +1045,47 @@ GAS_proportional_address_delete (void *solver,
 }
 
 
+/**
+ * Start a bulk operation
+ *
+ * @param solver the solver
+ */
+void
+GAS_proportional_bulk_start (void *solver)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Locking solver for bulk operation ...\n");
+  struct GAS_PROPORTIONAL_Handle *s = (struct GAS_PROPORTIONAL_Handle *) solver;
+
+  GNUNET_assert (NULL != solver);
+  s->bulk_lock ++;
+}
+
+/**
+ * Bulk operation done
+ */
+void
+GAS_proportional_bulk_stop (void *solver)
+{
+       LOG (GNUNET_ERROR_TYPE_DEBUG, "Unlocking solver from bulk operation ...\n");
+
+  struct GAS_PROPORTIONAL_Handle *s = (struct GAS_PROPORTIONAL_Handle *) solver;
+  GNUNET_assert (NULL != solver);
+
+  if (s->bulk_lock < 1)
+  {
+       GNUNET_break (0);
+       return;
+  }
+  s->bulk_lock --;
+  if ((0 == s->bulk_lock) && (0 < s->bulk_requests))
+  {
+       LOG (GNUNET_ERROR_TYPE_ERROR, "No lock pending, recalculating\n");
+       distribute_bandwidth_in_all_networks (s);
+       s->bulk_requests = 0;
+  }
+}
+
+
 /**
  * Add a new single address to a network
  *
@@ -1381,6 +1167,16 @@ GAS_proportional_address_update (void *solver,
 
         /* set new network type */
         new_net = get_network (solver, addr_net);
+        if (NULL == new_net)
+        {
+          /* Address changed to invalid network... */
+          LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot find network of type `%u' %s\n"),
+                       addr_net, GNUNET_ATS_print_network_type (addr_net));
+          address->assigned_bw_in = GNUNET_BANDWIDTH_value_init (0);
+          address->assigned_bw_out = GNUNET_BANDWIDTH_value_init (0);
+          s->bw_changed  (s->bw_changed_cls, address);
+          return;
+        }
         address->solver_information = new_net;
 
         /* Add to new network and update*/
@@ -1393,7 +1189,7 @@ GAS_proportional_address_update (void *solver,
               /* Suggest updated address */
               address->active = GNUNET_YES;
               addresse_increment (s, new_net, GNUNET_NO, GNUNET_YES);
-              update_quota_per_network (solver, new_net, NULL);
+              distribute_bandwidth_in_network (solver, new_net, NULL);
           }
           else
           {
@@ -1503,6 +1299,8 @@ GAS_proportional_address_add (void *solver,
  * @param dest_length array length for quota arrays
  * @param bw_changed_cb callback for changed bandwidth amounts
  * @param bw_changed_cb_cls cls for callback
+ * @param get_preference callback to get relative preferences for a peer
+ * @param get_preference_cls cls for callback to get relative preferences
  * @return handle for the solver on success, NULL on fail
  */
 void *
@@ -1513,7 +1311,9 @@ GAS_proportional_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
                        unsigned long long *in_quota,
                        int dest_length,
                        GAS_bandwidth_changed_cb bw_changed_cb,
-                       void *bw_changed_cb_cls)
+                       void *bw_changed_cb_cls,
+                       GAS_get_preferences get_preference,
+                       void *get_preference_cls)
 {
   int c;
   struct GAS_PROPORTIONAL_Handle *s = GNUNET_malloc (sizeof (struct GAS_PROPORTIONAL_Handle));
@@ -1524,11 +1324,13 @@ GAS_proportional_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
   s->stats = (struct GNUNET_STATISTICS_Handle *) stats;
   s->bw_changed = bw_changed_cb;
   s->bw_changed_cls = bw_changed_cb_cls;
+  s->get_preferences = get_preference;
+  s->get_preferences_cls = get_preference_cls;
   s->networks = dest_length;
   s->network_entries = GNUNET_malloc (dest_length * sizeof (struct Network));
   s->active_addresses = 0;
   s->total_addresses = 0;
-  s->prefs = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
+  s->bulk_lock = GNUNET_NO;
 
   for (c = 0; c < dest_length; c++)
   {
@@ -1555,10 +1357,6 @@ void
 GAS_proportional_done (void *solver)
 {
   struct GAS_PROPORTIONAL_Handle *s = solver;
-  struct PreferenceClient *pc;
-  struct PreferenceClient *next_pc;
-  struct PreferencePeer *p;
-  struct PreferencePeer *next_p;
   struct AddressWrapper *cur;
   struct AddressWrapper *next;
   int c;
@@ -1611,29 +1409,6 @@ GAS_proportional_done (void *solver)
     GNUNET_break (0);
   }
   GNUNET_free (s->network_entries);
-
-  next_pc = s->pc_head;
-  while (NULL != (pc = next_pc))
-  {
-      next_pc = pc->next;
-      GNUNET_CONTAINER_DLL_remove (s->pc_head, s->pc_tail, pc);
-      next_p = pc->p_head;
-      while (NULL != (p = next_p))
-      {
-          next_p = p->next;
-          if (GNUNET_SCHEDULER_NO_TASK != p->aging_task)
-          {
-               GNUNET_SCHEDULER_cancel(p->aging_task);
-               p->aging_task = GNUNET_SCHEDULER_NO_TASK;
-          }
-          GNUNET_CONTAINER_DLL_remove (pc->p_head, pc->p_tail, p);
-          GNUNET_free (p);
-      }
-      GNUNET_free (pc);
-  }
-
-  GNUNET_CONTAINER_multihashmap_iterate (s->prefs, &free_pref, NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (s->prefs);
   GNUNET_free (s);
 }