Skip to content
Snippets Groups Projects
tcp_output.c 88.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    int tcp_write_wakeup(struct sock *sk)
    {
    
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct sk_buff *skb;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (sk->sk_state == TCP_CLOSE)
    		return -1;
    
    	if ((skb = tcp_send_head(sk)) != NULL &&
    	    before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) {
    		int err;
    
    		unsigned int mss = tcp_current_mss(sk);
    
    		unsigned int seg_size = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
    
    		if (before(tp->pushed_seq, TCP_SKB_CB(skb)->end_seq))
    			tp->pushed_seq = TCP_SKB_CB(skb)->end_seq;
    
    		/* We are probing the opening of a window
    		 * but the window size is != 0
    		 * must have been a result SWS avoidance ( sender )
    		 */
    		if (seg_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq ||
    		    skb->len > mss) {
    			seg_size = min(seg_size, mss);
    
    			TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH;
    
    			if (tcp_fragment(sk, skb, seg_size, mss))
    				return -1;
    		} else if (!tcp_skb_pcount(skb))
    			tcp_set_skb_tso_segs(sk, skb, mss);
    
    
    		TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH;
    
    		TCP_SKB_CB(skb)->when = tcp_time_stamp;
    		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
    		if (!err)
    			tcp_event_new_data_sent(sk, skb);
    		return err;
    	} else {
    
    		if (between(tp->snd_up, tp->snd_una + 1, tp->snd_una + 0xFFFF))
    
    			tcp_xmit_probe_skb(sk, 1);
    		return tcp_xmit_probe_skb(sk, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    }
    
    /* A window probe timeout has occurred.  If window is not closed send
     * a partial packet else a zero probe.
     */
    void tcp_send_probe0(struct sock *sk)
    {
    
    	struct inet_connection_sock *icsk = inet_csk(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct tcp_sock *tp = tcp_sk(sk);
    	int err;
    
    	err = tcp_write_wakeup(sk);
    
    
    	if (tp->packets_out || !tcp_send_head(sk)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/* Cancel probe timer, if it is not required. */
    
    		icsk->icsk_backoff = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return;
    	}
    
    	if (err <= 0) {
    
    		if (icsk->icsk_backoff < sysctl_tcp_retries2)
    			icsk->icsk_backoff++;
    
    		inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
    
    					  min(icsk->icsk_rto << icsk->icsk_backoff, TCP_RTO_MAX),
    					  TCP_RTO_MAX);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else {
    		/* If packet was not sent due to local congestion,
    
    		 * do not backoff and do not remember icsk_probes_out.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		 * Let local senders to fight for local resources.
    		 *
    		 * Use accumulated backoff yet.
    		 */
    
    		if (!icsk->icsk_probes_out)
    			icsk->icsk_probes_out = 1;
    
    		inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
    
    					  min(icsk->icsk_rto << icsk->icsk_backoff,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    }