Messages in this thread Patch in this message | | | Date | Thu, 5 Mar 1998 22:35:37 -0800 (PST) | From | Dean Gaudet <> | Subject | tcp 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); }
| |