tcp: Add sosendtcp for further optimization
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 23 Oct 2011 10:30:39 +0000 (18:30 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 23 Oct 2011 10:30:39 +0000 (18:30 +0800)
Currently:
- Cleanup the 'atomic' logic
- Free the 'control' earlier
- Remove the "implied connection" related bits

sys/kern/uipc_socket.c
sys/netinet/tcp_usrreq.c
sys/sys/socketvar.h

index b20eb28..4f33215 100644 (file)
@@ -822,6 +822,159 @@ out:
        return (error);
 }
 
+int
+sosendtcp(struct socket *so, struct sockaddr *addr, struct uio *uio,
+       struct mbuf *top, struct mbuf *control, int flags,
+       struct thread *td)
+{
+       struct mbuf **mp;
+       struct mbuf *m;
+       size_t resid;
+       int space, len;
+       int error, mlen;
+       int allatonce;
+       int pru_flags;
+
+       if (uio) {
+               KKASSERT(top == NULL);
+               allatonce = 0;
+               resid = uio->uio_resid;
+       } else {
+               allatonce = 1;
+               resid = (size_t)top->m_pkthdr.len;
+#ifdef INVARIANTS
+               len = 0;
+               for (m = top; m; m = m->m_next)
+                       len += m->m_len;
+               KKASSERT(top->m_pkthdr.len == len);
+#endif
+       }
+
+       /*
+        * WARNING!  resid is unsigned, space and len are signed.  space
+        *           can wind up negative if the sockbuf is overcommitted.
+        *
+        * Also check to make sure that MSG_EOR isn't used on TCP
+        */
+       if (flags & MSG_EOR) {
+               error = EINVAL;
+               goto out;
+       }
+
+       if (control) {
+               /* TCP doesn't do control messages (rights, creds, etc) */
+               if (control->m_len) {
+                       error = EINVAL;
+                       goto out;
+               }
+               m_freem(control);       /* empty control, just free it */
+               control = NULL;
+       }
+
+       if (td->td_lwp != NULL)
+               td->td_lwp->lwp_ru.ru_msgsnd++;
+
+#define        gotoerr(errcode)        { error = errcode; goto release; }
+
+restart:
+       error = ssb_lock(&so->so_snd, SBLOCKWAIT(flags));
+       if (error)
+               goto out;
+
+       do {
+               if (so->so_state & SS_CANTSENDMORE)
+                       gotoerr(EPIPE);
+               if (so->so_error) {
+                       error = so->so_error;
+                       so->so_error = 0;
+                       goto release;
+               }
+               if ((so->so_state & SS_ISCONNECTED) == 0 &&
+                   (so->so_state & SS_ISCONFIRMING) == 0)
+                       gotoerr(ENOTCONN);
+               if (allatonce && resid > so->so_snd.ssb_hiwat)
+                       gotoerr(EMSGSIZE);
+
+               space = ssb_space(&so->so_snd);
+               if (flags & MSG_OOB)
+                       space += 1024;
+               if ((space < 0 || (size_t)space < resid) && !allatonce &&
+                   space < so->so_snd.ssb_lowat) {
+                       if (flags & (MSG_FNONBLOCKING|MSG_DONTWAIT))
+                               gotoerr(EWOULDBLOCK);
+                       ssb_unlock(&so->so_snd);
+                       error = ssb_wait(&so->so_snd);
+                       if (error)
+                               goto out;
+                       goto restart;
+               }
+               mp = &top;
+               do {
+                   if (uio == NULL) {
+                       /*
+                        * Data is prepackaged in "top".
+                        */
+                       resid = 0;
+                   } else do {
+                       if (resid > INT_MAX)
+                               resid = INT_MAX;
+                       m = m_getl((int)resid, MB_WAIT, MT_DATA,
+                                  top == NULL ? M_PKTHDR : 0, &mlen);
+                       if (top == NULL) {
+                               m->m_pkthdr.len = 0;
+                               m->m_pkthdr.rcvif = NULL;
+                       }
+                       len = imin((int)szmin(mlen, resid), space);
+                       space -= len;
+                       error = uiomove(mtod(m, caddr_t), (size_t)len, uio);
+                       resid = uio->uio_resid;
+                       m->m_len = len;
+                       *mp = m;
+                       top->m_pkthdr.len += len;
+                       if (error)
+                               goto release;
+                       mp = &m->m_next;
+                       if (resid == 0)
+                               break;
+                   } while (space > 0 && 0 /* XXX */);
+
+                   if (flags & MSG_OOB) {
+                           pru_flags = PRUS_OOB;
+                   } else if (resid > 0 && space > 0) {
+                           /* If there is more to send, set PRUS_MORETOCOME */
+                           pru_flags = PRUS_MORETOCOME;
+                   } else {
+                           pru_flags = 0;
+                   }
+
+                   /*
+                    * XXX all the SS_CANTSENDMORE checks previously
+                    * done could be out of date.  We could have recieved
+                    * a reset packet in an interrupt or maybe we slept
+                    * while doing page faults in uiomove() etc. We could
+                    * probably recheck again inside the splnet() protection
+                    * here, but there are probably other places that this
+                    * also happens.  We must rethink this.
+                    */
+                   error = so_pru_send(so, pru_flags, top, NULL, NULL, td);
+
+                   top = NULL;
+                   mp = &top;
+                   if (error)
+                           goto release;
+               } while (resid && space > 0);
+       } while (resid);
+
+release:
+       ssb_unlock(&so->so_snd);
+out:
+       if (top)
+               m_freem(top);
+       if (control)
+               m_freem(control);
+       return (error);
+}
+
 /*
  * Implement receive operations on a socket.
  *
index f23799c..5848a8f 100644 (file)
@@ -735,6 +735,8 @@ tcp_usr_send(netmsg_t msg)
        struct tcpcb *tp;
        TCPDEBUG0;
 
+       KKASSERT(control == NULL);
+
        inp = so->so_pcb;
 
        if (inp == NULL) {
@@ -744,8 +746,6 @@ tcp_usr_send(netmsg_t msg)
                 * network interrupt in the non-critical section of sosend().
                 */
                m_freem(m);
-               if (control)
-                       m_freem(control);
                error = ECONNRESET;     /* XXX EPIPE? */
                tp = NULL;
                TCPDEBUG1();
@@ -753,16 +753,6 @@ tcp_usr_send(netmsg_t msg)
        }
        tp = intotcpcb(inp);
        TCPDEBUG1();
-       if (control) {
-               /* TCP doesn't do control messages (rights, creds, etc) */
-               if (control->m_len) {
-                       m_freem(control);
-                       m_freem(m);
-                       error = EINVAL;
-                       goto out;
-               }
-               m_freem(control);       /* empty control, just free it */
-       }
 
        /*
         * Don't let too much OOB data build up
@@ -882,7 +872,7 @@ struct pr_usrreqs tcp_usrreqs = {
        .pru_sense = pru_sense_null,
        .pru_shutdown = tcp_usr_shutdown,
        .pru_sockaddr = in_setsockaddr_dispatch,
-       .pru_sosend = sosend,
+       .pru_sosend = sosendtcp,
        .pru_soreceive = soreceive
 };
 
@@ -905,7 +895,7 @@ struct pr_usrreqs tcp6_usrreqs = {
        .pru_sense = pru_sense_null,
        .pru_shutdown = tcp_usr_shutdown,
        .pru_sockaddr = in6_mapped_sockaddr_dispatch,
-       .pru_sosend = sosend,
+       .pru_sosend = sosendtcp,
        .pru_soreceive = soreceive
 };
 #endif /* INET6 */
index f5ac9a7..5f9c440 100644 (file)
@@ -437,6 +437,9 @@ int sosend (struct socket *so, struct sockaddr *addr, struct uio *uio,
 int    sosendudp (struct socket *so, struct sockaddr *addr, struct uio *uio,
                    struct mbuf *top, struct mbuf *control, int flags,
                    struct thread *td);
+int    sosendtcp (struct socket *so, struct sockaddr *addr, struct uio *uio,
+                   struct mbuf *top, struct mbuf *control, int flags,
+                   struct thread *td);
 int    sosetopt (struct socket *so, struct sockopt *sopt);
 int    soshutdown (struct socket *so, int how);
 void   sotoxsocket (struct socket *so, struct xsocket *xso);