more dead and duplicate code elimination
[oweals/gnunet.git] / src / ats / gnunet-service-ats_plugins.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2014 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  option) any later version.
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  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file ats/gnunet-service-ats_plugins.c
23  * @brief ats service plugin management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_plugin.h"
29 #include "gnunet-service-ats_connectivity.h"
30 #include "gnunet-service-ats_performance.h"
31 #include "gnunet-service-ats_preferences.h"
32 #include "gnunet-service-ats_plugins.h"
33 #include "gnunet-service-ats_scheduling.h"
34 #include "gnunet-service-ats_normalization.h"
35
36
37 /**
38  * Solver handle.
39  */
40 static struct GNUNET_ATS_SolverFunctions *sf;
41
42 /**
43  * Solver environment.
44  */
45 static struct GNUNET_ATS_PluginEnvironment env;
46
47 /**
48  * Solver plugin name as string
49  */
50 static char *plugin;
51
52
53 /**
54  * The preference changed for a peer, update solver.
55  *
56  * @param peer the peer
57  * @param kind the ATS kind
58  * @param pref_rel the new relative preference value
59  */
60 void
61 GAS_normalized_preference_changed (const struct GNUNET_PeerIdentity *peer,
62                                    enum GNUNET_ATS_PreferenceKind kind,
63                                    double pref_rel)
64 {
65   sf->s_pref (sf->cls,
66               peer,
67               kind,
68               pref_rel);
69 }
70
71
72 /**
73  * The relative value for a property changed
74  *
75  * @param address the peer
76  * @param type the ATS type
77  * @param prop_rel the new relative preference value
78  */
79 void
80 GAS_normalized_property_changed (struct ATS_Address *address,
81                                  uint32_t type,
82                                  double prop_rel)
83 {
84   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
85               "Normalized property %s for peer `%s' changed to %.3f \n",
86               GNUNET_ATS_print_property_type (type),
87               GNUNET_i2s (&address->peer),
88               prop_rel);
89   sf->s_address_update_property (sf->cls,
90                                  address,
91                                  type,
92                                  0,
93                                  prop_rel);
94 }
95
96
97 /**
98  * Solver information callback
99  *
100  * @param cls the closure
101  * @param op the operation
102  * @param status operation status
103  * @param add additional information
104  */
105 static void
106 solver_info_cb (void *cls,
107                 enum GAS_Solver_Operation op,
108                 enum GAS_Solver_Status status,
109                 enum GAS_Solver_Additional_Information add)
110 {
111   const char *add_info;
112
113   switch (add) {
114     case GAS_INFO_NONE:
115       add_info = "GAS_INFO_NONE";
116       break;
117     case GAS_INFO_FULL:
118       add_info = "GAS_INFO_MLP_FULL";
119       break;
120     case GAS_INFO_UPDATED:
121       add_info = "GAS_INFO_MLP_UPDATED";
122       break;
123     case GAS_INFO_PROP_ALL:
124       add_info = "GAS_INFO_PROP_ALL";
125       break;
126     case GAS_INFO_PROP_SINGLE:
127       add_info = "GAS_INFO_PROP_SINGLE";
128       break;
129     default:
130       add_info = "INVALID";
131       break;
132   }
133   switch (op)
134   {
135   case GAS_OP_SOLVE_START:
136     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137                 "Solver notifies `%s' with result `%s' `%s'\n",
138                 "GAS_OP_SOLVE_START",
139                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL",
140                 add_info);
141     return;
142   case GAS_OP_SOLVE_STOP:
143     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
144                 "Solver notifies `%s' with result `%s'\n",
145                 "GAS_OP_SOLVE_STOP",
146                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL",
147                 add_info);
148     return;
149   case GAS_OP_SOLVE_SETUP_START:
150     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151                 "Solver notifies `%s' with result `%s'\n",
152                 "GAS_OP_SOLVE_SETUP_START",
153                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
154     return;
155   case GAS_OP_SOLVE_SETUP_STOP:
156     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
157                 "Solver notifies `%s' with result `%s'\n",
158                 "GAS_OP_SOLVE_SETUP_STOP",
159                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
160     return;
161   case GAS_OP_SOLVE_MLP_LP_START:
162     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163                 "Solver notifies `%s' with result `%s'\n",
164                 "GAS_OP_SOLVE_LP_START",
165                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
166     return;
167   case GAS_OP_SOLVE_MLP_LP_STOP:
168     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169                 "Solver notifies `%s' with result `%s'\n",
170                 "GAS_OP_SOLVE_LP_STOP",
171                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
172     return;
173   case GAS_OP_SOLVE_MLP_MLP_START:
174     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
175                 "Solver notifies `%s' with result `%s'\n",
176                 "GAS_OP_SOLVE_MLP_START",
177                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
178     return;
179   case GAS_OP_SOLVE_MLP_MLP_STOP:
180     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
181                "Solver notifies `%s' with result `%s'\n",
182                "GAS_OP_SOLVE_MLP_STOP",
183                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
184     return;
185   case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START:
186     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
187                "Solver notifies `%s' with result `%s'\n",
188                "GAS_OP_SOLVE_UPDATE_NOTIFICATION_START",
189                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
190     return;
191   case GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP:
192     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
193                "Solver notifies `%s' with result `%s'\n",
194                "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP",
195                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
196     return;
197   default:
198     break;
199   }
200 }
201
202
203 /**
204  * Callback for solver to notify about assignment changes
205  *
206  * @param cls NULL
207  * @param address the address with changes
208  */
209 static void
210 bandwidth_changed_cb (void *cls,
211                       struct ATS_Address *address)
212 {
213   uint32_t diff_out;
214   uint32_t diff_in;
215
216   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217               "Bandwidth assignment changed for peer %s \n",
218               GNUNET_i2s (&address->peer));
219
220   /* Notify performance clients about changes to address */
221   GAS_performance_notify_all_clients (&address->peer,
222                                       address->plugin,
223                                       address->addr,
224                                       address->addr_len,
225                                       address->active,
226                                       address->atsi,
227                                       address->atsi_count,
228                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
229                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
230
231   if ( (0 == address->assigned_bw_in) &&
232        (0 == address->assigned_bw_out) )
233   {
234     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
235                "Telling transport to disconnect peer `%s'\n",
236                 GNUNET_i2s (&address->peer));
237
238     /* Notify scheduling clients about suggestion */
239     GAS_scheduling_transmit_address_suggestion (&address->peer,
240                                                 address->session_id,
241                                                 GNUNET_BANDWIDTH_ZERO,
242                                                 GNUNET_BANDWIDTH_ZERO);
243     return;
244   }
245
246   /* Do bandwidth stability check */
247   diff_out = abs (address->assigned_bw_out - address->last_notified_bw_out);
248   diff_in = abs (address->assigned_bw_in - address->last_notified_bw_in);
249
250   if ( (diff_out < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) &&
251        (diff_in < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) )
252     return;
253
254   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
255              "Sending bandwidth update for peer `%s': %u/%u\n",
256              GNUNET_i2s (&address->peer),
257              address->assigned_bw_out,
258              address->assigned_bw_out);
259
260   /* *Notify scheduling clients about suggestion */
261   GAS_scheduling_transmit_address_suggestion (&address->peer,
262                                               address->session_id,
263                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
264                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
265
266   address->last_notified_bw_out = address->assigned_bw_out;
267   address->last_notified_bw_in = address->assigned_bw_in;
268 }
269
270
271 /**
272  * Convert quota from text to numeric value.
273  *
274  * @param quota_str the value found in the configuration
275  * @param direction direction of the quota
276  * @param network network the quota applies to
277  * @return numeric quota value to use
278  */
279 static unsigned long long
280 parse_quota (const char *quota_str,
281              const char *direction,
282              enum GNUNET_ATS_Network_Type network)
283 {
284   int res;
285   unsigned long long ret;
286
287   res = GNUNET_NO;
288   if (0 == strcmp (quota_str, GNUNET_ATS_MaxBandwidthString))
289   {
290     ret = GNUNET_ATS_MaxBandwidth;
291     res = GNUNET_YES;
292   }
293   if ((GNUNET_NO == res) &&
294       (GNUNET_OK ==
295        GNUNET_STRINGS_fancy_size_to_bytes (quota_str,
296                                            &ret)))
297     res = GNUNET_YES;
298   if ((GNUNET_NO == res) &&
299       (1 ==
300        sscanf (quota_str,
301                "%llu",
302                &ret)))
303     res = GNUNET_YES;
304   if (GNUNET_NO == res)
305   {
306     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307                 _("Could not load %s quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
308                 direction,
309                 GNUNET_ATS_print_network_type (network),
310                 quota_str,
311                 GNUNET_ATS_DefaultBandwidth);
312     ret = GNUNET_ATS_DefaultBandwidth;
313   }
314   else
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
317                 _("%s quota configured for network `%s' is %llu\n"),
318                 direction,
319                 GNUNET_ATS_print_network_type (network),
320                 ret);
321   }
322   return ret;
323 }
324
325
326 /**
327  * Load quota value from the configuration @a cfg for the
328  * given network @a type and @a direction.
329  *
330  * @param cfg configuration to parse
331  * @param type network type to parse for
332  * @param direction traffic direction to parse for
333  * @return quota to apply
334  */
335 static unsigned long long
336 load_quota (const struct GNUNET_CONFIGURATION_Handle *cfg,
337             enum GNUNET_ATS_Network_Type type,
338             const char *direction)
339 {
340   char *entry;
341   char *quota_str;
342   unsigned long long ret;
343
344   GNUNET_asprintf (&entry,
345                    "%s_QUOTA_%s",
346                    GNUNET_ATS_print_network_type (type),
347                    direction);
348   if (GNUNET_OK ==
349       GNUNET_CONFIGURATION_get_value_string (cfg,
350                                              "ats",
351                                              entry,
352                                              &quota_str))
353   {
354     ret = parse_quota (quota_str,
355                        direction,
356                        type);
357     GNUNET_free (quota_str);
358   }
359   else
360   {
361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                 _("No %s-quota configured for network `%s', assigning default bandwidth %llu\n"),
363                 direction,
364                 GNUNET_ATS_print_network_type (type),
365                 GNUNET_ATS_DefaultBandwidth);
366     ret = GNUNET_ATS_DefaultBandwidth;
367   }
368   GNUNET_free (entry);
369   return ret;
370 }
371
372
373 /**
374  * Load quotas for networks from configuration
375  *
376  * @param cfg configuration handle
377  * @param out_dest where to write outbound quotas
378  * @param in_dest where to write inbound quotas
379  * @param dest_length length of inbound and outbound arrays
380  * @return number of networks loaded
381  */
382 static unsigned int
383 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg,
384              unsigned long long *out_dest,
385              unsigned long long *in_dest,
386              int dest_length)
387 {
388   unsigned int c;
389
390   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
391   {
392     in_dest[c] = load_quota (cfg,
393                              c,
394                              "out");
395     out_dest[c] = load_quota (cfg,
396                               c,
397                               "in");
398     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
399                 "Loaded quota for network `%s' (in/out): %llu %llu\n",
400                 GNUNET_ATS_print_network_type (c),
401                 in_dest[c],
402                 out_dest[c]);
403   }
404   return c;
405 }
406
407
408 /**
409  * Initialize plugins subsystem.
410  *
411  * @param cfg configuration to use
412  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (failed to load
413  *         solver plugin)
414  */
415 int
416 GAS_plugins_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
417 {
418   char *mode_str;
419
420   /* Figure out configured solution method */
421   if (GNUNET_SYSERR ==
422       GNUNET_CONFIGURATION_get_value_string (cfg,
423                                              "ats",
424                                              "MODE",
425                                              &mode_str))
426   {
427     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
428                 "No resource assignment method configured, using proportional approach\n");
429     mode_str = GNUNET_strdup ("proportional");
430   }
431   env.cls = NULL;
432   env.info_cb = &solver_info_cb;
433   env.bandwidth_changed_cb = &bandwidth_changed_cb;
434   env.get_preferences = &GAS_normalization_get_preferences_by_peer;
435   env.get_property = &GAS_normalization_get_properties;
436   env.cfg = cfg;
437   env.stats = GSA_stats;
438   env.addresses = GSA_addresses;
439   env.network_count = GNUNET_ATS_NetworkTypeCount;
440   load_quotas (cfg,
441                env.out_quota,
442                env.in_quota,
443                GNUNET_ATS_NetworkTypeCount);
444   GNUNET_asprintf (&plugin,
445                    "libgnunet_plugin_ats_%s",
446                    mode_str);
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448               "Initializing solver `%s'\n",
449               mode_str);
450   GNUNET_free (mode_str);
451   if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env)))
452   {
453     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454                 _("Failed to initialize solver `%s'!\n"),
455                 plugin);
456     return GNUNET_SYSERR;
457   }
458   return GNUNET_OK;
459 }
460
461
462 /**
463  * Shutdown address subsystem.
464  */
465 void
466 GAS_plugins_done ()
467 {
468   GNUNET_PLUGIN_unload (plugin,
469                         sf);
470   sf = NULL;
471   GNUNET_free (plugin);
472   plugin = NULL;
473 }
474
475
476 /**
477  * Tell the solver that the given address can now be used
478  * for talking to the respective peer.
479  *
480  * @param new_address the new address
481  * @param addr_net network scope the address is in
482  * @param atsi performance data for the address
483  * @param atsi_count size of the @a atsi array
484  */
485 void
486 GAS_plugin_new_address (struct ATS_Address *new_address,
487                         enum GNUNET_ATS_Network_Type addr_net,
488                         const struct GNUNET_ATS_Information *atsi,
489                         uint32_t atsi_count)
490 {
491   sf->s_add (sf->cls,
492              new_address,
493              addr_net);
494   sf->s_bulk_start (sf->cls);
495   GAS_normalization_normalize_property (new_address,
496                                         atsi,
497                                         atsi_count);
498   sf->s_bulk_stop (sf->cls);
499 }
500
501
502 /**
503  * Tell the solver that the given address is no longer valid
504  * can cannot be used any longer.
505  *
506  * @param address address that was deleted
507  */
508 void
509 GAS_plugin_delete_address (struct ATS_Address *address)
510 {
511   sf->s_del (sf->cls,
512              address,
513              GNUNET_NO);
514 }
515
516
517 /**
518  * Tell the solver that the given client has expressed its
519  * appreciation for the past performance of a given connection.
520  *
521  * @param application client providing the feedback
522  * @param peer peer the feedback is about
523  * @param scope timeframe the feedback applies to
524  * @param kind performance property the feedback relates to
525  * @param score_abs degree of the appreciation
526  */
527 void
528 GAS_plugin_preference_feedback (struct GNUNET_SERVER_Client *application,
529                                 const struct GNUNET_PeerIdentity *peer,
530                                 const struct GNUNET_TIME_Relative scope,
531                                 enum GNUNET_ATS_PreferenceKind kind,
532                                 float score_abs)
533 {
534   sf->s_feedback (sf->cls,
535                   application,
536                   peer,
537                   scope,
538                   kind,
539                   score_abs);
540 }
541
542
543 /**
544  * Stop instant solving, there are many state updates
545  * happening in bulk right now.
546  */
547 void
548 GAS_plugin_solver_lock ()
549 {
550   sf->s_bulk_start (sf->cls);
551 }
552
553
554 /**
555  * Resume instant solving, we are done with the bulk state updates.
556  */
557 void
558 GAS_plugin_solver_unlock ()
559 {
560   sf->s_bulk_start (sf->cls);
561 }
562
563
564 /**
565  * Notify the plugin that a request to connect to
566  * a particular peer was given to us.
567  *
568  * @param pid identity of peer we now care about
569  */
570 void
571 GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid)
572 {
573   const struct ATS_Address *aa;
574
575   aa = sf->s_get (sf->cls, pid);
576   if (NULL == aa)
577   {
578     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579                 "Cannot suggest address for peer `%s'\n",
580                 GNUNET_i2s (pid));
581     return;
582   }
583   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
584              "Suggesting address %p for peer `%s'\n",
585              aa,
586              GNUNET_i2s (pid));
587   GAS_scheduling_transmit_address_suggestion (pid,
588                                               aa->session_id,
589                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
590                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
591 }
592
593
594 /**
595  * Notify the plugin that a request to connect to
596  * a particular peer was dropped.
597  *
598  * @param pid identity of peer we care now less about
599  */
600 void
601 GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid)
602 {
603   sf->s_get_stop (sf->cls,
604                   pid);
605 }
606
607
608 /* end of gnunet-service-ats_plugins.c */