Internal change

PiperOrigin-RevId: 465087216
diff --git a/quiche/quic/core/quic_udp_socket.h b/quiche/quic/core/quic_udp_socket.h
index 441ceef..6e3199d 100644
--- a/quiche/quic/core/quic_udp_socket.h
+++ b/quiche/quic/core/quic_udp_socket.h
@@ -15,6 +15,10 @@
 #include "quiche/quic/platform/api/quic_ip_address.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
 
+#ifndef UDP_GRO
+#define UDP_GRO 104
+#endif
+
 namespace quic {
 
 using QuicUdpSocketFd = SocketFd;
@@ -31,6 +35,7 @@
   TTL,                   // Read & Write
   GOOGLE_PACKET_HEADER,  // Read
   NUM_BITS,
+  IS_GRO,  // Read
 };
 static_assert(static_cast<size_t>(QuicUdpPacketInfoBit::NUM_BITS) <=
                   BitMask64::NumBits(),
@@ -70,6 +75,13 @@
     bitmask_.Set(QuicUdpPacketInfoBit::DROPPED_PACKETS);
   }
 
+  void set_gso_size(size_t gso_size) {
+    gso_size_ = gso_size;
+    bitmask_.Set(QuicUdpPacketInfoBit::IS_GRO);
+  }
+
+  size_t gso_size() { return gso_size_; }
+
   const QuicIpAddress& self_v4_ip() const {
     QUICHE_DCHECK(HasValue(QuicUdpPacketInfoBit::V4_SELF_IP));
     return self_v4_ip_;
@@ -147,6 +159,7 @@
   QuicWallTime receive_timestamp_ = QuicWallTime::Zero();
   int ttl_;
   BufferSpan google_packet_headers_;
+  size_t gso_size_ = 0;
 };
 
 // QuicUdpSocketApi provides a minimal set of apis for sending and receiving
diff --git a/quiche/quic/core/quic_udp_socket_posix.cc b/quiche/quic/core/quic_udp_socket_posix.cc
index 8a7fa77..f6eac60 100644
--- a/quiche/quic/core/quic_udp_socket_posix.cc
+++ b/quiche/quic/core/quic_udp_socket_posix.cc
@@ -83,6 +83,13 @@
 void PopulatePacketInfoFromControlMessage(struct cmsghdr* cmsg,
                                           QuicUdpPacketInfo* packet_info,
                                           BitMask64 packet_info_interested) {
+#ifdef SOL_UDP
+  if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::IS_GRO) &&
+      cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
+    packet_info->set_gso_size(*reinterpret_cast<uint16_t*>(CMSG_DATA(cmsg)));
+  }
+#endif
+
 #if defined(__linux__) && defined(SO_RXQ_OVFL)
   if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_RXQ_OVFL) {
     if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::DROPPED_PACKETS)) {
@@ -427,87 +434,102 @@
                                              BitMask64 packet_info_interested,
                                              ReadPacketResults* results) {
 #if defined(__linux__) && !defined(__ANDROID__)
-  // Use recvmmsg.
-  size_t hdrs_size = sizeof(mmsghdr) * results->size();
-  mmsghdr* hdrs = static_cast<mmsghdr*>(alloca(hdrs_size));
-  memset(hdrs, 0, hdrs_size);
-
-  struct TempPerPacketData {
-    iovec iov;
-    sockaddr_storage raw_peer_address;
-  };
-  TempPerPacketData* packet_data_array = static_cast<TempPerPacketData*>(
-      alloca(sizeof(TempPerPacketData) * results->size()));
-
-  for (size_t i = 0; i < results->size(); ++i) {
-    (*results)[i].ok = false;
-
-    msghdr* hdr = &hdrs[i].msg_hdr;
-    TempPerPacketData* packet_data = &packet_data_array[i];
-    packet_data->iov.iov_base = (*results)[i].packet_buffer.buffer;
-    packet_data->iov.iov_len = (*results)[i].packet_buffer.buffer_len;
-
-    hdr->msg_name = &packet_data->raw_peer_address;
-    hdr->msg_namelen = sizeof(sockaddr_storage);
-    hdr->msg_iov = &packet_data->iov;
-    hdr->msg_iovlen = 1;
-    hdr->msg_flags = 0;
-    hdr->msg_control = (*results)[i].control_buffer.buffer;
-    hdr->msg_controllen = (*results)[i].control_buffer.buffer_len;
-
-    QUICHE_DCHECK_GE(hdr->msg_controllen, kMinCmsgSpaceForRead);
-  }
-  // If MSG_TRUNC is set on Linux, recvmmsg will return the real packet size in
-  // |hdrs[i].msg_len| even if packet buffer is too small to receive it.
-  int packets_read = recvmmsg(fd, hdrs, results->size(), MSG_TRUNC, nullptr);
-  if (packets_read <= 0) {
-    const int error_num = errno;
-    if (error_num != EAGAIN) {
-      QUIC_LOG_FIRST_N(ERROR, 100)
-          << "Error reading packets: " << strerror(error_num);
+  if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::IS_GRO)) {
+    size_t num_packets = 0;
+    for (ReadPacketResult& result : *results) {
+      result.ok = false;
     }
-    return 0;
-  }
+    for (ReadPacketResult& result : *results) {
+      ReadPacket(fd, packet_info_interested, &result);
+      if (!result.ok) {
+        break;
+      }
+      ++num_packets;
+    }
+    return num_packets;
+  } else {
+    // Use recvmmsg.
+    size_t hdrs_size = sizeof(mmsghdr) * results->size();
+    mmsghdr* hdrs = static_cast<mmsghdr*>(alloca(hdrs_size));
+    memset(hdrs, 0, hdrs_size);
 
-  for (int i = 0; i < packets_read; ++i) {
-    if (hdrs[i].msg_len == 0) {
-      continue;
+    struct TempPerPacketData {
+      iovec iov;
+      sockaddr_storage raw_peer_address;
+    };
+    TempPerPacketData* packet_data_array = static_cast<TempPerPacketData*>(
+        alloca(sizeof(TempPerPacketData) * results->size()));
+
+    for (size_t i = 0; i < results->size(); ++i) {
+      (*results)[i].ok = false;
+
+      msghdr* hdr = &hdrs[i].msg_hdr;
+      TempPerPacketData* packet_data = &packet_data_array[i];
+      packet_data->iov.iov_base = (*results)[i].packet_buffer.buffer;
+      packet_data->iov.iov_len = (*results)[i].packet_buffer.buffer_len;
+
+      hdr->msg_name = &packet_data->raw_peer_address;
+      hdr->msg_namelen = sizeof(sockaddr_storage);
+      hdr->msg_iov = &packet_data->iov;
+      hdr->msg_iovlen = 1;
+      hdr->msg_flags = 0;
+      hdr->msg_control = (*results)[i].control_buffer.buffer;
+      hdr->msg_controllen = (*results)[i].control_buffer.buffer_len;
+
+      QUICHE_DCHECK_GE(hdr->msg_controllen, kMinCmsgSpaceForRead);
+    }
+    // If MSG_TRUNC is set on Linux, recvmmsg will return the real packet size
+    // in |hdrs[i].msg_len| even if packet buffer is too small to receive it.
+    int packets_read = recvmmsg(fd, hdrs, results->size(), MSG_TRUNC, nullptr);
+    if (packets_read <= 0) {
+      const int error_num = errno;
+      if (error_num != EAGAIN) {
+        QUIC_LOG_FIRST_N(ERROR, 100)
+            << "Error reading packets: " << strerror(error_num);
+      }
+      return 0;
     }
 
-    msghdr& hdr = hdrs[i].msg_hdr;
-    if (ABSL_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) {
-      QUIC_BUG(quic_bug_10751_4) << "Control buffer too small. size:"
-                                 << (*results)[i].control_buffer.buffer_len
-                                 << ", need:" << hdr.msg_controllen;
-      continue;
-    }
+    for (int i = 0; i < packets_read; ++i) {
+      if (hdrs[i].msg_len == 0) {
+        continue;
+      }
 
-    if (ABSL_PREDICT_FALSE(hdr.msg_flags & MSG_TRUNC)) {
-      QUIC_LOG_FIRST_N(WARNING, 100)
-          << "Received truncated QUIC packet: buffer size:"
-          << (*results)[i].packet_buffer.buffer_len
-          << " packet size:" << hdrs[i].msg_len;
-      continue;
-    }
+      msghdr& hdr = hdrs[i].msg_hdr;
+      if (ABSL_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) {
+        QUIC_BUG(quic_bug_10751_4) << "Control buffer too small. size:"
+                                   << (*results)[i].control_buffer.buffer_len
+                                   << ", need:" << hdr.msg_controllen;
+        continue;
+      }
 
-    (*results)[i].ok = true;
-    (*results)[i].packet_buffer.buffer_len = hdrs[i].msg_len;
+      if (ABSL_PREDICT_FALSE(hdr.msg_flags & MSG_TRUNC)) {
+        QUIC_LOG_FIRST_N(WARNING, 100)
+            << "Received truncated QUIC packet: buffer size:"
+            << (*results)[i].packet_buffer.buffer_len
+            << " packet size:" << hdrs[i].msg_len;
+        continue;
+      }
 
-    QuicUdpPacketInfo* packet_info = &(*results)[i].packet_info;
-    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) {
-      packet_info->SetPeerAddress(
-          QuicSocketAddress(packet_data_array[i].raw_peer_address));
-    }
+      (*results)[i].ok = true;
+      (*results)[i].packet_buffer.buffer_len = hdrs[i].msg_len;
 
-    if (hdr.msg_controllen > 0) {
-      for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg != nullptr;
-           cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
-        PopulatePacketInfoFromControlMessage(cmsg, packet_info,
-                                             packet_info_interested);
+      QuicUdpPacketInfo* packet_info = &(*results)[i].packet_info;
+      if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) {
+        packet_info->SetPeerAddress(
+            QuicSocketAddress(packet_data_array[i].raw_peer_address));
+      }
+
+      if (hdr.msg_controllen > 0) {
+        for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg != nullptr;
+             cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
+          PopulatePacketInfoFromControlMessage(cmsg, packet_info,
+                                               packet_info_interested);
+        }
       }
     }
+    return packets_read;
   }
-  return packets_read;
 #else
   size_t num_packets = 0;
   for (ReadPacketResult& result : *results) {