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