]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add strong rwlock consistency checks to dns_rbtdb
authorOndřej Surý <ondrej@isc.org>
Fri, 14 Oct 2022 12:35:44 +0000 (14:35 +0200)
committerOndřej Surý <ondrej@isc.org>
Wed, 2 Nov 2022 07:45:48 +0000 (08:45 +0100)
The dns_rbtdb unit already tracks the state of the node and tree rwlocks
during the top level function and passes the states of the locks to the
called functions.

Add the tree locking family of macros modeled after node locking macros,
and expand both to track the state of the lock in an external variable.
Additionally, in developer mode, add precondition to the macros, so the
lock is in required state - this should cause an assertion failure on
double locking instead of the thread getting stuck.

configure.ac
lib/dns/rbtdb.c

index 2f36c47adc0d97faf3a22cdda0056c6afac96e7e..e3cbb6a3965a4c46b09512b602eb56baa767b4b9 100644 (file)
@@ -166,7 +166,7 @@ AC_ARG_ENABLE([developer],
 
 AS_IF([test "$enable_developer" = "yes"],
       [DEVELOPER_MODE=yes
-       STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1"
+       STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1 -DDNS_RBTDB_STRONG_RWLOCK_CHECK=1"
        test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
        test "${enable_querytrace+set}" = set || enable_querytrace=yes
        test "${with_cmocka+set}" = set || with_cmocka=yes
index 5d9473c17b78a7aafb2eaf37db1c2146193df5b9..f446b822e9b3504281d8ef30cefbfba7d51724c5 100644 (file)
@@ -110,20 +110,165 @@ typedef uint32_t rbtdb_rdatatype_t;
 #define RBTDB_LOCK(l, t)     RWLOCK((l), (t))
 #define RBTDB_UNLOCK(l, t)   RWUNLOCK((l), (t))
 
-/*
- * Since node locking is sensitive to both performance and memory footprint,
- * we need some trick here.  If we have both high-performance rwlock and
- * high performance and small-memory reference counters, we use rwlock for
- * node lock and isc_refcount for node references.  In this case, we don't have
- * to protect the access to the counters by locks.
- */
 typedef isc_rwlock_t nodelock_t;
 
+#ifdef DNS_RBTDB_STRONG_RWLOCK_CHECK
+
+#define NODE_INITLOCK(l)    isc_rwlock_init((l), 0, 0)
+#define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define NODE_LOCK(l, t, tp)                          \
+       {                                            \
+               REQUIRE(*tp == isc_rwlocktype_none); \
+               RWLOCK((l), (t));                    \
+               *tp = t;                             \
+       }
+#define NODE_RDLOCK(l, tp) NODE_LOCK(l, isc_rwlocktype_read, tp);
+#define NODE_WRLOCK(l, tp) NODE_LOCK(l, isc_rwlocktype_write, tp);
+
+#define NODE_UNLOCK(l, tp)                           \
+       {                                            \
+               REQUIRE(*tp != isc_rwlocktype_none); \
+               RWUNLOCK(l, *tp);                    \
+               *tp = isc_rwlocktype_none;           \
+       }
+#define NODE_TRYLOCK(l, t, tp)                                   \
+       ({                                                       \
+               REQUIRE(*tp == isc_rwlocktype_none);             \
+               isc_result_t _result = isc_rwlock_trylock(l, t); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = t;                                 \
+               };                                               \
+               _result;                                         \
+       })
+#define NODE_TRYRDLOCK(l, tp) NODE_TRYLOCK(l, isc_rwlocktype_read, tp)
+#define NODE_TRYWRLOCK(l, tp) NODE_TRYLOCK(l, isc_rwlocktype_write, tp)
+#define NODE_TRYUPGRADE(l, tp)                                   \
+       ({                                                       \
+               REQUIRE(*tp == isc_rwlocktype_read);             \
+               isc_result_t _result = isc_rwlock_tryupgrade(l); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = isc_rwlocktype_write;              \
+               };                                               \
+               _result;                                         \
+       })
+
+typedef isc_rwlock_t treelock_t;
+
+#define TREE_INITLOCK(l)    isc_rwlock_init(l, 0, 0)
+#define TREE_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define TREE_LOCK(l, t, tp)                          \
+       {                                            \
+               REQUIRE(*tp == isc_rwlocktype_none); \
+               RWLOCK(l, t);                        \
+               *tp = t;                             \
+       }
+#define TREE_RDLOCK(l, tp) TREE_LOCK(l, isc_rwlocktype_read, tp);
+#define TREE_WRLOCK(l, tp) TREE_LOCK(l, isc_rwlocktype_write, tp);
+#define TREE_UNLOCK(l, tp)                           \
+       {                                            \
+               REQUIRE(*tp != isc_rwlocktype_none); \
+               RWUNLOCK(l, *tp);                    \
+               *tp = isc_rwlocktype_none;           \
+       }
+#define TREE_TRYLOCK(l, t, tp)                                   \
+       ({                                                       \
+               REQUIRE(*tp == isc_rwlocktype_none);             \
+               isc_result_t _result = isc_rwlock_trylock(l, t); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = t;                                 \
+               };                                               \
+               _result;                                         \
+       })
+#define TREE_TRYRDLOCK(l, tp) TREE_TRYLOCK(l, isc_rwlocktype_read, tp)
+#define TREE_TRYWRLOCK(l, tp) TREE_TRYLOCK(l, isc_rwlocktype_write, tp)
+#define TREE_TRYUPGRADE(l, tp)                                   \
+       ({                                                       \
+               REQUIRE(*tp == isc_rwlocktype_read);             \
+               isc_result_t _result = isc_rwlock_tryupgrade(l); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = isc_rwlocktype_write;              \
+               };                                               \
+               _result;                                         \
+       })
+
+#else /* DNS_RBTDB_STRONG_RWLOCK_CHECK */
+
 #define NODE_INITLOCK(l)    isc_rwlock_init((l), 0, 0)
 #define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l)
-#define NODE_LOCK(l, t)            RWLOCK((l), (t))
-#define NODE_UNLOCK(l, t)   RWUNLOCK((l), (t))
-#define NODE_TRYUPGRADE(l)  isc_rwlock_tryupgrade(l)
+#define NODE_LOCK(l, t, tp)       \
+       {                         \
+               RWLOCK((l), (t)); \
+               *tp = t;          \
+       }
+#define NODE_RDLOCK(l, tp) NODE_LOCK(l, isc_rwlocktype_read, tp);
+#define NODE_WRLOCK(l, tp) NODE_LOCK(l, isc_rwlocktype_write, tp);
+
+#define NODE_UNLOCK(l, tp)                 \
+       {                                  \
+               RWUNLOCK(l, *tp);          \
+               *tp = isc_rwlocktype_none; \
+       }
+#define NODE_TRYLOCK(l, t, tp)                                   \
+       ({                                                       \
+               isc_result_t _result = isc_rwlock_trylock(l, t); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = t;                                 \
+               };                                               \
+               _result;                                         \
+       })
+#define NODE_TRYRDLOCK(l, tp) NODE_TRYLOCK(l, isc_rwlocktype_read, tp)
+#define NODE_TRYWRLOCK(l, tp) NODE_TRYLOCK(l, isc_rwlocktype_write, tp)
+#define NODE_TRYUPGRADE(l, tp)                                   \
+       ({                                                       \
+               isc_result_t _result = isc_rwlock_tryupgrade(l); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = isc_rwlocktype_write;              \
+               };                                               \
+               _result;                                         \
+       })
+
+typedef isc_rwlock_t treelock_t;
+
+#define TREE_INITLOCK(l)    isc_rwlock_init(l, 0, 0)
+#define TREE_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define TREE_LOCK(l, t, tp)   \
+       {                     \
+               RWLOCK(l, t); \
+               *tp = t;      \
+       }
+#define TREE_RDLOCK(l, tp)                             \
+       {                                              \
+               TREE_LOCK(l, isc_rwlocktype_read, tp); \
+       }
+#define TREE_WRLOCK(l, tp)                              \
+       {                                               \
+               TREE_LOCK(l, isc_rwlocktype_write, tp); \
+       }
+#define TREE_UNLOCK(l, tp)                 \
+       {                                  \
+               RWUNLOCK(l, *tp);          \
+               *tp = isc_rwlocktype_none; \
+       }
+#define TREE_TRYLOCK(l, t, tp)                                   \
+       ({                                                       \
+               isc_result_t _result = isc_rwlock_trylock(l, t); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = t;                                 \
+               };                                               \
+               _result;                                         \
+       })
+#define TREE_TRYRDLOCK(l, tp) TREE_TRYLOCK(l, isc_rwlocktype_read, tp)
+#define TREE_TRYWRLOCK(l, tp) TREE_TRYLOCK(l, isc_rwlocktype_write, tp)
+#define TREE_TRYUPGRADE(l, tp)                                   \
+       ({                                                       \
+               isc_result_t _result = isc_rwlock_tryupgrade(l); \
+               if (_result == ISC_R_SUCCESS) {                  \
+                       *tp = isc_rwlocktype_write;              \
+               };                                               \
+               _result;                                         \
+       })
+
+#endif
 
 /*%
  * Whether to rate-limit updating the LRU to avoid possible thread contention.
@@ -403,7 +548,7 @@ struct dns_rbtdb {
        /* Locks the data in this struct */
        isc_rwlock_t lock;
        /* Locks the tree structure (prevents nodes appearing/disappearing) */
-       isc_rwlock_t tree_lock;
+       treelock_t tree_lock;
        /* Locks for individual tree nodes */
        unsigned int node_lock_count;
        rbtdb_nodelock_t *node_locks;
@@ -1106,7 +1251,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
 
        isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
                    rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
-       isc_rwlock_destroy(&rbtdb->tree_lock);
+       TREE_DESTROYLOCK(&rbtdb->tree_lock);
        isc_refcount_destroy(&rbtdb->references);
        if (rbtdb->task != NULL) {
                isc_task_detach(&rbtdb->task);
@@ -1158,13 +1303,14 @@ maybe_free_rbtdb(dns_rbtdb_t *rbtdb) {
         * may be nodes in use.
         */
        for (i = 0; i < rbtdb->node_lock_count; i++) {
-               NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
+               isc_rwlocktype_t nodelock = isc_rwlocktype_none;
+               NODE_WRLOCK(&rbtdb->node_locks[i].lock, &nodelock);
                rbtdb->node_locks[i].exiting = true;
                if (isc_refcount_current(&rbtdb->node_locks[i].references) == 0)
                {
                        inactive++;
                }
-               NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
+               NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nodelock);
        }
 
        if (inactive != 0) {
@@ -1882,13 +2028,13 @@ cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) {
 static void
 reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
                isc_rwlocktype_t tlocktype) {
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        nodelock_t *nodelock = &rbtdb->node_locks[node->locknum].lock;
        bool maybe_cleanup = false;
 
        POST(nlocktype);
 
-       NODE_LOCK(nodelock, nlocktype);
+       NODE_RDLOCK(nodelock, &nlocktype);
 
        /*
         * Check if we can possibly cleanup the dead node.  If so, upgrade
@@ -1904,10 +2050,11 @@ reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
                /*
                 * Upgrade the lock and test if we still need to unlink.
                 */
-               NODE_UNLOCK(nodelock, nlocktype);
-               nlocktype = isc_rwlocktype_write;
+               if (NODE_TRYUPGRADE(nodelock, &nlocktype) != ISC_R_SUCCESS) {
+                       NODE_UNLOCK(nodelock, &nlocktype);
+                       NODE_WRLOCK(nodelock, &nlocktype);
+               }
                POST(nlocktype);
-               NODE_LOCK(nodelock, nlocktype);
                if (ISC_LINK_LINKED(node, deadlink)) {
                        ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum], node,
                                        deadlink);
@@ -1919,7 +2066,7 @@ reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
 
        new_reference(rbtdb, node, nlocktype);
 
-       NODE_UNLOCK(nodelock, nlocktype);
+       NODE_UNLOCK(nodelock, &nlocktype);
 }
 
 /*
@@ -1938,17 +2085,17 @@ reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
  */
 static bool
 decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
-                   rbtdb_serial_t least_serial, isc_rwlocktype_t *nlocktype,
-                   isc_rwlocktype_t *tlocktype, bool pruning) {
+                   rbtdb_serial_t least_serial, isc_rwlocktype_t *nlocktypep,
+                   isc_rwlocktype_t *tlocktypep, bool pruning) {
        isc_result_t result;
-       bool locked = *tlocktype != isc_rwlocktype_none;
+       bool locked = *tlocktypep != isc_rwlocktype_none;
        bool write_locked = false;
        rbtdb_nodelock_t *nodelock;
        int bucket = node->locknum;
        bool no_reference = true;
        uint_fast32_t refs;
 
-       REQUIRE(*nlocktype != isc_rwlocktype_none);
+       REQUIRE(*nlocktypep != isc_rwlocktype_none);
 
        nodelock = &rbtdb->node_locks[bucket];
 
@@ -1968,10 +2115,12 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
        }
 
        /* Upgrade the lock? */
-       if (*nlocktype == isc_rwlocktype_read) {
-               NODE_UNLOCK(&nodelock->lock, *nlocktype);
-               *nlocktype = isc_rwlocktype_write;
-               NODE_LOCK(&nodelock->lock, *nlocktype);
+       if (*nlocktypep == isc_rwlocktype_read) {
+               if (NODE_TRYUPGRADE(&nodelock->lock, nlocktypep) !=
+                   ISC_R_SUCCESS) {
+                       NODE_UNLOCK(&nodelock->lock, nlocktypep);
+                       NODE_WRLOCK(&nodelock->lock, nlocktypep);
+               }
        }
 
        if (isc_refcount_decrement(&node->references) > 1) {
@@ -2004,16 +2153,15 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
         * the node lock before acquiring the tree write lock because
         * we only do a trylock.
         */
-       switch (*tlocktype) {
+       switch (*tlocktypep) {
        case isc_rwlocktype_write:
                result = ISC_R_SUCCESS;
                break;
        case isc_rwlocktype_read:
-               result = isc_rwlock_tryupgrade(&rbtdb->tree_lock);
+               result = TREE_TRYUPGRADE(&rbtdb->tree_lock, tlocktypep);
                break;
        case isc_rwlocktype_none:
-               result = isc_rwlock_trylock(&rbtdb->tree_lock,
-                                           isc_rwlocktype_write);
+               result = TREE_TRYWRLOCK(&rbtdb->tree_lock, tlocktypep);
                break;
        default:
                UNREACHABLE();
@@ -2021,7 +2169,6 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
        RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_LOCKBUSY);
        if (result == ISC_R_SUCCESS) {
                write_locked = true;
-               *tlocktype = isc_rwlocktype_write;
        }
 
        refs = isc_refcount_decrement(&nodelock->references);
@@ -2073,8 +2220,7 @@ restore_locks:
         * Relock a read lock, or unlock the write lock if no lock was held.
         */
        if (!locked && write_locked) {
-               RWUNLOCK(&rbtdb->tree_lock, *tlocktype);
-               *tlocktype = isc_rwlocktype_none;
+               TREE_UNLOCK(&rbtdb->tree_lock, tlocktypep);
        }
 
        return (no_reference);
@@ -2093,16 +2239,16 @@ prune_tree(isc_task_t *task, isc_event_t *event) {
        dns_rbtnode_t *node = event->ev_arg;
        dns_rbtnode_t *parent;
        unsigned int locknum;
-       isc_rwlocktype_t tlocktype = isc_rwlocktype_write;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        UNUSED(task);
 
        isc_event_free(&event);
 
-       RWLOCK(&rbtdb->tree_lock, tlocktype);
+       TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
        locknum = node->locknum;
-       NODE_LOCK(&rbtdb->node_locks[locknum].lock, nlocktype);
+       NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
        do {
                parent = node->parent;
                decrement_reference(rbtdb, node, 0, &nlocktype, &tlocktype,
@@ -2117,10 +2263,10 @@ prune_tree(isc_task_t *task, isc_event_t *event) {
                         */
                        if (parent->locknum != locknum) {
                                NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
-                                           nlocktype);
+                                           &nlocktype);
                                locknum = parent->locknum;
-                               NODE_LOCK(&rbtdb->node_locks[locknum].lock,
-                                         nlocktype);
+                               NODE_WRLOCK(&rbtdb->node_locks[locknum].lock,
+                                           &nlocktype);
                        }
 
                        /*
@@ -2138,7 +2284,7 @@ prune_tree(isc_task_t *task, isc_event_t *event) {
 
                node = parent;
        } while (node != NULL);
-       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, nlocktype);
+       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
        RWUNLOCK(&rbtdb->tree_lock, tlocktype);
 
        detach((dns_db_t **)&rbtdb);
@@ -2254,12 +2400,13 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version) {
        unsigned char *raw; /* RDATASLAB */
        unsigned int count, length;
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        version->havensec3 = false;
        node = rbtdb->origin_node;
-       NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock), &nlocktype);
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
                do {
@@ -2324,9 +2471,8 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version) {
                }
        }
 unlock:
-       NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
-                   isc_rwlocktype_read);
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), &nlocktype);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 }
 
 static void
@@ -2334,19 +2480,19 @@ cleanup_dead_nodes_callback(isc_task_t *task, isc_event_t *event) {
        dns_rbtdb_t *rbtdb = event->ev_arg;
        bool again = false;
        unsigned int locknum;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+       TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
        for (locknum = 0; locknum < rbtdb->node_lock_count; locknum++) {
-               NODE_LOCK(&rbtdb->node_locks[locknum].lock,
-                         isc_rwlocktype_write);
+               NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
                cleanup_dead_nodes(rbtdb, locknum);
                if (ISC_LIST_HEAD(rbtdb->deadnodes[locknum]) != NULL) {
                        again = true;
                }
-               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
-                           isc_rwlocktype_write);
+               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
        }
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
        if (again) {
                isc_task_send(task, &event);
        } else {
@@ -2549,18 +2695,18 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
             header = HEAD(resigned_list)) {
                nodelock_t *lock;
                isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
-               isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
                ISC_LIST_UNLINK(resigned_list, header, link);
 
                lock = &rbtdb->node_locks[header->node->locknum].lock;
-               NODE_LOCK(lock, nlocktype);
+               NODE_WRLOCK(lock, &nlocktype);
                if (rollback && !IGNORE(header)) {
                        resign_insert(rbtdb, header->node->locknum, header);
                }
                decrement_reference(rbtdb, header->node, least_serial,
                                    &nlocktype, &tlocktype, false);
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                INSIST(tlocktype == isc_rwlocktype_none);
        }
 
@@ -2584,20 +2730,19 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
                         * expensive, but this event should be rare enough
                         * to justify the cost.
                         */
-                       tlocktype = isc_rwlocktype_write;
-                       RWLOCK(&rbtdb->tree_lock, tlocktype);
+                       TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
                }
 
                for (changed = HEAD(cleanup_list); changed != NULL;
                     changed = next_changed) {
                        nodelock_t *lock;
-                       isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
+                       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
                        next_changed = NEXT(changed, link);
                        rbtnode = changed->node;
                        lock = &rbtdb->node_locks[rbtnode->locknum].lock;
 
-                       NODE_LOCK(lock, nlocktype);
+                       NODE_WRLOCK(lock, &nlocktype);
                        /*
                         * This is a good opportunity to purge any dead nodes,
                         * so use it.
@@ -2612,7 +2757,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
                        decrement_reference(rbtdb, rbtnode, least_serial,
                                            &nlocktype, &tlocktype, false);
 
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
 
                        isc_mem_put(rbtdb->common.mctx, changed,
                                    sizeof(*changed));
@@ -2621,8 +2766,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
                        isc_refcount_increment(&rbtdb->references);
                        isc_task_send(rbtdb->task, &event);
                } else {
-                       RWUNLOCK(&rbtdb->tree_lock, tlocktype);
-                       tlocktype = isc_rwlocktype_none;
+                       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
                }
 
                INSIST(tlocktype == isc_rwlocktype_none);
@@ -2714,12 +2858,12 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name,
        dns_rbtnode_t *node = NULL;
        dns_name_t nodename;
        isc_result_t result;
-       isc_rwlocktype_t tlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        INSIST(tree == rbtdb->tree || tree == rbtdb->nsec3);
 
        dns_name_init(&nodename, NULL);
-       RWLOCK(&rbtdb->tree_lock, tlocktype);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        result = dns_rbt_findnode(tree, name, NULL, &node, NULL,
                                  DNS_RBTFIND_EMPTYDATA, NULL, NULL);
        if (result != ISC_R_SUCCESS) {
@@ -2730,12 +2874,13 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name,
                        goto unlock;
                }
                /*
-                * It would be nice to try to upgrade the lock instead of
-                * unlocking then relocking.
+                * Try to upgrade the lock and if that fails unlock then relock.
                 */
-               RWUNLOCK(&rbtdb->tree_lock, tlocktype);
-               tlocktype = isc_rwlocktype_write;
-               RWLOCK(&rbtdb->tree_lock, tlocktype);
+               if (TREE_TRYUPGRADE(&rbtdb->tree_lock, &tlocktype) !=
+                   ISC_R_SUCCESS) {
+                       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
+                       TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
+               }
                node = NULL;
                result = dns_rbt_addnode(tree, name, &node);
                if (result == ISC_R_SUCCESS) {
@@ -2769,7 +2914,7 @@ findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name,
        *nodep = (dns_dbnode_t *)node;
 
 unlock:
-       RWUNLOCK(&rbtdb->tree_lock, tlocktype);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        return (result);
 }
@@ -2802,6 +2947,7 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        rdatasetheader_t *found;
        isc_result_t result;
        dns_rbtnode_t *onode;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        /*
         * We only want to remember the topmost zone cut, since it's the one
@@ -2816,8 +2962,8 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        result = DNS_R_CONTINUE;
        onode = search->rbtdb->origin_node;
 
-       NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+                   &nlocktype);
 
        /*
         * Look for an NS or DNAME rdataset active in our version.
@@ -2937,7 +3083,7 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        }
 
        NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                   isc_rwlocktype_read);
+                   &nlocktype);
 
        return (result);
 }
@@ -3105,8 +3251,9 @@ setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
                search->need_cleanup = false;
        }
        if (rdataset != NULL) {
-               NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+               NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+                           &nlocktype);
                bind_rdataset(search->rbtdb, node, search->zonecut_rdataset,
                              search->now, isc_rwlocktype_read, rdataset);
                if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL)
@@ -3116,7 +3263,7 @@ setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
                                      isc_rwlocktype_read, sigrdataset);
                }
                NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+                           &nlocktype);
        }
 
        if (type == dns_rdatatype_dname) {
@@ -3203,14 +3350,15 @@ activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain,
 
        result = dns_rbtnodechain_next(chain, NULL, NULL);
        while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
                node = NULL;
                result = dns_rbtnodechain_current(chain, &prefix, origin,
                                                  &node);
                if (result != ISC_R_SUCCESS) {
                        break;
                }
-               NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock),
+                           &nlocktype);
                for (header = node->data; header != NULL; header = header->next)
                {
                        if (header->serial <= search->serial &&
@@ -3219,7 +3367,7 @@ activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain,
                        }
                }
                NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+                           &nlocktype);
                if (header != NULL) {
                        break;
                }
@@ -3272,13 +3420,14 @@ activeemptynode(rbtdb_search_t *search, const dns_name_t *qname,
 
        chain = search->chain;
        do {
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
                node = NULL;
                result = dns_rbtnodechain_current(&chain, &name, origin, &node);
                if (result != ISC_R_SUCCESS) {
                        break;
                }
-               NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock),
+                           &nlocktype);
                for (header = node->data; header != NULL; header = header->next)
                {
                        if (header->serial <= search->serial &&
@@ -3287,7 +3436,7 @@ activeemptynode(rbtdb_search_t *search, const dns_name_t *qname,
                        }
                }
                NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+                           &nlocktype);
                if (header != NULL) {
                        break;
                }
@@ -3302,13 +3451,14 @@ activeemptynode(rbtdb_search_t *search, const dns_name_t *qname,
 
        result = dns_rbtnodechain_next(&chain, NULL, NULL);
        while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
                node = NULL;
                result = dns_rbtnodechain_current(&chain, &name, origin, &node);
                if (result != ISC_R_SUCCESS) {
                        break;
                }
-               NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock),
+                           &nlocktype);
                for (header = node->data; header != NULL; header = header->next)
                {
                        if (header->serial <= search->serial &&
@@ -3317,7 +3467,7 @@ activeemptynode(rbtdb_search_t *search, const dns_name_t *qname,
                        }
                }
                NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+                           &nlocktype);
                if (header != NULL) {
                        break;
                }
@@ -3388,8 +3538,9 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
        done = false;
        node = *nodep;
        do {
-               NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               nodelock_t *lock = &rbtdb->node_locks[node->locknum].lock;
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+               NODE_RDLOCK(lock, &nlocktype);
 
                /*
                 * First we try to figure out if this node is active in
@@ -3418,8 +3569,7 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
                        wild = false;
                }
 
-               NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+               NODE_UNLOCK(lock, &nlocktype);
 
                if (wild) {
                        /*
@@ -3449,15 +3599,13 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
                                rbtdb->tree, wname, NULL, &wnode, &wchain,
                                DNS_RBTFIND_EMPTYDATA, NULL, NULL);
                        if (result == ISC_R_SUCCESS) {
-                               nodelock_t *lock;
-
                                /*
                                 * We have found the wildcard node.  If it
                                 * is active in the search's version, we're
                                 * done.
                                 */
                                lock = &rbtdb->node_locks[wnode->locknum].lock;
-                               NODE_LOCK(lock, isc_rwlocktype_read);
+                               NODE_RDLOCK(lock, &nlocktype);
                                for (header = wnode->data; header != NULL;
                                     header = header->next) {
                                        if (header->serial <= search->serial &&
@@ -3467,7 +3615,7 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
                                                break;
                                        }
                                }
-                               NODE_UNLOCK(lock, isc_rwlocktype_read);
+                               NODE_UNLOCK(lock, &nlocktype);
                                if (header != NULL ||
                                    activeempty(search, &wchain, wname)) {
                                        if (activeemptynode(search, qname,
@@ -3712,8 +3860,9 @@ again:
                return (result);
        }
        do {
-               NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                         isc_rwlocktype_read);
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+               NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+                           &nlocktype);
                found = NULL;
                foundsig = NULL;
                empty_node = true;
@@ -3830,7 +3979,7 @@ again:
                                                       &nsecchain, &first);
                }
                NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
-                           isc_rwlocktype_read);
+                           &nlocktype);
                node = prevnode;
                prevnode = NULL;
        } while (empty_node && result == ISC_R_SUCCESS);
@@ -3880,8 +4029,8 @@ zone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
        bool active;
        nodelock_t *lock;
        dns_rbt_t *tree;
-       isc_rwlocktype_t nlocktype;
-       isc_rwlocktype_t tlocktype;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        search.rbtdb = (dns_rbtdb_t *)db;
 
@@ -3919,8 +4068,7 @@ zone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
         */
        wild = false;
 
-       tlocktype = isc_rwlocktype_read;
-       RWLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        /*
         * Search down from the root of the tree.  If, while going down, we
@@ -4040,8 +4188,7 @@ found:
         */
 
        lock = &search.rbtdb->node_locks[node->locknum].lock;
-       nlocktype = isc_rwlocktype_read;
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
 
        found = NULL;
        foundsig = NULL;
@@ -4122,7 +4269,7 @@ found:
                         */
                        if (header->type == dns_rdatatype_nsec3 &&
                            !matchparams(header, &search)) {
-                               NODE_UNLOCK(lock, nlocktype);
+                               NODE_UNLOCK(lock, &nlocktype);
                                goto partial_match;
                        }
                        /*
@@ -4206,7 +4353,7 @@ found:
                 * we really have a partial match.
                 */
                if (!wild) {
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        goto partial_match;
                }
        }
@@ -4222,7 +4369,7 @@ found:
                         *
                         * Return the delegation.
                         */
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        result = setup_delegation(&search, nodep, foundname,
                                                  rdataset, sigrdataset);
                        goto tree_exit;
@@ -4244,7 +4391,7 @@ found:
                                goto node_exit;
                        }
 
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        result = find_closest_nsec(&search, nodep, foundname,
                                                   rdataset, sigrdataset,
                                                   search.rbtdb->tree,
@@ -4334,7 +4481,7 @@ found:
                    (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 &&
                    !valid_glue(&search, foundname, type, node))
                {
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        result = setup_delegation(&search, nodep, foundname,
                                                  rdataset, sigrdataset);
                        goto tree_exit;
@@ -4369,27 +4516,24 @@ found:
        }
 
 node_exit:
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
 tree_exit:
-       RWUNLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        /*
         * If we found a zonecut but aren't going to use it, we have to
         * let go of it.
         */
        if (search.need_cleanup) {
-               nlocktype = isc_rwlocktype_read;
-               tlocktype = isc_rwlocktype_none;
-
                node = search.zonecut;
                INSIST(node != NULL);
                lock = &(search.rbtdb->node_locks[node->locknum].lock);
 
-               NODE_LOCK(lock, nlocktype);
+               NODE_RDLOCK(lock, &nlocktype);
                decrement_reference(search.rbtdb, node, 0, &nlocktype,
                                    &tlocktype, false);
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                INSIST(tlocktype == isc_rwlocktype_none);
        }
 
@@ -4425,7 +4569,7 @@ zone_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
 
 static bool
 check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
-                  isc_rwlocktype_t *nlocktype, nodelock_t *lock,
+                  isc_rwlocktype_t *nlocktypep, nodelock_t *lock,
                   rbtdb_search_t *search, rdatasetheader_t **header_prev) {
        if (!ACTIVE(header, search->now)) {
                dns_ttl_t stale = header->rdh_ttl +
@@ -4486,8 +4630,8 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
                 * cleaned up later.
                 */
                if ((header->rdh_ttl < search->now - RBTDB_VIRTUAL) &&
-                   (*nlocktype == isc_rwlocktype_write ||
-                    NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS))
+                   (*nlocktypep == isc_rwlocktype_write ||
+                    NODE_TRYUPGRADE(lock, nlocktypep) == ISC_R_SUCCESS))
                {
                        /*
                         * We update the node's status only when we can
@@ -4497,7 +4641,6 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
                         * We won't downgrade the lock, since other
                         * rdatasets are probably stale, too.
                         */
-                       *nlocktype = isc_rwlocktype_write;
 
                        if (isc_refcount_current(&node->references) == 0) {
                                isc_mem_t *mctx;
@@ -4538,7 +4681,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        rdatasetheader_t *dname_header, *sigdname_header;
        isc_result_t result;
        nodelock_t *lock;
-       isc_rwlocktype_t nlocktype;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        /* XXX comment */
 
@@ -4550,8 +4693,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        UNUSED(name);
 
        lock = &(search->rbtdb->node_locks[node->locknum].lock);
-       nlocktype = isc_rwlocktype_read;
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
 
        /*
         * Look for a DNAME or RRSIG DNAME rdataset.
@@ -4597,7 +4739,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
                result = DNS_R_CONTINUE;
        }
 
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
        return (result);
 }
@@ -4614,8 +4756,6 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
        dns_name_t name;
        dns_rbtdb_t *rbtdb;
        bool done;
-       nodelock_t *lock;
-       isc_rwlocktype_t nlocktype;
 
        /*
         * Caller must be holding the tree lock.
@@ -4625,9 +4765,10 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
        i = search->chain.level_matches;
        done = false;
        do {
-               nlocktype = isc_rwlocktype_read;
-               lock = &rbtdb->node_locks[node->locknum].lock;
-               NODE_LOCK(lock, nlocktype);
+               nodelock_t *lock = &rbtdb->node_locks[node->locknum].lock;
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+
+               NODE_RDLOCK(lock, &nlocktype);
 
                /*
                 * Look for NS and RRSIG NS rdatasets.
@@ -4710,9 +4851,11 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
                             need_headerupdate(foundsig, search->now)))
                        {
                                if (nlocktype != isc_rwlocktype_write) {
-                                       NODE_UNLOCK(lock, nlocktype);
-                                       nlocktype = isc_rwlocktype_write;
-                                       NODE_LOCK(lock, nlocktype);
+                                       if (NODE_TRYUPGRADE(lock, &nlocktype) !=
+                                           ISC_R_SUCCESS) {
+                                               NODE_UNLOCK(lock, &nlocktype);
+                                               NODE_WRLOCK(lock, &nlocktype);
+                                       }
                                        POST(nlocktype);
                                }
                                if (need_headerupdate(found, search->now)) {
@@ -4728,7 +4871,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
                }
 
        node_exit:
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
 
                if (found == NULL && i > 0) {
                        i--;
@@ -4759,7 +4902,7 @@ find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name,
        dns_rbtnode_t *node = NULL;
        dns_rbtnodechain_t chain;
        isc_result_t result;
-       isc_rwlocktype_t nlocktype;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        nodelock_t *lock = NULL;
        rbtdb_rdatatype_t matchtype, sigmatchtype;
        rdatasetheader_t *found = NULL, *foundsig = NULL;
@@ -4783,7 +4926,6 @@ find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name,
        target = dns_fixedname_initname(&ftarget);
        fname = dns_fixedname_initname(&fixed);
 
-       nlocktype = isc_rwlocktype_read;
        matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0);
        sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig,
                                             dns_rdatatype_nsec);
@@ -4813,7 +4955,7 @@ find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name,
        }
 
        lock = &(search->rbtdb->node_locks[node->locknum].lock);
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
                if (check_stale_header(node, header, &nlocktype, lock, search,
@@ -4854,7 +4996,7 @@ find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name,
        } else {
                result = ISC_R_NOTFOUND;
        }
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
        return (result);
 }
 
@@ -4871,8 +5013,8 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
        bool all_negative = true;
        bool empty_node;
        nodelock_t *lock;
-       isc_rwlocktype_t tlocktype;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        rdatasetheader_t *header, *header_prev, *header_next;
        rdatasetheader_t *found, *nsheader;
        rdatasetheader_t *foundsig, *nssig, *cnamesig;
@@ -4906,8 +5048,7 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
        update = NULL;
        updatesig = NULL;
 
-       tlocktype = isc_rwlocktype_read;
-       RWLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        /*
         * Search down from the root of the tree.  If, while going down, we
@@ -4965,7 +5106,7 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
         */
 
        lock = &(search.rbtdb->node_locks[node->locknum].lock);
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
 
        found = NULL;
        foundsig = NULL;
@@ -5067,7 +5208,7 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
                 * extant rdatasets.  That means that this node doesn't
                 * meaningfully exist, and that we really have a partial match.
                 */
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) {
                        result = find_coveringnsec(&search, name, nodep, now,
                                                   foundname, rdataset,
@@ -5122,7 +5263,7 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
                if (found == NULL && (found_noqname || all_negative) &&
                    (search.options & DNS_DBFIND_COVERINGNSEC) != 0)
                {
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        result = find_coveringnsec(&search, name, nodep, now,
                                                   foundname, rdataset,
                                                   sigrdataset);
@@ -5161,7 +5302,7 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
                /*
                 * Go find the deepest zone cut.
                 */
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                goto find_ns;
        }
 
@@ -5219,9 +5360,10 @@ cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
 node_exit:
        if ((update != NULL || updatesig != NULL) &&
            nlocktype != isc_rwlocktype_write) {
-               NODE_UNLOCK(lock, nlocktype);
-               nlocktype = isc_rwlocktype_write;
-               NODE_LOCK(lock, nlocktype);
+               if (NODE_TRYUPGRADE(lock, &nlocktype) != ISC_R_SUCCESS) {
+                       NODE_UNLOCK(lock, &nlocktype);
+                       NODE_WRLOCK(lock, &nlocktype);
+               }
                POST(nlocktype);
        }
        if (update != NULL && need_headerupdate(update, search.now)) {
@@ -5231,27 +5373,24 @@ node_exit:
                update_header(search.rbtdb, updatesig, search.now);
        }
 
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
 tree_exit:
-       RWUNLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        /*
         * If we found a zonecut but aren't going to use it, we have to
         * let go of it.
         */
        if (search.need_cleanup) {
-               nlocktype = isc_rwlocktype_read;
-               tlocktype = isc_rwlocktype_none;
-
                node = search.zonecut;
                INSIST(node != NULL);
                lock = &(search.rbtdb->node_locks[node->locknum].lock);
 
-               NODE_LOCK(lock, nlocktype);
+               NODE_RDLOCK(lock, &nlocktype);
                decrement_reference(search.rbtdb, node, 0, &nlocktype,
                                    &tlocktype, false);
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                INSIST(tlocktype == isc_rwlocktype_none);
        }
 
@@ -5273,8 +5412,8 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
        rdatasetheader_t *header, *header_prev, *header_next;
        rdatasetheader_t *found, *foundsig;
        unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA;
-       isc_rwlocktype_t tlocktype = isc_rwlocktype_read;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        bool dcnull = (dcname == NULL);
 
        search.rbtdb = (dns_rbtdb_t *)db;
@@ -5304,8 +5443,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
                rbtoptions |= DNS_RBTFIND_NOEXACT;
        }
 
-       tlocktype = isc_rwlocktype_read;
-       RWLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        /*
         * Search down from the root of the tree.
@@ -5328,7 +5466,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
         */
 
        lock = &(search.rbtdb->node_locks[node->locknum].lock);
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
 
        found = NULL;
        foundsig = NULL;
@@ -5347,7 +5485,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
                         * zonecut we know about. If so, find the deepest
                         * zonecut from this node up and return that instead.
                         */
-                       NODE_UNLOCK(lock, nlocktype);
+                       NODE_UNLOCK(lock, &nlocktype);
                        result = find_deepest_zonecut(&search, node, nodep,
                                                      foundname, rdataset,
                                                      sigrdataset);
@@ -5382,7 +5520,7 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
                /*
                 * No NS records here.
                 */
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
                result = find_deepest_zonecut(&search, node, nodep, foundname,
                                              rdataset, sigrdataset);
                goto tree_exit;
@@ -5404,9 +5542,11 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
            (foundsig != NULL && need_headerupdate(foundsig, search.now)))
        {
                if (nlocktype != isc_rwlocktype_write) {
-                       NODE_UNLOCK(lock, nlocktype);
-                       nlocktype = isc_rwlocktype_write;
-                       NODE_LOCK(lock, nlocktype);
+                       if (NODE_TRYUPGRADE(lock, &nlocktype) != ISC_R_SUCCESS)
+                       {
+                               NODE_UNLOCK(lock, &nlocktype);
+                               NODE_WRLOCK(lock, &nlocktype);
+                       }
                        POST(nlocktype);
                }
                if (need_headerupdate(found, search.now)) {
@@ -5418,10 +5558,10 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
                }
        }
 
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
 tree_exit:
-       RWUNLOCK(&search.rbtdb->tree_lock, tlocktype);
+       TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype);
 
        INSIST(!search.need_cleanup);
 
@@ -5454,7 +5594,7 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
        bool want_free = false;
        bool inactive = false;
        rbtdb_nodelock_t *nodelock;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
@@ -5463,7 +5603,7 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
        node = (dns_rbtnode_t *)(*targetp);
        nodelock = &rbtdb->node_locks[node->locknum];
 
-       NODE_LOCK(&nodelock->lock, nlocktype);
+       NODE_RDLOCK(&nodelock->lock, &nlocktype);
 
        if (decrement_reference(rbtdb, node, 0, &nlocktype, &tlocktype, false))
        {
@@ -5473,7 +5613,7 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
                }
        }
 
-       NODE_UNLOCK(&nodelock->lock, nlocktype);
+       NODE_UNLOCK(&nodelock->lock, &nlocktype);
        INSIST(tlocktype == isc_rwlocktype_none);
 
        *targetp = NULL;
@@ -5515,6 +5655,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
        isc_logmodule_t *module = DNS_LOGMODULE_CACHE;
        int level = ISC_LOG_DEBUG(2);
        char printname[DNS_NAME_FORMATSIZE];
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
@@ -5554,8 +5695,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
         * We may not need write access, but this code path is not performance
         * sensitive, so it should be okay to always lock as a writer.
         */
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        for (header = rbtnode->data; header != NULL; header = header->next) {
                if (header->rdh_ttl + STALE_TTL(header, rbtdb) <=
@@ -5588,8 +5728,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
                }
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        return (ISC_R_SUCCESS);
 }
@@ -5610,11 +5749,11 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
        dns_rbtnode_t *rbtnode = node;
        bool first;
        uint32_t refs;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        refs = isc_refcount_current(&rbtnode->references);
        fprintf(out, "node %p, %" PRIu32 " references, locknum = %u\n", rbtnode,
@@ -5651,8 +5790,7 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
                fprintf(out, "(empty)\n");
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 static isc_result_t
@@ -5707,6 +5845,7 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        rbtdb_version_t *rbtversion = version;
        bool close_version = false;
        rbtdb_rdatatype_t matchtype, sigmatchtype;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(type != dns_rdatatype_any);
@@ -5719,8 +5858,7 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        serial = rbtversion->serial;
        now = 0;
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        found = NULL;
        foundsig = NULL;
@@ -5774,8 +5912,7 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                }
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        if (close_version) {
                closeversion(db, (dns_dbversion_t **)(void *)(&rbtversion),
@@ -5800,7 +5937,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        rbtdb_rdatatype_t matchtype, sigmatchtype, negtype;
        isc_result_t result;
        nodelock_t *lock;
-       isc_rwlocktype_t locktype;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(type != dns_rdatatype_any);
@@ -5814,8 +5951,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        }
 
        lock = &rbtdb->node_locks[rbtnode->locknum].lock;
-       locktype = isc_rwlocktype_read;
-       NODE_LOCK(lock, locktype);
+       NODE_RDLOCK(lock, &nlocktype);
 
        found = NULL;
        foundsig = NULL;
@@ -5832,14 +5968,14 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                if (!ACTIVE(header, now)) {
                        if ((header->rdh_ttl + STALE_TTL(header, rbtdb) <
                             now - RBTDB_VIRTUAL) &&
-                           (locktype == isc_rwlocktype_write ||
-                            NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS))
+                           (nlocktype == isc_rwlocktype_write ||
+                            NODE_TRYUPGRADE(lock, &nlocktype) ==
+                                    ISC_R_SUCCESS))
                        {
                                /*
                                 * We update the node's status only when we
                                 * can get write access.
                                 */
-                               locktype = isc_rwlocktype_write;
 
                                /*
                                 * We don't check if refcurrent(rbtnode) == 0
@@ -5862,14 +5998,14 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                }
        }
        if (found != NULL) {
-               bind_rdataset(rbtdb, rbtnode, found, now, locktype, rdataset);
+               bind_rdataset(rbtdb, rbtnode, found, now, nlocktype, rdataset);
                if (!NEGATIVE(found) && foundsig != NULL) {
-                       bind_rdataset(rbtdb, rbtnode, foundsig, now, locktype,
+                       bind_rdataset(rbtdb, rbtnode, foundsig, now, nlocktype,
                                      sigrdataset);
                }
        }
 
-       NODE_UNLOCK(lock, locktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
        if (found == NULL) {
                return (ISC_R_NOTFOUND);
@@ -6726,8 +6862,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        isc_result_t result;
        bool delegating;
        bool newnsec;
-       isc_rwlocktype_t tlocktype = isc_rwlocktype_read;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        bool cache_is_overmem = false;
        dns_fixedname_t fixed;
        dns_name_t *name;
@@ -6743,14 +6879,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                    node != rbtdb->origin_node) {
                        return (DNS_R_NOTZONETOP);
                }
-               RWLOCK(&rbtdb->tree_lock, tlocktype);
+               TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
                REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
                          (rdataset->type == dns_rdatatype_nsec3 ||
                           rdataset->covers == dns_rdatatype_nsec3)) ||
                         (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 &&
                          rdataset->type != dns_rdatatype_nsec3 &&
                          rdataset->covers != dns_rdatatype_nsec3)));
-               RWUNLOCK(&rbtdb->tree_lock, tlocktype);
+               TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
        }
 
        if (rbtversion == NULL) {
@@ -6851,8 +6987,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        /*
         * Add to the auxiliary NSEC tree if we're adding an NSEC record.
         */
-       tlocktype = isc_rwlocktype_read;
-       RWLOCK(&rbtdb->tree_lock, tlocktype);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        if (rbtnode->nsec != DNS_RBT_NSEC_HAS_NSEC &&
            rdataset->type == dns_rdatatype_nsec)
        {
@@ -6860,7 +6995,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        } else {
                newnsec = false;
        }
-       RWUNLOCK(&rbtdb->tree_lock, tlocktype);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        /*
         * If we're adding a delegation type, adding to the auxiliary NSEC
@@ -6873,17 +7008,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                cache_is_overmem = true;
        }
        if (delegating || newnsec || cache_is_overmem) {
-               tlocktype = isc_rwlocktype_write;
-               RWLOCK(&rbtdb->tree_lock, tlocktype);
-       } else {
-               tlocktype = isc_rwlocktype_none;
+               TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
        }
 
        if (cache_is_overmem) {
                overmem_purge(rbtdb, rbtnode->locknum, now, &tlocktype);
        }
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, nlocktype);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        if (rbtdb->rrsetstats != NULL) {
                RDATASET_ATTR_SET(newheader, RDATASET_ATTR_STATCOUNT);
@@ -6912,8 +7044,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                 */
                if (tlocktype == isc_rwlocktype_write && !delegating &&
                    !newnsec) {
-                       RWUNLOCK(&rbtdb->tree_lock, tlocktype);
-                       tlocktype = isc_rwlocktype_none;
+                       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
                }
        }
 
@@ -6940,11 +7071,12 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                rbtnode->find_callback = 1;
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, nlocktype);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        if (tlocktype != isc_rwlocktype_none) {
-               RWUNLOCK(&rbtdb->tree_lock, tlocktype);
+               TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
        }
+       INSIST(tlocktype == isc_rwlocktype_none);
 
        /*
         * Update the zone's secure status.  If version is non-NULL
@@ -6971,19 +7103,21 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        isc_region_t region;
        isc_result_t result;
        rbtdb_changed_t *changed;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(rbtversion != NULL && rbtversion->rbtdb == rbtdb);
 
        if (rbtdb->common.methods == &zone_methods) {
-               RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+               TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
                REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
                          (rdataset->type == dns_rdatatype_nsec3 ||
                           rdataset->covers == dns_rdatatype_nsec3)) ||
                         (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 &&
                          rdataset->type != dns_rdatatype_nsec3 &&
                          rdataset->covers != dns_rdatatype_nsec3)));
-               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+               TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
        }
 
        nodefullname(db, node, nodename);
@@ -7018,14 +7152,13 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                newheader->resign_lsb = 0;
        }
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        changed = add_changed(rbtdb, rbtversion, rbtnode);
        if (changed == NULL) {
                free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
                NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                           isc_rwlocktype_write);
+                           &nlocktype);
                return (ISC_R_NOMEMORY);
        }
 
@@ -7165,8 +7298,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        }
 
 unlock:
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        /*
         * Update the zone's secure status.  If version is non-NULL
@@ -7192,6 +7324,7 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        dns_name_t *nodename = dns_fixedname_initname(&fname);
        isc_result_t result;
        rdatasetheader_t *newheader;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
@@ -7225,12 +7358,10 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 
        nodefullname(db, node, nodename);
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
        result = add32(rbtdb, rbtnode, nodename, rbtversion, newheader,
                       DNS_DBADD_FORCE, false, NULL, 0);
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        /*
         * Update the zone's secure status.  If version is non-NULL
@@ -7334,6 +7465,7 @@ loading_addrdataset(void *arg, const dns_name_t *name,
        isc_result_t result;
        isc_region_t region;
        rdatasetheader_t *newheader;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(rdataset->rdclass == rbtdb->common.rdclass);
 
@@ -7424,11 +7556,10 @@ loading_addrdataset(void *arg, const dns_name_t *name,
                newheader->resign_lsb = 0;
        }
 
-       NODE_LOCK(&rbtdb->node_locks[node->locknum].lock, isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype);
        result = add32(rbtdb, node, name, rbtdb->current_version, newheader,
                       DNS_DBADD_MERGE, true, NULL, 0);
-       NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype);
 
        if (result == ISC_R_SUCCESS &&
            delegating_type(rbtdb, node, rdataset->type)) {
@@ -7532,16 +7663,17 @@ delete_callback(void *data, void *arg) {
        dns_rbtdb_t *rbtdb = arg;
        rdatasetheader_t *current, *next;
        unsigned int locknum;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        current = data;
        locknum = current->node->locknum;
-       NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
        while (current != NULL) {
                next = current->next;
                free_rdataset(rbtdb, rbtdb->common.mctx, current);
                current = next;
        }
-       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
 }
 
 static bool
@@ -7580,12 +7712,13 @@ static unsigned int
 nodecount(dns_db_t *db, dns_dbtree_t tree) {
        dns_rbtdb_t *rbtdb;
        unsigned int count;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        rbtdb = (dns_rbtdb_t *)db;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        switch (tree) {
        case dns_dbtree_main:
                count = dns_rbt_nodecount(rbtdb->tree);
@@ -7599,7 +7732,7 @@ nodecount(dns_db_t *db, dns_dbtree_t tree) {
        default:
                UNREACHABLE();
        }
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        return (count);
 }
@@ -7608,14 +7741,15 @@ static size_t
 hashsize(dns_db_t *db) {
        dns_rbtdb_t *rbtdb;
        size_t size;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        rbtdb = (dns_rbtdb_t *)db;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        size = dns_rbt_hashsize(rbtdb->tree);
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        return (size);
 }
@@ -7744,6 +7878,7 @@ static isc_result_t
 setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
        rdatasetheader_t *header, oldheader;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(!IS_CACHE(rbtdb));
@@ -7752,8 +7887,7 @@ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
        header = rdataset->private3;
        header--;
 
-       NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[header->node->locknum].lock, &nlocktype);
 
        oldheader = *header;
        /*
@@ -7783,8 +7917,7 @@ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
                RDATASET_ATTR_SET(header, RDATASET_ATTR_RESIGN);
                resign_insert(rbtdb, header->node->locknum, header);
        }
-       NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock, &nlocktype);
        return (ISC_R_SUCCESS);
 }
 
@@ -7795,13 +7928,15 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *foundname) {
        unsigned int i;
        isc_result_t result = ISC_R_NOTFOUND;
        unsigned int locknum = 0;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
 
        for (i = 0; i < rbtdb->node_lock_count; i++) {
-               NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_read);
+               NODE_RDLOCK(&rbtdb->node_locks[i].lock, &nlocktype);
 
                /*
                 * Find for the earliest signing time among all of the
@@ -7811,8 +7946,7 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *foundname) {
                this = isc_heap_element(rbtdb->heaps[i], 1);
                if (this == NULL) {
                        /* Nothing found; unlock and try the next heap. */
-                       NODE_UNLOCK(&rbtdb->node_locks[i].lock,
-                                   isc_rwlocktype_read);
+                       NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nlocktype);
                        continue;
                }
 
@@ -7824,13 +7958,14 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *foundname) {
                         */
                        header = this;
                        locknum = i;
+                       nlocktype = isc_rwlocktype_none;
                } else if (resign_sooner(this, header)) {
                        /*
                         * Found an earlier signing time; release the
                         * previous bucket lock and retain this one instead.
                         */
                        NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
-                                   isc_rwlocktype_read);
+                                   &nlocktype);
                        header = this;
                        locknum = i;
                } else {
@@ -7838,12 +7973,12 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *foundname) {
                         * Earliest signing time in this heap isn't
                         * an improvement; unlock and try the next heap.
                         */
-                       NODE_UNLOCK(&rbtdb->node_locks[i].lock,
-                                   isc_rwlocktype_read);
+                       NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nlocktype);
                }
        }
 
        if (header != NULL) {
+               nlocktype = isc_rwlocktype_read;
                /*
                 * Found something; pass back the answer and unlock
                 * the bucket.
@@ -7855,13 +7990,12 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *foundname) {
                        dns_rbt_fullnamefromnode(header->node, foundname);
                }
 
-               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
-                           isc_rwlocktype_read);
+               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
 
                result = ISC_R_SUCCESS;
        }
 
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        return (result);
 }
@@ -7872,6 +8006,8 @@ resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
        dns_rbtnode_t *node;
        rdatasetheader_t *header;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(rdataset != NULL);
@@ -7891,16 +8027,15 @@ resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) {
                return;
        }
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
-       NODE_LOCK(&rbtdb->node_locks[node->locknum].lock, isc_rwlocktype_write);
+       TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype);
+       NODE_WRLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype);
        /*
         * Delete from heap and save to re-signed list so that it can
         * be restored if we backout of this change.
         */
        resign_delete(rbtdb, rbtversion, header);
-       NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
-                   isc_rwlocktype_write);
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 }
 
 static isc_result_t
@@ -7942,14 +8077,15 @@ nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
        dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
        isc_result_t result;
+       isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
 
        REQUIRE(VALID_RBTDB(rbtdb));
        REQUIRE(node != NULL);
        REQUIRE(name != NULL);
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
        result = dns_rbt_fullnamefromnode(rbtnode, name);
-       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
 
        return (result);
 }
@@ -8141,7 +8277,7 @@ dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
 
        RBTDB_INITLOCK(&rbtdb->lock);
 
-       isc_rwlock_init(&rbtdb->tree_lock, 0, 0);
+       TREE_INITLOCK(&rbtdb->tree_lock);
 
        /*
         * Initialize node_lock_count in a generic way to support future
@@ -8352,7 +8488,7 @@ cleanup_node_locks:
                    rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
 
 cleanup_tree_lock:
-       isc_rwlock_destroy(&rbtdb->tree_lock);
+       TREE_DESTROYLOCK(&rbtdb->tree_lock);
        RBTDB_DESTROYLOCK(&rbtdb->lock);
        isc_mem_put(mctx, rbtdb, sizeof(*rbtdb));
        return (result);
@@ -8597,13 +8733,12 @@ rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
        dns_rbtdb_t *rbtdb = rdataset->private1;
        dns_rbtnode_t *rbtnode = rdataset->private2;
        rdatasetheader_t *header = rdataset->private3;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header--;
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
        header->trust = rdataset->trust = trust;
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 static void
@@ -8611,13 +8746,12 @@ rdataset_expire(dns_rdataset_t *rdataset) {
        dns_rbtdb_t *rbtdb = rdataset->private1;
        dns_rbtnode_t *rbtnode = rdataset->private2;
        rdatasetheader_t *header = rdataset->private3;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header--;
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
        expire_header(rbtdb, header, false, expire_flush);
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 static void
@@ -8625,13 +8759,12 @@ rdataset_clearprefetch(dns_rdataset_t *rdataset) {
        dns_rbtdb_t *rbtdb = rdataset->private1;
        dns_rbtnode_t *rbtnode = rdataset->private2;
        rdatasetheader_t *header = rdataset->private3;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header--;
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
        RDATASET_ATTR_CLR(header, RDATASET_ATTR_PREFETCH);
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 /*
@@ -8663,13 +8796,13 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
        rbtdb_version_t *rbtversion = rbtiterator->common.version;
        rdatasetheader_t *header, *top_next;
        rbtdb_serial_t serial = 1;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        if (!IS_CACHE(rbtdb)) {
                serial = rbtversion->serial;
        }
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        for (header = rbtnode->data; header != NULL; header = top_next) {
                top_next = header->next;
@@ -8692,8 +8825,7 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
                }
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        rbtiterator->current = header;
 
@@ -8714,6 +8846,7 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
        rbtdb_rdatatype_t type, negtype;
        dns_rdatatype_t rdtype, covers;
        rbtdb_serial_t serial = 1;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header = rbtiterator->current;
        if (header == NULL) {
@@ -8724,8 +8857,7 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
                serial = rbtversion->serial;
        }
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        type = header->type;
        rdtype = RBTDB_RDATATYPE_BASE(header->type);
@@ -8762,8 +8894,7 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
                }
        }
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        rbtiterator->current = header;
 
@@ -8780,18 +8911,17 @@ rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
        dns_rbtnode_t *rbtnode = rbtiterator->common.node;
        rdatasetheader_t *header;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header = rbtiterator->current;
        REQUIRE(header != NULL);
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now,
                      isc_rwlocktype_read, rdataset);
 
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 /*
@@ -8816,7 +8946,7 @@ dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
        dns_rbtnode_t *node = rbtdbiter->node;
        nodelock_t *lock;
-       isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
        isc_rwlocktype_t tlocktype = rbtdbiter->tree_locked;
 
        if (node == NULL) {
@@ -8826,17 +8956,15 @@ dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
        REQUIRE(tlocktype != isc_rwlocktype_write);
 
        lock = &rbtdb->node_locks[node->locknum].lock;
-       NODE_LOCK(lock, nlocktype);
+       NODE_RDLOCK(lock, &nlocktype);
        decrement_reference(rbtdb, node, 0, &nlocktype, &rbtdbiter->tree_locked,
                            false);
-       NODE_UNLOCK(lock, nlocktype);
+       NODE_UNLOCK(lock, &nlocktype);
 
        if (tlocktype != rbtdbiter->tree_locked) {
-               RWUNLOCK(&rbtdb->tree_lock, rbtdbiter->tree_locked);
-               rbtdbiter->tree_locked = isc_rwlocktype_none;
+               TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
                if (tlocktype == isc_rwlocktype_read) {
-                       RWLOCK(&rbtdb->tree_lock, tlocktype);
-                       rbtdbiter->tree_locked = tlocktype;
+                       TREE_RDLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
                }
        }
        INSIST(rbtdbiter->tree_locked == tlocktype);
@@ -8865,32 +8993,28 @@ flush_deletions(rbtdb_dbiterator_t *rbtdbiter) {
                      rbtdbiter->delcnt, dns_rbt_nodecount(rbtdb->tree));
 
        if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
-               RWUNLOCK(&rbtdb->tree_lock, tlocktype);
-               rbtdbiter->tree_locked = isc_rwlocktype_none;
+               TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
        }
        INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none);
 
-       rbtdbiter->tree_locked = isc_rwlocktype_write;
-       RWLOCK(&rbtdb->tree_lock, rbtdbiter->tree_locked);
+       TREE_WRLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
 
        for (size_t i = 0; i < (size_t)rbtdbiter->delcnt; i++) {
-               isc_rwlocktype_t nlocktype = isc_rwlocktype_read;
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
                dns_rbtnode_t *node = rbtdbiter->deletions[i];
                nodelock_t *lock = &rbtdb->node_locks[node->locknum].lock;
 
-               NODE_LOCK(lock, nlocktype);
+               NODE_RDLOCK(lock, &nlocktype);
                decrement_reference(rbtdb, node, 0, &nlocktype,
                                    &rbtdbiter->tree_locked, false);
-               NODE_UNLOCK(lock, nlocktype);
+               NODE_UNLOCK(lock, &nlocktype);
        }
 
        rbtdbiter->delcnt = 0;
 
-       RWUNLOCK(&rbtdb->tree_lock, rbtdbiter->tree_locked);
-       rbtdbiter->tree_locked = isc_rwlocktype_none;
+       TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
        if (tlocktype == isc_rwlocktype_read) {
-               RWLOCK(&rbtdb->tree_lock, tlocktype);
-               rbtdbiter->tree_locked = tlocktype;
+               TREE_RDLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
        }
        INSIST(rbtdbiter->tree_locked == tlocktype);
 }
@@ -8902,8 +9026,7 @@ resume_iteration(rbtdb_dbiterator_t *rbtdbiter) {
        REQUIRE(rbtdbiter->paused);
        REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none);
 
-       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-       rbtdbiter->tree_locked = isc_rwlocktype_read;
+       TREE_RDLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
 
        rbtdbiter->paused = false;
 }
@@ -8915,8 +9038,7 @@ dbiterator_destroy(dns_dbiterator_t **iteratorp) {
        dns_db_t *db = NULL;
 
        if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
-               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-               rbtdbiter->tree_locked = isc_rwlocktype_none;
+               TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
        }
        INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none);
 
@@ -9307,8 +9429,7 @@ dbiterator_pause(dns_dbiterator_t *iterator) {
        rbtdbiter->paused = true;
 
        if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
-               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-               rbtdbiter->tree_locked = isc_rwlocktype_none;
+               TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked);
        }
        INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none);
 
@@ -9359,14 +9480,13 @@ rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
        dns_rbtnode_t *rbtnode = rdataset->private2;
        unsigned char *raw = rdataset->private3; /* RDATASLAB */
        rdatasetheader_t *header;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header = (struct rdatasetheader *)(raw - sizeof(*header));
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_write);
+       NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
        setownercase(header, name);
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_write);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 static void
@@ -9377,11 +9497,11 @@ rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
        rdatasetheader_t *header = NULL;
        uint8_t mask = (1 << 7);
        uint8_t bits = 0;
+       isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
 
        header = (struct rdatasetheader *)(raw - sizeof(*header));
 
-       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                 isc_rwlocktype_read);
+       NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 
        if (!CASESET(header)) {
                goto unlock;
@@ -9404,8 +9524,7 @@ rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
        }
 
 unlock:
-       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
 }
 
 struct rbtdb_glue {
@@ -10024,8 +10143,8 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, isc_stdtime_t now,
             locknum != locknum_start && purgecount > 0;
             locknum = (locknum + 1) % rbtdb->node_lock_count)
        {
-               NODE_LOCK(&rbtdb->node_locks[locknum].lock,
-                         isc_rwlocktype_write);
+               isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+               NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
 
                header = isc_heap_element(rbtdb->heaps[locknum], 1);
                if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) {
@@ -10050,8 +10169,7 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, isc_stdtime_t now,
                        purgecount--;
                }
 
-               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
-                           isc_rwlocktype_write);
+               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
        }
 }