#include <ipxe/uri.h>
#include <ipxe/netdevice.h>
#include <ipxe/profile.h>
+#include <ipxe/process.h>
#include <ipxe/tcpip.h>
#include <ipxe/tcp.h>
struct list_head tx_queue;
/** Receive queue */
struct list_head rx_queue;
+ /** Transmission process */
+ struct process process;
/** Retransmission timer */
struct retry_timer timer;
/** Shutdown (TIME_WAIT) timer */
static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" };
/* Forward declarations */
+static struct process_descriptor tcp_process_desc;
static struct interface_descriptor tcp_xfer_desc;
static void tcp_expired ( struct retry_timer *timer, int over );
static void tcp_wait_expired ( struct retry_timer *timer, int over );
DBGC ( tcp, "TCP %p allocated\n", tcp );
ref_init ( &tcp->refcnt, NULL );
intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt );
+ process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt );
timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt );
timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt );
tcp->prev_tcp_state = TCP_CLOSED;
pending_put ( &tcp->pending_flags );
/* Remove from list and drop reference */
+ process_del ( &tcp->process );
stop_timer ( &tcp->timer );
stop_timer ( &tcp->wait );
list_del ( &tcp->list );
* will have been started if necessary, and so the stack will
* eventually attempt to retransmit the failed packet.
*/
-static int tcp_xmit ( struct tcp_connection *tcp ) {
+static void tcp_xmit ( struct tcp_connection *tcp ) {
struct io_buffer *iobuf;
struct tcp_header *tcphdr;
struct tcp_mss_option *mssopt;
/* If retransmission timer is already running, do nothing */
if ( timer_running ( &tcp->timer ) )
- return 0;
+ return;
/* Calculate both the actual (payload) and sequence space
* lengths that we wish to transmit.
/* If we have nothing to transmit, stop now */
if ( ( seq_len == 0 ) && ! ( tcp->flags & TCP_ACK_PENDING ) )
- return 0;
+ return;
/* If we are transmitting anything that requires
* acknowledgement (i.e. consumes sequence space), start the
DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x "
"%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ),
tcp->rcv_ack );
- return -ENOMEM;
+ return;
}
iob_reserve ( iobuf, TCP_MAX_HEADER_LEN );
DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n",
tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ),
tcp->rcv_ack, strerror ( rc ) );
- return rc;
+ return;
}
/* Clear ACK-pending flag */
tcp->flags &= ~TCP_ACK_PENDING;
profile_stop ( &tcp_tx_profiler );
- return 0;
}
+/** TCP process descriptor */
+static struct process_descriptor tcp_process_desc =
+ PROC_DESC_ONCE ( struct tcp_connection, process, tcp_xmit );
+
/**
* Retransmission timer expired
*
/* Dump out any state change as a result of the received packet */
tcp_dump_state ( tcp );
- /* Send out any pending data */
- tcp_xmit ( tcp );
+ /* Schedule transmission of ACK (and any pending data). If we
+ * have received any out-of-order packets (i.e. if the receive
+ * queue remains non-empty after processing) then send the ACK
+ * immediately in order to trigger Fast Retransmission.
+ */
+ if ( list_empty ( &tcp->rx_queue ) ) {
+ process_add ( &tcp->process );
+ } else {
+ tcp_xmit ( tcp );
+ }
/* If this packet was the last we expect to receive, set up
* timer to expire and cause the connection to be freed.