isc_stats_t *stats;
isc_nm_t *nm;
- /* Locked by "lock". */
- isc_mutex_t lock;
- ISC_LIST(dns_dispatch_t) list;
+ uint32_t nloops;
+
+ struct cds_lfht **tcps;
struct cds_lfht *qids;
/* Unlocked. */
unsigned int magic; /*%< magic */
uint32_t tid;
+ isc_mem_t *mctx;
dns_dispatchmgr_t *mgr; /*%< dispatch manager */
isc_nmhandle_t *handle; /*%< netmgr handle for TCP connection */
isc_sockaddr_t local; /*%< local address */
in_port_t localport; /*%< local UDP port */
isc_sockaddr_t peer; /*%< peer address (TCP) */
- /*% Locked by mgr->lock. */
- ISC_LINK(dns_dispatch_t) link;
-
isc_socktype_t socktype;
dns_dispatchstate_t state;
isc_refcount_t references;
uint_fast32_t requests; /*%< how many requests we have */
unsigned int timedout;
+
+ struct cds_lfht_node ht_node;
+ struct rcu_head rcu_head;
};
#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p')
/*%
* Choose a random port number for a dispatch entry.
- * The caller must hold the disp->lock
*/
static isc_result_t
setup_socket(dns_dispatch_t *disp, dns_dispentry_t *resp,
*/
isc_result_t
-dns_dispatchmgr_create(isc_mem_t *mctx, isc_nm_t *nm,
+dns_dispatchmgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
dns_dispatchmgr_t **mgrp) {
dns_dispatchmgr_t *mgr = NULL;
isc_portset_t *v4portset = NULL;
REQUIRE(mgrp != NULL && *mgrp == NULL);
mgr = isc_mem_get(mctx, sizeof(dns_dispatchmgr_t));
- *mgr = (dns_dispatchmgr_t){ .magic = 0 };
+ *mgr = (dns_dispatchmgr_t){
+ .magic = 0,
+ .nloops = isc_loopmgr_nloops(loopmgr),
+ };
#if DNS_DISPATCH_TRACE
fprintf(stderr, "dns_dispatchmgr__init:%s:%s:%d:%p->references = 1\n",
isc_mem_attach(mctx, &mgr->mctx);
isc_nm_attach(nm, &mgr->nm);
- isc_mutex_init(&mgr->lock);
-
- ISC_LIST_INIT(mgr->list);
+ mgr->tcps = isc_mem_cget(mgr->mctx, mgr->nloops, sizeof(mgr->tcps[0]));
+ for (size_t i = 0; i < mgr->nloops; i++) {
+ mgr->tcps[i] = cds_lfht_new(
+ 2, 2, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING,
+ NULL);
+ }
create_default_portset(mgr->mctx, AF_INET, &v4portset);
create_default_portset(mgr->mctx, AF_INET6, &v6portset);
isc_refcount_destroy(&mgr->references);
mgr->magic = 0;
- isc_mutex_destroy(&mgr->lock);
RUNTIME_CHECK(!cds_lfht_destroy(mgr->qids, NULL));
+ for (size_t i = 0; i < mgr->nloops; i++) {
+ RUNTIME_CHECK(!cds_lfht_destroy(mgr->tcps[i], NULL));
+ }
+ isc_mem_cput(mgr->mctx, mgr->tcps, mgr->nloops, sizeof(mgr->tcps[0]));
+
if (mgr->blackhole != NULL) {
dns_acl_detach(&mgr->blackhole);
}
void
dns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, isc_stats_t *stats) {
REQUIRE(VALID_DISPATCHMGR(mgr));
- REQUIRE(ISC_LIST_EMPTY(mgr->list));
REQUIRE(mgr->stats == NULL);
isc_stats_attach(stats, &mgr->stats);
disp = isc_mem_get(mgr->mctx, sizeof(*disp));
*disp = (dns_dispatch_t){
.socktype = type,
- .link = ISC_LINK_INITIALIZER,
.active = ISC_LIST_INITIALIZER,
.pending = ISC_LIST_INITIALIZER,
.tid = tid,
.magic = DISPATCH_MAGIC,
};
+ isc_mem_attach(mgr->mctx, &disp->mctx);
+
dns_dispatchmgr_attach(mgr, &disp->mgr);
#if DNS_DISPATCH_TRACE
fprintf(stderr, "dns_dispatch__init:%s:%s:%d:%p->references = 1\n",
*dispp = disp;
}
+struct dispatch_key {
+ const isc_sockaddr_t *local;
+ const isc_sockaddr_t *peer;
+};
+
+static uint32_t
+dispatch_hash(struct dispatch_key *key) {
+ uint32_t hashval = isc_sockaddr_hash(key->peer, false);
+ if (key->local) {
+ hashval ^= isc_sockaddr_hash(key->local, true);
+ }
+
+ return (hashval);
+}
+
+static int
+dispatch_match(struct cds_lfht_node *node, const void *key0) {
+ dns_dispatch_t *disp = caa_container_of(node, dns_dispatch_t, ht_node);
+ const struct dispatch_key *key = key0;
+ isc_sockaddr_t local;
+ isc_sockaddr_t peer;
+
+ if (disp->handle != NULL) {
+ local = isc_nmhandle_localaddr(disp->handle);
+ peer = isc_nmhandle_peeraddr(disp->handle);
+ } else {
+ local = disp->local;
+ peer = disp->peer;
+ }
+
+ return (isc_sockaddr_equal(&peer, key->peer) &&
+ (key->local == NULL || isc_sockaddr_equal(&local, key->local)));
+}
+
isc_result_t
dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
const isc_sockaddr_t *destaddr, dns_dispatch_t **dispp) {
dns_dispatch_t *disp = NULL;
+ uint32_t tid = isc_tid();
REQUIRE(VALID_DISPATCHMGR(mgr));
REQUIRE(destaddr != NULL);
- LOCK(&mgr->lock);
-
- dispatch_allocate(mgr, isc_socktype_tcp, isc_tid(), &disp);
+ dispatch_allocate(mgr, isc_socktype_tcp, tid, &disp);
disp->peer = *destaddr;
/*
* Append it to the dispatcher list.
*/
+ struct dispatch_key key = {
+ .local = &disp->local,
+ .peer = &disp->peer,
+ };
- /* FIXME: There should be a lookup hashtable here */
- ISC_LIST_APPEND(mgr->list, disp, link);
- UNLOCK(&mgr->lock);
+ rcu_read_lock();
+ cds_lfht_add(mgr->tcps[tid], dispatch_hash(&key), &disp->ht_node);
+ rcu_read_unlock();
if (isc_log_wouldlog(dns_lctx, 90)) {
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
dns_dispatch_t *disp_connected = NULL;
dns_dispatch_t *disp_fallback = NULL;
isc_result_t result = ISC_R_NOTFOUND;
+ uint32_t tid = isc_tid();
REQUIRE(VALID_DISPATCHMGR(mgr));
REQUIRE(destaddr != NULL);
REQUIRE(dispp != NULL && *dispp == NULL);
- LOCK(&mgr->lock);
-
- for (dns_dispatch_t *disp = ISC_LIST_HEAD(mgr->list); disp != NULL;
- disp = ISC_LIST_NEXT(disp, link))
- {
- isc_sockaddr_t sockname;
- isc_sockaddr_t peeraddr;
-
- if (disp->tid != isc_tid()) {
- continue;
- }
-
- REQUIRE(disp->tid == isc_tid());
-
- if (disp->handle != NULL) {
- sockname = isc_nmhandle_localaddr(disp->handle);
- peeraddr = isc_nmhandle_peeraddr(disp->handle);
- } else {
- sockname = disp->local;
- peeraddr = disp->peer;
- }
+ struct dispatch_key key = {
+ .local = localaddr,
+ .peer = destaddr,
+ };
- /*
- * The conditions match:
- * 1. socktype is TCP
- * 2. destination address is same
- * 3. local address is either NULL or same
- */
- if (disp->socktype != isc_socktype_tcp ||
- !isc_sockaddr_equal(destaddr, &peeraddr) ||
- (localaddr != NULL &&
- !isc_sockaddr_eqaddr(localaddr, &sockname)))
- {
- continue;
- }
+ rcu_read_lock();
+ struct cds_lfht_iter iter;
+ dns_dispatch_t *disp = NULL;
+ cds_lfht_for_each_entry_duplicate(mgr->tcps[tid], dispatch_hash(&key),
+ dispatch_match, &key, &iter, disp,
+ ht_node) {
+ INSIST(disp->tid == isc_tid());
+ INSIST(disp->socktype == isc_socktype_tcp);
switch (disp->state) {
case DNS_DISPATCHSTATE_NONE:
break;
}
}
+ rcu_read_unlock();
if (disp_connected != NULL) {
/* We found connected dispatch */
result = ISC_R_SUCCESS;
}
- UNLOCK(&mgr->lock);
-
return (result);
}
REQUIRE(localaddr != NULL);
REQUIRE(dispp != NULL && *dispp == NULL);
- LOCK(&mgr->lock);
result = dispatch_createudp(mgr, localaddr, isc_tid(), &disp);
if (result == ISC_R_SUCCESS) {
*dispp = disp;
}
- UNLOCK(&mgr->lock);
return (result);
}
return (result);
}
+static void
+dispatch_destroy_rcu(struct rcu_head *rcu_head) {
+ dns_dispatch_t *disp = caa_container_of(rcu_head, dns_dispatch_t,
+ rcu_head);
+
+ isc_mem_putanddetach(&disp->mctx, disp, sizeof(*disp));
+}
+
static void
dispatch_destroy(dns_dispatch_t *disp) {
dns_dispatchmgr_t *mgr = disp->mgr;
+ uint32_t tid = isc_tid();
isc_refcount_destroy(&disp->references);
disp->magic = 0;
- LOCK(&mgr->lock);
- if (ISC_LINK_LINKED(disp, link)) {
- ISC_LIST_UNLINK(disp->mgr->list, disp, link);
+ if (disp->socktype == isc_socktype_tcp) {
+ (void)cds_lfht_del(mgr->tcps[tid], &disp->ht_node);
}
- UNLOCK(&mgr->lock);
INSIST(disp->requests == 0);
INSIST(ISC_LIST_EMPTY(disp->pending));
INSIST(ISC_LIST_EMPTY(disp->active));
- INSIST(!ISC_LINK_LINKED(disp, link));
-
dispatch_log(disp, LVL(90), "destroying dispatch %p", disp);
if (disp->handle) {
disp->handle, &disp->handle);
isc_nmhandle_detach(&disp->handle);
}
+ dns_dispatchmgr_detach(&disp->mgr);
- isc_mem_put(mgr->mctx, disp, sizeof(*disp));
-
- /*
- * Because dispatch uses mgr->mctx, we must detach after freeing
- * dispatch, not before.
- */
- dns_dispatchmgr_detach(&mgr);
+ call_rcu(&disp->rcu_head, dispatch_destroy_rcu);
}
#if DNS_DISPATCH_TRACE
localport = isc_sockaddr_getport(&disp->local);
- resp = isc_mem_get(disp->mgr->mctx, sizeof(*resp));
+ resp = isc_mem_get(disp->mctx, sizeof(*resp));
*resp = (dns_dispentry_t){
.port = localport,
.timeout = timeout,
if (disp->socktype == isc_socktype_udp) {
result = setup_socket(disp, resp, dest, &localport);
if (result != ISC_R_SUCCESS) {
- isc_mem_put(disp->mgr->mctx, resp, sizeof(*resp));
+ isc_mem_put(disp->mctx, resp, sizeof(*resp));
inc_stats(disp->mgr, dns_resstatscounter_dispsockfail);
return (result);
}
} while (i++ < QID_MAX_TRIES);
fail:
if (result != ISC_R_SUCCESS) {
- isc_mem_put(disp->mgr->mctx, resp, sizeof(*resp));
+ isc_mem_put(disp->mctx, resp, sizeof(*resp));
return (ISC_R_NOMORE);
}
- isc_mem_attach(disp->mgr->mctx, &resp->mctx);
+ isc_mem_attach(disp->mctx, &resp->mctx);
if (transport != NULL) {
dns_transport_attach(transport, &resp->transport);
result = dns_transport_get_tlsctx(
resp->transport, &resp->peer, resp->tlsctx_cache,
- resp->disp->mgr->mctx, &tlsctx, &sess_cache);
+ resp->mctx, &tlsctx, &sess_cache);
if (result != ISC_R_SUCCESS) {
return (result);
dset->dispatches[0] = NULL;
dns_dispatch_attach(source, &dset->dispatches[0]); /* DISPATCH004 */
- LOCK(&mgr->lock);
for (i = 1; i < dset->ndisp; i++) {
result = dispatch_createudp(mgr, &source->local, i,
&dset->dispatches[i]);
}
}
- UNLOCK(&mgr->lock);
*dsetp = dset;
return (ISC_R_SUCCESS);
fail:
- UNLOCK(&mgr->lock);
-
for (size_t j = 0; j < i; j++) {
dns_dispatch_detach(&(dset->dispatches[j])); /* DISPATCH004 */
}
UNUSED(arg);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = make_dispatchset(1, &dset);
UNUSED(arg);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = make_dispatchset(1, &dset);
testdata.region.base = testdata.message;
testdata.region.length = sizeof(testdata.message);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock);
/* Client */
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
testdata.region.base = testdata.message;
testdata.region.length = sizeof(testdata.message);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
testdata.region.base = testdata.message;
testdata.region.length = sizeof(testdata.message);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createtcp(dispatchmgr, &tls_connect_addr,
uint16_t id;
/* Server */
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_nm_listenudp(netmgr, ISC_NM_LISTEN_ONE, &udp_server_addr,
testdata.region.base = testdata.message;
testdata.region.length = sizeof(testdata.message);
- result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+ &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr,