Skip to content

Commit e0dc2e7

Browse files
LeStarchdevin-ai-integration[bot]Copilot
authored
Remove FprimeRouter Copy (#4984)
* Remove unnecessary buffer allocation/copy in FprimeRouter for file and unknown packets Instead of allocating a new buffer and copying data for file and unknown packet types, pass the original buffer directly to the receiver. The buffer is not returned to the deframer via dataReturnOut until it comes back on fileBufferReturnIn, at which point an empty context is constructed for the return. This eliminates the need for bufferAllocate and bufferDeallocate ports, as well as the AllocationReason enum and AllocationError event. Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> * Remove dangling fprimeRouter buffer allocation connections from subtopologies Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> * Move dataReturnOut into each switch case instead of using early returns Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> * Fix formatting: remove extra blank line Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> * Update Svc/FprimeRouter/docs/sdd.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address review: empty context on all return paths, clarify buffer return contract, document context discard Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> * Clarify SDD: receivers must return buffers via fileBufferReturnIn Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent f3f6c66 commit e0dc2e7

9 files changed

Lines changed: 39 additions & 138 deletions

File tree

Svc/FprimeRouter/FprimeRouter.cpp

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,56 +42,40 @@ void FprimeRouter ::dataIn_handler(FwIndexType portNum, Fw::Buffer& packetBuffer
4242
} else {
4343
this->log_WARNING_HI_SerializationError(status);
4444
}
45+
// Return ownership of the incoming packetBuffer with an empty context
46+
ComCfg::FrameContext emptyContext;
47+
this->dataReturnOut_out(0, packetBuffer, emptyContext);
4548
break;
4649
}
4750
// Handle a file packet
4851
case Fw::ComPacketType::FW_PACKET_FILE: {
49-
// If the file uplink output port is connected, send the file packet. Otherwise take no action.
52+
// If the file uplink output port is connected, send the file packet directly.
53+
// Ownership is passed to the receiver and will come back on fileBufferReturnIn,
54+
// at which point we return it to the deframer via dataReturnOut.
5055
if (this->isConnected_fileOut_OutputPort(0)) {
51-
// Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut,
52-
// and FprimeRouter can handle the deallocation of the file buffer when it returns on fileBufferReturnIn
53-
Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize());
54-
// Confirm we got a valid buffer before using it
55-
if (packetBufferCopy.isValid()) {
56-
auto copySerializer = packetBufferCopy.getSerializer();
57-
status = copySerializer.serializeFrom(packetBuffer.getData(), packetBuffer.getSize(),
58-
Fw::Serialization::OMIT_LENGTH);
59-
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
60-
// Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done
61-
// with it
62-
this->fileOut_out(0, packetBufferCopy);
63-
} else {
64-
this->log_WARNING_HI_AllocationError(FprimeRouter_AllocationReason::FILE_UPLINK);
65-
}
56+
this->fileOut_out(0, packetBuffer);
57+
} else {
58+
// Port not connected, return the buffer immediately with an empty context
59+
ComCfg::FrameContext emptyContext;
60+
this->dataReturnOut_out(0, packetBuffer, emptyContext);
6661
}
6762
break;
6863
}
6964
default: {
7065
// Packet type is not known to the F Prime protocol. If the unknownDataOut port is
71-
// connected, forward packet and context for further processing
66+
// connected, forward packet and context for further processing.
67+
// Ownership is passed to the receiver and will come back on fileBufferReturnIn,
68+
// at which point we return it to the deframer via dataReturnOut.
7269
if (this->isConnected_unknownDataOut_OutputPort(0)) {
73-
// Copy buffer into a new allocated buffer. This lets us return the original buffer with dataReturnOut,
74-
// and FprimeRouter can handle the deallocation of the unknown buffer when it returns on bufferReturnIn
75-
Fw::Buffer packetBufferCopy = this->bufferAllocate_out(0, packetBuffer.getSize());
76-
// Confirm we got a valid buffer before using it
77-
if (packetBufferCopy.isValid()) {
78-
auto copySerializer = packetBufferCopy.getSerializer();
79-
status = copySerializer.serializeFrom(packetBuffer.getData(), packetBuffer.getSize(),
80-
Fw::Serialization::OMIT_LENGTH);
81-
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
82-
// Send the copied buffer out. It will come back on fileBufferReturnIn once the receiver is done
83-
// with it
84-
this->unknownDataOut_out(0, packetBufferCopy, context);
85-
} else {
86-
this->log_WARNING_HI_AllocationError(FprimeRouter_AllocationReason::USER_BUFFER);
87-
}
70+
this->unknownDataOut_out(0, packetBuffer, context);
71+
} else {
72+
// Port not connected, return the buffer immediately with an empty context
73+
ComCfg::FrameContext emptyContext;
74+
this->dataReturnOut_out(0, packetBuffer, emptyContext);
8875
}
8976
break;
9077
}
9178
}
92-
93-
// Return ownership of the incoming packetBuffer
94-
this->dataReturnOut_out(0, packetBuffer, context);
9579
}
9680

9781
void FprimeRouter ::cmdResponseIn_handler(FwIndexType portNum,
@@ -102,7 +86,9 @@ void FprimeRouter ::cmdResponseIn_handler(FwIndexType portNum,
10286
}
10387

10488
void FprimeRouter ::fileBufferReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
105-
this->bufferDeallocate_out(0, fwBuffer);
89+
// Return ownership of the buffer to the deframer with an empty context
90+
ComCfg::FrameContext context;
91+
this->dataReturnOut_out(0, fwBuffer, context);
10692
}
10793

10894
} // namespace Svc

Svc/FprimeRouter/FprimeRouter.fpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,11 @@ module Svc {
77
# ----------------------------------------------------------------------
88
import Router
99

10-
enum AllocationReason : U8{
11-
FILE_UPLINK, @< Buffer allocation for file uplink
12-
USER_BUFFER @< Buffer allocation for user handled buffer
13-
}
14-
1510
@ Port for forwarding non-recognized packet types
16-
@ Ownership of the buffer is retained by the FprimeRouter, meaning receiving
17-
@ components should either process data synchronously, or copy the data if needed
11+
@ Ownership of the buffer is passed to the receiver. The receiver must return
12+
@ the buffer via fileBufferReturnIn when done.
1813
output port unknownDataOut: Svc.ComDataWithContext
1914

20-
@ Port for allocating buffers
21-
output port bufferAllocate: Fw.BufferGet
22-
23-
@ Port for deallocating buffers
24-
output port bufferDeallocate: Fw.BufferSend
25-
2615
@ An error occurred while serializing a com buffer
2716
event SerializationError(
2817
status: U32 @< The status of the operation
@@ -37,11 +26,6 @@ module Svc {
3726
severity warning high \
3827
format "Deserializing packet type failed with status {}"
3928

40-
@ An allocation error occurred
41-
event AllocationError(reason: AllocationReason) severity warning high \
42-
format "Buffer allocation for {} failed"
43-
44-
4529
###############################################################################
4630
# Standard AC Ports for Events
4731
###############################################################################

Svc/FprimeRouter/docs/sdd.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ The `Svc::FprimeRouter` component receives F´ packets (as [Fw::Buffer](../../..
66

77
The `Svc::FprimeRouter` component supports `Fw::ComPacketType::FW_PACKET_COMMAND` and `Fw::ComPacketType::FW_PACKET_FILE` packet types. Unknown packet types are forwarded on the `unknownDataOut` port, which a project-specific component can connect to for custom routing.
88

9-
About memory management, all buffers sent by `Svc::FprimeRouter` on the `fileOut` and `unknownDataOut` ports are expected to be returned to the router through the `fileBufferReturnIn` port for deallocation.
9+
About memory management, buffers sent by `Svc::FprimeRouter` on the `fileOut` and `unknownDataOut` ports are passed through directly without copying. Receivers of these buffers **must** return them to `Svc::FprimeRouter` through the `fileBufferReturnIn` port when finished processing. The original buffer is not returned to the deframer until this happens.
10+
11+
**Note:** The `FrameContext` received on `dataIn` is not preserved across the buffer return path. When buffers are returned via `fileBufferReturnIn`, `Svc::FprimeRouter` constructs an empty `ComCfg::FrameContext` for the `dataReturnOut` call. This applies to all packet types — the context is always discarded on return.
1012

1113
## Custom Routing
1214

13-
The `Svc::FprimeRouter` component is designed to be extensible through the use of a project-specific router. The `unknownDataOut` port can be connected to a project-specific component that can receive all unknown packet types. This component can then implement custom handling of these unknown packets. After processing, the project-specific component shall return the received buffer to the `Svc::FprimeRouter` component through the `fileBufferReturnIn` port (named this way as it only receives file packets in the common use-case), which will deallocate the buffer.
15+
The `Svc::FprimeRouter` component is designed to be extensible through the use of a project-specific router. The `unknownDataOut` port can be connected to a project-specific component that can receive all unknown packet types. This component can then implement custom handling of these unknown packets. After processing, the project-specific component shall return the received buffer to the `Svc::FprimeRouter` component through the `fileBufferReturnIn` port (named this way as it only receives file packets in the common use-case), which will return the buffer to the deframer.
1416

1517
## Usage Examples
1618

@@ -32,8 +34,6 @@ In the canonical uplink communications stack, `Svc::FprimeRouter` is connected t
3234
| `output` | `fileOut` | `Fw.BufferSend` | Port for sending file packets as Fw::Buffer (ownership passed to receiver) |
3335
| `sync input` | `fileBufferReturnIn` | `Fw.BufferSend` | Receiving back ownership of buffer sent on `fileOut` and `unknownDataOut` |
3436
| `output` | `unknownDataOut` | `Svc.ComDataWithContext` | Port forwarding unknown data (useful for adding custom routing rules with a project-defined router) |
35-
| `output`| `bufferAllocate` | `Fw.BufferGet` | Port for allocating buffers, allowing copy of received data |
36-
| `output`| `bufferDeallocate` | `Fw.BufferSend` | Port for deallocating buffers |
3737

3838
## Requirements
3939

@@ -44,5 +44,5 @@ SVC-ROUTER-002 | `Svc::FprimeRouter` shall route packets of type `Fw::ComPacketT
4444
SVC-ROUTER-003 | `Svc::FprimeRouter` shall route packets of type `Fw::ComPacketType::FW_PACKET_FILE` to the `fileOut` output port. | Routing file packets | Unit test |
4545
SVC-ROUTER-004 | `Svc::FprimeRouter` shall route data that is neither `Fw::ComPacketType::FW_PACKET_COMMAND` nor `Fw::ComPacketType::FW_PACKET_FILE` to the `unknownDataOut` output port. | Allows for projects to provide custom routing for additional (project-specific) uplink data types | Unit test |
4646
SVC-ROUTER-005 | `Svc::FprimeRouter` shall emit warning events if serialization errors occur during processing of incoming packets | Aid in diagnosing uplink issues | Unit test |
47-
SVC-ROUTER-005 | `Svc::FprimeRouter` shall make a copy of buffers that represent a `FW_PACKET_FILE` | Aid in memory management of file buffers | Unit test |
48-
SVC-ROUTER-005 | `Svc::FprimeRouter` shall return ownership of all buffers received on `dataIn` through `dataReturnOut` | Memory management | Unit test |
47+
SVC-ROUTER-006 | `Svc::FprimeRouter` shall pass through buffers for `FW_PACKET_FILE` and unknown packet types without copying, and defer returning them to the deframer until they are returned via `fileBufferReturnIn` | Efficient memory management | Unit test |
48+
SVC-ROUTER-007 | `Svc::FprimeRouter` shall return ownership of all buffers received on `dataIn` through `dataReturnOut` | Memory management | Unit test |

Svc/FprimeRouter/test/ut/FprimeRouterTestMain.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,8 @@ TEST(FprimeRouter, TestRouteUnknownPacketUnconnected) {
2828
Svc::FprimeRouterTester tester(true);
2929
tester.testRouteUnknownPacketUnconnected();
3030
}
31-
TEST(FprimeRouter, TestAllocationFailureFile) {
32-
COMMENT("Test failure to allocate for files");
33-
Svc::FprimeRouterTester tester;
34-
tester.testAllocationFailureFile();
35-
}
36-
TEST(FprimeRouter, TestAllocationFailureUnknown) {
37-
COMMENT("Test failure to allocate for unknown packets");
38-
Svc::FprimeRouterTester tester;
39-
tester.testAllocationFailureUnknown();
40-
}
4131
TEST(FprimeRouter, TestBufferReturn) {
42-
COMMENT("Deallocate a returning buffer");
32+
COMMENT("Return a buffer via fileBufferReturnIn");
4333
Svc::FprimeRouterTester tester;
4434
tester.testBufferReturn();
4535
}

Svc/FprimeRouter/test/ut/FprimeRouterTester.cpp

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,22 @@ void FprimeRouterTester ::testRouteComInterface() {
3434
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
3535
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
3636
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
37-
ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation for Com packets
3837
}
3938

4039
void FprimeRouterTester ::testRouteFileInterface() {
4140
this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_FILE);
4241
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
4342
ASSERT_from_fileOut_SIZE(1); // one file packet emitted
4443
ASSERT_from_unknownDataOut_SIZE(0); // no unknown data emitted
45-
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
46-
ASSERT_from_bufferAllocate_SIZE(1); // file packet was copied into a new allocated buffer
44+
ASSERT_from_dataReturnOut_SIZE(0); // data ownership is not returned yet (will come back on fileBufferReturnIn)
4745
}
4846

4947
void FprimeRouterTester ::testRouteUnknownPacket() {
5048
this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_UNKNOWN);
5149
ASSERT_from_commandOut_SIZE(0); // no command packet emitted
5250
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
5351
ASSERT_from_unknownDataOut_SIZE(1); // one unknown data emitted
54-
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
55-
ASSERT_from_bufferAllocate_SIZE(1); // unknown packet was copied into a new allocated buffer
52+
ASSERT_from_dataReturnOut_SIZE(0); // data ownership is not returned yet (will come back on fileBufferReturnIn)
5653
}
5754

5855
void FprimeRouterTester ::testRouteUnknownPacketUnconnected() {
@@ -61,32 +58,15 @@ void FprimeRouterTester ::testRouteUnknownPacketUnconnected() {
6158
ASSERT_from_fileOut_SIZE(0); // no file packet emitted
6259
ASSERT_from_unknownDataOut_SIZE(0); // zero unknown data emitted when port is unconnected
6360
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
64-
ASSERT_from_bufferAllocate_SIZE(0); // no buffer allocation when port is unconnected
65-
}
66-
67-
void FprimeRouterTester ::testAllocationFailureFile() {
68-
this->m_forceAllocationError = true;
69-
this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_FILE);
70-
ASSERT_EVENTS_AllocationError_SIZE(1); // allocation error should be logged
71-
ASSERT_EVENTS_AllocationError(0, FprimeRouter_AllocationReason::FILE_UPLINK);
72-
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
73-
}
74-
75-
void FprimeRouterTester ::testAllocationFailureUnknown() {
76-
this->m_forceAllocationError = true;
77-
this->mockReceivePacketType(Fw::ComPacketType::FW_PACKET_UNKNOWN);
78-
ASSERT_EVENTS_AllocationError_SIZE(1); // allocation error should be logged
79-
ASSERT_EVENTS_AllocationError(0, FprimeRouter_AllocationReason::USER_BUFFER);
80-
ASSERT_from_dataReturnOut_SIZE(1); // data ownership should always be returned
8161
}
8262

8363
void FprimeRouterTester ::testBufferReturn() {
8464
U8 data[1];
8565
Fw::Buffer buffer(data, sizeof(data));
8666
this->invoke_to_fileBufferReturnIn(0, buffer);
87-
ASSERT_from_bufferDeallocate_SIZE(1); // incoming buffer should be deallocated
88-
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getData(), data);
89-
ASSERT_EQ(this->fromPortHistory_bufferDeallocate->at(0).fwBuffer.getSize(), sizeof(data));
67+
ASSERT_from_dataReturnOut_SIZE(1); // buffer should be returned via dataReturnOut
68+
ASSERT_EQ(this->fromPortHistory_dataReturnOut->at(0).data.getData(), data);
69+
ASSERT_EQ(this->fromPortHistory_dataReturnOut->at(0).data.getSize(), sizeof(data));
9070
}
9171

9272
void FprimeRouterTester ::testCommandResponse() {
@@ -120,27 +100,9 @@ void FprimeRouterTester::connectPortsExceptUnknownData() {
120100
this->connect_to_dataIn(0, this->component.get_dataIn_InputPort(0));
121101
this->connect_to_fileBufferReturnIn(0, this->component.get_fileBufferReturnIn_InputPort(0));
122102
// Connect typed output ports
123-
this->component.set_bufferAllocate_OutputPort(0, this->get_from_bufferAllocate(0));
124-
this->component.set_bufferDeallocate_OutputPort(0, this->get_from_bufferDeallocate(0));
125103
this->component.set_commandOut_OutputPort(0, this->get_from_commandOut(0));
126104
this->component.set_dataReturnOut_OutputPort(0, this->get_from_dataReturnOut(0));
127105
this->component.set_fileOut_OutputPort(0, this->get_from_fileOut(0));
128106
}
129107

130-
// ----------------------------------------------------------------------
131-
// Port handler overrides
132-
// ----------------------------------------------------------------------
133-
Fw::Buffer FprimeRouterTester::from_bufferAllocate_handler(FwIndexType portNum, FwSizeType size) {
134-
this->pushFromPortEntry_bufferAllocate(size);
135-
if (this->m_forceAllocationError) {
136-
this->m_buffer.setData(nullptr);
137-
this->m_buffer.setSize(0);
138-
} else {
139-
this->m_buffer.setData(this->m_buffer_slot);
140-
this->m_buffer.setSize(size);
141-
::memset(this->m_buffer.getData(), 0, size);
142-
}
143-
return this->m_buffer;
144-
}
145-
146108
} // namespace Svc

Svc/FprimeRouter/test/ut/FprimeRouterTester.hpp

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,7 @@ class FprimeRouterTester : public FprimeRouterGTestBase {
5656
//! Route a packet of unknown type
5757
void testRouteUnknownPacketUnconnected();
5858

59-
//! Route a packet of unknown type
60-
void testAllocationFailureFile();
61-
62-
//! Deallocate a returning buffer
63-
void testAllocationFailureUnknown();
64-
65-
//! Deallocate a returning buffer
59+
//! Test buffer return via fileBufferReturnIn
6660
void testBufferReturn();
6761

6862
//! Invoke the command response input port
@@ -85,23 +79,13 @@ class FprimeRouterTester : public FprimeRouterGTestBase {
8579
//! Mock the reception of a packet of a specific type
8680
void mockReceivePacketType(Fw::ComPacketType packetType);
8781

88-
// ----------------------------------------------------------------------
89-
// Port handler overrides
90-
// ----------------------------------------------------------------------
91-
//! Overriding bufferAllocate handler to be able to request a buffer in component tests
92-
Fw::Buffer from_bufferAllocate_handler(FwIndexType portNum, FwSizeType size) override;
93-
9482
private:
9583
// ----------------------------------------------------------------------
9684
// Member variables
9785
// ----------------------------------------------------------------------
9886

9987
//! The component under test
10088
FprimeRouter component;
101-
102-
Fw::Buffer m_buffer; // buffer to be returned by mocked bufferAllocate call
103-
U8 m_buffer_slot[64];
104-
bool m_forceAllocationError = false; // Flag to force allocation error
10589
};
10690

10791
} // namespace Svc

Svc/Interfaces/Router.fpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ module Svc {
1717
@ Port for sending file packets as Fw::Buffer (ownership passed to receiver)
1818
output port fileOut: Fw.BufferSend
1919

20-
@ Port for receiving ownership back of buffers sent on fileOut
20+
@ Port for receiving back ownership of buffers sent on fileOut or any other
21+
@ output port that passes buffer ownership to the receiver
2122
sync input port fileBufferReturnIn: Fw.BufferSend
2223

2324
@ Port for sending command packets as Fw::ComBuffers

Svc/Subtopologies/ComCcsds/ComCcsds.fpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,6 @@ module ComCcsds {
177177
# SpacePacketDeframer <-> Router
178178
spacePacketDeframer.dataOut -> fprimeRouter.dataIn
179179
fprimeRouter.dataReturnOut -> spacePacketDeframer.dataReturnIn
180-
# Router buffer allocations
181-
fprimeRouter.bufferAllocate -> commsBufferManager.bufferGetCallee
182-
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
183180
}
184181
} # end FramingSubtopology
185182

Svc/Subtopologies/ComFprime/ComFprime.fpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,6 @@ module ComFprime {
141141
# Deframer <-> Router
142142
deframer.dataOut -> fprimeRouter.dataIn
143143
fprimeRouter.dataReturnOut -> deframer.dataReturnIn
144-
# Router buffer allocations
145-
fprimeRouter.bufferAllocate -> commsBufferManager.bufferGetCallee
146-
fprimeRouter.bufferDeallocate -> commsBufferManager.bufferSendIn
147144
}
148145
} # end FramingSubtopology
149146

0 commit comments

Comments
 (0)