Skip to content

Commit 6bc2ef3

Browse files
Fix some DMA bugs:
- Broken check in Tx DMA meant it was always copying packets - Tx DMA would assert fail from trying to free nullptr if an allocation failed during packet copy - Rx DMA would not reset firstDescIdx after seeing an error descriptor, leading to an attempt to receive a 0 length packet about 20% of the time we ran the EMAC memory test
1 parent 0776a75 commit 6bc2ef3

File tree

1 file changed

+43
-29
lines changed

1 file changed

+43
-29
lines changed

connectivity/drivers/emac/include/GenericEthDMA.h

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ namespace mbed {
191191
needToCopy = true;
192192
}
193193

194-
if(!needToCopy && (neededDescs < txDescsOwnedByApplication || txDescsOwnedByApplication > 0)) {
194+
if(!needToCopy && (neededDescs > txDescsOwnedByApplication && txDescsOwnedByApplication > 0)) {
195195
// Packet uses more buffers than we have descriptors, but we can send it immediately if we copy
196196
// it into a single buffer.
197197
needToCopy = true;
@@ -228,7 +228,6 @@ namespace mbed {
228228
if(newBuf == nullptr)
229229
{
230230
// No free memory, drop packet
231-
memory_manager->free(newBuf);
232231
return CompositeEMAC::ErrCode::OUT_OF_MEMORY;
233232
}
234233

@@ -478,10 +477,31 @@ namespace mbed {
478477
return false;
479478
}
480479

480+
private:
481+
482+
/// Helper function: Discard received Rx descriptors from a given start index (inclusive) to stop index (exclusive)
483+
void discardRxDescs(size_t startIdx, size_t stopIdx)
484+
{
485+
for(size_t descToCleanIdx = startIdx; descToCleanIdx != stopIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) {
486+
// Free Rx buffer attached to this desc
487+
memory_manager->free(rxDescStackBufs[descToCleanIdx]);
488+
rxDescStackBufs[descToCleanIdx] = nullptr;
489+
490+
// Allow desc to be rebuilt
491+
++rxDescsOwnedByApplication;
492+
++rxNextIndex;
493+
}
494+
}
495+
496+
public:
497+
481498
net_stack_mem_buf_t * dequeuePacket() override {
482499
// Indices of the first and last descriptors for the packet will be saved here
483500
std::optional<size_t> firstDescIdx, lastDescIdx;
484501

502+
// Packet length is stored here once we check it
503+
size_t pktLen;
504+
485505
// Prevent looping around into descriptors waiting for rebuild by limiting how many
486506
// we can process.
487507
const size_t maxDescsToProcess = RX_NUM_DESCS - rxDescsOwnedByApplication;
@@ -508,39 +528,33 @@ namespace mbed {
508528
// Error or non-first-descriptor before a first descriptor
509529
// (could be caused by incomplete packets/junk in the DMA buffer).
510530
// Ignore, free associated memory, and schedule for rebuild.
511-
memory_manager->free(rxDescStackBufs[descIdx]);
512-
rxDescStackBufs[descIdx] = nullptr;
513-
++rxDescsOwnedByApplication;
514-
++rxNextIndex;
515-
531+
discardRxDescs(descIdx, (descIdx + 1) % RX_NUM_DESCS);
516532
continue;
517533
}
518-
else if(firstDescIdx.has_value() && (isError || isFirst))
534+
else if(firstDescIdx.has_value() && isError)
519535
{
520-
// Already seen a first descriptor, but we have an error descriptor or another first descriptor.
521-
// So, delete the in-progress packet up to this point.
522-
523-
// Clean up the old first descriptor and any descriptors between there and here
524-
const size_t endIdx = isFirst ? descIdx : (descIdx + 1) % RX_NUM_DESCS;
525-
526-
for(size_t descToCleanIdx = *firstDescIdx; descToCleanIdx != endIdx; descToCleanIdx = (descToCleanIdx + 1) % RX_NUM_DESCS) {
527-
memory_manager->free(rxDescStackBufs[descToCleanIdx]);
528-
rxDescStackBufs[descToCleanIdx] = nullptr;
529-
++rxDescsOwnedByApplication;
530-
++rxNextIndex;
531-
}
532-
533-
if(!isError)
534-
{
535-
firstDescIdx = descIdx;
536-
}
536+
// Already seen a first descriptor, but we have an error descriptor.
537+
// So, delete the in-progress packet up to this point.
538+
discardRxDescs(*firstDescIdx, (descIdx + 1) % RX_NUM_DESCS);
539+
firstDescIdx.reset();
540+
continue;
541+
}
542+
else if(firstDescIdx.has_value() && isFirst)
543+
{
544+
// Already seen a first descriptor, but we have another first descriptor.
545+
// Some MACs do this if they run out of Rx descs when halfway through a packet.
546+
// Delete the in-progress packet up to this point and start over from descIdx.
547+
discardRxDescs(*firstDescIdx, descIdx);
548+
firstDescIdx = descIdx;
537549
}
538550
else if(isFirst)
539551
{
552+
// Normal first descriptor.
540553
firstDescIdx = descIdx;
541554
}
542555

543-
if (isLast) {
556+
if(isLast) {
557+
pktLen = getTotalLen(*firstDescIdx, descIdx);
544558
lastDescIdx = descIdx;
545559
}
546560
}
@@ -559,9 +573,9 @@ namespace mbed {
559573

560574
// Set length of first buffer
561575
net_stack_mem_buf_t *const headBuffer = rxDescStackBufs[*firstDescIdx];
562-
size_t lenRemaining = getTotalLen(*firstDescIdx, *lastDescIdx);
563-
memory_manager->set_len(headBuffer, std::min(lenRemaining, rxPoolPayloadSize));
564-
lenRemaining -= std::min(lenRemaining, rxPoolPayloadSize);
576+
577+
memory_manager->set_len(headBuffer, std::min(pktLen, rxPoolPayloadSize));
578+
size_t lenRemaining = pktLen - std::min(pktLen, rxPoolPayloadSize);
565579

566580
// Iterate through the subsequent descriptors in this packet and link the buffers
567581
// Note that this also transfers ownership of subsequent buffers to the first buffer,

0 commit comments

Comments
 (0)