Skip to content

Commit 5b3dc61

Browse files
updates
1 parent 024b8d5 commit 5b3dc61

File tree

3 files changed

+82
-53
lines changed

3 files changed

+82
-53
lines changed

libudpard/udpard.c

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,25 @@ static void mem_free_payload(const udpard_mem_deleter_t memory, const udpard_byt
9999
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
100100
static void mem_zero(const size_t size, void* const data) { (void)memset(data, 0, size); }
101101

102+
void udpard_fragment_free_all(udpard_fragment_t* const frag, const udpard_mem_resource_t fragment_memory_resource)
103+
{
104+
if (frag != NULL) {
105+
// Descend the tree
106+
for (uint_fast8_t i = 0; i < 2; i++) {
107+
if (frag->index_offset.lr[i] != NULL) {
108+
udpard_fragment_free_all((udpard_fragment_t*)frag->index_offset.lr[i], fragment_memory_resource);
109+
}
110+
}
111+
// Delete this fragment
112+
udpard_fragment_t* const parent = (udpard_fragment_t*)frag->index_offset.up;
113+
parent->index_offset.lr[parent->index_offset.lr[1] == &frag->index_offset] = NULL;
114+
mem_free_payload(frag->payload_deleter, frag->origin);
115+
mem_free(fragment_memory_resource, sizeof(udpard_fragment_t), frag);
116+
// Ascend the tree, tail call
117+
udpard_fragment_free_all(parent, fragment_memory_resource);
118+
}
119+
}
120+
102121
// --------------------------------------------- CRC ---------------------------------------------
103122

104123
#define CRC_INITIAL 0xFFFFFFFFUL
@@ -683,6 +702,15 @@ void udpard_tx_free(const udpard_tx_mem_resources_t memory, udpard_tx_item_t* co
683702

684703
// --------------------------------------------- RX PIPELINE ---------------------------------------------
685704

705+
// The fragment tree is built from frames arriving from all redundant interfaces simultaneously.
706+
// Said frames may have different MTU, so the fragment offsets and sizes may vary significantly.
707+
// The reassembler decides if a newly arrived fragment is needed based on gap detection in the fragment tree.
708+
// An accepted fragment may overlap with neighboring fragments; however, the reassembler guarantees that no fragment is
709+
// fully contained within another fragment; this also implies that there are no fragments sharing the same offset,
710+
// and that fragments ordered by offset are also ordered by their ends.
711+
// The reassembler prefers to keep fewer large fragments over many small fragments, to reduce the overhead of
712+
// managing the fragment tree and the amount of auxiliary memory required for it.
713+
686714
/// All but the transfer metadata: fields that change from frame to frame within the same transfer.
687715
typedef struct
688716
{
@@ -699,56 +727,47 @@ typedef struct
699727
meta_t meta;
700728
} rx_frame_t;
701729

702-
/// This is designed to be convertible to/from udpard_fragment_t, so that the application could be
703-
/// given a linked list of these objects represented as a list of udpard_fragment_t.
704-
typedef struct rx_fragment_t
705-
{
706-
udpard_fragment_t base; ///< Must be the first member; do not move!
707-
udpard_tree_t tree;
708-
} rx_fragment_t;
709-
710730
/// We require that the fragment tree does not contain fully-contained or equal-range fragments.
711731
/// One implication is that no two fragments can have the same offset.
712732
static int32_t rx_cavl_compare_fragment_offset(const void* const user, const udpard_tree_t* const node)
713733
{
714-
return ((*(const size_t*)user) - CAVL2_TO_OWNER(node, rx_fragment_t, tree)->base.offset) ? +1 : -1;
734+
return ((*(const size_t*)user) - ((udpard_fragment_t*)node)->offset) ? +1 : -1;
715735
}
716736

717737
/// Find the first fragment where offset >= left in log time. Returns NULL if no such fragment exists.
718738
/// This is intended for overlap removal and gap detection when deciding if a new fragment is needed.
719-
static rx_fragment_t* rx_fragment_tree_lower_bound(udpard_tree_t* const root, const size_t left)
739+
static udpard_fragment_t* rx_fragment_tree_lower_bound(udpard_tree_t* const root, const size_t left)
720740
{
721-
return CAVL2_TO_OWNER(cavl2_lower_bound(root, &left, &rx_cavl_compare_fragment_offset), rx_fragment_t, tree);
741+
return (udpard_fragment_t*)cavl2_lower_bound(root, &left, &rx_cavl_compare_fragment_offset);
722742
}
723743

724744
/// True if the fragment tree does not have a contiguous payload coverage in [left, right).
725-
/// This function requires that the tree does not have fully-contained fragments; one implication is that
726-
/// no two fragments may have the same offset.
745+
/// This function requires that the tree does not have fully-overlapped fragments; the implications are that
746+
/// no two fragments may have the same offset, and that fragments ordered by offset also order by their ends.
727747
/// The complexity is O(log n + k), where n is the number of fragments in the tree and k is the number of fragments
728-
/// overlapping the specified range, assuming there are no fully-contained fragments.
748+
/// overlapping the specified range, assuming there are no fully-overlapped fragments.
729749
static bool rx_fragment_tree_has_gap_in_range(udpard_tree_t* const root, const size_t left, const size_t right)
730750
{
731751
if (left >= right) {
732752
return false; // empty fragment; no gap by convention
733753
}
734-
rx_fragment_t* frag =
735-
CAVL2_TO_OWNER(cavl2_predecessor(root, &left, &rx_cavl_compare_fragment_offset), rx_fragment_t, tree);
754+
udpard_fragment_t* frag = (udpard_fragment_t*)cavl2_predecessor(root, &left, &rx_cavl_compare_fragment_offset);
736755
if (frag == NULL) {
737756
return true;
738757
}
739-
if ((frag->base.offset + frag->base.view.size) <= left) { // The predecessor ends before the left edge.
758+
if ((frag->offset + frag->view.size) <= left) { // The predecessor ends before the left edge.
740759
return true;
741760
}
742761
size_t covered = left; // This is the O(k) part. The scan starting point search is O(log n).
743-
while ((frag != NULL) && (frag->base.offset < right)) {
744-
if (frag->base.offset > covered) {
762+
while ((frag != NULL) && (frag->offset < right)) {
763+
if (frag->offset > covered) {
745764
return true;
746765
}
747-
covered = larger(covered, frag->base.offset + frag->base.view.size);
766+
covered = larger(covered, frag->offset + frag->view.size);
748767
if (covered >= right) {
749768
return false; // Reached the end of the requested range without gaps.
750769
}
751-
frag = CAVL2_TO_OWNER(cavl2_next_greater(&frag->tree), rx_fragment_t, tree);
770+
frag = (udpard_fragment_t*)cavl2_next_greater(&frag->index_offset);
752771
}
753772
return covered < right;
754773
}
@@ -761,10 +780,10 @@ static bool rx_fragment_is_needed(udpard_tree_t* const root,
761780
const size_t transfer_size,
762781
const size_t extent)
763782
{
764-
const size_t total_size = smaller(transfer_size, extent);
765-
const size_t left = fragment_offset;
766-
const size_t right = smaller(fragment_offset + fragment_size, total_size);
767-
return (left < total_size) && rx_fragment_tree_has_gap_in_range(root, left, right);
783+
const size_t size = smaller(transfer_size, extent);
784+
const size_t left = fragment_offset;
785+
const size_t right = smaller(fragment_offset + fragment_size, size);
786+
return (left < size) && rx_fragment_tree_has_gap_in_range(root, left, right);
768787
}
769788

770789
typedef enum
@@ -799,7 +818,7 @@ static uint64_t rx_transfer_id_forward_distance(const uint64_t from, const uint6
799818

800819
/// Change the head of the transfer-ID window to new_head, shifting the bitset accordingly.
801820
/// The head is always increased, possibly with wrapping around, so going from 2^64-1 to 0 is considered an increment.
802-
static void rx_transfer_id_window_update_head(rx_transfer_id_window_t* const self, const uint64_t new_head)
821+
static void rx_transfer_id_window_slide(rx_transfer_id_window_t* const self, const uint64_t new_head)
803822
{
804823
static const size_t num_words = RX_TRANSFER_ID_WINDOW_BITS / 64U;
805824
const uint64_t shift_distance = rx_transfer_id_forward_distance(self->head, new_head);

libudpard/udpard.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ typedef struct udpard_mem_resource_t
236236
/// at the expense of extra time and memory utilization.
237237
typedef struct udpard_fragment_t
238238
{
239-
struct udpard_fragment_t* next; ///< Next in the fragmented payload buffer chain; NULL in the last entry.
239+
/// The index_offset BST orders fragments by their offset within the full payload buffer.
240+
/// It must be the first member.
241+
/// The linked list links all fragments by their offset in ascending order, for convenience.
242+
udpard_tree_t index_offset;
243+
struct udpard_fragment_t* next;
240244

241245
/// Contains the actual data to be used by the application.
242246
/// The memory pointed to by this fragment shall not be freed by the application.
@@ -246,9 +250,7 @@ typedef struct udpard_fragment_t
246250
/// The application can use this pointer to free the outer buffer after the payload has been consumed.
247251
udpard_bytes_mut_t origin;
248252

249-
/// Zero-based index and byte offset of this fragment view from the beginning of the transfer payload.
250-
size_t offset;
251-
uint32_t index;
253+
size_t offset; ///< Offset of this fragment's payload within the full payload buffer.
252254

253255
/// When the fragment is no longer needed, this deleter shall be used to free the origin buffer.
254256
/// We provide a dedicated deleter per fragment to allow NIC drivers to manage the memory directly,
@@ -257,13 +259,16 @@ typedef struct udpard_fragment_t
257259
udpard_mem_deleter_t payload_deleter;
258260
} udpard_fragment_t;
259261

260-
/// Frees the memory allocated for the payload and its fragment headers using the correct memory resources.
262+
/// Frees the memory allocated for the payload and its fragment headers using the correct memory resources: the memory
263+
/// resource for the fragments is given explicitly, and the payload is freed using the payload_deleter per fragment.
264+
/// All fragments in the tree will be freed and invalidated.
265+
/// The passed fragment can be any fragment inside the tree (not necessarily the root).
266+
///
261267
/// The application can do the same thing manually if it has access to the required context to compute the size,
262268
/// or if the memory resource implementation does not require deallocation size.
263-
/// The head of the fragment list is passed by value so it is not freed. This is in line with the udpard_rx_transfer_t
264-
/// design, where the head is stored by value to reduce indirection in small transfers. We call it Scott's Head.
265-
/// If any of the arguments are NULL, the function has no effect.
266-
void udpard_fragment_free(const udpard_fragment_t head, const udpard_mem_resource_t memory_fragment);
269+
///
270+
/// If any of the arguments are NULL, the function has no effect. The complexity is linear in the number of fragments.
271+
void udpard_fragment_free_all(udpard_fragment_t* const frag, const udpard_mem_resource_t fragment_memory_resource);
267272

268273
// =====================================================================================================================
269274
// ================================================= TX PIPELINE =================================================
@@ -591,7 +596,12 @@ typedef struct udpard_rx_transfer_t
591596
/// the excess payload as it has already been discarded. Cannot be less than payload_size_stored.
592597
size_t payload_size_wire;
593598

594-
udpard_fragment_t payload;
599+
/// The payload is stored in a tree, which is also linked-listed for convenience.
600+
/// Hence we have two pointers to the same payload tree: one points to the leftmost tree node aka the 1st fragment;
601+
/// the other points to the root of the tree.
602+
/// Either can be used with udpard_fragment_free_all() to free the entire payload.
603+
udpard_fragment_t* payload_first;
604+
udpard_fragment_t* payload_root;
595605
} udpard_rx_transfer_t;
596606

597607
/// Emitted when the stack detects the need to send a reception acknowledgment back to the remote node.

tests/src/test_intrusive_rx.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static void test_rx_transfer_id_forward_distance(void)
5151
rx_transfer_id_forward_distance(0x0FEDCBA987654321ULL, 0x123456789ABCDEF0ULL));
5252
}
5353

54-
static void test_rx_transfer_id_window_update_head(void)
54+
static void test_rx_transfer_id_window_slide(void)
5555
{
5656
rx_transfer_id_window_t obj = { 0 };
5757

@@ -61,7 +61,7 @@ static void test_rx_transfer_id_window_update_head(void)
6161
obj.bitset[1] = 0xF000000010000000ULL;
6262
obj.bitset[2] = 0x8000000100000002ULL;
6363
obj.bitset[3] = 0x3000001000000003ULL;
64-
rx_transfer_id_window_update_head(&obj, 100);
64+
rx_transfer_id_window_slide(&obj, 100);
6565
TEST_ASSERT_EQUAL_UINT64(100, obj.head);
6666
TEST_ASSERT_EQUAL_UINT64(0x0000000001000001ULL, obj.bitset[0]);
6767
TEST_ASSERT_EQUAL_UINT64(0xF000000010000000ULL, obj.bitset[1]);
@@ -74,7 +74,7 @@ static void test_rx_transfer_id_window_update_head(void)
7474
obj.bitset[1] = 0xF000000010000000ULL;
7575
obj.bitset[2] = 0x8000000100000002ULL;
7676
obj.bitset[3] = 0x3000001000000003ULL;
77-
rx_transfer_id_window_update_head(&obj, 101);
77+
rx_transfer_id_window_slide(&obj, 101);
7878
TEST_ASSERT_EQUAL_UINT64(101, obj.head);
7979
TEST_ASSERT_EQUAL_UINT64(0x0000000002000002ULL, obj.bitset[0]);
8080
TEST_ASSERT_EQUAL_UINT64(0xE000000020000000ULL, obj.bitset[1]);
@@ -87,7 +87,7 @@ static void test_rx_transfer_id_window_update_head(void)
8787
obj.bitset[1] = 0x0000000000000002ULL;
8888
obj.bitset[2] = 0x0000000000000004ULL;
8989
obj.bitset[3] = 0x0000000000000008ULL;
90-
rx_transfer_id_window_update_head(&obj, 205);
90+
rx_transfer_id_window_slide(&obj, 205);
9191
TEST_ASSERT_EQUAL_UINT64(205, obj.head);
9292
TEST_ASSERT_EQUAL_UINT64(0x0000000000000020ULL, obj.bitset[0]);
9393
TEST_ASSERT_EQUAL_UINT64(0x0000000000000040ULL, obj.bitset[1]);
@@ -100,7 +100,7 @@ static void test_rx_transfer_id_window_update_head(void)
100100
obj.bitset[1] = 0x8000000000000002ULL;
101101
obj.bitset[2] = 0x8000000000000004ULL;
102102
obj.bitset[3] = 0x8000000000000008ULL;
103-
rx_transfer_id_window_update_head(&obj, 363);
103+
rx_transfer_id_window_slide(&obj, 363);
104104
TEST_ASSERT_EQUAL_UINT64(363, obj.head);
105105
TEST_ASSERT_EQUAL_UINT64(0x8000000000000000ULL, obj.bitset[0]);
106106
TEST_ASSERT_EQUAL_UINT64(0x4000000000000000ULL, obj.bitset[1]);
@@ -113,7 +113,7 @@ static void test_rx_transfer_id_window_update_head(void)
113113
obj.bitset[1] = 0xE000000020000000ULL;
114114
obj.bitset[2] = 0x0000000200000005ULL;
115115
obj.bitset[3] = 0x6000002000000007ULL;
116-
rx_transfer_id_window_update_head(&obj, 164);
116+
rx_transfer_id_window_slide(&obj, 164);
117117
TEST_ASSERT_EQUAL_UINT64(164, obj.head);
118118
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
119119
TEST_ASSERT_EQUAL_UINT64(0x0000000002000002ULL, obj.bitset[1]);
@@ -126,7 +126,7 @@ static void test_rx_transfer_id_window_update_head(void)
126126
obj.bitset[1] = 0x0000000000000003ULL;
127127
obj.bitset[2] = 0x0000000000000007ULL;
128128
obj.bitset[3] = 0x000000000000000FULL;
129-
rx_transfer_id_window_update_head(&obj, 565);
129+
rx_transfer_id_window_slide(&obj, 565);
130130
TEST_ASSERT_EQUAL_UINT64(565, obj.head);
131131
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
132132
TEST_ASSERT_EQUAL_UINT64(0x0000000000000002ULL, obj.bitset[1]);
@@ -139,7 +139,7 @@ static void test_rx_transfer_id_window_update_head(void)
139139
obj.bitset[1] = 0x2222222222222222ULL;
140140
obj.bitset[2] = 0x3333333333333333ULL;
141141
obj.bitset[3] = 0x4444444444444444ULL;
142-
rx_transfer_id_window_update_head(&obj, 1128);
142+
rx_transfer_id_window_slide(&obj, 1128);
143143
TEST_ASSERT_EQUAL_UINT64(1128, obj.head);
144144
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
145145
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[1]);
@@ -152,7 +152,7 @@ static void test_rx_transfer_id_window_update_head(void)
152152
obj.bitset[1] = 0xBBBBBBBBBBBBBBBBULL;
153153
obj.bitset[2] = 0xCCCCCCCCCCCCCCCCULL;
154154
obj.bitset[3] = 0xDDDDDDDDDDDDDDDDULL;
155-
rx_transfer_id_window_update_head(&obj, 2192);
155+
rx_transfer_id_window_slide(&obj, 2192);
156156
TEST_ASSERT_EQUAL_UINT64(2192, obj.head);
157157
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
158158
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[1]);
@@ -165,7 +165,7 @@ static void test_rx_transfer_id_window_update_head(void)
165165
obj.bitset[1] = 0xFEDCBA0987654321ULL;
166166
obj.bitset[2] = 0xAAAAAAAAAAAAAAAAULL;
167167
obj.bitset[3] = 0x5555555555555555ULL;
168-
rx_transfer_id_window_update_head(&obj, 5256);
168+
rx_transfer_id_window_slide(&obj, 5256);
169169
TEST_ASSERT_EQUAL_UINT64(5256, obj.head);
170170
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
171171
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[1]);
@@ -178,7 +178,7 @@ static void test_rx_transfer_id_window_update_head(void)
178178
obj.bitset[1] = 0xFFFFFFFFFFFFFFFFULL;
179179
obj.bitset[2] = 0xFFFFFFFFFFFFFFFFULL;
180180
obj.bitset[3] = 0xFFFFFFFFFFFFFFFFULL;
181-
rx_transfer_id_window_update_head(&obj, 10500);
181+
rx_transfer_id_window_slide(&obj, 10500);
182182
TEST_ASSERT_EQUAL_UINT64(10500, obj.head);
183183
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
184184
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[1]);
@@ -191,7 +191,7 @@ static void test_rx_transfer_id_window_update_head(void)
191191
obj.bitset[1] = 0x0000000000000000ULL;
192192
obj.bitset[2] = 0x0000000000000000ULL;
193193
obj.bitset[3] = 0x0000000000000000ULL;
194-
rx_transfer_id_window_update_head(&obj, 10);
194+
rx_transfer_id_window_slide(&obj, 10);
195195
TEST_ASSERT_EQUAL_UINT64(10, obj.head);
196196
TEST_ASSERT_EQUAL_UINT64(0x0000000000000400ULL, obj.bitset[0]);
197197
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[1]);
@@ -204,7 +204,7 @@ static void test_rx_transfer_id_window_update_head(void)
204204
obj.bitset[1] = 0x0000000000000002ULL;
205205
obj.bitset[2] = 0x0000000000000004ULL;
206206
obj.bitset[3] = 0x0000000000000008ULL;
207-
rx_transfer_id_window_update_head(&obj, 0);
207+
rx_transfer_id_window_slide(&obj, 0);
208208
TEST_ASSERT_EQUAL_UINT64(0, obj.head);
209209
TEST_ASSERT_EQUAL_UINT64(0x0000000000000002ULL, obj.bitset[0]);
210210
TEST_ASSERT_EQUAL_UINT64(0x0000000000000004ULL, obj.bitset[1]);
@@ -217,7 +217,7 @@ static void test_rx_transfer_id_window_update_head(void)
217217
obj.bitset[1] = 0x0000000000000000ULL;
218218
obj.bitset[2] = 0x0000000000000000ULL;
219219
obj.bitset[3] = 0x0000000000000000ULL;
220-
rx_transfer_id_window_update_head(&obj, 5);
220+
rx_transfer_id_window_slide(&obj, 5);
221221
TEST_ASSERT_EQUAL_UINT64(5, obj.head);
222222
TEST_ASSERT_EQUAL_UINT64(0xFFFFFFFFFFFFF800ULL, obj.bitset[0]);
223223
TEST_ASSERT_EQUAL_UINT64(0x00000000000007FFULL, obj.bitset[1]);
@@ -230,7 +230,7 @@ static void test_rx_transfer_id_window_update_head(void)
230230
obj.bitset[1] = 0xFFFFFFFF00000000ULL;
231231
obj.bitset[2] = 0xFFFFFFFF00000000ULL;
232232
obj.bitset[3] = 0xFFFFFFFF00000000ULL;
233-
rx_transfer_id_window_update_head(&obj, 1032);
233+
rx_transfer_id_window_slide(&obj, 1032);
234234
TEST_ASSERT_EQUAL_UINT64(1032, obj.head);
235235
TEST_ASSERT_EQUAL_UINT64(0x0000000000000000ULL, obj.bitset[0]);
236236
TEST_ASSERT_EQUAL_UINT64(0x00000000FFFFFFFFULL, obj.bitset[1]);
@@ -243,7 +243,7 @@ static void test_rx_transfer_id_window_update_head(void)
243243
obj.bitset[1] = 0xFFFFFFFFFFFFFFFFULL;
244244
obj.bitset[2] = 0xFFFFFFFFFFFFFFFFULL;
245245
obj.bitset[3] = 0xFFFFFFFFFFFFFFFFULL;
246-
rx_transfer_id_window_update_head(&obj, 7778);
246+
rx_transfer_id_window_slide(&obj, 7778);
247247
TEST_ASSERT_EQUAL_UINT64(7778, obj.head);
248248
TEST_ASSERT_EQUAL_UINT64(0xFFFFFFFFFFFFFFFEULL, obj.bitset[0]);
249249
TEST_ASSERT_EQUAL_UINT64(0xFFFFFFFFFFFFFFFFULL, obj.bitset[1]);
@@ -289,7 +289,7 @@ int main(void)
289289
{
290290
UNITY_BEGIN();
291291
RUN_TEST(test_rx_transfer_id_forward_distance);
292-
RUN_TEST(test_rx_transfer_id_window_update_head);
292+
RUN_TEST(test_rx_transfer_id_window_slide);
293293
RUN_TEST(test_rx_transfer_id_window_manip);
294294
return UNITY_END();
295295
}

0 commit comments

Comments
 (0)