These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / peermux.c
1 /*
2  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <ipxe/uri.h>
29 #include <ipxe/xferbuf.h>
30 #include <ipxe/peerblk.h>
31 #include <ipxe/peermux.h>
32
33 /** @file
34  *
35  * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer
36  *
37  */
38
39 /**
40  * Free PeerDist download multiplexer
41  *
42  * @v refcnt            Reference count
43  */
44 static void peermux_free ( struct refcnt *refcnt ) {
45         struct peerdist_multiplexer *peermux =
46                 container_of ( refcnt, struct peerdist_multiplexer, refcnt );
47
48         uri_put ( peermux->uri );
49         xferbuf_free ( &peermux->buffer );
50         free ( peermux );
51 }
52
53 /**
54  * Close PeerDist download multiplexer
55  *
56  * @v peermux           PeerDist download multiplexer
57  * @v rc                Reason for close
58  */
59 static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) {
60         unsigned int i;
61
62         /* Stop block download initiation process */
63         process_del ( &peermux->process );
64
65         /* Shut down all block downloads */
66         for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ )
67                 intf_shutdown ( &peermux->block[i].xfer, rc );
68
69         /* Shut down all other interfaces (which may be connected to
70          * the same object).
71          */
72         intf_nullify ( &peermux->info ); /* avoid potential loops */
73         intf_shutdown ( &peermux->xfer, rc );
74         intf_shutdown ( &peermux->info, rc );
75 }
76
77 /**
78  * Receive content information
79  *
80  * @v peermux           PeerDist download multiplexer
81  * @v iobuf             I/O buffer
82  * @v meta              Data transfer metadata
83  * @ret rc              Return status code
84  */
85 static int peermux_info_deliver ( struct peerdist_multiplexer *peermux,
86                                   struct io_buffer *iobuf,
87                                   struct xfer_metadata *meta ) {
88         int rc;
89
90         /* Add data to buffer */
91         if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 )
92                 goto err;
93
94         return 0;
95
96  err:
97         peermux_close ( peermux, rc );
98         return rc;
99 }
100
101 /**
102  * Close content information interface
103  *
104  * @v peermux           PeerDist download multiplexer
105  * @v rc                Reason for close
106  */
107 static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){
108         struct peerdist_info *info = &peermux->cache.info;
109         size_t len;
110
111         /* Terminate download on error */
112         if ( rc != 0 )
113                 goto err;
114
115         /* Successfully closing the content information interface
116          * indicates that the content information has been fully
117          * received, and initiates the actual PeerDist download.
118          */
119
120         /* Shut down content information interface */
121         intf_shutdown ( &peermux->info, rc );
122
123         /* Parse content information */
124         if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len,
125                                     info ) ) != 0 ) {
126                 DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n",
127                        peermux, strerror ( rc ) );
128                 goto err;
129         }
130
131         /* Notify recipient of total download size */
132         len = ( info->trim.end - info->trim.start );
133         if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) {
134                 DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n",
135                        peermux, strerror ( rc ) );
136                 goto err;
137         }
138         xfer_seek ( &peermux->xfer, 0 );
139
140         /* Start block download process */
141         process_add ( &peermux->process );
142
143         return;
144
145  err:
146         peermux_close ( peermux, rc );
147 }
148
149 /**
150  * Initiate multiplexed block download
151  *
152  * @v peermux           PeerDist download multiplexer
153  */
154 static void peermux_step ( struct peerdist_multiplexer *peermux ) {
155         struct peerdist_info *info = &peermux->cache.info;
156         struct peerdist_info_segment *segment = &peermux->cache.segment;
157         struct peerdist_info_block *block = &peermux->cache.block;
158         struct peerdist_multiplexed_block *peermblk;
159         unsigned int next_segment;
160         unsigned int next_block;
161         int rc;
162
163         /* Stop initiation process if all block downloads are busy */
164         peermblk = list_first_entry ( &peermux->idle,
165                                       struct peerdist_multiplexed_block, list );
166         if ( ! peermblk ) {
167                 process_del ( &peermux->process );
168                 return;
169         }
170
171         /* Increment block index */
172         next_block = ( block->index + 1 );
173
174         /* Move to first/next segment, if applicable */
175         if ( next_block >= segment->blocks ) {
176
177                 /* Reset block index */
178                 next_block = 0;
179
180                 /* Calculate segment index */
181                 next_segment = ( segment->info ? ( segment->index + 1 ) : 0 );
182
183                 /* If we have finished all segments and have no
184                  * remaining block downloads, then we are finished.
185                  */
186                 if ( next_segment >= info->segments ) {
187                         process_del ( &peermux->process );
188                         if ( list_empty ( &peermux->busy ) )
189                                 peermux_close ( peermux, 0 );
190                         return;
191                 }
192
193                 /* Get content information segment */
194                 if ( ( rc = peerdist_info_segment ( info, segment,
195                                                     next_segment ) ) != 0 ) {
196                         DBGC ( peermux, "PEERMUX %p could not get segment %d "
197                                "information: %s\n", peermux, next_segment,
198                                strerror ( rc ) );
199                         goto err;
200                 }
201         }
202
203         /* Get content information block */
204         if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){
205                 DBGC ( peermux, "PEERMUX %p could not get segment %d block "
206                        "%d information: %s\n", peermux, segment->index,
207                        next_block, strerror ( rc ) );
208                 goto err;
209         }
210
211         /* Ignore block if it lies entirely outside the trimmed range */
212         if ( block->trim.start == block->trim.end ) {
213                 DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n",
214                        peermux, segment->index, block->index );
215                 return;
216         }
217
218         /* Start downloading this block */
219         if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri,
220                                    block ) ) != 0 ) {
221                 DBGC ( peermux, "PEERMUX %p could not start download for "
222                        "segment %d block %d: %s\n", peermux, segment->index,
223                        block->index, strerror ( rc ) );
224                 goto err;
225         }
226
227         /* Move to list of busy block downloads */
228         list_del ( &peermblk->list );
229         list_add_tail ( &peermblk->list, &peermux->busy );
230
231         return;
232
233  err:
234         peermux_close ( peermux, rc );
235 }
236
237 /**
238  * Receive data from multiplexed block download
239  *
240  * @v peermblk          PeerDist multiplexed block download
241  * @v iobuf             I/O buffer
242  * @v meta              Data transfer metadata
243  * @ret rc              Return status code
244  */
245 static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk,
246                                    struct io_buffer *iobuf,
247                                    struct xfer_metadata *meta ) {
248         struct peerdist_multiplexer *peermux = peermblk->peermux;
249
250         /* Sanity check: all block downloads must use absolute
251          * positions for all deliveries, since they run concurrently.
252          */
253         assert ( meta->flags & XFER_FL_ABS_OFFSET );
254
255         /* We can't use a simple passthrough interface descriptor,
256          * since there are multiple block download interfaces.
257          */
258         return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta );
259 }
260
261 /**
262  * Get multiplexed block download underlying data transfer buffer
263  *
264  * @v peermblk          PeerDist multiplexed download block
265  * @ret xferbuf         Data transfer buffer, or NULL on error
266  */
267 static struct xfer_buffer *
268 peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) {
269         struct peerdist_multiplexer *peermux = peermblk->peermux;
270
271         /* We can't use a simple passthrough interface descriptor,
272          * since there are multiple block download interfaces.
273          */
274         return xfer_buffer ( &peermux->xfer );
275 }
276
277 /**
278  * Close multiplexed block download
279  *
280  * @v peermblk          PeerDist multiplexed block download
281  * @v rc                Reason for close
282  */
283 static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk,
284                                   int rc ) {
285         struct peerdist_multiplexer *peermux = peermblk->peermux;
286
287         /* Move to list of idle downloads */
288         list_del ( &peermblk->list );
289         list_add_tail ( &peermblk->list, &peermux->idle );
290
291         /* If any error occurred, terminate the whole multiplexer */
292         if ( rc != 0 ) {
293                 peermux_close ( peermux, rc );
294                 return;
295         }
296
297         /* Restart data transfer interface */
298         intf_restart ( &peermblk->xfer, rc );
299
300         /* Restart block download initiation process */
301         process_add ( &peermux->process );
302 }
303
304 /** Data transfer interface operations */
305 static struct interface_operation peermux_xfer_operations[] = {
306         INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ),
307 };
308
309 /** Data transfer interface descriptor */
310 static struct interface_descriptor peermux_xfer_desc =
311         INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, xfer,
312                              peermux_xfer_operations, info );
313
314 /** Content information interface operations */
315 static struct interface_operation peermux_info_operations[] = {
316         INTF_OP ( xfer_deliver, struct peerdist_multiplexer *,
317                   peermux_info_deliver ),
318         INTF_OP ( intf_close, struct peerdist_multiplexer *,
319                   peermux_info_close ),
320 };
321
322 /** Content information interface descriptor */
323 static struct interface_descriptor peermux_info_desc =
324         INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, info,
325                              peermux_info_operations, xfer );
326
327 /** Block download data transfer interface operations */
328 static struct interface_operation peermux_block_operations[] = {
329         INTF_OP ( xfer_deliver, struct peerdist_multiplexed_block *,
330                   peermux_block_deliver ),
331         INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *,
332                   peermux_block_buffer ),
333         INTF_OP ( intf_close, struct peerdist_multiplexed_block *,
334                   peermux_block_close ),
335 };
336
337 /** Block download data transfer interface descriptor */
338 static struct interface_descriptor peermux_block_desc =
339         INTF_DESC ( struct peerdist_multiplexed_block, xfer,
340                     peermux_block_operations );
341
342 /** Block download initiation process descriptor */
343 static struct process_descriptor peermux_process_desc =
344         PROC_DESC ( struct peerdist_multiplexer, process, peermux_step );
345
346 /**
347  * Add PeerDist content-encoding filter
348  *
349  * @v xfer              Data transfer interface
350  * @v info              Content information interface
351  * @v uri               Original URI
352  * @ret rc              Return status code
353  */
354 int peermux_filter ( struct interface *xfer, struct interface *info,
355                      struct uri *uri ) {
356         struct peerdist_multiplexer *peermux;
357         struct peerdist_multiplexed_block *peermblk;
358         unsigned int i;
359
360         /* Allocate and initialise structure */
361         peermux = zalloc ( sizeof ( *peermux ) );
362         if ( ! peermux )
363                 return -ENOMEM;
364         ref_init ( &peermux->refcnt, peermux_free );
365         intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt );
366         intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt );
367         peermux->uri = uri_get ( uri );
368         xferbuf_umalloc_init ( &peermux->buffer,
369                                &peermux->cache.info.raw.data );
370         process_init_stopped ( &peermux->process, &peermux_process_desc,
371                                &peermux->refcnt );
372         INIT_LIST_HEAD ( &peermux->busy );
373         INIT_LIST_HEAD ( &peermux->idle );
374         for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) {
375                 peermblk = &peermux->block[i];
376                 peermblk->peermux = peermux;
377                 list_add_tail ( &peermblk->list, &peermux->idle );
378                 intf_init ( &peermblk->xfer, &peermux_block_desc,
379                             &peermux->refcnt );
380         }
381
382         /* Attach to parent interfaces, mortalise self, and return */
383         intf_plug_plug ( &peermux->xfer, xfer );
384         intf_plug_plug ( &peermux->info, info );
385         ref_put ( &peermux->refcnt );
386         return 0;
387 }