[ipxe-devel] [PATCH 4/4] [tcp core][RFC] Wait for TCP to safely close
Guo-Fu Tseng
cooldavid at cooldavid.org
Fri Jul 30 12:40:38 UTC 2010
From: Guo-Fu Tseng <cooldavid at cooldavid.org>
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.
Signed-off-by: Guo-Fu Tseng <cooldavid at cooldavid.org>
---
src/core/activity.c | 64 +++++++++++++++++++++++++++++++++++++++++++
src/core/image.c | 8 +++++
src/core/main.c | 2 +
src/include/gpxe/activity.h | 28 +++++++++++++++++++
src/include/ipxe/tcp.h | 13 +++++++++
src/net/tcp.c | 15 ++++++++++
src/usr/autoboot.c | 6 ++++
7 files changed, 136 insertions(+), 0 deletions(-)
create mode 100644 src/core/activity.c
create mode 100644 src/include/gpxe/activity.h
diff --git a/src/core/activity.c b/src/core/activity.c
new file mode 100644
index 0000000..099d85a
--- /dev/null
+++ b/src/core/activity.c
@@ -0,0 +1,64 @@
+/*
+ * The original idea is from: Michael Brown <mbrown at fensystems.co.uk>.
+ * Implemented into gPXE by: Guo-Fu Tseng <cooldavid at 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;
+}
diff --git a/src/core/image.c b/src/core/image.c
index ec4b461..8434e77 100644
--- a/src/core/image.c
+++ b/src/core/image.c
@@ -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
@@ -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",
diff --git a/src/core/main.c b/src/core/main.c
index a1128dd..021ac00 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -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>
@@ -85,6 +86,7 @@ __asmcall int main ( void ) {
shell();
}
+ activity_wait ( ACTIVITY_TIMEOUT );
shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );
return 0;
diff --git a/src/include/gpxe/activity.h b/src/include/gpxe/activity.h
new file mode 100644
index 0000000..a77a679
--- /dev/null
+++ b/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
diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h
index b57e365..f0e6bdd 100644
--- a/src/include/ipxe/tcp.h
+++ b/src/include/ipxe/tcp.h
@@ -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 */
diff --git a/src/net/tcp.c b/src/net/tcp.c
index 4318004..a5120d6 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -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>
@@ -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
@@ -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;
}
diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c
index 48d1fa6..47f3f54 100644
--- a/src/usr/autoboot.c
+++ b/src/usr/autoboot.c
@@ -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>
@@ -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 );
}
--
1.7.1
More information about the ipxe-devel
mailing list