Browse Source

[peerdist] Add block download multiplexer

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
d2b2a0adae
3 changed files with 461 additions and 0 deletions
  1. 1
    0
      src/include/ipxe/errfile.h
  2. 73
    0
      src/include/ipxe/peermux.h
  3. 387
    0
      src/net/peermux.c

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -254,6 +254,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
254 254
 #define ERRFILE_httpdigest		( ERRFILE_NET | 0x00440000 )
255 255
 #define ERRFILE_peerdisc		( ERRFILE_NET | 0x00450000 )
256 256
 #define ERRFILE_peerblk			( ERRFILE_NET | 0x00460000 )
257
+#define ERRFILE_peermux			( ERRFILE_NET | 0x00470000 )
257 258
 
258 259
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
259 260
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 73
- 0
src/include/ipxe/peermux.h View File

@@ -0,0 +1,73 @@
1
+#ifndef _IPXE_PEERMUX_H
2
+#define _IPXE_PEERMUX_H
3
+
4
+/** @file
5
+ *
6
+ * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/list.h>
14
+#include <ipxe/refcnt.h>
15
+#include <ipxe/interface.h>
16
+#include <ipxe/process.h>
17
+#include <ipxe/uri.h>
18
+#include <ipxe/xferbuf.h>
19
+#include <ipxe/pccrc.h>
20
+
21
+/** Maximum number of concurrent block downloads */
22
+#define PEERMUX_MAX_BLOCKS 32
23
+
24
+/** PeerDist download content information cache */
25
+struct peerdist_info_cache {
26
+	/** Content information */
27
+	struct peerdist_info info;
28
+	/** Content information segment */
29
+	struct peerdist_info_segment segment;
30
+	/** Content information block */
31
+	struct peerdist_info_block block;
32
+};
33
+
34
+/** A PeerDist multiplexed block download */
35
+struct peerdist_multiplexed_block {
36
+	/** PeerDist download multiplexer */
37
+	struct peerdist_multiplexer *peermux;
38
+	/** List of multiplexed blocks */
39
+	struct list_head list;
40
+	/** Data transfer interface */
41
+	struct interface xfer;
42
+};
43
+
44
+/** A PeerDist download multiplexer */
45
+struct peerdist_multiplexer {
46
+	/** Reference count */
47
+	struct refcnt refcnt;
48
+	/** Data transfer interface */
49
+	struct interface xfer;
50
+	/** Content information interface */
51
+	struct interface info;
52
+	/** Original URI */
53
+	struct uri *uri;
54
+
55
+	/** Content information data transfer buffer */
56
+	struct xfer_buffer buffer;
57
+	/** Content information cache */
58
+	struct peerdist_info_cache cache;
59
+
60
+	/** Block download initiation process */
61
+	struct process process;
62
+	/** List of busy block downloads */
63
+	struct list_head busy;
64
+	/** List of idle block downloads */
65
+	struct list_head idle;
66
+	/** Block downloads */
67
+	struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS];
68
+};
69
+
70
+extern int peermux_filter ( struct interface *xfer, struct interface *info,
71
+			    struct uri *uri );
72
+
73
+#endif /* _IPXE_PEERMUX_H */

+ 387
- 0
src/net/peermux.c View File

@@ -0,0 +1,387 @@
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
+}

Loading…
Cancel
Save