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