]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
quic: enable UDP GRO
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Tue, 25 Jun 2024 09:51:03 +0000 (18:51 +0900)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 26 Jun 2024 13:15:23 +0000 (15:15 +0200)
Closes #14012

lib/cf-socket.c
lib/vquic/vquic.c

index 6ff5bdf22664a30de304f0bc4868a141f3e67584..f58c78e029914dfade02e86e72d2bba945e60703 100644 (file)
@@ -35,6 +35,9 @@
 #elif defined(HAVE_NETINET_TCP_H)
 #include <netinet/tcp.h>
 #endif
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
@@ -1852,6 +1855,9 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   int rc;
+  int one = 1;
+
+  (void)one;
 
   /* QUIC needs a connected socket, nonblocking */
   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
@@ -1892,6 +1898,13 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
   }
 #endif
   }
+
+#if defined(UDP_GRO) && (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) &&  \
+  ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
+  (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
+                   (socklen_t)sizeof(one));
+#endif
+
   return CURLE_OK;
 }
 
index 9ce1e4626d6d6c763a10b9b65de5264808dcda87..1ec2ba249c744a2db3b8af8ce4ab0e65ad636b85 100644 (file)
@@ -36,6 +36,9 @@
 
 #include "curl_setup.h"
 
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
@@ -329,6 +332,36 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data,
   return vquic_flush(cf, data, qctx);
 }
 
+#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)
+static size_t msghdr_get_udp_gro(struct msghdr *msg)
+{
+  uint16_t gso_size = 0;
+#ifdef UDP_GRO
+  struct cmsghdr *cmsg;
+
+  /* Workaround musl CMSG_NXTHDR issue */
+#ifndef __GLIBC__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-compare"
+#pragma clang diagnostic ignored "-Wcast-align"
+#endif
+  for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+#ifndef __GLIBC__
+#pragma clang diagnostic pop
+#endif
+    if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
+      memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size));
+
+      break;
+    }
+  }
+#endif
+  (void)msg;
+
+  return gso_size;
+}
+#endif
+
 #ifdef HAVE_SENDMMSG
 static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
@@ -339,12 +372,16 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
 #define MMSG_NUM  64
   struct iovec msg_iov[MMSG_NUM];
   struct mmsghdr mmsg[MMSG_NUM];
+  uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(uint16_t))];
   uint8_t bufs[MMSG_NUM][2*1024];
   struct sockaddr_storage remote_addr[MMSG_NUM];
   size_t total_nread, pkts;
   int mcount, i, n;
   char errstr[STRERROR_LEN];
   CURLcode result = CURLE_OK;
+  size_t gso_size;
+  size_t pktlen;
+  size_t offset, to;
 
   DEBUGASSERT(max_pkts > 0);
   pkts = 0;
@@ -359,6 +396,8 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
       mmsg[i].msg_hdr.msg_iovlen = 1;
       mmsg[i].msg_hdr.msg_name = &remote_addr[i];
       mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
+      mmsg[i].msg_hdr.msg_control = &msg_ctrl[i];
+      mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
     }
 
     while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
@@ -385,14 +424,30 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
     }
 
     CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
-    pkts += mcount;
     for(i = 0; i < mcount; ++i) {
       total_nread += mmsg[i].msg_len;
-      result = recv_cb(bufs[i], mmsg[i].msg_len,
-                       mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen,
-                       0, userp);
-      if(result)
-        goto out;
+
+      gso_size = msghdr_get_udp_gro(&mmsg[i].msg_hdr);
+      if(gso_size == 0) {
+        gso_size = mmsg[i].msg_len;
+      }
+
+      for(offset = 0; offset < mmsg[i].msg_len; offset = to) {
+        ++pkts;
+
+        to = offset + gso_size;
+        if(to > mmsg[i].msg_len) {
+          pktlen = mmsg[i].msg_len - offset;
+        }
+        else {
+          pktlen = gso_size;
+        }
+
+        result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name,
+                         mmsg[i].msg_hdr.msg_namelen, 0, userp);
+        if(result)
+          goto out;
+      }
     }
   }
 
@@ -418,6 +473,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
   ssize_t nread;
   char errstr[STRERROR_LEN];
   CURLcode result = CURLE_OK;
+  uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))];
+  size_t gso_size;
+  size_t pktlen;
+  size_t offset, to;
 
   msg_iov.iov_base = buf;
   msg_iov.iov_len = (int)sizeof(buf);
@@ -425,11 +484,13 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
   memset(&msg, 0, sizeof(msg));
   msg.msg_iov = &msg_iov;
   msg.msg_iovlen = 1;
+  msg.msg_control = msg_ctrl;
 
   DEBUGASSERT(max_pkts > 0);
   for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
     msg.msg_name = &remote_addr;
     msg.msg_namelen = sizeof(remote_addr);
+    msg.msg_controllen = sizeof(msg_ctrl);
     while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
           SOCKERRNO == EINTR)
       ;
@@ -452,12 +513,29 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
       goto out;
     }
 
-    ++pkts;
     total_nread += (size_t)nread;
-    result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen,
-                     0, userp);
-    if(result)
-      goto out;
+
+    gso_size = msghdr_get_udp_gro(&msg);
+    if(gso_size == 0) {
+      gso_size = (size_t)nread;
+    }
+
+    for(offset = 0; offset < (size_t)nread; offset = to) {
+      ++pkts;
+
+      to = offset + gso_size;
+      if(to > (size_t)nread) {
+        pktlen = (size_t)nread - offset;
+      }
+      else {
+        pktlen = gso_size;
+      }
+
+      result =
+        recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp);
+      if(result)
+        goto out;
+    }
   }
 
 out: