When closing a TCP connection a handshake procedure is executed between the
peers. The close routine of the rttcp driver did not participate in
detecting the end of this handshake but rather waited one second inside
a close call unconditionally. Especially when peers are directly connected
this is a waste of time which can hurt a lot in some situations.

This patch replaces the msleep(1000) call with a timed wait on a
semaphore which gets sigalled when the termination handshake is complete.

Signed-off-by: Sebastian Smolorz <[email protected]>
---
 kernel/drivers/net/stack/ipv4/tcp/tcp.c | 29 +++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/kernel/drivers/net/stack/ipv4/tcp/tcp.c 
b/kernel/drivers/net/stack/ipv4/tcp/tcp.c
index 54bafa80f..81089afd1 100644
--- a/kernel/drivers/net/stack/ipv4/tcp/tcp.c
+++ b/kernel/drivers/net/stack/ipv4/tcp/tcp.c
@@ -147,6 +147,9 @@ struct tcp_socket {
        struct rtskb_queue retransmit_queue;
        struct timerwheel_timer timer;

+       struct semaphore close_sem;
+       rtdm_nrtsig_t close_sig;
+
 #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_TCP_ERROR_INJECTION
        unsigned int packet_counter;
        unsigned int error_rate;
@@ -1042,6 +1045,7 @@ static void rt_tcp_rcv(struct rtskb *skb)
                        rt_tcp_send(ts, TCP_FLAG_ACK);
                        /* data receiving is not possible anymore */
                        rtdm_sem_destroy(&ts->sock.pending_sem);
+                       rtdm_nrtsig_pend(&ts->close_sig);
                        goto feed;
                } else if (ts->tcp_state == TCP_FIN_WAIT1) {
                        /* Send ACK */
@@ -1105,6 +1109,7 @@ static void rt_tcp_rcv(struct rtskb *skb)
                        ts->tcp_state = TCP_CLOSE;
                        rtdm_lock_put_irqrestore(&ts->socket_lock, context);
                        /* socket destruction will be done on close() */
+                       rtdm_nrtsig_pend(&ts->close_sig);
                        goto drop;
                } else if (ts->tcp_state == TCP_FIN_WAIT1) {
                        ts->tcp_state = TCP_FIN_WAIT2;
@@ -1119,6 +1124,7 @@ static void rt_tcp_rcv(struct rtskb *skb)
                        ts->tcp_state = TCP_TIME_WAIT;
                        rtdm_lock_put_irqrestore(&ts->socket_lock, context);
                        /* socket destruction will be done on close() */
+                       rtdm_nrtsig_pend(&ts->close_sig);
                        goto feed;
                }
        }
@@ -1190,6 +1196,11 @@ static int rt_tcp_window_send(struct tcp_socket *ts, u32 
data_len, u8 *data_ptr)
        return ret;
 }

+static void rt_tcp_close_signal_handler(rtdm_nrtsig_t *nrtsig, void *arg)
+{
+       up((struct semaphore *)arg);
+}
+
 static int rt_tcp_socket_create(struct tcp_socket *ts)
 {
        rtdm_lockctx_t context;
@@ -1226,6 +1237,10 @@ static int rt_tcp_socket_create(struct tcp_socket *ts)
        timerwheel_init_timer(&ts->timer, rt_tcp_retransmit_handler, ts);
        rtskb_queue_init(&ts->retransmit_queue);

+       sema_init(&ts->close_sem, 0);
+       rtdm_nrtsig_init(&ts->close_sig, rt_tcp_close_signal_handler,
+                        &ts->close_sem);
+
 #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_TCP_ERROR_INJECTION
        ts->packet_counter = counter_start;
        ts->error_rate = error_rate;
@@ -1237,6 +1252,7 @@ static int rt_tcp_socket_create(struct tcp_socket *ts)
        /* enforce maximum number of TCP sockets */
        if (free_ports == 0) {
                rtdm_lock_put_irqrestore(&tcp_socket_base_lock, context);
+               rtdm_nrtsig_destroy(&ts->close_sig);
                return -EAGAIN;
        }
        free_ports--;
@@ -1338,6 +1354,8 @@ static void rt_tcp_socket_destruct(struct tcp_socket *ts)

        rtdm_event_destroy(&ts->conn_evt);

+       rtdm_nrtsig_destroy(&ts->close_sig);
+
        /* cleanup already collected fragments */
        rt_ip_frag_invalidate_socket(sock);

@@ -1362,6 +1380,7 @@ static void rt_tcp_close(struct rtdm_fd *fd)
        struct rt_tcp_dispatched_packet_send_cmd send_cmd;
        rtdm_lockctx_t context;
        int signal = 0;
+       int ret;

        rtdm_lock_get_irqsave(&ts->socket_lock, context);

@@ -1380,7 +1399,10 @@ static void rt_tcp_close(struct rtdm_fd *fd)
                /* result is ignored */

                /* Give the peer some time to reply to our FIN. */
-               msleep(1000);
+               ret = down_timeout(&ts->close_sem, msecs_to_jiffies(1000));
+               if (ret)
+                       rtdm_printk("rttcp: waiting for FIN-ACK handshake 
returned %d\n",
+                                   ret);
        } else if (ts->tcp_state == TCP_CLOSE_WAIT) {
                /* Send FIN in CLOSE_WAIT */
                send_cmd.ts = ts;
@@ -1394,7 +1416,10 @@ static void rt_tcp_close(struct rtdm_fd *fd)
                /* result is ignored */

                /* Give the peer some time to reply to our FIN. */
-               msleep(1000);
+               ret = down_timeout(&ts->close_sem, msecs_to_jiffies(1000));
+               if (ret)
+                       rtdm_printk("rttcp: waiting for FIN-ACK handshake 
returned %d\n",
+                                   ret);
        } else {
                /*
          rt_tcp_socket_validate() has not been called at all,
--
2.20.1


Reply via email to