extending ats api to inform about addresses in use
[oweals/gnunet.git] / src / ats / ats_api_peer_change_preference.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_peer_change_preference.c
22  * @brief automatic transport selection API, preference management
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  *
26  * TODO:
27  * - write test case
28  * - extend API to get performance data
29  * - implement simplistic strategy based on say 'lowest latency' or strict ordering
30  * - extend API to get peer preferences, implement proportional bandwidth assignment
31  * - re-implement API against a real ATS service (!)
32  */
33 #include "platform.h"
34 #include "gnunet_ats_service.h"
35 #include "ats_api.h"
36
37 struct GNUNET_ATS_InformationRequestContext
38 {
39
40   /**
41    * Our connection to the service.
42    */
43   struct GNUNET_ATS_SchedulingHandle *h;
44
45   int32_t amount;
46
47   uint64_t preference;
48
49   GNUNET_ATS_PeerConfigurationInfoCallback info;
50
51   void *info_cls;
52   
53   struct GNUNET_PeerIdentity peer;
54   
55   GNUNET_SCHEDULER_TaskIdentifier task;
56
57 };
58
59
60 static void
61 exec_pcp (void *cls,
62           const struct GNUNET_SCHEDULER_TaskContext *tc)
63 {
64   struct GNUNET_ATS_InformationRequestContext *irc = cls;
65   int32_t want_reserv;
66   int32_t got_reserv;
67   struct GNUNET_TIME_Relative rdelay;
68   struct AllocationRecord *ar;
69
70   rdelay = GNUNET_TIME_UNIT_ZERO;
71   want_reserv = irc->amount;
72   ar = GNUNET_CONTAINER_multihashmap_get (irc->h->peers, &irc->peer.hashPubKey);  
73   if (NULL == ar)
74   {
75     /* attempt to change preference on peer that is not connected */
76     /* FIXME: this can happen if the 'service' didn't yet tell us about
77        a new connection, fake it! */
78     irc->info (irc->info_cls,
79                &irc->peer,
80                want_reserv,
81                rdelay);
82     GNUNET_free (irc);
83     return;
84   }
85   if (want_reserv < 0)
86   {
87     got_reserv = want_reserv;
88   }
89   else if (want_reserv > 0)
90   {
91     rdelay =
92       GNUNET_BANDWIDTH_tracker_get_delay (&ar->available_recv_window,
93                                           want_reserv);
94     if (rdelay.rel_value == 0)
95       got_reserv = want_reserv;
96     else
97       got_reserv = 0;         /* all or nothing */
98   }
99   else
100     got_reserv = 0;
101   GNUNET_BANDWIDTH_tracker_consume (&ar->available_recv_window, got_reserv);
102
103   irc->info (irc->info_cls,
104              &irc->peer,
105              got_reserv,
106              rdelay);
107   GNUNET_free (irc);
108 }
109
110
111 /**
112  * Obtain statistics and/or change preferences for the given peer.
113  *
114  * @param h core handle
115  * @param peer identifies the peer
116  * @param amount reserve N bytes for receiving, negative
117  *                amounts can be used to undo a (recent) reservation;
118  * @param preference increase incoming traffic share preference by this amount;
119  *                in the absence of "amount" reservations, we use this
120  *                preference value to assign proportional bandwidth shares
121  *                to all connected peers
122  * @param info function to call with the resulting configuration information
123  * @param info_cls closure for info
124  * @return NULL on error
125  */
126 struct GNUNET_ATS_InformationRequestContext *
127 GNUNET_ATS_peer_change_preference (struct GNUNET_ATS_SchedulingHandle *h,
128                                    const struct GNUNET_PeerIdentity *peer,
129                                     int32_t amount, uint64_t preference,
130                                     GNUNET_ATS_PeerConfigurationInfoCallback
131                                     info, void *info_cls)
132 {
133   struct GNUNET_ATS_InformationRequestContext *irc;
134
135   irc = GNUNET_malloc (sizeof (struct GNUNET_ATS_InformationRequestContext));
136   irc->h = h;
137   irc->peer = *peer;
138   irc->amount = amount;
139   irc->preference = preference;
140   irc->info = info;
141   irc->info_cls = info_cls;
142   irc->task = GNUNET_SCHEDULER_add_now (&exec_pcp, irc);
143   return irc;
144 }
145
146
147 /**
148  * Cancel request for getting information about a peer.
149  * Note that an eventual change in preference, trust or bandwidth
150  * assignment MAY have already been committed at the time,
151  * so cancelling a request is NOT sure to undo the original
152  * request.  The original request may or may not still commit.
153  * The only thing cancellation ensures is that the callback
154  * from the original request will no longer be called.
155  *
156  * @param irc context returned by the original GNUNET_ATS_peer_get_info call
157  */
158 void
159 GNUNET_ATS_peer_change_preference_cancel (struct
160                                            GNUNET_ATS_InformationRequestContext
161                                            *irc)
162 {
163   GNUNET_SCHEDULER_cancel (irc->task);
164   GNUNET_free (irc);
165 }
166
167
168 #if 0
169 /* old CORE API implementation follows for future reference */
170 struct GNUNET_ATS_InformationRequestContext
171 {
172
173   /**
174    * Our connection to the service.
175    */
176   struct GNUNET_ATS_SchedulingHandle *h;
177
178   /**
179    * Link to control message, NULL if CM was sent.
180    */
181   struct ControlMessage *cm;
182
183   /**
184    * Link to peer record.
185    */
186   struct PeerRecord *pr;
187 };
188
189
190 /**
191  * CM was sent, remove link so we don't double-free.
192  *
193  * @param cls the 'struct GNUNET_ATS_InformationRequestContext'
194  * @param success were we successful?
195  */
196 static void
197 change_preference_send_continuation (void *cls, int success)
198 {
199   struct GNUNET_ATS_InformationRequestContext *irc = cls;
200
201   irc->cm = NULL;
202 }
203
204
205 /**
206  * Obtain statistics and/or change preferences for the given peer.
207  *
208  * @param h core handle
209  * @param peer identifies the peer
210  * @param amount reserve N bytes for receiving, negative
211  *                amounts can be used to undo a (recent) reservation;
212  * @param preference increase incoming traffic share preference by this amount;
213  *                in the absence of "amount" reservations, we use this
214  *                preference value to assign proportional bandwidth shares
215  *                to all connected peers
216  * @param info function to call with the resulting configuration information
217  * @param info_cls closure for info
218  * @return NULL on error
219  */
220 struct GNUNET_ATS_InformationRequestContext *
221 GNUNET_ATS_peer_change_preference (struct GNUNET_ATS_SchedulingHandle *h,
222                                     const struct GNUNET_PeerIdentity *peer,
223                                     int32_t amount, uint64_t preference,
224                                     GNUNET_ATS_PeerConfigurationInfoCallback
225                                     info, void *info_cls)
226 {
227   struct GNUNET_ATS_InformationRequestContext *irc;
228   struct PeerRecord *pr;
229   struct RequestInfoMessage *rim;
230   struct ControlMessage *cm;
231
232   pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &peer->hashPubKey);
233   if (NULL == pr)
234   {
235     /* attempt to change preference on peer that is not connected */
236     GNUNET_assert (0);
237     return NULL;
238   }
239   if (pr->pcic != NULL)
240   {
241     /* second change before first one is done */
242     GNUNET_break (0);
243     return NULL;
244   }
245   irc = GNUNET_malloc (sizeof (struct GNUNET_ATS_InformationRequestContext));
246   irc->h = h;
247   irc->pr = pr;
248   cm = GNUNET_malloc (sizeof (struct ControlMessage) +
249                       sizeof (struct RequestInfoMessage));
250   cm->cont = &change_preference_send_continuation;
251   cm->cont_cls = irc;
252   irc->cm = cm;
253   rim = (struct RequestInfoMessage *) &cm[1];
254   rim->header.size = htons (sizeof (struct RequestInfoMessage));
255   rim->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_INFO);
256   rim->rim_id = htonl (pr->rim_id = h->rim_id_gen++);
257   rim->reserved = htonl (0);
258   rim->reserve_inbound = htonl (amount);
259   rim->preference_change = GNUNET_htonll (preference);
260   rim->peer = *peer;
261 #if DEBUG_ATS
262   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263               "Queueing CHANGE PREFERENCE request for peer `%s' with RIM %u\n",
264               GNUNET_i2s (peer), (unsigned int) pr->rim_id);
265 #endif
266   GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
267                                     h->control_pending_tail, cm);
268   pr->pcic = info;
269   pr->pcic_cls = info_cls;
270   pr->pcic_ptr = irc;           /* for free'ing irc */
271   if (NULL != h->client)
272     trigger_next_request (h, GNUNET_NO);
273   return irc;
274 }
275
276
277 /**
278  * Cancel request for getting information about a peer.
279  * Note that an eventual change in preference, trust or bandwidth
280  * assignment MAY have already been committed at the time,
281  * so cancelling a request is NOT sure to undo the original
282  * request.  The original request may or may not still commit.
283  * The only thing cancellation ensures is that the callback
284  * from the original request will no longer be called.
285  *
286  * @param irc context returned by the original GNUNET_ATS_peer_get_info call
287  */
288 void
289 GNUNET_ATS_peer_change_preference_cancel (struct
290                                            GNUNET_ATS_InformationRequestContext
291                                            *irc)
292 {
293   struct GNUNET_ATS_SchedulingHandle *h = irc->h;
294   struct PeerRecord *pr = irc->pr;
295
296   GNUNET_assert (pr->pcic_ptr == irc);
297   if (irc->cm != NULL)
298   {
299     GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
300                                  h->control_pending_tail, irc->cm);
301     GNUNET_free (irc->cm);
302   }
303   pr->pcic = NULL;
304   pr->pcic_cls = NULL;
305   pr->pcic_ptr = NULL;
306   GNUNET_free (irc);
307 }
308 #endif
309
310 /* end of ats_api_peer_change_preference.c */