Browse Source

[peerdist] Limit number of concurrent raw block downloads

Raw block downloads are expensive if the origin server uses HTTPS,
since each concurrent download will require local TLS resources
(including potentially large received encrypted data buffers).

Raw block downloads may also be prohibitively slow to initiate when
the origin server is using HTTPS and client certificates.  Origin
servers for PeerDist downloads are likely to be running IIS, which has
a bug that breaks session resumption and requires each connection to
go through the full client certificate verification.

Limit the total number of concurrent raw block downloads to ameliorate
these problems.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 4 years ago
parent
commit
d8a1958ba5
2 changed files with 155 additions and 3 deletions
  1. 24
    0
      src/include/ipxe/peerblk.h
  2. 131
    3
      src/net/peerblk.c

+ 24
- 0
src/include/ipxe/peerblk.h View File

111
 	struct peerdisc_client discovery;
111
 	struct peerdisc_client discovery;
112
 	/** Current position in discovered peer list */
112
 	/** Current position in discovered peer list */
113
 	struct peerdisc_peer *peer;
113
 	struct peerdisc_peer *peer;
114
+	/** Block download queue */
115
+	struct peerdist_block_queue *queue;
116
+	/** List of queued block downloads */
117
+	struct list_head queued;
114
 	/** Retry timer */
118
 	/** Retry timer */
115
 	struct retry_timer timer;
119
 	struct retry_timer timer;
116
 	/** Number of full attempt cycles completed */
120
 	/** Number of full attempt cycles completed */
124
 	unsigned long attempted;
128
 	unsigned long attempted;
125
 };
129
 };
126
 
130
 
131
+/** PeerDist block download queue */
132
+struct peerdist_block_queue {
133
+	/** Download opening process */
134
+	struct process process;
135
+	/** List of queued downloads */
136
+	struct list_head list;
137
+
138
+	/** Number of open downloads */
139
+	unsigned int count;
140
+	/** Maximum number of open downloads */
141
+	unsigned int max;
142
+
143
+	/** Open block download
144
+	 *
145
+	 * @v peerblk		PeerDist block download
146
+	 * @ret rc		Return status code
147
+	 */
148
+	int ( * open ) ( struct peerdist_block *peerblk );
149
+};
150
+
127
 /** Retrieval protocol block fetch response (including transport header)
151
 /** Retrieval protocol block fetch response (including transport header)
128
  *
152
  *
129
  * @v digestsize	Digest size
153
  * @v digestsize	Digest size

+ 131
- 3
src/net/peerblk.c View File

48
  */
48
  */
49
 #define PEERBLK_DECRYPT_CHUNKSIZE 2048
49
 #define PEERBLK_DECRYPT_CHUNKSIZE 2048
50
 
50
 
51
+/** PeerDist maximum number of concurrent raw block downloads
52
+ *
53
+ * Raw block downloads are expensive if the origin server uses HTTPS,
54
+ * since each concurrent download will require local TLS resources
55
+ * (including potentially large received encrypted data buffers).
56
+ *
57
+ * Raw block downloads may also be prohibitively slow to initiate when
58
+ * the origin server is using HTTPS and client certificates.  Origin
59
+ * servers for PeerDist downloads are likely to be running IIS, which
60
+ * has a bug that breaks session resumption and requires each
61
+ * connection to go through the full client certificate verification.
62
+ *
63
+ * Limit the total number of concurrent raw block downloads to
64
+ * ameliorate these problems.
65
+ *
66
+ * This is a policy decision.
67
+ */
68
+#define PEERBLK_RAW_MAX 2
69
+
51
 /** PeerDist raw block download attempt initial progress timeout
70
 /** PeerDist raw block download attempt initial progress timeout
52
  *
71
  *
53
  * This is a policy decision.
72
  * This is a policy decision.
107
 static struct profiler peerblk_discovery_timeout_profiler __profiler =
126
 static struct profiler peerblk_discovery_timeout_profiler __profiler =
108
 	{ .name = "peerblk.discovery.timeout" };
127
 	{ .name = "peerblk.discovery.timeout" };
109
 
128
 
129
+static void peerblk_dequeue ( struct peerdist_block *peerblk );
130
+
110
 /**
131
 /**
111
  * Get profiling timestamp
132
  * Get profiling timestamp
112
  *
133
  *
154
 	intf_restart ( &peerblk->raw, rc );
175
 	intf_restart ( &peerblk->raw, rc );
155
 	intf_restart ( &peerblk->retrieval, rc );
176
 	intf_restart ( &peerblk->retrieval, rc );
156
 
177
 
178
+	/* Remove from download queue, if applicable */
179
+	if ( peerblk->queue )
180
+		peerblk_dequeue ( peerblk );
181
+
157
 	/* Empty received data buffer */
182
 	/* Empty received data buffer */
158
 	xferbuf_free ( &peerblk->buffer );
183
 	xferbuf_free ( &peerblk->buffer );
159
 	peerblk->pos = 0;
184
 	peerblk->pos = 0;
439
 	peerblk_done ( peerblk, rc );
464
 	peerblk_done ( peerblk, rc );
440
 }
465
 }
441
 
466
 
467
+/******************************************************************************
468
+ *
469
+ * Block download queue
470
+ *
471
+ ******************************************************************************
472
+ */
473
+
474
+/**
475
+ * PeerDist block download queue process
476
+ *
477
+ * @v queue		Block download queue
478
+ */
479
+static void peerblk_step ( struct peerdist_block_queue *queue ) {
480
+	struct peerdist_block *peerblk;
481
+	int rc;
482
+
483
+	/* Do nothing yet if we have too many open block downloads */
484
+	if ( queue->count >= queue->max )
485
+		return;
486
+
487
+	/* Do nothing unless there are queued block downloads */
488
+	peerblk = list_first_entry ( &queue->list, struct peerdist_block,
489
+				     queued );
490
+	if ( ! peerblk )
491
+		return;
492
+
493
+	/* Reschedule queue process */
494
+	process_add ( &queue->process );
495
+
496
+	/* Remove block from queue */
497
+	list_del ( &peerblk->queued );
498
+	INIT_LIST_HEAD ( &peerblk->queued );
499
+
500
+	/* Attempt download */
501
+	if ( ( rc = queue->open ( peerblk ) ) != 0 ) {
502
+		peerblk_close ( peerblk, rc );
503
+		return;
504
+	}
505
+
506
+	/* Increment open block download count */
507
+	queue->count++;
508
+}
509
+
510
+/**
511
+ * Add block to download queue
512
+ *
513
+ * @v peerblk		PeerDist block download
514
+ * @v queue		Block download queue
515
+ */
516
+static void peerblk_enqueue ( struct peerdist_block *peerblk,
517
+			      struct peerdist_block_queue *queue ) {
518
+
519
+	/* Sanity checks */
520
+	assert ( peerblk->queue == NULL );
521
+	assert ( list_empty ( &peerblk->queued ) );
522
+
523
+	/* Add block to queue */
524
+	peerblk->queue = queue;
525
+	list_add_tail ( &peerblk->queued, &queue->list );
526
+
527
+	/* Schedule queue process */
528
+	process_add ( &queue->process );
529
+}
530
+
531
+/**
532
+ * Remove block from download queue
533
+ *
534
+ * @v peerblk		PeerDist block download
535
+ */
536
+static void peerblk_dequeue ( struct peerdist_block *peerblk ) {
537
+	struct peerdist_block_queue *queue = peerblk->queue;
538
+
539
+	/* Sanity checks */
540
+	assert ( queue != NULL );
541
+
542
+	/* Remove block from queue */
543
+	peerblk->queue = NULL;
544
+	if ( list_empty ( &peerblk->queued ) ) {
545
+
546
+		/* Open download: decrement count and reschedule queue */
547
+		queue->count--;
548
+		process_add ( &queue->process );
549
+
550
+	} else {
551
+
552
+		/* Queued download: remove from queue */
553
+		list_del ( &peerblk->queued );
554
+		INIT_LIST_HEAD ( &peerblk->queued );
555
+	}
556
+}
557
+
558
+/** PeerDist block download queue process descriptor */
559
+static struct process_descriptor peerblk_queue_desc =
560
+	PROC_DESC_ONCE ( struct peerdist_block_queue, process, peerblk_step );
561
+
562
+/** Raw block download queue */
563
+static struct peerdist_block_queue peerblk_raw_queue = {
564
+	.process = PROC_INIT ( peerblk_raw_queue.process, &peerblk_queue_desc ),
565
+	.list = LIST_HEAD_INIT ( peerblk_raw_queue.list ),
566
+	.max = PEERBLK_RAW_MAX,
567
+	.open = peerblk_raw_open,
568
+};
569
+
442
 /******************************************************************************
570
 /******************************************************************************
443
  *
571
  *
444
  * Retrieval protocol block download attempts (using HTTP POST)
572
  * Retrieval protocol block download attempts (using HTTP POST)
1213
 		return;
1341
 		return;
1214
 	}
1342
 	}
1215
 
1343
 
1216
-	/* Attempt raw download */
1217
-	if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 )
1218
-		goto err;
1344
+	/* Add to raw download queue */
1345
+	peerblk_enqueue ( peerblk, &peerblk_raw_queue );
1219
 
1346
 
1220
 	return;
1347
 	return;
1221
 
1348
 
1338
 	process_init_stopped ( &peerblk->process, &peerblk_process_desc,
1465
 	process_init_stopped ( &peerblk->process, &peerblk_process_desc,
1339
 			       &peerblk->refcnt );
1466
 			       &peerblk->refcnt );
1340
 	peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
1467
 	peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
1468
+	INIT_LIST_HEAD ( &peerblk->queued );
1341
 	timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
1469
 	timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
1342
 	DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
1470
 	DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
1343
 		"%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,
1471
 		"%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,

Loading…
Cancel
Save