aboutsummaryrefslogtreecommitdiff
path: root/src/ats/plugin_ats_proportional.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ats/plugin_ats_proportional.c')
-rw-r--r--src/ats/plugin_ats_proportional.c1243
1 files changed, 0 insertions, 1243 deletions
diff --git a/src/ats/plugin_ats_proportional.c b/src/ats/plugin_ats_proportional.c
deleted file mode 100644
index d3062a5c0..000000000
--- a/src/ats/plugin_ats_proportional.c
+++ /dev/null
@@ -1,1243 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file ats/plugin_ats_proportional.c
22 * @brief ATS proportional solver
23 * @author Matthias Wachs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_statistics_service.h"
28#include "gnunet_ats_service.h"
29#include "gnunet_ats_plugin.h"
30#include "gnunet-service-ats_addresses.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "ats-proportional", __VA_ARGS__)
33
34/**
35 * How much do we value stability over adaptation by default. A low
36 * value (close to 1.0) means we adapt as soon as possible, a larger
37 * value means that we have to have the respective factor of an
38 * advantage (or delay) before we adapt and sacrifice stability.
39 */
40#define PROP_STABILITY_FACTOR 1.25
41
42
43/**
44 * Default value to assume for the proportionality factor, if none is
45 * given in the configuration. This factor determines how strong the
46 * bandwidth allocation will orient itself on the application
47 * preferences. A lower factor means a more balanced bandwidth
48 * distribution while a larger number means a distribution more in
49 * line with application (bandwidth) preferences.
50 */
51#define PROPORTIONALITY_FACTOR 2.0
52
53
54/**
55 * Address information stored for the proportional solver in the
56 * `solver_information` member of `struct GNUNET_ATS_Address`.
57 *
58 * They are also stored in the respective `struct Network`'s linked
59 * list.
60 */
61struct AddressWrapper
62{
63 /**
64 * Next in DLL
65 */
66 struct AddressWrapper *next;
67
68 /**
69 * Previous in DLL
70 */
71 struct AddressWrapper *prev;
72
73 /**
74 * The address
75 */
76 struct ATS_Address *addr;
77
78 /**
79 * Network scope this address is in
80 */
81 struct Network *network;
82
83 /**
84 * Inbound quota
85 */
86 uint32_t calculated_quota_in;
87
88 /**
89 * Outbound quota
90 */
91 uint32_t calculated_quota_out;
92
93 /**
94 * When was this address activated
95 */
96 struct GNUNET_TIME_Absolute activated;
97};
98
99
100/**
101 * Representation of a network
102 */
103struct Network
104{
105 /**
106 * Network description
107 */
108 const char *desc;
109
110 /**
111 * String for statistics total addresses
112 */
113 char *stat_total;
114
115 /**
116 * String for statistics active addresses
117 */
118 char *stat_active;
119
120 /**
121 * Linked list of addresses in this network: head
122 */
123 struct AddressWrapper *head;
124
125 /**
126 * Linked list of addresses in this network: tail
127 */
128 struct AddressWrapper *tail;
129
130 /**
131 * Total inbound quota
132 */
133 unsigned long long total_quota_in;
134
135 /**
136 * Total outbound quota
137 */
138 unsigned long long total_quota_out;
139
140 /**
141 * ATS network type
142 */
143 enum GNUNET_NetworkType type;
144
145 /**
146 * Number of active addresses for this network
147 */
148 unsigned int active_addresses;
149
150 /**
151 * Number of total addresses for this network
152 */
153 unsigned int total_addresses;
154};
155
156
157/**
158 * A handle for the proportional solver
159 */
160struct GAS_PROPORTIONAL_Handle
161{
162 /**
163 * Our execution environment.
164 */
165 struct GNUNET_ATS_PluginEnvironment *env;
166
167 /**
168 * Networks array
169 */
170 struct Network *network_entries;
171
172 /**
173 * Proportionality factor
174 */
175 double prop_factor;
176
177 /**
178 * Stability factor
179 */
180 double stability_factor;
181
182 /**
183 * Bulk lock counter. If zero, we are not locked.
184 */
185 unsigned int bulk_lock;
186
187 /**
188 * Number of changes made while solver was locked. We really only
189 * use 0/non-zero to check on unlock if we have to run the update.
190 */
191 unsigned int bulk_requests;
192
193 /**
194 * Number of active addresses for solver
195 */
196 unsigned int active_addresses;
197};
198
199
200/**
201 * Test if bandwidth is available in this network to add an additional address.
202 *
203 * @param net the network type to check
204 * @param extra for how many extra addresses do we check?
205 * @return #GNUNET_YES or #GNUNET_NO
206 */
207static int
208is_bandwidth_available_in_network (struct Network *net,
209 int extra)
210{
211 unsigned int na;
212 uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
213
214 GNUNET_assert (((int) net->active_addresses) + extra >= 0);
215 na = net->active_addresses + extra;
216 if (0 == na)
217 return GNUNET_YES;
218 if (((net->total_quota_in / na) > min_bw) &&
219 ((net->total_quota_out / na) > min_bw))
220 return GNUNET_YES;
221 LOG (GNUNET_ERROR_TYPE_DEBUG,
222 "No bandwidth available in network\n");
223 return GNUNET_NO;
224}
225
226
227/**
228 * Test if all peers in this network require connectivity at level at
229 * least @a con.
230 *
231 * @param s the solver handle
232 * @param net the network type to check
233 * @param con connection return value threshold to check
234 * @return #GNUNET_YES or #GNUNET_NO
235 */
236static int
237all_require_connectivity (struct GAS_PROPORTIONAL_Handle *s,
238 struct Network *net,
239 unsigned int con)
240{
241 struct AddressWrapper *aw;
242
243 for (aw = net->head; NULL != aw; aw = aw->next)
244 if (con >
245 s->env->get_connectivity (s->env->cls,
246 &aw->addr->peer))
247 return GNUNET_NO;
248 return GNUNET_YES;
249}
250
251
252/**
253 * Update bandwidth assigned to peers in this network. The basic idea
254 * is to assign every peer in the network the minimum bandwidth, and
255 * then distribute the remaining bandwidth proportional to application
256 * preferences.
257 *
258 * @param s the solver handle
259 * @param net the network type to update
260 */
261static void
262distribute_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
263 struct Network *net)
264{
265 const uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
266 struct AddressWrapper *aw;
267 unsigned long long remaining_quota_in;
268 unsigned long long quota_out_used;
269 unsigned long long remaining_quota_out;
270 unsigned long long quota_in_used;
271 unsigned int count_addresses;
272 double sum_relative_peer_prefences;
273 double peer_weight;
274 double total_weight;
275 const double *peer_relative_prefs;
276
277 LOG (GNUNET_ERROR_TYPE_INFO,
278 "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
279 net->desc,
280 net->active_addresses,
281 net->total_quota_in,
282 net->total_quota_in);
283
284 if (0 == net->active_addresses)
285 return; /* no addresses to update */
286
287 /* sanity checks */
288 if ((net->active_addresses * min_bw) > net->total_quota_in)
289 {
290 GNUNET_break (0);
291 return;
292 }
293 if ((net->active_addresses * min_bw) > net->total_quota_out)
294 {
295 GNUNET_break (0);
296 return;
297 }
298
299 /* Calculate sum of relative preference for active addresses in this
300 network */
301 sum_relative_peer_prefences = 0.0;
302 count_addresses = 0;
303 for (aw = net->head; NULL != aw; aw = aw->next)
304 {
305 if (GNUNET_YES != aw->addr->active)
306 continue;
307 peer_relative_prefs = s->env->get_preferences (s->env->cls,
308 &aw->addr->peer);
309 sum_relative_peer_prefences
310 += peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
311 count_addresses++;
312 }
313 if (count_addresses != net->active_addresses)
314 {
315 GNUNET_break (0);
316 LOG (GNUNET_ERROR_TYPE_WARNING,
317 "%s: Counted %u active addresses, expected %u active addresses\n",
318 net->desc,
319 count_addresses,
320 net->active_addresses);
321 /* try to fix... */
322 net->active_addresses = count_addresses;
323 }
324 LOG (GNUNET_ERROR_TYPE_INFO,
325 "Total relative preference %.3f for %u addresses in network %s\n",
326 sum_relative_peer_prefences,
327 net->active_addresses,
328 net->desc);
329
330 /* check how much we have to distribute */
331 remaining_quota_in = net->total_quota_in - (net->active_addresses * min_bw);
332 remaining_quota_out = net->total_quota_out - (net->active_addresses * min_bw);
333 LOG (GNUNET_ERROR_TYPE_DEBUG,
334 "Proportionally distributable bandwidth (in/out): %llu/%llu\n",
335 remaining_quota_in,
336 remaining_quota_out);
337
338 /* distribute remaining quota; we do not do it exactly proportional,
339 but balance "even" distribution ("net->active_addresses") with
340 the preference sum using the "prop_factor". */
341 total_weight = net->active_addresses
342 + s->prop_factor * sum_relative_peer_prefences;
343 quota_out_used = 0;
344 quota_in_used = 0;
345 for (aw = net->head; NULL != aw; aw = aw->next)
346 {
347 if (GNUNET_YES != aw->addr->active)
348 {
349 /* set to 0, just to be sure */
350 aw->calculated_quota_in = 0;
351 aw->calculated_quota_out = 0;
352 continue;
353 }
354 peer_relative_prefs = s->env->get_preferences (s->env->cls,
355 &aw->addr->peer);
356 peer_weight = 1.0
357 + s->prop_factor
358 * peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
359
360 aw->calculated_quota_in = min_bw
361 + (peer_weight / total_weight)
362 * remaining_quota_in;
363 aw->calculated_quota_out = min_bw
364 + (peer_weight / total_weight)
365 * remaining_quota_out;
366
367 LOG (GNUNET_ERROR_TYPE_INFO,
368 "New quotas for peer `%s' with weight (cur/total) %.3f/%.3f (in/out) are: %u/%u\n",
369 GNUNET_i2s (&aw->addr->peer),
370 peer_weight,
371 total_weight,
372 (unsigned int) aw->calculated_quota_in,
373 (unsigned int) aw->calculated_quota_out);
374 quota_in_used += aw->calculated_quota_in;
375 quota_out_used += aw->calculated_quota_out;
376 }
377 LOG (GNUNET_ERROR_TYPE_DEBUG,
378 "Total bandwidth assigned is (in/out): %llu /%llu\n",
379 quota_in_used,
380 quota_out_used);
381 /* +1 due to possible rounding errors */
382 GNUNET_break (quota_out_used <= net->total_quota_out + 1);
383 GNUNET_break (quota_in_used <= net->total_quota_in + 1);
384}
385
386
387/**
388 * Notify ATS service of bandwidth changes to addresses.
389 *
390 * @param s solver handle
391 * @param net the network to propagate changes in
392 */
393static void
394propagate_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
395 struct Network *net)
396{
397 struct AddressWrapper *cur;
398
399 for (cur = net->head; NULL != cur; cur = cur->next)
400 {
401 if ((cur->addr->assigned_bw_in == cur->calculated_quota_in) &&
402 (cur->addr->assigned_bw_out == cur->calculated_quota_out))
403 continue;
404 cur->addr->assigned_bw_in = cur->calculated_quota_in;
405 cur->addr->assigned_bw_out = cur->calculated_quota_out;
406 if (GNUNET_YES == cur->addr->active)
407 s->env->bandwidth_changed_cb (s->env->cls,
408 cur->addr);
409 }
410}
411
412
413/**
414 * Distribute bandwidth. The addresses have already been selected,
415 * this is merely distributed the bandwidth among the addresses.
416 *
417 * @param s the solver handle
418 * @param n the network, can be NULL for all networks
419 */
420static void
421distribute_bandwidth_in_network (struct GAS_PROPORTIONAL_Handle *s,
422 struct Network *n)
423{
424 unsigned int i;
425
426 if (0 != s->bulk_lock)
427 {
428 s->bulk_requests++;
429 return;
430 }
431 if (NULL != n)
432 {
433 LOG (GNUNET_ERROR_TYPE_DEBUG,
434 "Redistributing bandwidth in network %s with %u active and %u total addresses\n",
435 GNUNET_NT_to_string (n->type),
436 n->active_addresses,
437 n->total_addresses);
438 s->env->info_cb (s->env->cls,
439 GAS_OP_SOLVE_START,
440 GAS_STAT_SUCCESS,
441 GAS_INFO_PROP_SINGLE);
442 distribute_bandwidth (s,
443 n);
444 s->env->info_cb (s->env->cls,
445 GAS_OP_SOLVE_STOP,
446 GAS_STAT_SUCCESS,
447 GAS_INFO_PROP_SINGLE);
448 s->env->info_cb (s->env->cls,
449 GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
450 GAS_STAT_SUCCESS,
451 GAS_INFO_PROP_SINGLE);
452 propagate_bandwidth (s,
453 n);
454
455 s->env->info_cb (s->env->cls,
456 GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
457 GAS_STAT_SUCCESS,
458 GAS_INFO_PROP_SINGLE);
459 }
460 else
461 {
462 LOG (GNUNET_ERROR_TYPE_DEBUG,
463 "Redistributing bandwidth in all %u networks\n",
464 s->env->network_count);
465 s->env->info_cb (s->env->cls,
466 GAS_OP_SOLVE_START,
467 GAS_STAT_SUCCESS,
468 GAS_INFO_PROP_ALL);
469 for (i = 0; i < s->env->network_count; i++)
470 distribute_bandwidth (s,
471 &s->network_entries[i]);
472 s->env->info_cb (s->env->cls,
473 GAS_OP_SOLVE_STOP,
474 GAS_STAT_SUCCESS,
475 GAS_INFO_PROP_ALL);
476 s->env->info_cb (s->env->cls,
477 GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
478 GAS_STAT_SUCCESS,
479 GAS_INFO_PROP_ALL);
480 for (i = 0; i < s->env->network_count; i++)
481 propagate_bandwidth (s,
482 &s->network_entries[i]);
483 s->env->info_cb (s->env->cls,
484 GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
485 GAS_STAT_SUCCESS,
486 GAS_INFO_PROP_ALL);
487 }
488}
489
490
491/**
492 * Context for finding the best address* Linked list of addresses in this network: head
493 */
494struct FindBestAddressCtx
495{
496 /**
497 * The solver handle
498 */
499 struct GAS_PROPORTIONAL_Handle *s;
500
501 /**
502 * The currently best address
503 */
504 struct ATS_Address *best;
505};
506
507
508/**
509 * Find a "good" address to use for a peer by iterating over the
510 * addresses for this peer. If we already have an existing address,
511 * we stick to it. Otherwise, we pick by lowest distance and then by
512 * lowest latency.
513 *
514 * @param cls the `struct FindBestAddressCtx *' where we store the result
515 * @param key the peer we are trying to find the best address for
516 * @param value another `struct ATS_Address*` to consider using
517 * @return #GNUNET_OK (continue to iterate)
518 */
519static int
520find_best_address_it (void *cls,
521 const struct GNUNET_PeerIdentity *key,
522 void *value)
523{
524 struct FindBestAddressCtx *ctx = cls;
525 struct ATS_Address *current = value;
526 struct AddressWrapper *asi = current->solver_information;
527 struct GNUNET_TIME_Relative active_time;
528 double best_delay;
529 double best_distance;
530 double cur_delay;
531 double cur_distance;
532 unsigned int con;
533 int bw_available;
534 int need;
535
536 /* we need +1 slot if 'current' is not yet active */
537 need = (GNUNET_YES == current->active) ? 0 : 1;
538 /* we save -1 slot if 'best' is active and belongs
539 to the same network (as we would replace it) */
540 if ((NULL != ctx->best) &&
541 (GNUNET_YES == ctx->best->active) &&
542 (((struct AddressWrapper *) ctx->best->solver_information)->network ==
543 asi->network))
544 need--;
545 /* we can gain -1 slot if this peers connectivity
546 requirement is higher than that of another peer
547 in that network scope */
548 con = ctx->s->env->get_connectivity (ctx->s->env->cls,
549 key);
550 if (GNUNET_YES !=
551 all_require_connectivity (ctx->s,
552 asi->network,
553 con))
554 need--;
555 /* test if minimum bandwidth for 'current' would be available */
556 bw_available
557 = is_bandwidth_available_in_network (asi->network,
558 need);
559 if (! bw_available)
560 {
561 /* Bandwidth for this address is unavailable, so we cannot use
562 it. */
563 return GNUNET_OK;
564 }
565 if (GNUNET_YES == current->active)
566 {
567 active_time = GNUNET_TIME_absolute_get_duration (asi->activated);
568 if (active_time.rel_value_us <=
569 ((double) GNUNET_TIME_UNIT_SECONDS.rel_value_us)
570 * ctx->s->stability_factor)
571 {
572 /* Keep active address for stability reasons */
573 ctx->best = current;
574 return GNUNET_NO;
575 }
576 }
577 if (NULL == ctx->best)
578 {
579 /* We so far have nothing else, so go with it! */
580 ctx->best = current;
581 return GNUNET_OK;
582 }
583
584 /* Now compare ATS information */
585 cur_distance = current->norm_distance.norm;
586 best_distance = ctx->best->norm_distance.norm;
587 cur_delay = current->norm_delay.norm;
588 best_delay = ctx->best->norm_delay.norm;
589
590 /* user shorter distance */
591 if (cur_distance < best_distance)
592 {
593 if (GNUNET_NO == ctx->best->active)
594 {
595 /* Activity doesn't influence the equation, use current */
596 ctx->best = current;
597 }
598 else if ((best_distance / cur_distance) > ctx->s->stability_factor)
599 {
600 /* Distance change is significant, switch active address! */
601 ctx->best = current;
602 }
603 }
604
605 /* User connection with less delay */
606 if (cur_delay < best_delay)
607 {
608 if (GNUNET_NO == ctx->best->active)
609 {
610 /* Activity doesn't influence the equation, use current */
611 ctx->best = current;
612 }
613 else if ((best_delay / cur_delay) > ctx->s->stability_factor)
614 {
615 /* Latency change is significant, switch active address! */
616 ctx->best = current;
617 }
618 }
619 return GNUNET_OK;
620}
621
622
623/**
624 * Find the currently best address for a peer from the set of
625 * addresses available or return NULL of no address is available.
626 *
627 * @param s the proportional handle
628 * @param addresses the address hashmap
629 * @param id the peer id
630 * @return the address or NULL
631 */
632struct ATS_Address *
633get_best_address (struct GAS_PROPORTIONAL_Handle *s,
634 struct GNUNET_CONTAINER_MultiPeerMap *addresses,
635 const struct GNUNET_PeerIdentity *id)
636{
637 struct FindBestAddressCtx fba_ctx;
638
639 fba_ctx.best = NULL;
640 fba_ctx.s = s;
641 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
642 id,
643 &find_best_address_it,
644 &fba_ctx);
645 return fba_ctx.best;
646}
647
648
649/**
650 * Decrease number of active addresses in network.
651 *
652 * @param s the solver handle
653 * @param net the network type
654 */
655static void
656address_decrement_active (struct GAS_PROPORTIONAL_Handle *s,
657 struct Network *net)
658{
659 GNUNET_assert (net->active_addresses > 0);
660 net->active_addresses--;
661 GNUNET_STATISTICS_update (s->env->stats,
662 net->stat_active,
663 -1,
664 GNUNET_NO);
665 GNUNET_assert (s->active_addresses > 0);
666 s->active_addresses--;
667 GNUNET_STATISTICS_update (s->env->stats,
668 "# ATS addresses total",
669 -1,
670 GNUNET_NO);
671}
672
673
674/**
675 * Address map iterator to find current active address for peer.
676 * Asserts that only one address is active per peer.
677 *
678 * @param cls last active address
679 * @param key peer's key
680 * @param value address to check
681 * @return #GNUNET_NO on double active address else #GNUNET_YES;
682 */
683static int
684get_active_address_it (void *cls,
685 const struct GNUNET_PeerIdentity *key,
686 void *value)
687{
688 struct ATS_Address **dest = cls;
689 struct ATS_Address *aa = value;
690
691 if (GNUNET_YES != aa->active)
692 return GNUNET_OK;
693 GNUNET_assert (NULL == (*dest));
694 (*dest) = aa;
695 return GNUNET_OK;
696}
697
698
699/**
700 * Find current active address for peer
701 *
702 * @param s the solver handle
703 * @param peer the peer
704 * @return active address or NULL
705 */
706static struct ATS_Address *
707get_active_address (struct GAS_PROPORTIONAL_Handle *s,
708 const struct GNUNET_PeerIdentity *peer)
709{
710 struct ATS_Address *dest;
711
712 dest = NULL;
713 GNUNET_CONTAINER_multipeermap_get_multiple (s->env->addresses,
714 peer,
715 &get_active_address_it,
716 &dest);
717 return dest;
718}
719
720
721/**
722 * Update active address for a peer. Check if active address exists
723 * and what the best address is, if addresses are different switch.
724 * Then reallocate bandwidth within the affected network scopes.
725 *
726 * @param s solver handle
727 * @param current_address the address currently active for the peer,
728 * NULL for none
729 * @param peer the peer to check
730 */
731static void
732update_active_address (struct GAS_PROPORTIONAL_Handle *s,
733 struct ATS_Address *current_address,
734 const struct GNUNET_PeerIdentity *peer)
735{
736 struct ATS_Address *best_address;
737 struct AddressWrapper *asi_cur;
738 struct AddressWrapper *asi_best;
739 struct AddressWrapper *aw;
740 struct AddressWrapper *aw_min;
741 unsigned int a_con;
742 unsigned int con_min;
743
744 best_address = get_best_address (s,
745 s->env->addresses,
746 peer);
747 if (NULL != best_address)
748 asi_best = best_address->solver_information;
749 else
750 asi_best = NULL;
751 if (current_address == best_address)
752 return; /* no changes */
753 if (NULL != current_address)
754 {
755 /* We switch to a new address (or to none);
756 mark old address as inactive. */
757 asi_cur = current_address->solver_information;
758 GNUNET_assert (GNUNET_YES == current_address->active);
759 LOG (GNUNET_ERROR_TYPE_INFO,
760 "Disabling previous active address for peer `%s'\n",
761 GNUNET_i2s (peer));
762 asi_cur->activated = GNUNET_TIME_UNIT_ZERO_ABS;
763 current_address->active = GNUNET_NO;
764 current_address->assigned_bw_in = 0;
765 current_address->assigned_bw_out = 0;
766 address_decrement_active (s,
767 asi_cur->network);
768 if ((NULL == best_address) ||
769 (asi_best->network != asi_cur->network))
770 distribute_bandwidth_in_network (s,
771 asi_cur->network);
772 if (NULL == best_address)
773 {
774 /* We previously had an active address, but now we cannot
775 * suggest one. Therefore we have to disconnect the peer.
776 * The above call to "distribute_bandwidth_in_network()
777 * does not see 'current_address' so we need to trigger
778 * the update here. */LOG (GNUNET_ERROR_TYPE_DEBUG,
779 "Disconnecting peer `%s'.\n",
780 GNUNET_i2s (peer));
781 s->env->bandwidth_changed_cb (s->env->cls,
782 current_address);
783 return;
784 }
785 }
786 if (NULL == best_address)
787 {
788 /* We do not have a new address, so we are done. */
789 LOG (GNUNET_ERROR_TYPE_DEBUG,
790 "Cannot suggest address for peer `%s'\n",
791 GNUNET_i2s (peer));
792 return;
793 }
794 /* We do have a new address, activate it */
795 LOG (GNUNET_ERROR_TYPE_DEBUG,
796 "Selecting new address %p for peer `%s'\n",
797 best_address,
798 GNUNET_i2s (peer));
799 /* Mark address as active */
800 best_address->active = GNUNET_YES;
801 asi_best->activated = GNUNET_TIME_absolute_get ();
802 asi_best->network->active_addresses++;
803 s->active_addresses++;
804 GNUNET_STATISTICS_update (s->env->stats,
805 "# ATS active addresses total",
806 1,
807 GNUNET_NO);
808 GNUNET_STATISTICS_update (s->env->stats,
809 asi_best->network->stat_active,
810 1,
811 GNUNET_NO);
812 LOG (GNUNET_ERROR_TYPE_INFO,
813 "Address %p for peer `%s' is now active\n",
814 best_address,
815 GNUNET_i2s (peer));
816
817 if (GNUNET_NO ==
818 is_bandwidth_available_in_network (asi_best->network,
819 0))
820 {
821 /* we went over the maximum number of addresses for
822 this scope; remove the address with the smallest
823 connectivity requirement */
824 con_min = UINT32_MAX;
825 aw_min = NULL;
826 for (aw = asi_best->network->head; NULL != aw; aw = aw->next)
827 {
828 if ((con_min >
829 (a_con = s->env->get_connectivity (s->env->cls,
830 &aw->addr->peer))) &&
831 (GNUNET_YES == aw->addr->active))
832 {
833 aw_min = aw;
834 con_min = a_con;
835 if (0 == con_min)
836 break;
837 }
838 }
839 update_active_address (s,
840 aw_min->addr,
841 &aw_min->addr->peer);
842 }
843 distribute_bandwidth_in_network (s,
844 asi_best->network);
845}
846
847
848/**
849 * The preferences for a peer in the problem changed.
850 *
851 * @param solver the solver handle
852 * @param peer the peer to change the preference for
853 * @param kind the kind to change the preference
854 * @param pref_rel the normalized preference value for this kind over all clients
855 */
856static void
857GAS_proportional_change_preference (void *solver,
858 const struct GNUNET_PeerIdentity *peer,
859 enum GNUNET_ATS_PreferenceKind kind,
860 double pref_rel)
861{
862 struct GAS_PROPORTIONAL_Handle *s = solver;
863
864 if (GNUNET_ATS_PREFERENCE_BANDWIDTH != kind)
865 return; /* we do not care */
866 distribute_bandwidth_in_network (s,
867 NULL);
868}
869
870
871/**
872 * Get application feedback for a peer
873 *
874 * @param solver the solver handle
875 * @param application the application
876 * @param peer the peer to change the preference for
877 * @param scope the time interval for this feedback: [now - scope .. now]
878 * @param kind the kind to change the preference
879 * @param score the score
880 */
881static void
882GAS_proportional_feedback (void *solver,
883 struct GNUNET_SERVICE_Client *application,
884 const struct GNUNET_PeerIdentity *peer,
885 const struct GNUNET_TIME_Relative scope,
886 enum GNUNET_ATS_PreferenceKind kind,
887 double score)
888{
889 /* Proportional does not care about feedback */
890}
891
892
893/**
894 * Get the preferred address for a specific peer
895 *
896 * @param solver the solver handle
897 * @param peer the identity of the peer
898 */
899static void
900GAS_proportional_start_get_address (void *solver,
901 const struct GNUNET_PeerIdentity *peer)
902{
903 struct GAS_PROPORTIONAL_Handle *s = solver;
904
905 update_active_address (s,
906 get_active_address (s,
907 peer),
908 peer);
909}
910
911
912/**
913 * Stop notifying about address and bandwidth changes for this peer
914 *
915 * @param solver the solver handle
916 * @param peer the peer
917 */
918static void
919GAS_proportional_stop_get_address (void *solver,
920 const struct GNUNET_PeerIdentity *peer)
921{
922 struct GAS_PROPORTIONAL_Handle *s = solver;
923 struct ATS_Address *cur;
924 struct AddressWrapper *asi;
925
926 cur = get_active_address (s,
927 peer);
928 if (NULL == cur)
929 return;
930 asi = cur->solver_information;
931 distribute_bandwidth_in_network (s,
932 asi->network);
933}
934
935
936/**
937 * Start a bulk operation
938 *
939 * @param solver the solver
940 */
941static void
942GAS_proportional_bulk_start (void *solver)
943{
944 struct GAS_PROPORTIONAL_Handle *s = solver;
945
946 LOG (GNUNET_ERROR_TYPE_DEBUG,
947 "Locking solver for bulk operation ...\n");
948 GNUNET_assert (NULL != solver);
949 s->bulk_lock++;
950}
951
952
953/**
954 * Bulk operation done.
955 *
956 * @param solver our `struct GAS_PROPORTIONAL_Handle *`
957 */
958static void
959GAS_proportional_bulk_stop (void *solver)
960{
961 struct GAS_PROPORTIONAL_Handle *s = solver;
962
963 LOG (GNUNET_ERROR_TYPE_DEBUG,
964 "Unlocking solver from bulk operation ...\n");
965 if (s->bulk_lock < 1)
966 {
967 GNUNET_break (0);
968 return;
969 }
970 s->bulk_lock--;
971 if ((0 == s->bulk_lock) &&
972 (0 < s->bulk_requests))
973 {
974 LOG (GNUNET_ERROR_TYPE_INFO,
975 "No lock pending, recalculating\n");
976 distribute_bandwidth_in_network (s,
977 NULL);
978 s->bulk_requests = 0;
979 }
980}
981
982
983/**
984 * Transport properties for this address have changed
985 *
986 * @param solver solver handle
987 * @param address the address
988 */
989static void
990GAS_proportional_address_property_changed (void *solver,
991 struct ATS_Address *address)
992{
993 struct GAS_PROPORTIONAL_Handle *s = solver;
994 struct AddressWrapper *asi = address->solver_information;
995
996 distribute_bandwidth_in_network (s,
997 asi->network);
998}
999
1000
1001/**
1002 * Add a new single address to a network
1003 *
1004 * @param solver the solver Handle
1005 * @param address the address to add
1006 * @param network network type of this address
1007 */
1008static void
1009GAS_proportional_address_add (void *solver,
1010 struct ATS_Address *address,
1011 uint32_t network)
1012{
1013 struct GAS_PROPORTIONAL_Handle *s = solver;
1014 struct Network *net;
1015 struct AddressWrapper *aw;
1016
1017 GNUNET_assert (network < s->env->network_count);
1018 net = &s->network_entries[network];
1019 net->total_addresses++;
1020
1021 aw = GNUNET_new (struct AddressWrapper);
1022 aw->addr = address;
1023 aw->network = net;
1024 address->solver_information = aw;
1025 GNUNET_CONTAINER_DLL_insert (net->head,
1026 net->tail,
1027 aw);
1028 GNUNET_STATISTICS_update (s->env->stats,
1029 "# ATS addresses total",
1030 1,
1031 GNUNET_NO);
1032 GNUNET_STATISTICS_update (s->env->stats,
1033 net->stat_total,
1034 1,
1035 GNUNET_NO);
1036 update_active_address (s,
1037 get_active_address (s,
1038 &address->peer),
1039 &address->peer);
1040 LOG (GNUNET_ERROR_TYPE_INFO,
1041 "Added new address for `%s', now total %u and active %u addresses in network `%s'\n",
1042 GNUNET_i2s (&address->peer),
1043 net->total_addresses,
1044 net->active_addresses,
1045 net->desc);
1046}
1047
1048
1049/**
1050 * Remove an address from the solver. To do so, we:
1051 * - Removed it from specific network
1052 * - Decrease the number of total addresses
1053 * - If active:
1054 * - decrease number of active addresses
1055 * - update quotas
1056 *
1057 * @param solver the solver handle
1058 * @param address the address to remove
1059 */
1060static void
1061GAS_proportional_address_delete (void *solver,
1062 struct ATS_Address *address)
1063{
1064 struct GAS_PROPORTIONAL_Handle *s = solver;
1065 struct AddressWrapper *aw = address->solver_information;
1066 struct Network *net = aw->network;
1067
1068 LOG (GNUNET_ERROR_TYPE_DEBUG,
1069 "Deleting %s address for peer `%s' from network `%s' (total: %u/active: %u)\n",
1070 (GNUNET_NO == address->active) ? "inactive" : "active",
1071 GNUNET_i2s (&address->peer),
1072 net->desc,
1073 net->total_addresses,
1074 net->active_addresses);
1075
1076 GNUNET_CONTAINER_DLL_remove (net->head,
1077 net->tail,
1078 aw);
1079 GNUNET_assert (net->total_addresses > 0);
1080 net->total_addresses--;
1081 GNUNET_STATISTICS_update (s->env->stats,
1082 net->stat_total,
1083 -1,
1084 GNUNET_NO);
1085 if (GNUNET_YES == address->active)
1086 {
1087 /* Address was active, remove from network and update quotas */
1088 update_active_address (s,
1089 address,
1090 &address->peer);
1091 distribute_bandwidth_in_network (s, net);
1092 }
1093 GNUNET_free (aw);
1094 address->solver_information = NULL;
1095 LOG (GNUNET_ERROR_TYPE_DEBUG,
1096 "After deleting address now total %u and active %u addresses in network `%s'\n",
1097 net->total_addresses,
1098 net->active_addresses,
1099 net->desc);
1100}
1101
1102
1103/**
1104 * Function invoked when the plugin is loaded.
1105 *
1106 * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1107 * modified to return the API functions (ugh).
1108 * @return the `struct GAS_PROPORTIONAL_Handle` to pass as a closure
1109 */
1110void *
1111libgnunet_plugin_ats_proportional_init (void *cls)
1112{
1113 static struct GNUNET_ATS_SolverFunctions sf;
1114 struct GNUNET_ATS_PluginEnvironment *env = cls;
1115 struct GAS_PROPORTIONAL_Handle *s;
1116 struct Network *cur;
1117 float f_tmp;
1118 unsigned int c;
1119
1120 s = GNUNET_new (struct GAS_PROPORTIONAL_Handle);
1121 s->env = env;
1122 sf.cls = s;
1123 sf.s_add = &GAS_proportional_address_add;
1124 sf.s_address_update_property = &GAS_proportional_address_property_changed;
1125 sf.s_get = &GAS_proportional_start_get_address;
1126 sf.s_get_stop = &GAS_proportional_stop_get_address;
1127 sf.s_pref = &GAS_proportional_change_preference;
1128 sf.s_feedback = &GAS_proportional_feedback;
1129 sf.s_del = &GAS_proportional_address_delete;
1130 sf.s_bulk_start = &GAS_proportional_bulk_start;
1131 sf.s_bulk_stop = &GAS_proportional_bulk_stop;
1132 s->stability_factor = PROP_STABILITY_FACTOR;
1133 if (GNUNET_SYSERR !=
1134 GNUNET_CONFIGURATION_get_value_float (env->cfg,
1135 "ats",
1136 "PROP_STABILITY_FACTOR",
1137 &f_tmp))
1138 {
1139 if ((f_tmp < 1.0) || (f_tmp > 2.0))
1140 {
1141 LOG (GNUNET_ERROR_TYPE_ERROR,
1142 _ ("Invalid %s configuration %f \n"),
1143 "PROP_STABILITY_FACTOR",
1144 f_tmp);
1145 }
1146 else
1147 {
1148 s->stability_factor = f_tmp;
1149 LOG (GNUNET_ERROR_TYPE_INFO,
1150 "Using %s of %.3f\n",
1151 "PROP_STABILITY_FACTOR",
1152 f_tmp);
1153 }
1154 }
1155 s->prop_factor = PROPORTIONALITY_FACTOR;
1156 if (GNUNET_SYSERR !=
1157 GNUNET_CONFIGURATION_get_value_float (env->cfg,
1158 "ats",
1159 "PROP_PROPORTIONALITY_FACTOR",
1160 &f_tmp))
1161 {
1162 if (f_tmp < 1.0)
1163 {
1164 LOG (GNUNET_ERROR_TYPE_ERROR,
1165 _ ("Invalid %s configuration %f\n"),
1166 "PROP_PROPORTIONALITY_FACTOR",
1167 f_tmp);
1168 }
1169 else
1170 {
1171 s->prop_factor = f_tmp;
1172 LOG (GNUNET_ERROR_TYPE_INFO,
1173 "Using %s of %.3f\n",
1174 "PROP_PROPORTIONALITY_FACTOR",
1175 f_tmp);
1176 }
1177 }
1178
1179 s->network_entries = GNUNET_malloc (env->network_count
1180 * sizeof(struct Network));
1181 for (c = 0; c < env->network_count; c++)
1182 {
1183 cur = &s->network_entries[c];
1184 cur->type = c;
1185 cur->total_quota_in = env->in_quota[c];
1186 cur->total_quota_out = env->out_quota[c];
1187 cur->desc = GNUNET_NT_to_string (c);
1188 GNUNET_asprintf (&cur->stat_total,
1189 "# ATS addresses %s total",
1190 cur->desc);
1191 GNUNET_asprintf (&cur->stat_active,
1192 "# ATS active addresses %s total",
1193 cur->desc);
1194 LOG (GNUNET_ERROR_TYPE_INFO,
1195 "Added network %u `%s' (%llu/%llu)\n",
1196 c,
1197 cur->desc,
1198 cur->total_quota_in,
1199 cur->total_quota_out);
1200 }
1201 return &sf;
1202}
1203
1204
1205/**
1206 * Function used to unload the plugin.
1207 *
1208 * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1209 */
1210void *
1211libgnunet_plugin_ats_proportional_done (void *cls)
1212{
1213 struct GNUNET_ATS_SolverFunctions *sf = cls;
1214 struct GAS_PROPORTIONAL_Handle *s = sf->cls;
1215 struct AddressWrapper *cur;
1216 struct AddressWrapper *next;
1217 unsigned int c;
1218
1219 for (c = 0; c < s->env->network_count; c++)
1220 {
1221 GNUNET_break (0 == s->network_entries[c].total_addresses);
1222 GNUNET_break (0 == s->network_entries[c].active_addresses);
1223 next = s->network_entries[c].head;
1224 while (NULL != (cur = next))
1225 {
1226 next = cur->next;
1227 GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
1228 s->network_entries[c].tail,
1229 cur);
1230 GNUNET_free (cur->addr->solver_information);
1231 GNUNET_free (cur);
1232 }
1233 GNUNET_free (s->network_entries[c].stat_total);
1234 GNUNET_free (s->network_entries[c].stat_active);
1235 }
1236 GNUNET_break (0 == s->active_addresses);
1237 GNUNET_free (s->network_entries);
1238 GNUNET_free (s);
1239 return NULL;
1240}
1241
1242
1243/* end of plugin_ats_proportional.c */