Index: sys/kern/kern_sendfile.c =================================================================== --- sys/kern/kern_sendfile.c +++ sys/kern/kern_sendfile.c @@ -866,9 +866,16 @@ CURVNET_RESTORE(); m = NULL; /* pru_send always consumes */ + /* if pru_send errored, then it didn't add data to the socket */ if (error) goto done; sbytes += space + hdrlen; + /* pru_send stores errors after adding data to the socket here */ + if (so->so_error) { + error = so->so_error; + so->so_error = 0; + goto done; + } if (hdrlen) hdrlen = 0; if (softerr) { Index: sys/netinet/tcp_usrreq.c =================================================================== --- sys/netinet/tcp_usrreq.c +++ sys/netinet/tcp_usrreq.c @@ -877,13 +877,19 @@ * marker if URG set. Possibly send more data. Unlike the other * pru_*() routines, the mbuf chains are our responsibility. We * must either enqueue them or free them. The other pru_* routines - * generally are caller-frees. + * generally are caller-frees. We must either add the mbuf chain + * to the socket send buffer or return an error, not both. We + * ensure that after adding the mbufs to the send buffer, we will + * not return any more errors. Errors from tcp_output are not + * passed back to the caller, but are instead deposited in + * so->so_error for the next call. */ static int tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct thread *td) { int error = 0; + int so_error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; #ifdef INET6 @@ -929,7 +935,6 @@ m_freem(control); /* empty control, just free it */ } if (!(flags & PRUS_OOB)) { - sbappendstream(&so->so_snd, m, flags); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, @@ -951,6 +956,7 @@ tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); } + sbappendstream(&so->so_snd, m, flags); if (flags & PRUS_EOF) { /* * Close the send side of the connection after @@ -964,7 +970,10 @@ !(flags & PRUS_NOTREADY)) { if (flags & PRUS_MORETOCOME) tp->t_flags |= TF_MORETOCOME; - error = tp->t_fb->tfb_tcp_output(tp); + so_error = tp->t_fb->tfb_tcp_output(tp); + /* next socket syscall call will return this error */ + if (so_error && !so->so_error) + so->so_error = so_error; if (flags & PRUS_MORETOCOME) tp->t_flags &= ~TF_MORETOCOME; } @@ -972,23 +981,6 @@ /* * XXXRW: PRUS_EOF not implemented with PRUS_OOB? */ - SOCKBUF_LOCK(&so->so_snd); - if (sbspace(&so->so_snd) < -512) { - SOCKBUF_UNLOCK(&so->so_snd); - m_freem(m); - error = ENOBUFS; - goto out; - } - /* - * According to RFC961 (Assigned Protocols), - * the urgent pointer points to the last octet - * of urgent data. We continue, however, - * to consider it to indicate the first octet - * of data past the urgent section. - * Otherwise, snd_up should be one lower. - */ - sbappendstream_locked(&so->so_snd, m, flags); - SOCKBUF_UNLOCK(&so->so_snd); if (nam && tp->t_state < TCPS_SYN_SENT) { /* * Do implied connect if not yet connected, @@ -1010,10 +1002,30 @@ tp->snd_wnd = TTCP_CLIENT_SND_WND; tcp_mss(tp, -1); } + SOCKBUF_LOCK(&so->so_snd); + if (sbspace(&so->so_snd) < -512) { + SOCKBUF_UNLOCK(&so->so_snd); + m_freem(m); + error = ENOBUFS; + goto out; + } + /* + * According to RFC961 (Assigned Protocols), + * the urgent pointer points to the last octet + * of urgent data. We continue, however, + * to consider it to indicate the first octet + * of data past the urgent section. + * Otherwise, snd_up should be one lower. + */ + sbappendstream_locked(&so->so_snd, m, flags); tp->snd_up = tp->snd_una + sbavail(&so->so_snd); + SOCKBUF_UNLOCK(&so->so_snd); if (!(flags & PRUS_NOTREADY)) { tp->t_flags |= TF_FORCEDATA; - error = tp->t_fb->tfb_tcp_output(tp); + so_error = tp->t_fb->tfb_tcp_output(tp); + /* next socket syscall call will return this error */ + if (so_error && !so->so_error) + so->so_error = so_error; tp->t_flags &= ~TF_FORCEDATA; } }