4dda50ccee8a916476fda502381ace241156694b
[oweals/gnunet.git] / src / ats / ats_api_performance.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 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  * @file ats/ats_api_performance.c
22  * @brief automatic transport selection and outbound bandwidth determination
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25   */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "ats.h"
29
30
31 /**
32  * Message in linked list we should send to the ATS service.  The
33  * actual binary message follows this struct.
34  */
35 struct PendingMessage
36 {
37
38   /**
39    * Kept in a DLL.
40    */ 
41   struct PendingMessage *next;
42
43   /**
44    * Kept in a DLL.
45    */ 
46   struct PendingMessage *prev;
47
48   /**
49    * Size of the message.
50    */
51   size_t size;
52
53   /**
54    * Is this the 'ATS_START' message?
55    */ 
56   int is_init;
57 };
58
59
60 /**
61  * Linked list of pending reservations.
62  */
63 struct GNUNET_ATS_ReservationContext
64 {
65
66   /**
67    * Kept in a DLL.
68    */ 
69   struct GNUNET_ATS_ReservationContext *next;
70
71   /**
72    * Kept in a DLL.
73    */ 
74   struct GNUNET_ATS_ReservationContext *prev;
75
76   /**
77    * Target peer.
78    */
79   struct GNUNET_PeerIdentity peer;
80                             
81   /**
82    * Desired reservation
83    */
84   int32_t size;
85
86   /**
87    * Function to call on result.
88    */
89   GNUNET_ATS_ReservationCallback info;
90
91   /**
92    * Closure for 'info'
93    */
94   void *info_cls;
95
96   /**
97    * Do we need to undo this reservation if it succeeded?  Set to
98    * GNUNET_YES if a reservation is cancelled.  (at that point, 'info'
99    * is also set to NULL; however, info will ALSO be NULL for the
100    * reservation context that is created to undo the original request,
101    * so 'info' being NULL cannot be used to check if undo is
102    * required).
103    */
104   int undo;
105 };
106
107
108 /**
109  * ATS Handle to obtain and/or modify performance information.
110  */
111 struct GNUNET_ATS_PerformanceHandle
112 {
113  
114   /**
115    * Our configuration.
116    */
117   const struct GNUNET_CONFIGURATION_Handle *cfg;
118
119   /**
120    * Callback to invoke on performance changes.
121    */
122   GNUNET_ATS_PeerInformationCallback infocb;
123   
124   /**
125    * Closure for 'infocb'.
126    */
127   void *infocb_cls;
128
129   /**
130    * Connection to ATS service.
131    */
132   struct GNUNET_CLIENT_Connection *client;
133
134   /**
135    * Head of list of messages for the ATS service.
136    */
137   struct PendingMessage *pending_head;
138
139   /**
140    * Tail of list of messages for the ATS service
141    */
142   struct PendingMessage *pending_tail;
143
144   /**
145    * Head of linked list of pending reservation requests.
146    */
147   struct GNUNET_ATS_ReservationContext *reservation_head;
148
149   /**
150    * Tail of linked list of pending reservation requests.
151    */
152   struct GNUNET_ATS_ReservationContext *reservation_tail;
153
154   /**
155    * Current request for transmission to ATS.
156    */
157   struct GNUNET_CLIENT_TransmitHandle *th;
158
159 };
160
161
162 /**
163  * Re-establish the connection to the ATS service.
164  *
165  * @param sh handle to use to re-connect.
166  */
167 static void
168 reconnect (struct GNUNET_ATS_PerformanceHandle *ph);
169
170
171 /**
172  * Transmit messages from the message queue to the service
173  * (if there are any, and if we are not already trying).
174  *
175  * @param sh handle to use
176  */
177 static void
178 do_transmit (struct GNUNET_ATS_PerformanceHandle *ph);
179
180
181 /**
182  * We can now transmit a message to ATS. Do it.
183  *
184  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
185  * @param size number of bytes we can transmit to ATS
186  * @param buf where to copy the messages
187  * @return number of bytes copied into buf
188  */
189 static size_t
190 transmit_message_to_ats (void *cls,
191                          size_t size,
192                          void *buf)
193 {
194   struct GNUNET_ATS_PerformanceHandle *ph = cls;
195   struct PendingMessage *p;
196   size_t ret;
197   char *cbuf;
198
199   ph->th = NULL;
200   ret = 0;
201   cbuf = buf;
202   while ( (NULL != (p = ph->pending_head)) &&
203           (p->size <= size) )
204   {
205     memcpy (&cbuf[ret], &p[1], p->size);    
206     ret += p->size;
207     GNUNET_CONTAINER_DLL_remove (ph->pending_head,
208                                  ph->pending_tail,
209                                  p);
210     GNUNET_free (p);
211   }
212   do_transmit (ph);
213   return ret;
214 }
215
216
217 /**
218  * Transmit messages from the message queue to the service
219  * (if there are any, and if we are not already trying).
220  *
221  * @param ph handle to use
222  */
223 static void
224 do_transmit (struct GNUNET_ATS_PerformanceHandle *ph)
225 {
226   struct PendingMessage *p;
227
228   if (NULL != ph->th)
229     return;
230   if (NULL == (p = ph->pending_head))
231     return;
232   ph->th = GNUNET_CLIENT_notify_transmit_ready (ph->client,
233                                                 p->size,
234                                                 GNUNET_TIME_UNIT_FOREVER_REL,
235                                                 GNUNET_YES,
236                                                 &transmit_message_to_ats, ph);
237 }
238
239
240 /**
241  * Type of a function to call when we receive a message
242  * from the service.
243  *
244  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
245  * @param msg message received, NULL on timeout or fatal error
246  */
247 static void
248 process_ats_message (void *cls,
249                      const struct GNUNET_MessageHeader *msg)
250 {
251   struct GNUNET_ATS_PerformanceHandle *ph = cls;
252
253   if (NULL == msg) 
254   {
255     GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO);
256     ph->client = NULL;
257     reconnect (ph);
258     return;
259   }
260   switch (ntohs (msg->type))
261   {
262     // FIXME
263   default:
264     GNUNET_break (0);
265     GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO);
266     ph->client = NULL;
267     reconnect (ph);
268     return;
269   }
270   GNUNET_CLIENT_receive (ph->client,
271                          &process_ats_message, ph,
272                          GNUNET_TIME_UNIT_FOREVER_REL);
273 }
274
275
276 /**
277  * Re-establish the connection to the ATS service.
278  *
279  * @param ph handle to use to re-connect.
280  */
281 static void
282 reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
283 {
284   struct PendingMessage *p;
285   struct ClientStartMessage *init;
286
287   GNUNET_assert (NULL == ph->client);
288   ph->client = GNUNET_CLIENT_connect ("ats", ph->cfg);
289   GNUNET_assert (NULL != ph->client);
290   GNUNET_CLIENT_receive (ph->client,
291                          &process_ats_message, ph,
292                          GNUNET_TIME_UNIT_FOREVER_REL);
293   if ( (NULL == (p = ph->pending_head)) ||
294        (GNUNET_YES != p->is_init) )
295   {
296     p = GNUNET_malloc (sizeof (struct PendingMessage) +
297                        sizeof (struct ClientStartMessage));
298     p->size = sizeof (struct ClientStartMessage);
299     p->is_init = GNUNET_YES;
300     init = (struct ClientStartMessage *) &p[1];
301     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
302     init->header.size = htons (sizeof (struct ClientStartMessage));
303     init->start_flag = htonl ((ph->infocb == NULL) 
304                               ? START_FLAG_PERFORMANCE_NO_PIC 
305                               : START_FLAG_PERFORMANCE_WITH_PIC);
306     GNUNET_CONTAINER_DLL_insert (ph->pending_head,
307                                  ph->pending_tail,
308                                  p);
309   }
310   do_transmit (ph);
311 }
312
313
314
315 /**
316  * Get handle to access performance API of the ATS subsystem.
317  *
318  * @param cfg configuration to use
319  * @param infocb function to call on allocation changes, can be NULL
320  * @param infocb_cls closure for infocb
321  * @return ats performance context
322  */
323 struct GNUNET_ATS_PerformanceHandle *
324 GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
325                              GNUNET_ATS_PeerInformationCallback infocb,
326                              void *infocb_cls)
327 {
328   struct GNUNET_ATS_PerformanceHandle *ph;
329
330   ph = GNUNET_malloc (sizeof (struct GNUNET_ATS_PerformanceHandle));
331   ph->cfg = cfg;
332   ph->infocb = infocb;
333   ph->infocb_cls = infocb_cls;
334   reconnect (ph);
335   return ph;
336 }
337
338
339 /**
340  * Client is done using the ATS performance subsystem, release resources.
341  *
342  * @param ph handle
343  */
344 void
345 GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph)
346 {
347   struct PendingMessage *p;
348   struct GNUNET_ATS_ReservationContext *rc;
349   
350   while (NULL != (p = ph->pending_head))
351   {
352     GNUNET_CONTAINER_DLL_remove (ph->pending_head,
353                                  ph->pending_tail,
354                                  p);
355     GNUNET_free (p);
356   }
357   while (NULL != (rc = ph->reservation_head))
358   {
359     GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
360                                  ph->reservation_tail,
361                                  rc);
362     GNUNET_break (NULL == rc->info);
363     GNUNET_free (p);
364   }  
365   GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO);
366   GNUNET_free (ph);
367 }
368
369
370 /**
371  * Reserve inbound bandwidth from the given peer.  ATS will look at
372  * the current amount of traffic we receive from the peer and ensure
373  * that the peer could add 'amount' of data to its stream.
374  *
375  * @param ph performance handle
376  * @param peer identifies the peer
377  * @param amount reserve N bytes for receiving, negative
378  *                amounts can be used to undo a (recent) reservation;
379  * @param info function to call with the resulting reservation information
380  * @param info_cls closure for info
381  * @return NULL on error
382  * @deprecated will be replaced soon
383  */
384 struct GNUNET_ATS_ReservationContext *
385 GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph,
386                               const struct GNUNET_PeerIdentity *peer,
387                               int32_t amount, 
388                               GNUNET_ATS_ReservationCallback info, 
389                               void *info_cls)
390 {
391   struct GNUNET_ATS_ReservationContext *rc;
392   struct PendingMessage *p;
393   struct ReservationRequestMessage *m;
394
395   rc = GNUNET_malloc (sizeof (struct GNUNET_ATS_ReservationContext));
396   rc->size = amount;
397   rc->peer = *peer;
398   rc->info = info;
399   rc->info_cls = info_cls;
400   GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head,
401                                     ph->reservation_tail,
402                                     rc);
403   
404   p = GNUNET_malloc (sizeof (struct PendingMessage) + 
405                      sizeof (struct ReservationRequestMessage));
406   p->size = sizeof (struct ReservationRequestMessage);
407   p->is_init = GNUNET_NO;
408   m = (struct ReservationRequestMessage*) &p[1];
409   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
410   m->header.size = htons (sizeof (struct ReservationRequestMessage));
411   m->amount = htonl (amount);
412   m->peer = *peer;
413   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head,
414                                     ph->pending_tail,
415                                     p);
416   return rc;
417 }
418
419
420 /**
421  * Cancel request for reserving bandwidth.
422  *
423  * @param rc context returned by the original GNUNET_ATS_reserve_bandwidth call
424  */
425 void
426 GNUNET_ATS_reserve_bandwidth_cancel (struct
427                                      GNUNET_ATS_ReservationContext *rc)
428 {
429   rc->info = NULL;
430 }
431
432
433 /**
434  * Change preferences for the given peer. Preference changes are forgotten if peers
435  * disconnect.
436  * 
437  * @param ph performance handle
438  * @param peer identifies the peer
439  * @param ... 0-terminated specification of the desired changes
440  */
441 void
442 GNUNET_ATS_change_preference (struct GNUNET_ATS_PerformanceHandle *ph,
443                               const struct GNUNET_PeerIdentity *peer,
444                               ...)
445 {
446   struct PendingMessage *p;
447   struct ChangePreferenceMessage *m;
448   size_t msize;
449   uint32_t count;
450   struct PreferenceInformation *pi;
451
452   // FIXME: set 'count'
453   p = GNUNET_malloc (sizeof (struct PendingMessage) + 
454                      sizeof (struct ChangePreferenceMessage) + 
455                      count * sizeof (struct PreferenceInformation));
456   p->size = msize;
457   p->is_init = GNUNET_NO;
458   m = (struct ReservationRequestMessage*) &p[1];
459   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
460   m->header.size = htons (msize);
461   m->num_preferences = htonl (count);
462   m->peer = *peer;
463   pi = (struct PreferenceInformation*) &m[1];
464   // FIXME: fill in 'pi'
465
466   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head,
467                                     ph->pending_tail,
468                                     p);
469 }
470
471 /* end of ats_api_performance.c */
472