Optimise TCP send.

In the DNS TCP code, there are a couple of places where
we have a buffer containing a message which we need to
send via TCP. The DNS protocol is that this is sent as
<16-bit length in network order><message>

Making two write calls, one for the length and one
for the message causes the TCP stack to send two packets,
one for each. A single packet containing both is
preferable from a performance POV.

Implement a scatter-gather version of our read_write()
wrapper and use it where necessary to send TCP DNS messages.
This commit is contained in:
Simon Kelley committed 2026-01-28 14:57:50 +00:00
1 parent 729c16a8ac
commit ed2ba5b07a
3 files changed
+58 -38

No files matched your search

+17 -15
View File
@@ -2029,9 +2029,9 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
int class, rclass, type, rtype;
unsigned char *p;
struct timeval tv;
struct iovec sendio[2];
#ifdef MSG_FASTOPEN
struct msghdr msg;
struct iovec sendio[2];
#endif
(void)mark;
@@ -2044,6 +2044,12 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
GETSHORT(type, p);
GETSHORT(class, p);
length = htons(qsize);
sendio[0].iov_base = &length;
sendio[0].iov_len = sizeof(length);
sendio[1].iov_base = header;
sendio[1].iov_len = qsize;
while (1)
{
int data_sent = 0, fatal = 0;
@@ -2065,8 +2071,6 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
*servp = serv = daemon->serverarray[start];
retry:
length = htons(qsize);
if (serv->tcpfd == -1)
{
if ((serv->tcpfd = socket(serv->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
@@ -2097,12 +2101,7 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
tv.tv_sec += TCP_TIMEOUT;
setsockopt(serv->tcpfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
#ifdef MSG_FASTOPEN
sendio[0].iov_base = (unsigned char *)&length;
sendio[0].iov_len = sizeof(length);
sendio[1].iov_base = (unsigned char *)header;
sendio[1].iov_len = qsize;
msg.msg_name = &serv->addr.sa;
msg.msg_namelen = sa_len(&serv->addr);
msg.msg_iov = sendio;
@@ -2138,11 +2137,9 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
serv->flags &= ~SERV_GOT_TCP;
}
/* We us the _ONCE variant of read_write() here because we've set a timeout on the tcp socket
/* We use the _ONCE variant of read_write() here because we've set a timeout on the tcp socket
and wish to abort if the whole data is not read/written within the timeout. */
if ((!data_sent &&
(!read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_WRITE_ONCE) ||
!read_write(serv->tcpfd, (unsigned char *)header, qsize, RW_WRITE_ONCE))) ||
if ((!data_sent && !read_writev(serv->tcpfd, sendio, 2, RW_WRITE_ONCE)) ||
!read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_READ_ONCE) ||
!expand_buf(recvbuff, (rsize = ntohs(length))) ||
!read_write(serv->tcpfd, recvbuff->iov_base, rsize, RW_READ_ONCE))
@@ -2376,7 +2373,8 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
unsigned int mark = 0;
int have_mark = 0;
int first, last, filtered, do_stale = 0;
struct iovec out_iov[2];
bigbuff->iov_base = NULL;
bigbuff->iov_len = 0;
@@ -2746,9 +2744,13 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
report_addresses(header, m, mark);
#endif
/* use scatter-gather IO so that length doesn't end up in separate packet. */
out_len = htons(m);
if (!read_write(confd, (unsigned char *)&out_len, sizeof(out_len), RW_WRITE) |
!read_write(confd, bigbuff->iov_base, m, RW_WRITE))
out_iov[0].iov_len = sizeof(out_len);
out_iov[0].iov_base = &out_len;
out_iov[1].iov_len = m;
out_iov[1].iov_base = bigbuff->iov_base;
if (!read_writev(confd, out_iov, 2, RW_WRITE))
break;
/* If we answered with stale data, this process will now try and get fresh data into