lkml.org 
[lkml]   [1998]   [Mar]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
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 13:41    [W:0.041 / U:0.548 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site