📄 rbtdb.c
字号:
top->down = NULL;}static inline voidclean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { rdatasetheader_t *current, *top_prev, *top_next; isc_mem_t *mctx = rbtdb->common.mctx; /* * Caller must be holding the node lock. */ top_prev = NULL; for (current = node->data; current != NULL; current = top_next) { top_next = current->next; clean_stale_headers(mctx, current); /* * If current is nonexistent or stale, we can clean it up. */ if ((current->attributes & (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) { if (top_prev != NULL) top_prev->next = current->next; else node->data = current->next; free_rdataset(mctx, current); } else top_prev = current; } node->dirty = 0;}static inline voidclean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rbtdb_serial_t least_serial){ rdatasetheader_t *current, *dcurrent, *down_next, *dparent; rdatasetheader_t *top_prev, *top_next; isc_mem_t *mctx = rbtdb->common.mctx; isc_boolean_t still_dirty = ISC_FALSE; /* * Caller must be holding the node lock. */ REQUIRE(least_serial != 0); top_prev = NULL; for (current = node->data; current != NULL; current = top_next) { top_next = current->next; /* * First, we clean up any instances of multiple rdatasets * with the same serial number, or that have the IGNORE * attribute. */ dparent = current; for (dcurrent = current->down; dcurrent != NULL; dcurrent = down_next) { down_next = dcurrent->down; INSIST(dcurrent->serial <= dparent->serial); if (dcurrent->serial == dparent->serial || IGNORE(dcurrent)) { if (down_next != NULL) down_next->next = dparent; dparent->down = down_next; free_rdataset(mctx, dcurrent); } else dparent = dcurrent; } /* * We've now eliminated all IGNORE datasets with the possible * exception of current, which we now check. */ if (IGNORE(current)) { down_next = current->down; if (down_next == NULL) { if (top_prev != NULL) top_prev->next = current->next; else node->data = current->next; free_rdataset(mctx, current); /* * current no longer exists, so we can * just continue with the loop. */ continue; } else { /* * Pull up current->down, making it the new * current. */ if (top_prev != NULL) top_prev->next = down_next; else node->data = down_next; down_next->next = top_next; free_rdataset(mctx, current); current = down_next; } } /* * We now try to find the first down node less than the * least serial. */ dparent = current; for (dcurrent = current->down; dcurrent != NULL; dcurrent = down_next) { down_next = dcurrent->down; if (dcurrent->serial < least_serial) break; dparent = dcurrent; } /* * If there is a such an rdataset, delete it and any older * versions. */ if (dcurrent != NULL) { do { down_next = dcurrent->down; INSIST(dcurrent->serial <= least_serial); free_rdataset(mctx, dcurrent); dcurrent = down_next; } while (dcurrent != NULL); dparent->down = NULL; } /* * Note. The serial number of 'current' might be less than * least_serial too, but we cannot delete it because it is * the most recent version, unless it is a NONEXISTENT * rdataset. */ if (current->down != NULL) { still_dirty = ISC_TRUE; top_prev = current; } else { /* * If this is a NONEXISTENT rdataset, we can delete it. */ if (NONEXISTENT(current)) { if (top_prev != NULL) top_prev->next = current->next; else node->data = current->next; free_rdataset(mctx, current); } else top_prev = current; } } if (!still_dirty) node->dirty = 0;}/* * Caller must be holding the node lock if its reference must be protected * by the lock. */static inline voidnew_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { unsigned int lockrefs, noderefs; isc_refcount_t *lockref; dns_rbtnode_refincrement0(node, &noderefs); if (noderefs == 1) { /* this is the first reference to the node */ lockref = &rbtdb->node_locks[node->locknum].references; isc_refcount_increment0(lockref, &lockrefs); INSIST(lockrefs != 0); } INSIST(noderefs != 0);}/* * Caller must be holding the node lock; either the "strong", read or write * lock. Note that the lock must be held even when node references are * atomically modified; in that case the decrement operation itself does not * have to be protected, but we must avoid a race condition where multiple * threads are decreasing the reference to zero simultaneously and at least * one of them is going to free the node. * This function returns ISC_TRUE if and only if the node reference decreases * to zero. */static isc_boolean_tdecrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rbtdb_serial_t least_serial, isc_rwlocktype_t nlock, isc_rwlocktype_t tlock){ isc_result_t result; isc_boolean_t write_locked; rbtdb_nodelock_t *nodelock; unsigned int refs, nrefs; nodelock = &rbtdb->node_locks[node->locknum]; /* Handle easy and typical case first. */ if (!node->dirty && (node->data != NULL || node->down != NULL)) { dns_rbtnode_refdecrement(node, &nrefs); INSIST((int)nrefs >= 0); if (nrefs == 0) { isc_refcount_decrement(&nodelock->references, &refs); INSIST((int)refs >= 0); } return ((nrefs == 0) ? ISC_TRUE : ISC_FALSE); } /* Upgrade the lock? */ if (nlock == isc_rwlocktype_read) { NODE_WEAKUNLOCK(&nodelock->lock, isc_rwlocktype_read); NODE_WEAKLOCK(&nodelock->lock, isc_rwlocktype_write); } dns_rbtnode_refdecrement(node, &nrefs); INSIST((int)nrefs >= 0); if (nrefs > 0) { /* Restore the lock? */ if (nlock == isc_rwlocktype_read) NODE_WEAKDOWNGRADE(&nodelock->lock); return (ISC_FALSE); } if (node->dirty && dns_rbtnode_refcurrent(node) == 0) { if (IS_CACHE(rbtdb)) clean_cache_node(rbtdb, node); else { if (least_serial == 0) { /* * Caller doesn't know the least serial. * Get it. */ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); least_serial = rbtdb->least_serial; RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); } clean_zone_node(rbtdb, node, least_serial); } } isc_refcount_decrement(&nodelock->references, &refs); INSIST((int)refs >= 0); /* * XXXDCL should this only be done for cache zones? */ if (node->data != NULL || node->down != NULL) { /* Restore the lock? */ if (nlock == isc_rwlocktype_read) NODE_WEAKDOWNGRADE(&nodelock->lock); return (ISC_TRUE); } /* * XXXDCL need to add a deferred delete method for ISC_R_LOCKBUSY. */ if (tlock != isc_rwlocktype_write) { /* * Locking hierarchy notwithstanding, we don't need to free * the node lock before acquiring the tree write lock because * we only do a trylock. */ if (tlock == isc_rwlocktype_read) result = isc_rwlock_tryupgrade(&rbtdb->tree_lock); else result = isc_rwlock_trylock(&rbtdb->tree_lock, isc_rwlocktype_write); RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_LOCKBUSY); write_locked = ISC_TF(result == ISC_R_SUCCESS); } else write_locked = ISC_TRUE; if (write_locked && dns_rbtnode_refcurrent(node) == 0) { /* * We can now delete the node if the reference counter is * zero. This should be typically the case, but a different * thread may still gain a (new) reference just before the * current thread locks the tree (e.g., in findnode()). */ if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) { char printname[DNS_NAME_FORMATSIZE]; isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "decrement_reference: " "delete from rbt: %p %s", node, dns_rbt_formatnodename(node, printname, sizeof(printname))); } result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); if (result != ISC_R_SUCCESS) isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, "decrement_reference: " "dns_rbt_deletenode: %s", isc_result_totext(result)); } /* Restore the lock? */ if (nlock == isc_rwlocktype_read) NODE_WEAKDOWNGRADE(&nodelock->lock); /* * Relock a read lock, or unlock the write lock if no lock was held. */ if (tlock == isc_rwlocktype_none) if (write_locked) RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); if (tlock == isc_rwlocktype_read) if (write_locked) isc_rwlock_downgrade(&rbtdb->tree_lock); return (ISC_TRUE);}static inline voidmake_least_version(dns_rbtdb_t *rbtdb, rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list){ /* * Caller must be holding the database lock. */ rbtdb->least_serial = version->serial; *cleanup_list = version->changed_list; ISC_LIST_INIT(version->changed_list);}static inline voidcleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) { rbtdb_changed_t *changed, *next_changed; /* * If the changed record is dirty, then * an update created multiple versions of * a given rdataset. We keep this list * until we're the least open version, at * which point it's safe to get rid of any * older versions. * * If the changed record isn't dirty, then * we don't need it anymore since we're * committing and not rolling back. * * The caller must be holding the database lock. */ for (changed = HEAD(version->changed_list); changed != NULL; changed = next_changed) { next_changed = NEXT(changed, link); if (!changed->dirty) { UNLINK(version->changed_list, changed, link); APPEND(*cleanup_list, changed, link); } }}static isc_boolean_tiszonesecure(dns_db_t *db, dns_dbnode_t *origin) { dns_rdataset_t keyset; dns_rdataset_t nsecset, signsecset; isc_boolean_t haszonekey = ISC_FALSE; isc_boolean_t hasnsec = ISC_FALSE; isc_result_t result; dns_rdataset_init(&keyset); result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_dnskey, 0, 0, &keyset, NULL); if (result == ISC_R_SUCCESS) { dns_rdata_t keyrdata = DNS_RDATA_INIT; result = dns_rdataset_first(&keyset); while (result == ISC_R_SUCCESS) { dns_rdataset_current(&keyset, &keyrdata); if (dns_zonekey_iszonekey(&keyrdata)) { haszonekey = ISC_TRUE; break; } result = dns_rdataset_next(&keyset); } dns_rdataset_disassociate(&keyset); } if (!haszonekey) return (ISC_FALSE); dns_rdataset_init(&nsecset); dns_rdataset_init(&signsecset); result = dns_db_findrdataset(db, origin, NULL, dns_rdatatype_nsec, 0, 0, &nsecset, &signsecset); if (result == ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&signsecset)) { hasnsec = ISC_TRUE; dns_rdataset_disassociate(&signsecset); } dns_rdataset_disassociate(&nsecset); } return (hasnsec);}static voidcloseversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; rbtdb_version_t *version, *cleanup_version, *least_greater; isc_boolean_t rollback = ISC_FALSE; rbtdb_changedlist_t cleanup_list; rbtdb_changed_t *changed, *next_changed; rbtdb_serial_t serial, least_serial; dns_rbtnode_t *rbtnode; unsigned int refs; REQUIRE(VALID_RBTDB(rbtdb)); version = (rbtdb_version_t *)*versionp; cleanup_version = NULL; ISC_LIST_INIT(cleanup_list); isc_refcount_decrement(&version->references, &refs); if (refs > 0) { /* typical and easy case first */ if (commit) { RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); INSIST(!version->writer); RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); } goto end; } RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); serial = version->serial; if (version->writer) { if (commit) { unsigned cur_ref; rbtdb_version_t *cur_version; INSIST(version->commit_ok); INSIST(version == rbtdb->future_version); /* * The current version is going to be replaced. * Release the (likely last) reference to it from the * DB itself and unlink it from the open list. */ cur_version = rbtdb->current_version; isc_refcount_decrement(&cur_version->references, &cur_ref); if (cur_ref == 0) { if (cur_version->serial == rbtdb->least_serial) INSIST(EMPTY(cur_version->changed_list)); UNLINK(rbtdb->open_versions, cur_version, link); } if (EMPTY(rbtdb->open_versions)) { /* * We're going to become the least open * version. */ make_least_version(rbtdb, version, &cleanup_list); } else { /* * Some other open version is the * least version. We can't cleanup * records that were changed in this * version because the older versions * may still be in use by an open * version. * * We can, however, discard the * changed records for things that * we've added that didn't exist in * prior versions. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -