On Fri, Nov 13, 2020 at 05:53:53PM +0100, Florian Obser wrote:
> The recent fix for handling large (about 16k) answers in unwind has
> the downside that we are now always copying at least 16k per answer
> between the resolver and frontend process.
> That seems wasteful.
>
> This re-arranges things to only copy what its needed.
>
> Tests, OKs?
>
Anyone? So far I had one (1) test report.
diff --git frontend.c frontend.c
index 8adbb07219b..74715087c5f 100644
--- frontend.c
+++ frontend.c
@@ -86,7 +86,8 @@ struct pending_query {
int fd;
int bogus;
int rcode_override;
- ssize_t answer_len;
+ int answer_len;
+ int received;
uint8_t *answer;
};
@@ -424,10 +425,7 @@ frontend_dispatch_resolver(int fd, short event, void *bula)
struct imsgev *iev = bula;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
- struct query_imsg *query_imsg;
- struct answer_imsg *answer_imsg;
int n, shut = 0, chg;
- uint8_t *p;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -449,45 +447,30 @@ frontend_dispatch_resolver(int fd, short event, void
*bula)
break;
switch (imsg.hdr.type) {
- case IMSG_ANSWER_HEADER:
- if (IMSG_DATA_SIZE(imsg) != sizeof(*query_imsg))
- fatalx("%s: IMSG_ANSWER_HEADER wrong length: "
- "%lu", __func__, IMSG_DATA_SIZE(imsg));
- query_imsg = (struct query_imsg *)imsg.data;
- if ((pq = find_pending_query(query_imsg->id)) ==
- NULL) {
- log_warnx("cannot find pending query %llu",
- query_imsg->id);
- break;
- }
- if (query_imsg->err) {
- send_answer(pq);
- pq = NULL;
- break;
- }
- pq->bogus = query_imsg->bogus;
- break;
- case IMSG_ANSWER:
- if (IMSG_DATA_SIZE(imsg) != sizeof(*answer_imsg))
+ case IMSG_ANSWER: {
+ struct answer_header *answer_header;
+ int data_len;
+ uint8_t *data;
+
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*answer_header))
fatalx("%s: IMSG_ANSWER wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
- answer_imsg = (struct answer_imsg *)imsg.data;
- if ((pq = find_pending_query(answer_imsg->id)) ==
+ answer_header = (struct answer_header *)imsg.data;
+ data = (uint8_t *)imsg.data + sizeof(*answer_header);
+ if (answer_header->answer_len > 65535)
+ fatalx("%s: IMSG_ANSWER answer too big: %d",
+ __func__, answer_header->answer_len);
+ data_len = IMSG_DATA_SIZE(imsg) -
+ sizeof(*answer_header);
+
+ if ((pq = find_pending_query(answer_header->id)) ==
NULL) {
- log_warnx("cannot find pending query %llu",
- answer_imsg->id);
+ log_warnx("%s: cannot find pending query %llu",
+ __func__, answer_header->id);
break;
}
- p = realloc(pq->answer, pq->answer_len +
- answer_imsg->len);
-
- if (p != NULL) {
- pq->answer = p;
- memcpy(pq->answer + pq->answer_len,
- answer_imsg->answer, answer_imsg->len);
- pq->answer_len += answer_imsg->len;
- } else {
+ if (answer_header->srvfail) {
free(pq->answer);
pq->answer_len = 0;
pq->answer = NULL;
@@ -495,9 +478,32 @@ frontend_dispatch_resolver(int fd, short event, void *bula)
send_answer(pq);
break;
}
- if (!answer_imsg->truncated)
+
+ if (pq->answer == NULL) {
+ pq->answer = malloc(answer_header->answer_len);
+ if (pq->answer == NULL) {
+ pq->answer_len = 0;
+ pq->rcode_override =
+ LDNS_RCODE_SERVFAIL;
+ send_answer(pq);
+ break;
+ }
+ pq->answer_len = answer_header->answer_len;
+ pq->received = 0;
+ pq->bogus = answer_header->bogus;
+ }
+
+ if (pq->received + data_len > pq->answer_len)
+ fatalx("%s: IMSG_ANSWER answer too big: %d",
+ __func__, data_len);
+
+ memcpy(pq->answer + pq->received, data, data_len);
+ pq->received += data_len;
+
+ if (pq->received == pq->answer_len)
send_answer(pq);
break;
+ }
case IMSG_CTL_RESOLVER_INFO:
case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
case IMSG_CTL_MEM_INFO:
diff --git resolver.c resolver.c
index fb34dee87d6..c3faba08faf 100644
--- resolver.c
+++ resolver.c
@@ -884,7 +884,7 @@ resolve_done(struct uw_resolver *res, void *arg, int rcode,
sldns_buffer *buf = NULL;
struct regional *region = NULL;
struct query_imsg *query_imsg;
- struct answer_imsg answer_imsg;
+ struct answer_header *answer_header;
struct running_query *rq;
struct timespec tp, elapsed;
int64_t ms;
@@ -894,12 +894,19 @@ resolve_done(struct uw_resolver *res, void *arg, int
rcode,
char rcode_buf[16];
char qclass_buf[16];
char qtype_buf[16];
- uint8_t *p;
+ uint8_t *p, *data;
+ uint8_t answer_imsg[MAX_IMSGSIZE - IMSG_HEADER_SIZE];
clock_gettime(CLOCK_MONOTONIC, &tp);
query_imsg = (struct query_imsg *)arg;
+ answer_header = (struct answer_header *)answer_imsg;
+ data = answer_imsg + sizeof(*answer_header);
+ answer_header->id = query_imsg->id;
+ answer_header->srvfail = 0;
+ answer_header->answer_len = answer_len;
+
timespecsub(&tp, &query_imsg->tp, &elapsed);
ms = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
@@ -1004,43 +1011,33 @@ resolve_done(struct uw_resolver *res, void *arg, int
rcode,
if (result->rcode == LDNS_RCODE_SERVFAIL)
goto servfail;
- query_imsg->err = 0;
-
if (sec == SECURE)
res->state = VALIDATING;
if (res->state == VALIDATING && sec == BOGUS) {
- query_imsg->bogus = !force_acceptbogus;
- if (query_imsg->bogus && why_bogus != NULL)
+ answer_header->bogus = !force_acceptbogus;
+ if (answer_header->bogus && why_bogus != NULL)
log_warnx("%s", why_bogus);
} else
- query_imsg->bogus = 0;
+ answer_header->bogus = 0;
- if (resolver_imsg_compose_frontend(IMSG_ANSWER_HEADER, 0, query_imsg,
- sizeof(*query_imsg)) == -1)
- fatalx("IMSG_ANSWER_HEADER failed for \"%s %s %s\"",
- query_imsg->qname, qclass_buf, qtype_buf);
-
- answer_imsg.id = query_imsg->id;
p = answer_packet;
- while ((size_t)answer_len > MAX_ANSWER_SIZE) {
- answer_imsg.truncated = 1;
- answer_imsg.len = MAX_ANSWER_SIZE;
- memcpy(&answer_imsg.answer, p, MAX_ANSWER_SIZE);
- if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0, &answer_imsg,
- sizeof(answer_imsg)) == -1)
+ do {
+ int len;
+
+ if ((size_t)answer_len > sizeof(answer_imsg) -
+ sizeof(*answer_header))
+ len = sizeof(answer_imsg) - sizeof(*answer_header);
+ else
+ len = answer_len;
+ memcpy(data, p, len);
+ if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0,
+ &answer_imsg, sizeof(*answer_header) + len) == -1)
fatalx("IMSG_ANSWER failed for \"%s %s %s\"",
query_imsg->qname, qclass_buf, qtype_buf);
- p += MAX_ANSWER_SIZE;
- answer_len -= MAX_ANSWER_SIZE;
- }
- answer_imsg.truncated = 0;
- answer_imsg.len = answer_len;
- memcpy(&answer_imsg.answer, p, answer_len);
- if (resolver_imsg_compose_frontend(IMSG_ANSWER, 0, &answer_imsg,
- sizeof(answer_imsg)) == -1)
- fatalx("IMSG_ANSWER failed for \"%s %s %s\"",
- query_imsg->qname, qclass_buf, qtype_buf);
+ answer_len -= len;
+ p += len;
+ } while (answer_len > 0);
TAILQ_REMOVE(&running_queries, rq, entry);
evtimer_del(&rq->timer_ev);
@@ -1052,9 +1049,9 @@ resolve_done(struct uw_resolver *res, void *arg, int
rcode,
/* try_next_resolver() might free rq */
if (try_next_resolver(rq) != 0 && running_res == 0) {
/* we are the last one, send SERVFAIL */
- query_imsg->err = -4; /* UB_SERVFAIL */
- resolver_imsg_compose_frontend(IMSG_ANSWER_HEADER, 0,
- query_imsg, sizeof(*query_imsg));
+ answer_header->srvfail = 1;
+ resolver_imsg_compose_frontend(IMSG_ANSWER, 0,
+ answer_imsg, sizeof(*answer_header));
}
out:
free(query_imsg);
diff --git unwind.h unwind.h
index 9ebd8f47f87..161619cd34c 100644
--- unwind.h
+++ unwind.h
@@ -116,7 +116,6 @@ enum imsg_type {
IMSG_SOCKET_IPC_FRONTEND,
IMSG_SOCKET_IPC_RESOLVER,
IMSG_QUERY,
- IMSG_ANSWER_HEADER,
IMSG_ANSWER,
IMSG_CTL_RESOLVER_INFO,
IMSG_CTL_AUTOCONF_RESOLVER_INFO,
@@ -170,18 +169,14 @@ struct query_imsg {
char qname[NI_MAXHOST];
int t;
int c;
- int err;
- int bogus;
struct timespec tp;
};
-struct answer_imsg {
-#define MAX_ANSWER_SIZE MAX_IMSGSIZE - IMSG_HEADER_SIZE -
sizeof(uint64_t) - \
- 2 * sizeof(int)
- uint64_t id;
- int truncated;
- int len;
- uint8_t answer[MAX_ANSWER_SIZE];
+struct answer_header {
+ uint64_t id;
+ int srvfail;
+ int bogus;
+ int answer_len;
};
extern uint32_t cmd_opts;
--
I'm not entirely sure you are real.