iPXE
peermux.c
Go to the documentation of this file.
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 <stdio.h>
28 #include <errno.h>
29 #include <ipxe/uri.h>
30 #include <ipxe/xferbuf.h>
31 #include <ipxe/job.h>
32 #include <ipxe/peerblk.h>
33 #include <ipxe/peermux.h>
34 
35 /** @file
36  *
37  * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer
38  *
39  */
40 
41 /**
42  * Free PeerDist download multiplexer
43  *
44  * @v refcnt Reference count
45  */
46 static void peermux_free ( struct refcnt *refcnt ) {
47  struct peerdist_multiplexer *peermux =
49 
50  uri_put ( peermux->uri );
51  xferbuf_free ( &peermux->buffer );
52  free ( peermux );
53 }
54 
55 /**
56  * Close PeerDist download multiplexer
57  *
58  * @v peermux PeerDist download multiplexer
59  * @v rc Reason for close
60  */
61 static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) {
62  unsigned int i;
63 
64  /* Stop block download initiation process */
65  process_del ( &peermux->process );
66 
67  /* Shut down all block downloads */
68  for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ )
69  intf_shutdown ( &peermux->block[i].xfer, rc );
70 
71  /* Shut down all other interfaces (which may be connected to
72  * the same object).
73  */
74  intf_nullify ( &peermux->info ); /* avoid potential loops */
75  intf_shutdown ( &peermux->xfer, rc );
76  intf_shutdown ( &peermux->info, rc );
77 }
78 
79 /**
80  * Report progress of PeerDist download
81  *
82  * @v peermux PeerDist download multiplexer
83  * @v progress Progress report to fill in
84  * @ret ongoing_rc Ongoing job status code (if known)
85  */
86 static int peermux_progress ( struct peerdist_multiplexer *peermux,
87  struct job_progress *progress ) {
88  struct peerdist_statistics *stats = &peermux->stats;
89  unsigned int percentage;
90 
91  /* Construct PeerDist status message */
92  if ( stats->total ) {
93  percentage = ( ( 100 * stats->local ) / stats->total );
94  snprintf ( progress->message, sizeof ( progress->message ),
95  "%3d%% from %d peers", percentage, stats->peers );
96  }
97 
98  return 0;
99 }
100 
101 /**
102  * Receive content information
103  *
104  * @v peermux PeerDist download multiplexer
105  * @v iobuf I/O buffer
106  * @v meta Data transfer metadata
107  * @ret rc Return status code
108  */
109 static int peermux_info_deliver ( struct peerdist_multiplexer *peermux,
110  struct io_buffer *iobuf,
111  struct xfer_metadata *meta ) {
112  int rc;
113 
114  /* Add data to buffer */
115  if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 )
116  goto err;
117 
118  return 0;
119 
120  err:
121  peermux_close ( peermux, rc );
122  return rc;
123 }
124 
125 /**
126  * Close content information interface
127  *
128  * @v peermux PeerDist download multiplexer
129  * @v rc Reason for close
130  */
131 static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){
132  struct peerdist_info *info = &peermux->cache.info;
133  size_t len;
134 
135  /* Terminate download on error */
136  if ( rc != 0 )
137  goto err;
138 
139  /* Successfully closing the content information interface
140  * indicates that the content information has been fully
141  * received, and initiates the actual PeerDist download.
142  */
143 
144  /* Shut down content information interface */
145  intf_shutdown ( &peermux->info, rc );
146 
147  /* Parse content information */
148  if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len,
149  info ) ) != 0 ) {
150  DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n",
151  peermux, strerror ( rc ) );
152  goto err;
153  }
154 
155  /* Notify recipient of total download size */
156  len = ( info->trim.end - info->trim.start );
157  if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) {
158  DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n",
159  peermux, strerror ( rc ) );
160  goto err;
161  }
162  xfer_seek ( &peermux->xfer, 0 );
163 
164  /* Start block download process */
165  process_add ( &peermux->process );
166 
167  return;
168 
169  err:
170  peermux_close ( peermux, rc );
171 }
172 
173 /**
174  * Initiate multiplexed block download
175  *
176  * @v peermux PeerDist download multiplexer
177  */
178 static void peermux_step ( struct peerdist_multiplexer *peermux ) {
179  struct peerdist_info *info = &peermux->cache.info;
180  struct peerdist_info_segment *segment = &peermux->cache.segment;
181  struct peerdist_info_block *block = &peermux->cache.block;
182  struct peerdist_multiplexed_block *peermblk;
183  unsigned int next_segment;
184  unsigned int next_block;
185  int rc;
186 
187  /* Stop initiation process if all block downloads are busy */
188  peermblk = list_first_entry ( &peermux->idle,
190  if ( ! peermblk ) {
192  return;
193  }
194 
195  /* Increment block index */
196  next_block = ( block->index + 1 );
197 
198  /* Move to first/next segment, if applicable */
199  if ( next_block >= segment->blocks ) {
200 
201  /* Reset block index */
202  next_block = 0;
203 
204  /* Calculate segment index */
205  next_segment = ( segment->info ? ( segment->index + 1 ) : 0 );
206 
207  /* If we have finished all segments and have no
208  * remaining block downloads, then we are finished.
209  */
210  if ( next_segment >= info->segments ) {
212  if ( list_empty ( &peermux->busy ) )
213  peermux_close ( peermux, 0 );
214  return;
215  }
216 
217  /* Get content information segment */
218  if ( ( rc = peerdist_info_segment ( info, segment,
219  next_segment ) ) != 0 ) {
220  DBGC ( peermux, "PEERMUX %p could not get segment %d "
221  "information: %s\n", peermux, next_segment,
222  strerror ( rc ) );
223  goto err;
224  }
225  }
226 
227  /* Get content information block */
228  if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){
229  DBGC ( peermux, "PEERMUX %p could not get segment %d block "
230  "%d information: %s\n", peermux, segment->index,
231  next_block, strerror ( rc ) );
232  goto err;
233  }
234 
235  /* Ignore block if it lies entirely outside the trimmed range */
236  if ( block->trim.start == block->trim.end ) {
237  DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n",
238  peermux, segment->index, block->index );
239  return;
240  }
241 
242  /* Start downloading this block */
243  if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri,
244  block ) ) != 0 ) {
245  DBGC ( peermux, "PEERMUX %p could not start download for "
246  "segment %d block %d: %s\n", peermux, segment->index,
247  block->index, strerror ( rc ) );
248  goto err;
249  }
250 
251  /* Move to list of busy block downloads */
252  list_del ( &peermblk->list );
253  list_add_tail ( &peermblk->list, &peermux->busy );
254 
255  return;
256 
257  err:
258  peermux_close ( peermux, rc );
259 }
260 
261 /**
262  * Receive data from multiplexed block download
263  *
264  * @v peermblk PeerDist multiplexed block download
265  * @v iobuf I/O buffer
266  * @v meta Data transfer metadata
267  * @ret rc Return status code
268  */
269 static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk,
270  struct io_buffer *iobuf,
271  struct xfer_metadata *meta ) {
272  struct peerdist_multiplexer *peermux = peermblk->peermux;
273 
274  /* Sanity check: all block downloads must use absolute
275  * positions for all deliveries, since they run concurrently.
276  */
277  assert ( meta->flags & XFER_FL_ABS_OFFSET );
278 
279  /* We can't use a simple passthrough interface descriptor,
280  * since there are multiple block download interfaces.
281  */
282  return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta );
283 }
284 
285 /**
286  * Get multiplexed block download underlying data transfer buffer
287  *
288  * @v peermblk PeerDist multiplexed download block
289  * @ret xferbuf Data transfer buffer, or NULL on error
290  */
291 static struct xfer_buffer *
293  struct peerdist_multiplexer *peermux = peermblk->peermux;
294 
295  /* We can't use a simple passthrough interface descriptor,
296  * since there are multiple block download interfaces.
297  */
298  return xfer_buffer ( &peermux->xfer );
299 }
300 
301 /**
302  * Record peer discovery statistics
303  *
304  * @v peermblk PeerDist multiplexed block download
305  * @v peer Selected peer (or NULL)
306  * @v peers List of available peers
307  */
308 static void peermux_block_stat ( struct peerdist_multiplexed_block *peermblk,
309  struct peerdisc_peer *peer,
310  struct list_head *peers ) {
311  struct peerdist_multiplexer *peermux = peermblk->peermux;
312  struct peerdist_statistics *stats = &peermux->stats;
313  struct peerdisc_peer *tmp;
314  unsigned int count = 0;
315 
316  /* Record maximum number of available peers */
317  list_for_each_entry ( tmp, peers, list )
318  count++;
319  if ( count > stats->peers )
320  stats->peers = count;
321 
322  /* Update block counts */
323  if ( peer )
324  stats->local++;
325  stats->total++;
326  DBGC2 ( peermux, "PEERMUX %p downloaded %d/%d from %d peers\n",
327  peermux, stats->local, stats->total, stats->peers );
328 }
329 
330 /**
331  * Close multiplexed block download
332  *
333  * @v peermblk PeerDist multiplexed block download
334  * @v rc Reason for close
335  */
336 static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk,
337  int rc ) {
338  struct peerdist_multiplexer *peermux = peermblk->peermux;
339 
340  /* Move to list of idle downloads */
341  list_del ( &peermblk->list );
342  list_add_tail ( &peermblk->list, &peermux->idle );
343 
344  /* If any error occurred, terminate the whole multiplexer */
345  if ( rc != 0 ) {
346  peermux_close ( peermux, rc );
347  return;
348  }
349 
350  /* Restart data transfer interface */
351  intf_restart ( &peermblk->xfer, rc );
352 
353  /* Restart block download initiation process */
354  process_add ( &peermux->process );
355 }
356 
357 /** Data transfer interface operations */
362 };
363 
364 /** Data transfer interface descriptor */
368 
369 /** Content information interface operations */
375 };
376 
377 /** Content information interface descriptor */
380  peermux_info_operations, xfer );
381 
382 /** Block download data transfer interface operations */
392 };
393 
394 /** Block download data transfer interface descriptor */
396  INTF_DESC ( struct peerdist_multiplexed_block, xfer,
398 
399 /** Block download initiation process descriptor */
402 
403 /**
404  * Add PeerDist content-encoding filter
405  *
406  * @v xfer Data transfer interface
407  * @v info Content information interface
408  * @v uri Original URI
409  * @ret rc Return status code
410  */
411 int peermux_filter ( struct interface *xfer, struct interface *info,
412  struct uri *uri ) {
413  struct peerdist_multiplexer *peermux;
414  struct peerdist_multiplexed_block *peermblk;
415  unsigned int i;
416 
417  /* Allocate and initialise structure */
418  peermux = zalloc ( sizeof ( *peermux ) );
419  if ( ! peermux )
420  return -ENOMEM;
424  peermux->uri = uri_get ( uri );
425  xferbuf_umalloc_init ( &peermux->buffer,
426  &peermux->cache.info.raw.data );
428  &peermux->refcnt );
431  for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) {
432  peermblk = &peermux->block[i];
433  peermblk->peermux = peermux;
434  list_add_tail ( &peermblk->list, &peermux->idle );
435  intf_init ( &peermblk->xfer, &peermux_block_desc,
436  &peermux->refcnt );
437  }
438 
439  /* Attach to parent interfaces, mortalise self, and return */
442  ref_put ( &peermux->refcnt );
443  return 0;
444 }
A process.
Definition: process.h:17
static struct interface_operation peermux_xfer_operations[]
Data transfer interface operations.
Definition: peermux.c:358
An object interface operation.
Definition: interface.h:17
uint16_t segment
Code segment.
Definition: librm.h:252
static int peermux_block_deliver(struct peerdist_multiplexed_block *peermblk, struct io_buffer *iobuf, struct xfer_metadata *meta)
Receive data from multiplexed block download.
Definition: peermux.c:269
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
void intf_close(struct interface *intf, int rc)
Close an object interface.
Definition: interface.c:244
static void peermux_block_stat(struct peerdist_multiplexed_block *peermblk, struct peerdisc_peer *peer, struct list_head *peers)
Record peer discovery statistics.
Definition: peermux.c:308
void intf_restart(struct interface *intf, int rc)
Shut down and restart an object interface.
Definition: interface.c:337
int xferbuf_deliver(struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta)
Add received data to data transfer buffer.
Definition: xferbuf.c:152
Data transfer metadata.
Definition: xfer.h:22
void intf_shutdown(struct interface *intf, int rc)
Shut down an object interface.
Definition: interface.c:273
u32 info
Definition: ar9003_mac.h:67
A content information segment.
Definition: pccrc.h:347
static void uri_put(struct uri *uri)
Decrement URI reference count.
Definition: uri.h:188
userptr_t data
Data buffer.
Definition: pccrc.h:303
static struct uri * uri_get(struct uri *uri)
Increment URI reference count.
Definition: uri.h:177
#define XFER_FL_ABS_OFFSET
Offset is absolute.
Definition: xfer.h:47
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition: refcnt.h:64
Error codes.
int peermux_filter(struct interface *xfer, struct interface *info, struct uri *uri)
Add PeerDist content-encoding filter.
Definition: peermux.c:411
A data transfer buffer.
Definition: xferbuf.h:19
uint16_t block
Definition: tftp.h:12
#define DBGC(...)
Definition: compiler.h:505
A process descriptor.
Definition: process.h:31
PeerDist statistics.
Definition: peermux.h:45
void intf_plug_plug(struct interface *a, struct interface *b)
Plug two object interfaces together.
Definition: interface.c:102
int peerblk_open(struct interface *xfer, struct uri *uri, struct peerdist_info_block *block)
Open PeerDist block download.
Definition: peerblk.c:1431
void xferbuf_free(struct xfer_buffer *xferbuf)
Free data transfer buffer.
Definition: xferbuf.c:58
Uniform Resource Identifiers.
A PeerDist multiplexed block download.
Definition: peermux.h:35
unsigned int peers
Maximum observed number of peers.
Definition: peermux.h:47
void process_del(struct process *process)
Remove process from process list.
Definition: process.c:79
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static struct interface_operation peermux_block_operations[]
Block download data transfer interface operations.
Definition: peermux.c:383
struct peerdist_info_cache cache
Content information cache.
Definition: peermux.h:68
A doubly-linked list entry (or list head)
Definition: list.h:18
static int peermux_info_deliver(struct peerdist_multiplexer *peermux, struct io_buffer *iobuf, struct xfer_metadata *meta)
Receive content information.
Definition: peermux.c:109
struct peerdist_multiplexer * peermux
PeerDist download multiplexer.
Definition: peermux.h:37
A reference counter.
Definition: refcnt.h:26
size_t len
Size of data.
Definition: xferbuf.h:23
void peerdisc_stat(struct interface *intf, struct peerdisc_peer *peer, struct list_head *peers)
Report peer discovery statistics.
Definition: peerdisc.c:97
#define list_empty(list)
Test whether a list is empty.
Definition: list.h:136
#define list_first_entry(list, type, member)
Get the container of the first entry in a list.
Definition: list.h:333
static struct interface_descriptor peermux_info_desc
Content information interface descriptor.
Definition: peermux.c:378
A content information block.
Definition: pccrc.h:394
A PeerDist download multiplexer.
Definition: peermux.h:55
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
unsigned int total
Number of blocks downloaded in total.
Definition: peermux.h:49
#define ENOMEM
Not enough space.
Definition: errno.h:534
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition: iobuf.h:208
static void peermux_close(struct peerdist_multiplexer *peermux, int rc)
Close PeerDist download multiplexer.
Definition: peermux.c:61
Peer Content Caching and Retrieval (PeerDist) protocol multiplexer.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
static void peermux_free(struct refcnt *refcnt)
Free PeerDist download multiplexer.
Definition: peermux.c:46
An object interface.
Definition: interface.h:109
struct process process
Block download initiation process.
Definition: peermux.h:71
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition: list.h:420
#define list_add_tail(new, head)
Add a new entry to the tail of a list.
Definition: list.h:93
static void peermux_block_close(struct peerdist_multiplexed_block *peermblk, int rc)
Close multiplexed block download.
Definition: peermux.c:336
Peer Content Caching and Retrieval (PeerDist) protocol block downloads.
static int peermux_progress(struct peerdist_multiplexer *peermux, struct job_progress *progress)
Report progress of PeerDist download.
Definition: peermux.c:86
Content information.
Definition: pccrc.h:317
int xfer_seek(struct interface *intf, off_t offset)
Seek to position.
Definition: xfer.c:350
void process_add(struct process *process)
Add process to process list.
Definition: process.c:59
int meta(WINDOW *, bool)
static struct interface_descriptor peermux_block_desc
Block download data transfer interface descriptor.
Definition: peermux.c:395
An object interface descriptor.
Definition: interface.h:40
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:624
struct list_head list
List of multiplexed blocks.
Definition: peermux.h:39
Job progress.
Definition: job.h:15
struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS]
Block downloads.
Definition: peermux.h:77
uint8_t * tmp
Definition: entropy.h:156
#define INTF_OP(op_type, object_type, op_func)
Define an object interface operation.
Definition: interface.h:32
int xfer_deliver(struct interface *intf, struct io_buffer *iobuf, struct xfer_metadata *meta)
Deliver datagram.
Definition: xfer.c:193
struct peerdist_info_block block
Content information block.
Definition: peermux.h:31
struct xfer_buffer * xfer_buffer(struct interface *intf)
Get underlying data transfer buffer.
Definition: xferbuf.c:301
static void process_init_stopped(struct process *process, struct process_descriptor *desc, struct refcnt *refcnt)
Initialise process without adding to process list.
Definition: process.h:145
struct refcnt refcnt
Reference count.
Definition: peermux.h:57
struct interface xfer
Data transfer interface.
Definition: peermux.h:41
struct list_head idle
List of idle block downloads.
Definition: peermux.h:75
Job control interfaces.
char message[32]
Message (optional)
Definition: job.h:32
static struct process_descriptor peermux_process_desc
Block download initiation process descriptor.
Definition: peermux.c:400
#define INIT_LIST_HEAD(list)
Initialise a list head.
Definition: list.h:45
#define INTF_DESC(object_type, intf, operations)
Define an object interface descriptor.
Definition: interface.h:65
struct peerdist_statistics stats
Statistics.
Definition: peermux.h:80
uint32_t len
Length.
Definition: ena.h:14
void intf_nullify(struct interface *intf)
Ignore all further operations on an object interface.
Definition: interface.c:124
#define DBGC2(...)
Definition: compiler.h:522
struct interface xfer
Data transfer interface.
Definition: peermux.h:59
#define PROC_DESC(object_type, process, _step)
Define a process descriptor.
Definition: process.h:82
static struct xfer_buffer * peermux_block_buffer(struct peerdist_multiplexed_block *peermblk)
Get multiplexed block download underlying data transfer buffer.
Definition: peermux.c:292
static void peermux_step(struct peerdist_multiplexer *peermux)
Initiate multiplexed block download.
Definition: peermux.c:178
uint16_t count
Number of entries.
Definition: ena.h:22
static struct interface_descriptor peermux_xfer_desc
Data transfer interface descriptor.
Definition: peermux.c:365
struct peerdist_info_segment segment
Content information segment.
Definition: peermux.h:29
unsigned int local
Number of blocks downloaded from peers.
Definition: peermux.h:51
struct list_head busy
List of busy block downloads.
Definition: peermux.h:73
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:382
A Uniform Resource Identifier.
Definition: uri.h:50
#define INTF_DESC_PASSTHRU(object_type, intf, operations, passthru)
Define an object interface descriptor with pass-through interface.
Definition: interface.h:82
struct xfer_buffer buffer
Content information data transfer buffer.
Definition: peermux.h:66
#define PEERMUX_MAX_BLOCKS
Maximum number of concurrent block downloads.
Definition: peermux.h:22
static void peermux_info_close(struct peerdist_multiplexer *peermux, int rc)
Close content information interface.
Definition: peermux.c:131
struct peerdist_raw raw
Raw content information.
Definition: pccrc.h:319
struct list_head list
List of peers.
Definition: peerdisc.h:73
struct interface info
Content information interface.
Definition: peermux.h:61
static void intf_init(struct interface *intf, struct interface_descriptor *desc, struct refcnt *refcnt)
Initialise an object interface.
Definition: interface.h:173
static struct interface_operation peermux_info_operations[]
Content information interface operations.
Definition: peermux.c:370
struct uri * uri
Original URI.
Definition: peermux.h:63
struct peerdist_info info
Content information.
Definition: peermux.h:27
Data transfer buffer.
#define ref_put(refcnt)
Drop reference to object.
Definition: refcnt.h:106
A persistent I/O buffer.
Definition: iobuf.h:32
A PeerDist discovery peer.
Definition: peerdisc.h:71