Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I have taken a look at the kernel source and both assumptions seem to be true. The following code are extracts from <code>net/ipv4/tcp.c</code> (2.6.33.1).</p> <pre><code>static inline void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle) { struct tcp_sock *tp = tcp_sk(sk); if (tcp_send_head(sk)) { struct sk_buff *skb = tcp_write_queue_tail(sk); if (!(flags &amp; MSG_MORE) || forced_push(tp)) tcp_mark_push(tp, skb); tcp_mark_urg(tp, flags, skb); __tcp_push_pending_frames(sk, mss_now, (flags &amp; MSG_MORE) ? TCP_NAGLE_CORK : nonagle); } } </code></pre> <p>Hence, if the flag is <em>not</em> set, the pending frames will definitely be flushed. But this is be <strong>only the case when the buffer is not empty</strong>:</p> <pre><code>static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags) { (...) ssize_t copied; (...) copied = 0; while (psize &gt; 0) { (...) if (forced_push(tp)) { tcp_mark_push(tp, skb); __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH); } else if (skb == tcp_send_head(sk)) tcp_push_one(sk, mss_now); continue; wait_for_sndbuf: set_bit(SOCK_NOSPACE, &amp;sk-&gt;sk_socket-&gt;flags); wait_for_memory: if (copied) tcp_push(sk, flags &amp; ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); if ((err = sk_stream_wait_memory(sk, &amp;timeo)) != 0) goto do_error; mss_now = tcp_send_mss(sk, &amp;size_goal, flags); } out: if (copied) tcp_push(sk, flags, mss_now, tp-&gt;nonagle); return copied; do_error: if (copied) goto out; out_err: return sk_stream_error(sk, flags, err); } </code></pre> <p>The <code>while</code> loop's body will never be executed because <code>psize</code> is not greater 0. Then, in the <code>out</code> section, there is another chance, <code>tcp_push()</code> gets called but because <code>copied</code> still has its default value, it will fail as well.</p> <p>So sending a packet with the length 0 will never result in a flush.</p> <p>The next theory was to re-apply <code>TCP_CORK</code>. Let's take a look at the code first:</p> <pre><code>static int do_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { (...) switch (optname) { (...) case TCP_NODELAY: if (val) { /* TCP_NODELAY is weaker than TCP_CORK, so that * this option on corked socket is remembered, but * it is not activated until cork is cleared. * * However, when TCP_NODELAY is set we make * an explicit push, which overrides even TCP_CORK * for currently queued segments. */ tp-&gt;nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH; tcp_push_pending_frames(sk); } else { tp-&gt;nonagle &amp;= ~TCP_NAGLE_OFF; } break; case TCP_CORK: /* When set indicates to always queue non-full frames. * Later the user clears this option and we transmit * any pending partial frames in the queue. This is * meant to be used alongside sendfile() to get properly * filled frames when the user (for example) must write * out headers with a write() call first and then use * sendfile to send out the data parts. * * TCP_CORK can be set together with TCP_NODELAY and it is * stronger than TCP_NODELAY. */ if (val) { tp-&gt;nonagle |= TCP_NAGLE_CORK; } else { tp-&gt;nonagle &amp;= ~TCP_NAGLE_CORK; if (tp-&gt;nonagle&amp;TCP_NAGLE_OFF) tp-&gt;nonagle |= TCP_NAGLE_PUSH; tcp_push_pending_frames(sk); } break; (...) </code></pre> <p>As you can see, there are two ways to flush. You can either set <code>TCP_NODELAY</code> to 1 or <code>TCP_CORK</code> to 0. Luckily, both won't check whether the flag is already set. Thus, my initial plan to re-apply the <code>TCP_CORK</code> flag can be optimized to just disable it, even if it's currently not set.</p> <p>I hope this helps someone with similar issues.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload