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