mirror of https://github.com/ipxe/ipxe.git
[tcp] Mark any unacknowledged transmission as a pending operation
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/6/head
parent
021d7b21b7
commit
5482b0abb6
|
@ -10,6 +10,7 @@
|
||||||
#include <ipxe/init.h>
|
#include <ipxe/init.h>
|
||||||
#include <ipxe/retry.h>
|
#include <ipxe/retry.h>
|
||||||
#include <ipxe/refcnt.h>
|
#include <ipxe/refcnt.h>
|
||||||
|
#include <ipxe/pending.h>
|
||||||
#include <ipxe/xfer.h>
|
#include <ipxe/xfer.h>
|
||||||
#include <ipxe/open.h>
|
#include <ipxe/open.h>
|
||||||
#include <ipxe/uri.h>
|
#include <ipxe/uri.h>
|
||||||
|
@ -95,6 +96,11 @@ struct tcp_connection {
|
||||||
struct retry_timer timer;
|
struct retry_timer timer;
|
||||||
/** Shutdown (TIME_WAIT) timer */
|
/** Shutdown (TIME_WAIT) timer */
|
||||||
struct retry_timer wait;
|
struct retry_timer wait;
|
||||||
|
|
||||||
|
/** Pending operations for SYN and FIN */
|
||||||
|
struct pending_operation pending_flags;
|
||||||
|
/** Pending operations for transmit queue */
|
||||||
|
struct pending_operation pending_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** TCP flags */
|
/** TCP flags */
|
||||||
|
@ -291,6 +297,9 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
|
||||||
/* Start timer to initiate SYN */
|
/* Start timer to initiate SYN */
|
||||||
start_timer_nodelay ( &tcp->timer );
|
start_timer_nodelay ( &tcp->timer );
|
||||||
|
|
||||||
|
/* Add a pending operation for the SYN */
|
||||||
|
pending_get ( &tcp->pending_flags );
|
||||||
|
|
||||||
/* Attach parent interface, transfer reference to connection
|
/* Attach parent interface, transfer reference to connection
|
||||||
* list and return
|
* list and return
|
||||||
*/
|
*/
|
||||||
|
@ -340,7 +349,13 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) {
|
||||||
list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) {
|
list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) {
|
||||||
list_del ( &iobuf->list );
|
list_del ( &iobuf->list );
|
||||||
free_iob ( iobuf );
|
free_iob ( iobuf );
|
||||||
|
pending_put ( &tcp->pending_data );
|
||||||
}
|
}
|
||||||
|
assert ( ! is_pending ( &tcp->pending_data ) );
|
||||||
|
|
||||||
|
/* Remove pending operations for SYN and FIN, if applicable */
|
||||||
|
pending_put ( &tcp->pending_flags );
|
||||||
|
pending_put ( &tcp->pending_flags );
|
||||||
|
|
||||||
/* Remove from list and drop reference */
|
/* Remove from list and drop reference */
|
||||||
stop_timer ( &tcp->timer );
|
stop_timer ( &tcp->timer );
|
||||||
|
@ -359,9 +374,14 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) {
|
||||||
tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
|
tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
|
||||||
|
|
||||||
/* If we have no data remaining to send, start sending FIN */
|
/* If we have no data remaining to send, start sending FIN */
|
||||||
if ( list_empty ( &tcp->tx_queue ) ) {
|
if ( list_empty ( &tcp->tx_queue ) &&
|
||||||
|
! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
|
||||||
|
|
||||||
tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
|
tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
|
||||||
tcp_dump_state ( tcp );
|
tcp_dump_state ( tcp );
|
||||||
|
|
||||||
|
/* Add a pending operation for the FIN */
|
||||||
|
pending_get ( &tcp->pending_flags );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,6 +466,7 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len,
|
||||||
if ( ! iob_len ( iobuf ) ) {
|
if ( ! iob_len ( iobuf ) ) {
|
||||||
list_del ( &iobuf->list );
|
list_del ( &iobuf->list );
|
||||||
free_iob ( iobuf );
|
free_iob ( iobuf );
|
||||||
|
pending_put ( &tcp->pending_data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len += frag_len;
|
len += frag_len;
|
||||||
|
@ -869,8 +890,10 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
|
||||||
len = ack_len;
|
len = ack_len;
|
||||||
acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
|
acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
|
||||||
( TCP_SYN | TCP_FIN ) );
|
( TCP_SYN | TCP_FIN ) );
|
||||||
if ( acked_flags )
|
if ( acked_flags ) {
|
||||||
len--;
|
len--;
|
||||||
|
pending_put ( &tcp->pending_flags );
|
||||||
|
}
|
||||||
|
|
||||||
/* Update SEQ and sent counters, and window size */
|
/* Update SEQ and sent counters, and window size */
|
||||||
tcp->snd_seq = ack;
|
tcp->snd_seq = ack;
|
||||||
|
@ -885,8 +908,12 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
|
||||||
tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags );
|
tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags );
|
||||||
|
|
||||||
/* Start sending FIN if we've had all possible data ACKed */
|
/* Start sending FIN if we've had all possible data ACKed */
|
||||||
if ( list_empty ( &tcp->tx_queue ) && ( tcp->flags & TCP_XFER_CLOSED ) )
|
if ( list_empty ( &tcp->tx_queue ) &&
|
||||||
|
( tcp->flags & TCP_XFER_CLOSED ) &&
|
||||||
|
! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) {
|
||||||
tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
|
tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
|
||||||
|
pending_get ( &tcp->pending_flags );
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1320,6 +1347,9 @@ static int tcp_xfer_deliver ( struct tcp_connection *tcp,
|
||||||
/* Enqueue packet */
|
/* Enqueue packet */
|
||||||
list_add_tail ( &iobuf->list, &tcp->tx_queue );
|
list_add_tail ( &iobuf->list, &tcp->tx_queue );
|
||||||
|
|
||||||
|
/* Each enqueued packet is a pending operation */
|
||||||
|
pending_get ( &tcp->pending_data );
|
||||||
|
|
||||||
/* Transmit data, if possible */
|
/* Transmit data, if possible */
|
||||||
tcp_xmit ( tcp );
|
tcp_xmit ( tcp );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue