lkml.org 
[lkml]   [1998]   [Mar]   [6]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
DateThu, 5 Mar 1998 22:35:37 -0800 (PST)
FromDean Gaudet <>
Subjecttcp writev() bug in 2.1.x
Eric Schenk fixed this bug in 2.0.31.  It's still in 2.1.x.  The attached
test-writev.c program will send three 100byte buffers using writev()
and with a 2.1.x kernel (or 2.0.x x<31) this will cause three segments
to be put on the wire.

Usage:  ./test-writev.c a.b.c.d port#
(I usually choose a.b.c.d to be a nearby webserver.)

Here's a tcpdump.  10.7.63.4 is a 2.1.86 box and 10.7.63.6 is a 2.0.33 box.
They're both running webservers on port 8080... and I do the test both ways
to show the difference.

23:22:02.960371 10.7.63.6.5735 > 10.7.63.4.8080: S 1781928942:1781928942(0) win 512 <mss 1460>
23:22:02.960500 10.7.63.4.8080 > 10.7.63.6.5735: S 4026453022:4026453022(0) ack 1781928943 win 32120 <mss 1460> (DF)
23:22:02.961013 10.7.63.6.5735 > 10.7.63.4.8080: . ack 1 win 32120 (DF)
23:22:02.961948 10.7.63.6.5735 > 10.7.63.4.8080: P 1:301(300) ack 1 win 32120 (DF)
                                                   ^^^^^^^^^^ note one 300 byte packet
23:22:02.963124 10.7.63.6.5735 > 10.7.63.4.8080: F 301:301(0) ack 1 win 32120
23:22:02.963215 10.7.63.4.8080 > 10.7.63.6.5735: . ack 302 win 32120 (DF)
23:22:02.968917 10.7.63.4.8080 > 10.7.63.6.5735: P 1:452(451) ack 302 win 32120 (DF)
23:22:02.969571 10.7.63.4.8080 > 10.7.63.6.5735: F 452:452(0) ack 302 win 32120 (DF)
23:22:02.970320 10.7.63.6.5735 > 10.7.63.4.8080: R 1781929244:1781929244(0) win 0
23:22:02.970446 10.7.63.6.5735 > 10.7.63.4.8080: R 1781929244:1781929244(0) win 0
23:22:10.170960 10.7.63.4.1660 > 10.7.63.6.8080: S 4031765920:4031765920(0) win 32120 <mss 1460> (DF)
23:22:10.171624 10.7.63.6.8080 > 10.7.63.4.1660: S 2422489659:2422489659(0) ack 4031765921 win 32736 <mss 1460>
23:22:10.171741 10.7.63.4.1660 > 10.7.63.6.8080: . ack 1 win 32120 (DF)
23:22:10.173215 10.7.63.4.1660 > 10.7.63.6.8080: . 1:101(100) ack 1 win 32120 (DF)
                                                   ^^^^^^^^^ 100 bytes
23:22:10.173317 10.7.63.4.1660 > 10.7.63.6.8080: . 101:201(100) ack 1 win 32120 (DF)
                                                   ^^^^^^^^^ 100 bytes
23:22:10.338739 10.7.63.6.8080 > 10.7.63.4.1660: . ack 201 win 32736 (DF)
23:22:10.338866 10.7.63.4.1660 > 10.7.63.6.8080: P 201:301(100) ack 1 win 32120 (DF)
                                                   ^^^^^^^^^ 100 bytes
23:22:10.338955 10.7.63.4.1660 > 10.7.63.6.8080: F 301:301(0) ack 1 win 32120 (DF)
23:22:10.339621 10.7.63.6.8080 > 10.7.63.4.1660: . ack 302 win 32635 (DF)
23:22:10.347525 10.7.63.6.8080 > 10.7.63.4.1660: P 1:451(450) ack 302 win 32736 (DF)
23:22:10.347625 10.7.63.4.1660 > 10.7.63.6.8080: R 4031766222:4031766222(0) win 0
23:22:10.347553 10.7.63.6.8080 > 10.7.63.4.1660: F 451:451(0) ack 302 win 32736
23:22:10.347693 10.7.63.4.1660 > 10.7.63.6.8080: R 4031766222:4031766222(0) win 0
Motivation: Apache 1.3 uses writev().

Dean

Below is the patch that fixed writev in 2.0.31... I figure it may be useful
to someone wanting to bring it forward to 2.1.x.

--- vanilla/linux/net/ipv4/tcp.c	Wed Apr  9 20:31:10 1997
+++ linux/net/ipv4/tcp.c	Wed May  7 23:11:08 1997
 					sk->write_seq += copy;
 					seglen -= copy;
 				}
-				if (tcp_size >= sk->mss || (flags & MSG_OOB) || !sk->packets_out)
+				/* If we have a full packet or a new OOB message, we have to
+				 * force this packet out.
+				 */
+				if (tcp_size >= sk->mss || (flags & MSG_OOB))
 					tcp_send_skb(sk, skb);
 				else
 					tcp_enqueue_partial(skb, sk);
@@ -1290,8 +1286,14 @@
 
 			delay = 0;
 			tmp = copy + sk->prot->max_header + 15;
-			if (copy < sk->mss && !(flags & MSG_OOB) && sk->packets_out)
-			{
+			/* If won't fill the current packet, and it's not an OOB message,
+			 * then we might want to delay to allow data in the later parts
+			 * of iov to fill this packet out. Note that if we aren't
+			 * Nagling or there are no packets currently out then the top
+			 * level code in tcp_sendmsg() will force any partial packets out
+			 * after we finish building the largest packets this write allows.
+			 */
+			if (copy < sk->mss && !(flags & MSG_OOB)) {
 				tmp = tmp - copy + sk->mtu + 128;
 				delay = 1;
 			}#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <errno.h>

#ifndef INADDR_NONE

#define INADDR_NONE (-1ul)

#endif


void main( int argc, char **argv )

{
    struct sockaddr_in server_addr;
    int s;
    struct iovec vector[3];
    char buf[100];
    int i;
    const int just_say_no = 1;

    if( argc != 3 ) {
usage:
	fprintf( stderr, "usage: test-writev a.b.c.d port#\n" );
	exit( 1 );
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr( argv[1] );
    if( server_addr.sin_addr.s_addr == INADDR_NONE ) {
	fprintf( stderr, "bogus address\n" );
	goto usage;
    }
    server_addr.sin_port = htons( atoi( argv[2] ) );

    s = socket( AF_INET, SOCK_STREAM, 0 );
    if( s < 0 ) {
	perror("socket");
	exit(1);
    }
    if( connect( s, (struct sockaddr *)&server_addr, sizeof( server_addr ) )
	!= 0 ) {
	perror("connect");
	exit(1);
    }

    if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
	sizeof(just_say_no)) != 0 ) {
	perror( "TCP_NODELAY" );
	exit(1);
    }
    /* now build up a two part writev and write it out */
    for( i = 0; i < sizeof( buf ); ++i ) {
	buf[i] = 'x';
    }
    vector[0].iov_base = buf;
    vector[0].iov_len = sizeof(buf);
    vector[1].iov_base = buf;
    vector[1].iov_len = sizeof(buf);
    vector[2].iov_base = buf;
    vector[2].iov_len = sizeof(buf);

    i = writev( s, &vector[0], 3 );
    fprintf( stdout, "i=%d, errno=%d\n", i, errno );
    exit(0);
}
\
 
 \ /
  Last update: 2005-03-22 12:41    [from the cache]
©2003-2008