udp: Implement asynchronized pru_connect.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 23 Oct 2015 13:40:23 +0000 (21:40 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 29 Oct 2015 01:26:02 +0000 (09:26 +0800)
This is mainly used to improve UDP connect(2) performance.  Most
of the DNS resolvers (getaddrinfo(3) and curl's DNS resolver, etc)
and RTP application will see improvement.

Before this commit the user space thread uses connect(2) will have
to wait for the netisr to complete all preceeding messages.  This
could be performance hit for connect(2): the user space thread is
suspended, even if the UDP connect(2) should always be nonblocking.

The only drawback is that bunch of datagrams sent immediately after
connect(2) may not appear on the network in the sending order, but
UDP applications should always be able to handle out-of-order data-
grams.

UDP's asynchronous pru_connect implementation will set ISCONNECTED
before dispatching connect message to netisr.  The errors like
EADDRNOTAVAIL, i.e. out of local port space, will be notified
through later send(2)/recv(2), or getsockopt(2) SOL_SOCKET/SO_ERROR.

The measured effect of this change on 2-ways E5-2600v2 with Intel
82599 (10Gbe) using tools/kq_connect_client -u, i.e. UDP socket/
connect/send /close before initiating a TCP connection:
- Connect rate increases by ~45Kconns/s; we are now doing
  300Kconns/s.
- IPIs rate to the CPUs not running netisrs reduces (23Kipis/s ->
  16Kipis/s).

sys/netinet/in_proto.c
sys/netinet/udp_usrreq.c

index 63c8a83..c3a262c 100644 (file)
@@ -113,7 +113,7 @@ struct protosw inetsw[] = {
        .pr_domain = &inetdomain,
        .pr_protocol = IPPROTO_UDP,
        .pr_flags = PR_ATOMIC|PR_ADDR|PR_MPSAFE|
-           PR_ASYNC_SEND|PR_ASEND_HOLDTD,
+           PR_ASYNC_SEND|PR_ASEND_HOLDTD|PR_ACONN_HOLDTD,
 
        .pr_initport = udp_initport,
        .pr_input = udp_input,
index 1a08491..55c2f3a 100644 (file)
@@ -1410,6 +1410,14 @@ udp_bind(netmsg_t msg)
        lwkt_replymsg(&msg->bind.base.lmsg, error);
 }
 
+static int
+udp_preconnect(struct socket *so, const struct sockaddr *nam __unused,
+    struct thread *td __unused)
+{
+       sosetstate(so, SS_ISCONNECTED);         /* XXX */
+       return 0;
+}
+
 static void
 udp_connect(netmsg_t msg)
 {
@@ -1519,6 +1527,12 @@ udp_connect(netmsg_t msg)
        }
        error = udp_connect_oncpu(inp, sin, if_sin);
 out:
+       if (msg->connect.nm_flags & PRUC_HELDTD)
+               lwkt_rele(td);
+       if (error && (msg->connect.nm_flags & PRUC_ASYNC)) {
+               so->so_error = error;
+               soclrstate(so, SS_ISCONNECTED);         /* XXX */
+       }
        if (error && inp != NULL && inp->inp_lport != 0 &&
            (inp->inp_flags & INP_WILDCARD) == 0) {
                boolean_t forwarded;
@@ -1820,5 +1834,6 @@ struct pr_usrreqs udp_usrreqs = {
        .pru_sockaddr = in_setsockaddr_dispatch,
        .pru_sosend = sosendudp,
        .pru_soreceive = soreceive,
+       .pru_preconnect = udp_preconnect,
        .pru_preattach = udp_preattach
 };