cancel
Showing results for 
Search instead for 
Did you mean: 

lwIP / TCP tx'd pbuf ACK'd?

LCE
Principal

Hello,

I have to admit that this is more a question concerning lwIP, but as I'm using a STM32F7...

And the search engines haven't helped yet.

I need to transmit TCP packets quickly, without waiting for TCP's ACK.

Payload for these packets come from an array of DMA buffers (sourced by SAI) which I might need for other interfaces, like USB output, not at the same time though.

Each of these buffers is given a state, like FREE, WRITING, TRANSMITTING, WAIT_ACK.

So I give a buffer to lwIP with tcp_write() (building its own pbuf) and call tcp_output() immediately.

Most examples I found also set the tcp_sent callback, and only continue transmitting after the latest data has been ACK'd by the receiver. I have no time for that, wireshark showed me that ACK can take a few milli seconds - too long.

I've been through lwIP's TCP functions, but I cannot find how to pass back the info from lwIP which pbuf / data was recently ACK'd to free the DMA buffers.

tcp_sent() length does not help, because all DMA buffers are the same size.

Any hints or ideas?

I hope there's something I have not yet understood or have overseen.

I could start fiddling within the lwIP functions, but I hope there's a more elegant way.

PS: otherwise zero-copy TX ethernet is working, as is http, PTP

13 REPLIES 13
LCE
Principal

It looks like the solution might be using the tcp_pcb, which has the member unacked, which again holds info to unacked segments including the sent pbuf:

/** the TCP protocol control block */
struct tcp_pcb
{
...
	/* These are ordered by sequence number: */
	struct tcp_seg *unsent;   		/* Unsent (queued) segments. */
	struct tcp_seg *unacked;  		/* Sent but unacknowledged segments. */
...
}
 
/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
struct tcp_seg
{
	struct tcp_seg *next;		/* used when putting segments on a queue */
	struct pbuf *p;      		/* buffer containing data + TCP header */
	u16_t len;           		/* the TCP length of this segment */
...
}

I had overseen that the segment holds a pbuf...

Bob S
Principal

Disable Nagel's algorithm. That is the code that buffers outgoing data until the previously sent data is ACK'd. How you do that depends on which LwIP interface you are using (raw, netconn or sockets)..

Piranha
Chief II

> I need to transmit TCP packets quickly, without waiting for TCP's ACK.

If you don't need guaranteed delivery, you don't need TCP at all. Sounds like you should use UDP transport protocol for this purpose.

> I cannot find how to pass back the info from lwIP which pbuf / data was recently ACK'd to free the DMA buffers

Create pbuf_custom buffers and wait for the custom_free_function() to be called.

Raw, and yes, Nagle is disabled.

I need guaranteed delivery, so I need the ACK.

But until now I found that the ACK might take a few ms, and I can't afford to wait that long before sending the next packet, but have enough buffers to keep data for about 40 ms.

Thanks again, I'll have a look at the custom buffers.

I can't see how the custom buffers help me on the transmit side, because tcp_write takes only the payload as input and creates its own pbufs, with no defines for custom pbuf.

I guess I'll write my own tcp_write, and then use the standard pbuf's LWIP_PBUF_CUSTOM_DATA which is then used in the tcp_sent callback.

Using LWIP_PBUF_CUSTOM_DATA and tcp_sent doesn't help, because tcp_sent callback is called after ACK'd packets have been removed from the PCB's unacked list, so the ACK'd pbuf is already freed.

Indeed you are right about tcp_write(). But cannot you really use tcp_sent()? Your code knows what buffers of what sizes it sends. The tcp_sent() callback will confirm those bytes sequentially. Your code can count those confirmed bytes and, when the size of the next buffer has been sent, the buffer can be released.

No chance, I'm sending same size buffers...

For now I'll try checking the PCB's unacked list in tcp_sent CB - if the packet is not on the list but in "WAIT_ACK" state, it gets freed.

If that won't work or will take too long, I'll modify the lwIP TCP functions. I don't like that, but I see no other way.