tcp: Make setsockopt(2) TCP_{NODELAY,NOPUSH,NOOPT,FASTKEEP} asynchronous
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 26 May 2016 13:22:58 +0000 (21:22 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 26 May 2016 13:22:58 +0000 (21:22 +0800)
This makes all network system calls used by nginx asynchronous and greatly
improves nginx performance.

For 1 requests/connection (30K concurrent connections), this commit gives
12% improvement for 16KB web object (was 16.8Gbps, now 19.2Gbps, which
maxes out 2x10Ge).  And it gives 19% improvement for 1KB web object (was
156Kreqs/s, now 186Kreqs/s).

sys/kern/uipc_msg.c
sys/net/netmsg.h
sys/netinet/in_proto.c
sys/netinet/tcp_usrreq.c
sys/netinet/tcp_var.h
sys/sys/protosw.h

index f73afba..713206d 100644 (file)
@@ -527,8 +527,25 @@ so_pr_ctloutput(struct socket *so, struct sockopt *sopt)
        int error;
 
        KKASSERT(!sopt->sopt_val || kva_p(sopt->sopt_val));
+
+       if (sopt->sopt_dir == SOPT_SET && so->so_proto->pr_ctloutmsg != NULL) {
+               struct netmsg_pr_ctloutput *amsg;
+
+               /* Fast path: asynchronous pr_ctloutput */
+               amsg = so->so_proto->pr_ctloutmsg(sopt);
+               if (amsg != NULL) {
+                       netmsg_init(&amsg->base, so, &netisr_afree_rport, 0,
+                           so->so_proto->pr_ctloutput);
+                       /* nm_flags and nm_sopt are setup by pr_ctloutmsg */
+                       lwkt_sendmsg(so->so_port, &amsg->base.lmsg);
+                       return 0;
+               }
+               /* FALLTHROUGH */
+       }
+
        netmsg_init(&msg.base, so, &curthread->td_msgport,
                    0, so->so_proto->pr_ctloutput);
+       msg.nm_flags = 0;
        msg.nm_sopt = sopt;
        error = lwkt_domsg(so->so_port, &msg.base.lmsg, 0);
        return (error);
index f276328..f81ac44 100644 (file)
@@ -267,9 +267,12 @@ struct netmsg_pru_soreceive {
 
 struct netmsg_pr_ctloutput {
        struct netmsg_base      base;
+       int                     nm_flags;       /* PRCO_xx */
        struct sockopt          *nm_sopt;
 };
 
+#define PRCO_HELDTD            0x1
+
 struct netmsg_pr_ctlinput {
        struct netmsg_base      base;
        int                     nm_cmd;
index c3a262c..33620b5 100644 (file)
@@ -136,6 +136,7 @@ struct protosw inetsw[] = {
        .pr_input = tcp_input,
        .pr_output = NULL,
        .pr_ctlinput = tcp_ctlinput,
+       .pr_ctloutmsg = tcp_ctloutmsg,
        .pr_ctloutput = tcp_ctloutput,
 
        .pr_ctlport = tcp_ctlport,
index c83d539..5c7f9a7 100644 (file)
@@ -1440,10 +1440,14 @@ tcp_ctloutput(netmsg_t msg)
 {
        struct socket *so = msg->base.nm_so;
        struct sockopt *sopt = msg->ctloutput.nm_sopt;
+       struct thread *td = NULL;
        int     error, opt, optval, opthz;
        struct  inpcb *inp;
        struct  tcpcb *tp;
 
+       if (msg->ctloutput.nm_flags & PRCO_HELDTD)
+               td = sopt->sopt_td;
+
        error = 0;
        inp = so->so_pcb;
        if (inp == NULL) {
@@ -1497,6 +1501,8 @@ tcp_ctloutput(netmsg_t msg)
 #endif /* INET6 */
                ip_ctloutput(msg);
                /* msg invalid now */
+               if (td != NULL)
+                       lwkt_rele(td);
                return;
        }
 
@@ -1666,9 +1672,71 @@ tcp_ctloutput(netmsg_t msg)
                break;
        }
 done:
+       if (td != NULL)
+               lwkt_rele(td);
        lwkt_replymsg(&msg->lmsg, error);
 }
 
+struct netmsg_tcp_ctloutput {
+       struct netmsg_pr_ctloutput ctloutput;
+       struct sockopt          sopt;
+       int                     sopt_val;
+};
+
+/*
+ * Allocate netmsg_pr_ctloutput for asynchronous tcp_ctloutput.
+ */
+struct netmsg_pr_ctloutput *
+tcp_ctloutmsg(struct sockopt *sopt)
+{
+       struct netmsg_tcp_ctloutput *msg;
+       int flags = 0, error;
+
+       KASSERT(sopt->sopt_dir == SOPT_SET, ("not from ctloutput"));
+
+       /* Only small set of options allows asynchronous setting. */
+       if (sopt->sopt_level != IPPROTO_TCP)
+               return NULL;
+       switch (sopt->sopt_name) {
+       case TCP_NODELAY:
+       case TCP_NOOPT:
+       case TCP_NOPUSH:
+       case TCP_FASTKEEP:
+               break;
+       default:
+               return NULL;
+       }
+
+       msg = kmalloc(sizeof(*msg), M_LWKTMSG, M_WAITOK | M_NULLOK);
+       if (msg == NULL) {
+               /* Fallback to synchronous tcp_ctloutput */
+               return NULL;
+       }
+
+       /* Save the sockopt */
+       msg->sopt = *sopt;
+
+       /* Fixup the sopt.sopt_val ptr */
+       error = sooptcopyin(sopt, &msg->sopt_val,
+           sizeof(msg->sopt_val), sizeof(msg->sopt_val));
+       if (error) {
+               kfree(msg, M_LWKTMSG);
+               return NULL;
+       }
+       msg->sopt.sopt_val = &msg->sopt_val;
+
+       /* Hold the current thread */
+       if (msg->sopt.sopt_td != NULL) {
+               flags |= PRCO_HELDTD;
+               lwkt_hold(msg->sopt.sopt_td);
+       }
+
+       msg->ctloutput.nm_flags = flags;
+       msg->ctloutput.nm_sopt = &msg->sopt;
+
+       return &msg->ctloutput;
+}
+
 /*
  * tcp_sendspace and tcp_recvspace are the default send and receive window
  * sizes, respectively.  These are obsolescent (this information should
index e05f719..4f27cec 100644 (file)
@@ -654,6 +654,7 @@ struct tcpcb *
         tcp_close (struct tcpcb *);
 void    tcp_ctlinput(union netmsg *);
 void    tcp_ctloutput(union netmsg *);
+struct netmsg_pr_ctloutput *tcp_ctloutmsg(struct sockopt *);
 inp_notify_t tcp_get_inpnotify(int cmd, const struct sockaddr *sa,
            int *arg, struct ip **ip0, int *cpuid);
 struct tcpcb *
index 0ed5ce2..11518a5 100644 (file)
@@ -103,6 +103,8 @@ struct protosw {
                                        /* output to protocol (from above) */
        void    (*pr_ctlinput)(netmsg_t);
                                        /* control input (from below) */
+       struct netmsg_pr_ctloutput *(*pr_ctloutmsg)(struct sockopt *);
+                                       /* allocate netmsg for ctloutput */
        void    (*pr_ctloutput)(netmsg_t);
                                        /* control output (from above) */
        struct lwkt_port *(*pr_ctlport)(int, struct sockaddr *, void *, int *);