Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[tcp core][RFC] Wait for TCP to safely close
After some discussion with Piotr and Michael, I think it might
be an acceptable solution for the issue that gPXE leaves remote
peer TCP in undesired state.

The reason I don't mark activity_start at somewhere like tcp_open
or ESTABLISHED is that sanboot do not close the TCP connection
before hand of the control to OS.
  • Loading branch information
cooldavid committed Jul 30, 2010
1 parent 8aa458e commit e380940
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 0 deletions.
64 changes: 64 additions & 0 deletions src/core/activity.c
@@ -0,0 +1,64 @@
/*
* The original idea is from: Michael Brown <mbrown@fensystems.co.uk>.
* Implemented into gPXE by: Guo-Fu Tseng <cooldavid@cooldavid.org>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

FILE_LICENCE ( GPL2_OR_LATER );

#include <gpxe/timer.h>
#include <gpxe/process.h>
#include <gpxe/activity.h>

/** @file
*
* Activities
*
* We implement a facility to keep the processes running while waiting
* for some critical events to complete.
*/


/** Currently waiting activities */
static unsigned int num_activities = 0;

/**
* Start an activity
*/
void activity_start ( void ) {
num_activities++;
}

/**
* Stop an activity
*/
void activity_stop ( void ) {
num_activities--;
}

/**
* Wait for activities to complete
*
* @v max_timeout The max waiting time in tenths of a second.
* @ret boolean If all the activities are cleared.
*/
int activity_wait ( unsigned long max_timeout ) {
max_timeout = currticks() + ( max_timeout * TICKS_PER_SEC ) / 10;
while ( num_activities && currticks() < max_timeout ) {
step();
}
return !num_activities;
}
8 changes: 8 additions & 0 deletions src/core/image.c
Expand Up @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/list.h>
#include <ipxe/umalloc.h>
#include <ipxe/uri.h>
#include <ipxe/activity.h>
#include <ipxe/image.h>

/** @file
Expand Down Expand Up @@ -262,6 +263,13 @@ int image_exec ( struct image *image ) {
*/
image_get ( image );

/**
* Wait for possible pending activities before possible leaves gPXE
* at later image execution.
* Currently only TCP close event implemented it.
*/
activity_wait( ACTIVITY_TIMEOUT );

/* Try executing the image */
if ( ( rc = image->type->exec ( image ) ) != 0 ) {
DBGC ( image, "IMAGE %p could not execute: %s\n",
Expand Down
2 changes: 2 additions & 0 deletions src/core/main.c
Expand Up @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/shell.h>
#include <ipxe/shell_banner.h>
#include <ipxe/image.h>
#include <ipxe/activity.h>
#include <usr/autoboot.h>
#include <config/general.h>

Expand Down Expand Up @@ -85,6 +86,7 @@ __asmcall int main ( void ) {
shell();
}

activity_wait ( ACTIVITY_TIMEOUT );
shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );

return 0;
Expand Down
28 changes: 28 additions & 0 deletions src/include/gpxe/activity.h
@@ -0,0 +1,28 @@
#ifndef _GPXE_ACTIVITY_H
#define _GPXE_ACTIVITY_H

/** @file
*
* gPXE activity API
*
* The activity API provides facility to keep the processes running
* while waiting for some critical events to complete.
*
* It is suggested that we don't call activity_wait() within any
* process's scope.
*/

FILE_LICENCE ( GPL2_OR_LATER );

extern void activity_start ( void );
extern void activity_stop ( void );

/* Default timeout for 1 second */
#define ACTIVITY_TIMEOUT 10

/**
* max_timeout is in tenths of a second
*/
extern int activity_wait ( unsigned long max_timeout );

#endif
13 changes: 13 additions & 0 deletions src/include/ipxe/tcp.h
Expand Up @@ -278,6 +278,19 @@ struct tcp_options {
TCP_STATE_RCVD ( TCP_FIN ) ) ) \
== ( TCP_STATE_ACKED ( TCP_FIN ) | TCP_STATE_RCVD ( TCP_FIN ) ) )

/** TCP close activities in progress
*
* TCP is in a state that connection is trying to close, and should
* wait for the state to TIME_WAIT or CLOSED.
* This is used for preventing remote peer dangling TCP state.
*/
#define TCP_CLOSE_INPROGRESS(state) \
( (state) == TCP_CLOSE_WAIT || \
(state) == TCP_LAST_ACK || \
(state) == TCP_CLOSING || \
(state) == TCP_FIN_WAIT_1 || \
(state) == TCP_FIN_WAIT_2 )

/** @} */

/** Mask for TCP header length field */
Expand Down
15 changes: 15 additions & 0 deletions src/net/tcp.c
Expand Up @@ -7,6 +7,7 @@
#include <ipxe/timer.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/activity.h>
#include <ipxe/retry.h>
#include <ipxe/refcnt.h>
#include <ipxe/xfer.h>
Expand Down Expand Up @@ -97,6 +98,8 @@ enum tcp_flags {
TCP_TS_ENABLED = 0x0002,
/** TCP acknowledgement is pending */
TCP_ACK_PENDING = 0x0004,
/** Activity started **/
TCP_ACT_STARTED = 0x0008,
};

/** TCP internal header
Expand Down Expand Up @@ -171,6 +174,18 @@ tcp_dump_state ( struct tcp_connection *tcp ) {
DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp,
tcp_state ( tcp->prev_tcp_state ),
tcp_state ( tcp->tcp_state ) );

if ( TCP_CLOSE_INPROGRESS ( tcp->tcp_state ) &&
! ( tcp->flags & TCP_ACT_STARTED ) ) {
activity_start();
tcp->flags |= TCP_ACT_STARTED;
}

if ( TCP_CLOSED_GRACEFULLY( tcp->tcp_state ) &&
( tcp->flags & TCP_ACT_STARTED ) ) {
activity_stop();
tcp->flags &= ~TCP_ACT_STARTED;
}
}
tcp->prev_tcp_state = tcp->tcp_state;
}
Expand Down
6 changes: 6 additions & 0 deletions src/usr/autoboot.c
Expand Up @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/image.h>
#include <ipxe/sanboot.h>
#include <ipxe/uri.h>
#include <ipxe/activity.h>
#include <usr/ifmgmt.h>
#include <usr/route.h>
#include <usr/dhcpmgmt.h>
Expand Down Expand Up @@ -232,6 +233,11 @@ void autoboot ( void ) {
for_each_netdev ( netdev ) {
if ( netdev == boot_netdev )
continue;
/**
* Wait for possible pending activities.
* Currently only TCP close event implemented it.
*/
activity_wait( ACTIVITY_TIMEOUT );
close_all_netdevs();
netboot ( netdev );
}
Expand Down

0 comments on commit e380940

Please sign in to comment.