@@ -167,27 +167,27 @@ QuicRecvChunkInitialize(
167
167
_Inout_ QUIC_RECV_CHUNK * Chunk ,
168
168
_In_ uint32_t AllocLength ,
169
169
_Inout_updates_ (AllocLength ) uint8_t * Buffer ,
170
- _In_ BOOLEAN AppOwnedBuffer
170
+ _In_ BOOLEAN AllocatedFromPool
171
171
)
172
172
{
173
173
Chunk -> AllocLength = AllocLength ;
174
174
Chunk -> Buffer = Buffer ;
175
175
Chunk -> ExternalReference = FALSE;
176
- Chunk -> AppOwnedBuffer = AppOwnedBuffer ;
176
+ Chunk -> AllocatedFromPool = AllocatedFromPool ;
177
177
}
178
178
179
179
_IRQL_requires_max_ (DISPATCH_LEVEL )
180
180
void
181
181
QuicRecvChunkFree (
182
- _In_ QUIC_RECV_BUFFER * RecvBuffer ,
183
182
_In_ QUIC_RECV_CHUNK * Chunk
184
183
)
185
184
{
186
- if (Chunk == RecvBuffer -> PreallocatedChunk ) {
187
- return ;
188
- }
189
-
190
- if (Chunk -> AppOwnedBuffer ) {
185
+ //
186
+ // The data buffer of the chunk is allocated in the same allocation
187
+ // as the chunk itself if and only if it is owned by the receive buffer:
188
+ // freeing the chunk will free the data buffer as needed.
189
+ //
190
+ if (Chunk -> AllocatedFromPool ) {
191
191
CxPlatPoolFree (Chunk );
192
192
} else {
193
193
CXPLAT_FREE (Chunk , QUIC_POOL_RECVBUF );
@@ -273,6 +273,7 @@ QuicRecvBufferInitialize(
273
273
{
274
274
CXPLAT_DBG_ASSERT (AllocBufferLength != 0 || RecvMode == QUIC_RECV_BUF_MODE_APP_OWNED );
275
275
CXPLAT_DBG_ASSERT (VirtualBufferLength != 0 || RecvMode == QUIC_RECV_BUF_MODE_APP_OWNED );
276
+ CXPLAT_DBG_ASSERT (PreallocatedChunk == NULL || RecvMode != QUIC_RECV_BUF_MODE_APP_OWNED );
276
277
CXPLAT_DBG_ASSERT ((AllocBufferLength & (AllocBufferLength - 1 )) == 0 ); // Power of 2
277
278
CXPLAT_DBG_ASSERT ((VirtualBufferLength & (VirtualBufferLength - 1 )) == 0 ); // Power of 2
278
279
CXPLAT_DBG_ASSERT (AllocBufferLength <= VirtualBufferLength );
@@ -282,7 +283,6 @@ QuicRecvBufferInitialize(
282
283
RecvBuffer -> ReadPendingLength = 0 ;
283
284
RecvBuffer -> ReadLength = 0 ;
284
285
RecvBuffer -> RecvMode = RecvMode ;
285
- RecvBuffer -> PreallocatedChunk = PreallocatedChunk ;
286
286
RecvBuffer -> RetiredChunk = NULL ;
287
287
QuicRangeInitialize (QUIC_MAX_RANGE_ALLOC_SIZE , & RecvBuffer -> WrittenRanges );
288
288
CxPlatListInitializeHead (& RecvBuffer -> Chunks );
@@ -330,11 +330,11 @@ QuicRecvBufferUninitialize(
330
330
CxPlatListRemoveHead (& RecvBuffer -> Chunks ),
331
331
QUIC_RECV_CHUNK ,
332
332
Link );
333
- QuicRecvChunkFree (RecvBuffer , Chunk );
333
+ QuicRecvChunkFree (Chunk );
334
334
}
335
335
336
336
if (RecvBuffer -> RetiredChunk != NULL ) {
337
- QuicRecvChunkFree (RecvBuffer , RecvBuffer -> RetiredChunk );
337
+ QuicRecvChunkFree (RecvBuffer -> RetiredChunk );
338
338
}
339
339
}
340
340
@@ -454,11 +454,9 @@ QuicRecvBufferResize(
454
454
TargetBufferLength != 0 &&
455
455
(TargetBufferLength & (TargetBufferLength - 1 )) == 0 ); // Power of 2
456
456
CXPLAT_DBG_ASSERT (!CxPlatListIsEmpty (& RecvBuffer -> Chunks )); // Should always have at least one chunk
457
+
457
458
QUIC_RECV_CHUNK * LastChunk =
458
- CXPLAT_CONTAINING_RECORD (
459
- RecvBuffer -> Chunks .Blink ,
460
- QUIC_RECV_CHUNK ,
461
- Link );
459
+ CXPLAT_CONTAINING_RECORD (RecvBuffer -> Chunks .Blink , QUIC_RECV_CHUNK , Link );
462
460
CXPLAT_DBG_ASSERT (TargetBufferLength > LastChunk -> AllocLength ); // Should only be called when buffer needs to grow
463
461
BOOLEAN LastChunkIsFirst = LastChunk -> Link .Blink == & RecvBuffer -> Chunks ;
464
462
@@ -476,95 +474,69 @@ QuicRecvBufferResize(
476
474
QuicRecvChunkInitialize (NewChunk , TargetBufferLength , (uint8_t * )(NewChunk + 1 ), FALSE);
477
475
CxPlatListInsertTail (& RecvBuffer -> Chunks , & NewChunk -> Link );
478
476
479
- if (! LastChunk -> ExternalReference ) {
477
+ if (RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_MULTIPLE && LastChunk -> ExternalReference ) {
480
478
//
481
- // If the last chunk isn't externally referenced, then we can just
482
- // replace it with the new chunk.
479
+ // In Multiple mode, if the last chunk is referenced, simply add the new chunk to the list.
480
+ // The last chunk is still used for reads and writes but drains reduce its capacity until it
481
+ // can be freed.
483
482
//
484
- if (LastChunkIsFirst ) {
485
- //
486
- // If it's the first chunk, then the data may not start from the
487
- // beginning.
488
- //
489
- uint32_t Span = QuicRecvBufferGetSpan (RecvBuffer );
490
- if (Span < LastChunk -> AllocLength ) {
491
- Span = LastChunk -> AllocLength ;
492
- }
493
- uint32_t LengthTillWrap = LastChunk -> AllocLength - RecvBuffer -> ReadStart ;
494
- if (Span <= LengthTillWrap ) {
495
- CxPlatCopyMemory (
496
- NewChunk -> Buffer ,
497
- LastChunk -> Buffer + RecvBuffer -> ReadStart ,
498
- Span );
499
- } else {
500
- CxPlatCopyMemory (
501
- NewChunk -> Buffer ,
502
- LastChunk -> Buffer + RecvBuffer -> ReadStart ,
503
- LengthTillWrap );
504
- CxPlatCopyMemory (
505
- NewChunk -> Buffer + LengthTillWrap ,
506
- LastChunk -> Buffer ,
507
- Span - LengthTillWrap );
508
- }
509
- RecvBuffer -> ReadStart = 0 ;
510
- CXPLAT_DBG_ASSERT (NewChunk -> AllocLength == TargetBufferLength );
511
- RecvBuffer -> Capacity = NewChunk -> AllocLength ;
483
+ return TRUE;
484
+ }
485
+
486
+ //
487
+ // In Single and Circular modes, or in Multiple mode when the last chunk is not referenced,
488
+ // replace the last chunk is with the new one:
489
+ // - copy the data to the new chunk
490
+ // - remove the last chunk from the list
491
+ //
512
492
493
+ if (LastChunkIsFirst ) {
494
+ uint32_t WrittenSpan =
495
+ CXPLAT_MIN (LastChunk -> AllocLength , QuicRecvBufferGetSpan (RecvBuffer ));
496
+ uint32_t LengthBeforeWrap = LastChunk -> AllocLength - RecvBuffer -> ReadStart ;
497
+ if (WrittenSpan <= LengthBeforeWrap ) {
498
+ CxPlatCopyMemory (
499
+ NewChunk -> Buffer ,
500
+ LastChunk -> Buffer + RecvBuffer -> ReadStart ,
501
+ WrittenSpan );
513
502
} else {
514
- //
515
- // If it's not the first chunk, then it always starts from the
516
- // beginning of the buffer.
517
- //
518
503
CxPlatCopyMemory (
519
504
NewChunk -> Buffer ,
505
+ LastChunk -> Buffer + RecvBuffer -> ReadStart ,
506
+ LengthBeforeWrap );
507
+ CxPlatCopyMemory (
508
+ NewChunk -> Buffer + LengthBeforeWrap ,
520
509
LastChunk -> Buffer ,
521
- LastChunk -> AllocLength );
510
+ WrittenSpan - LengthBeforeWrap );
522
511
}
523
-
524
- CxPlatListEntryRemove (& LastChunk -> Link );
525
- QuicRecvChunkFree (RecvBuffer , LastChunk );
526
-
527
- return TRUE;
512
+ RecvBuffer -> ReadStart = 0 ;
513
+ RecvBuffer -> Capacity = NewChunk -> AllocLength ;
514
+ } else {
515
+ //
516
+ // If it isn't the first chunk, it always starts from the beginning of the buffer.
517
+ //
518
+ CxPlatCopyMemory (NewChunk -> Buffer , LastChunk -> Buffer , LastChunk -> AllocLength );
528
519
}
529
520
530
521
//
531
- // If the chunk is already referenced, and if we're in multiple receive
532
- // mode, we can just add the new chunk to the end of the list.
522
+ // The chunk data has been copied, remove the chunk from the list.
533
523
//
534
- if (RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_MULTIPLE ) {
535
- return TRUE;
536
- }
524
+ CxPlatListEntryRemove (& LastChunk -> Link );
537
525
538
- //
539
- // Otherwise, we need to copy the data from the existing chunk
540
- // into the new chunk, and retire the existing chunk until we can free it.
541
- //
526
+ if (LastChunk -> ExternalReference ) {
527
+ //
528
+ // The chunk is referenced, so we need to retire it until we can free it.
529
+ // (only one read can be pending at a time, so there is no retired chunk)
530
+ //
531
+ CXPLAT_DBG_ASSERT (
532
+ RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_SINGLE ||
533
+ RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_CIRCULAR );
534
+ CXPLAT_DBG_ASSERT (RecvBuffer -> RetiredChunk == NULL );
542
535
543
- //
544
- // If it's the first chunk, then it may not start from the beginning.
545
- //
546
- uint32_t Span = QuicRecvBufferGetSpan (RecvBuffer );
547
- uint32_t LengthTillWrap = LastChunk -> AllocLength - RecvBuffer -> ReadStart ;
548
- if (Span <= LengthTillWrap ) {
549
- CxPlatCopyMemory (
550
- NewChunk -> Buffer ,
551
- LastChunk -> Buffer + RecvBuffer -> ReadStart ,
552
- Span );
536
+ RecvBuffer -> RetiredChunk = LastChunk ;
553
537
} else {
554
- CxPlatCopyMemory (
555
- NewChunk -> Buffer ,
556
- LastChunk -> Buffer + RecvBuffer -> ReadStart ,
557
- LengthTillWrap );
558
- CxPlatCopyMemory (
559
- NewChunk -> Buffer + LengthTillWrap ,
560
- LastChunk -> Buffer ,
561
- Span - LengthTillWrap );
538
+ QuicRecvChunkFree (LastChunk );
562
539
}
563
- RecvBuffer -> ReadStart = 0 ;
564
- RecvBuffer -> Capacity = NewChunk -> AllocLength ;
565
- CxPlatListEntryRemove (& LastChunk -> Link );
566
- CXPLAT_DBG_ASSERT (RecvBuffer -> RetiredChunk == NULL );
567
- RecvBuffer -> RetiredChunk = LastChunk ;
568
540
569
541
return TRUE;
570
542
}
@@ -575,55 +547,17 @@ QuicRecvBufferGetTotalAllocLength(
575
547
_In_ QUIC_RECV_BUFFER * RecvBuffer
576
548
)
577
549
{
578
- if (RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_SINGLE ||
579
- RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_CIRCULAR ) {
580
- //
581
- // In single and circular mode, the last chunk is the only chunk being
582
- // written to at any given time, and therefore the only chunk we care
583
- // about in terms of total allocation space.
584
- //
585
- QUIC_RECV_CHUNK * Chunk =
586
- CXPLAT_CONTAINING_RECORD (
587
- RecvBuffer -> Chunks .Blink ,
588
- QUIC_RECV_CHUNK ,
589
- Link );
590
- return Chunk -> AllocLength ;
591
- }
592
-
593
- //
594
- // For multiple mode and app-owned mode, several chunks may be used at any
595
- // point in time, so we need to consider the space allocated for all of them.
596
- // Additionally, the first one is special because it may be already partially
597
- // drained, making it only partially usable.
598
- //
599
- QUIC_RECV_CHUNK * Chunk =
600
- CXPLAT_CONTAINING_RECORD (
601
- RecvBuffer -> Chunks .Flink ,
602
- QUIC_RECV_CHUNK ,
603
- Link );
604
- if (RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_MULTIPLE &&
605
- Chunk -> Link .Flink == & RecvBuffer -> Chunks ) {
606
- //
607
- // In Multiple mode, only one chunk means we don't have an artificial
608
- // "end", and are using the full allocated length of the buffer in a
609
- // circular fashion.
610
- //
611
- return Chunk -> AllocLength ;
612
- }
550
+ CXPLAT_DBG_ASSERT (!CxPlatListIsEmpty (& RecvBuffer -> Chunks ));
613
551
614
552
//
615
- // Otherwise, it is possible part of the first chunk has already been
616
- // drained, so we don't use the allocated length, but the Capacity instead
617
- // when calculating total available space.
553
+ // The first chunk might have a reduced capacity (if more chunks are present and it is being
554
+ // consumed). Other chunks are always allocated at their full alloc size.
618
555
//
619
556
uint32_t AllocLength = RecvBuffer -> Capacity ;
620
- while (Chunk -> Link .Flink != & RecvBuffer -> Chunks ) {
621
- Chunk =
622
- CXPLAT_CONTAINING_RECORD (
623
- Chunk -> Link .Flink ,
624
- QUIC_RECV_CHUNK ,
625
- Link );
626
- CXPLAT_DBG_ASSERT ((uint64_t )AllocLength + (uint64_t )Chunk -> AllocLength < UINT32_MAX );
557
+ for (CXPLAT_LIST_ENTRY * Link = RecvBuffer -> Chunks .Flink -> Flink ; // Skip the first chunk
558
+ Link != & RecvBuffer -> Chunks ;
559
+ Link = Link -> Flink ) {
560
+ QUIC_RECV_CHUNK * Chunk = CXPLAT_CONTAINING_RECORD (Link , QUIC_RECV_CHUNK , Link );
627
561
AllocLength += Chunk -> AllocLength ;
628
562
}
629
563
return AllocLength ;
@@ -702,7 +636,7 @@ QuicRecvBufferWrite(
702
636
//
703
637
// Check if the write buffer has already been completely written before.
704
638
//
705
- uint64_t AbsoluteLength = WriteOffset + WriteLength ;
639
+ const uint64_t AbsoluteLength = WriteOffset + WriteLength ;
706
640
if (AbsoluteLength <= RecvBuffer -> BaseOffset ) {
707
641
* WriteLimit = 0 ;
708
642
return QUIC_STATUS_SUCCESS ;
@@ -744,15 +678,13 @@ QuicRecvBufferWrite(
744
678
uint32_t AllocLength = QuicRecvBufferGetTotalAllocLength (RecvBuffer );
745
679
if (AbsoluteLength > RecvBuffer -> BaseOffset + AllocLength ) {
746
680
//
747
- // If we don 't currently have enough room then we will want to resize
748
- // the last chunk to be big enough to hold everything. We do this by
749
- // repeatedly doubling its size until it is large enough .
681
+ // There isn 't enough space to write the data.
682
+ // Add a new chunk (or replace the existing one), doubling the size of the largest chunk
683
+ // until there is enough space for the write .
750
684
//
751
- uint32_t NewBufferLength =
752
- CXPLAT_CONTAINING_RECORD (
753
- RecvBuffer -> Chunks .Blink ,
754
- QUIC_RECV_CHUNK ,
755
- Link )-> AllocLength << 1 ;
685
+ QUIC_RECV_CHUNK * LastChunk =
686
+ CXPLAT_CONTAINING_RECORD (RecvBuffer -> Chunks .Blink , QUIC_RECV_CHUNK , Link );
687
+ uint32_t NewBufferLength = LastChunk -> AllocLength << 1 ;
756
688
while (AbsoluteLength > RecvBuffer -> BaseOffset + NewBufferLength ) {
757
689
NewBufferLength <<= 1 ;
758
690
}
@@ -979,7 +911,7 @@ QuicRecvBufferDrainFullChunks(
979
911
ChunkIt = ChunkIt -> Flink ;
980
912
981
913
CxPlatListEntryRemove (& Chunk -> Link );
982
- QuicRecvChunkFree (RecvBuffer , Chunk );
914
+ QuicRecvChunkFree (Chunk );
983
915
}
984
916
985
917
RecvBuffer -> Capacity = NewFirstChunk != NULL ? NewFirstChunk -> AllocLength : 0 ;
@@ -1025,10 +957,10 @@ QuicRecvBufferDrainFirstChunk(
1025
957
// In Single mode, the readable data must always start at the front of the buffer,
1026
958
// move all written data if needed.
1027
959
//
960
+ uint32_t WrittenSpan =
961
+ CXPLAT_MIN (FirstChunk -> AllocLength , QuicRecvBufferGetSpan (RecvBuffer ));
1028
962
CxPlatMoveMemory (
1029
- FirstChunk -> Buffer ,
1030
- FirstChunk -> Buffer + RecvBuffer -> ReadStart ,
1031
- FirstChunk -> AllocLength - RecvBuffer -> ReadStart ); // TODO - Might be able to copy less than the full alloc length
963
+ FirstChunk -> Buffer , FirstChunk -> Buffer + RecvBuffer -> ReadStart , WrittenSpan );
1032
964
RecvBuffer -> ReadStart = 0 ;
1033
965
}
1034
966
}
@@ -1071,7 +1003,7 @@ QuicRecvBufferDrain(
1071
1003
RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_SINGLE ||
1072
1004
RecvBuffer -> RecvMode == QUIC_RECV_BUF_MODE_CIRCULAR );
1073
1005
1074
- QuicRecvChunkFree (RecvBuffer , RecvBuffer -> RetiredChunk );
1006
+ QuicRecvChunkFree (RecvBuffer -> RetiredChunk );
1075
1007
RecvBuffer -> RetiredChunk = NULL ;
1076
1008
}
1077
1009
0 commit comments