summaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-04-05 11:23:36 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-04-05 11:23:36 +0000
commit4318fbda2a7ee51caafdc4eb1f8028a3f0605142 (patch)
treecddb50a81d7d1a628cc400519162080c6d87868e /net/ipv4/tcp.c
parent36ea5120664550fae6d31f1c6f695e4f8975cb06 (diff)
o Merge with Linux 2.1.91.
o First round of bugfixes for the SC/MC CPUs. o FPU context switch fixes. o Lazy context switches. o Faster syscalls. o Removed dead code. o Shitloads of other things I forgot ...
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r--net/ipv4/tcp.c127
1 files changed, 67 insertions, 60 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b20df83d2..d57b7e3ef 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp.c,v 1.96 1998/03/16 02:25:55 davem Exp $
+ * Version: $Id: tcp.c,v 1.104 1998/03/22 22:10:30 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -668,7 +668,7 @@ static int wait_for_tcp_connect(struct sock * sk, int flags)
return sock_error(sk);
if((1 << sk->state) &
~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
- if(sk->keepopen)
+ if(sk->keepopen && !(flags&MSG_NOSIGNAL))
send_sig(SIGPIPE, tsk, 0);
return -EPIPE;
}
@@ -733,15 +733,25 @@ static void wait_for_tcp_memory(struct sock * sk)
int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int mss_now = sk->mss;
int err = 0;
int copied = 0;
- struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
/* Wait for a connection to finish. */
if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
if((err = wait_for_tcp_connect(sk, flags)) != 0)
return err;
+ /* The socket is locked, nothing can change the state of pending
+ * SACKs or IP options.
+ */
+ if(tp->sack_ok && tp->num_sacks)
+ mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
+ (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+ if(sk->opt && sk->opt->optlen)
+ mss_now -= (sk->opt->optlen);
+
/* Ok commence sending. */
while(--iovlen >= 0) {
int seglen=iov->iov_len;
@@ -769,22 +779,19 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
*/
if (tp->send_head && !(flags & MSG_OOB)) {
skb = sk->write_queue.prev;
- copy = skb->tail -
- ((unsigned char *)(skb->h.th) +
- tp->tcp_header_len);
- /* This window_seq test is somewhat dangerous
- * If the remote does SWS avoidance we should
+ copy = skb->len;
+ /* If the remote does SWS avoidance we should
* queue the best we can if not we should in
* fact send multiple packets...
- * a method for detecting this would be most
- * welcome
+ * A method for detecting this would be most
+ * welcome.
*/
if (skb_tailroom(skb) > 0 &&
- (sk->mss - copy) > 0 &&
+ (mss_now - copy) > 0 &&
tp->snd_nxt < skb->end_seq) {
- int last_byte_was_odd = (copy & 1);
+ int last_byte_was_odd = (copy % 4);
- copy = sk->mss - copy;
+ copy = mss_now - copy;
if(copy > skb_tailroom(skb))
copy = skb_tailroom(skb);
if(copy > seglen)
@@ -793,12 +800,8 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
if(copy_from_user(skb_put(skb, copy),
from, copy))
err = -EFAULT;
- skb->csum = csum_partial(
- (((unsigned char *)skb->h.th) +
- tp->tcp_header_len),
- (skb->tail -
- (((unsigned char *)skb->h.th) +
- tp->tcp_header_len)), 0);
+ skb->csum = csum_partial(skb->data,
+ skb->len, 0);
} else {
skb->csum =
csum_and_copy_from_user(
@@ -810,6 +813,8 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
from += copy;
copied += copy;
seglen -= copy;
+ if(!seglen && !iovlen)
+ TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
continue;
}
}
@@ -828,18 +833,17 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
*/
copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
if(copy >= (tp->max_window >> 1))
- copy = min(copy, sk->mss);
+ copy = min(copy, mss_now);
else
- copy = sk->mss;
+ copy = mss_now;
if(copy > seglen)
copy = seglen;
- tmp = MAX_HEADER + sk->prot->max_header +
- sizeof(struct sk_buff) + 15;
+ tmp = MAX_HEADER + sk->prot->max_header + 15;
queue_it = 0;
- if (copy < min(sk->mss, tp->max_window >> 1) &&
+ if (copy < min(mss_now, tp->max_window >> 1) &&
!(flags & MSG_OOB)) {
- tmp += min(sk->mss, tp->max_window);
+ tmp += min(mss_now, tp->max_window);
/* What is happening here is that we want to
* tack on later members of the users iovec
@@ -869,35 +873,34 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
continue;
}
- /* FIXME: we need to optimize this.
- * Perhaps some hints here would be good.
- */
- tmp = tp->af_specific->build_net_header(sk, skb);
- if (tmp < 0) {
- kfree_skb(skb);
- err = tmp;
- goto do_interrupted;
- }
-
- skb->h.th =(struct tcphdr *)
- skb_put(skb,tp->tcp_header_len);
-
seglen -= copy;
- tcp_build_header_data(skb->h.th, sk, seglen || iovlen);
+ /* Prepare control bits for TCP header creation engine. */
+ TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK |
+ ((!seglen && !iovlen) ?
+ TCPCB_FLAG_PSH : 0));
+ TCP_SKB_CB(skb)->sacked = 0;
if (flags & MSG_OOB) {
- skb->h.th->urg = 1;
- skb->h.th->urg_ptr = ntohs(copy);
- }
-
+ TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_URG;
+ TCP_SKB_CB(skb)->urg_ptr = copy;
+ } else
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* TCP data bytes are SKB_PUT() on top, later
+ * TCP+IP+DEV headers are SKB_PUSH()'d beneath.
+ * Reserve header space and checksum the data.
+ */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
skb->csum = csum_and_copy_from_user(from,
skb_put(skb, copy), copy, 0, &err);
from += copy;
copied += copy;
- tp->write_seq += copy;
+ skb->seq = tp->write_seq;
+ skb->end_seq = skb->seq + copy;
+ /* This advances tp->write_seq for us. */
tcp_send_skb(sk, skb, queue_it);
}
}
@@ -913,7 +916,8 @@ do_sock_err:
do_shutdown:
if(copied)
return copied;
- send_sig(SIGPIPE, current, 0);
+ if (!(flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
return -EPIPE;
do_interrupted:
if(copied)
@@ -1044,9 +1048,20 @@ static void cleanup_rbuf(struct sock *sk, int copied)
/* We send an ACK if we can now advertise a non-zero window
* which has been raised "significantly".
*/
- if((copied > 0) &&
- (copied >= tcp_receive_window(&sk->tp_pinfo.af_tcp)))
- tcp_read_wakeup(sk);
+ if(copied > 0) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ __u32 rcv_window_now = tcp_receive_window(tp);
+
+ /* We won't be raising the window any further than
+ * the window-clamp allows. Our window selection
+ * also keeps things a nice multiple of MSS. These
+ * checks are necessary to prevent spurious ACKs
+ * which don't advertize a larger window.
+ */
+ if((copied >= rcv_window_now) &&
+ ((rcv_window_now + sk->mss) <= tp->window_clamp))
+ tcp_read_wakeup(sk);
+ }
}
@@ -1319,12 +1334,8 @@ static int tcp_close_state(struct sock *sk, int dead)
* that we won't make the old 4*rto = almost no time - whoops
* reset mistake.
*/
- if(dead && ns==TCP_FIN_WAIT2) {
- if(sk->timer.prev && del_timer(&sk->timer))
- add_timer(&sk->timer);
- else
- tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
- }
+ if(dead && ns == TCP_FIN_WAIT2 && !sk->timer.prev)
+ tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
return send_fin;
}
@@ -1448,12 +1459,8 @@ void tcp_close(struct sock *sk, unsigned long timeout)
/* Now that the socket is dead, if we are in the FIN_WAIT2 state
* we may need to set up a timer.
*/
- if (sk->state==TCP_FIN_WAIT2) {
- if(sk->timer.prev && del_timer(&sk->timer))
- add_timer(&sk->timer);
- else
- tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
- }
+ if (sk->state == TCP_FIN_WAIT2 && !sk->timer.prev)
+ tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
sk->dead = 1;
release_sock(sk);