📄 rbtdb.c
字号:
} }}static voiddetach(dns_db_t **dbp) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(*dbp); unsigned int refs; REQUIRE(VALID_RBTDB(rbtdb)); isc_refcount_decrement(&rbtdb->references, &refs); if (refs == 0) maybe_free_rbtdb(rbtdb); *dbp = NULL;}static voidcurrentversion(dns_db_t *db, dns_dbversion_t **versionp) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; rbtdb_version_t *version; REQUIRE(VALID_RBTDB(rbtdb)); LOCK(&rbtdb->lock); version = rbtdb->current_version; if (version->references == 0) PREPEND(rbtdb->open_versions, version, link); version->references++; UNLOCK(&rbtdb->lock); *versionp = (dns_dbversion_t *)version;}static inline rbtdb_version_t *allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial, unsigned int references, isc_boolean_t writer){ rbtdb_version_t *version; version = isc_mem_get(mctx, sizeof(*version)); if (version == NULL) return (NULL); version->serial = serial; version->references = references; version->writer = writer; version->commit_ok = ISC_FALSE; ISC_LIST_INIT(version->changed_list); ISC_LINK_INIT(version, link); return (version);}static isc_result_tnewversion(dns_db_t *db, dns_dbversion_t **versionp) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; rbtdb_version_t *version; REQUIRE(VALID_RBTDB(rbtdb)); REQUIRE(versionp != NULL && *versionp == NULL); REQUIRE(rbtdb->future_version == NULL); LOCK(&rbtdb->lock); RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */ version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1, ISC_TRUE); if (version != NULL) { version->commit_ok = ISC_TRUE; rbtdb->next_serial++; rbtdb->future_version = version; } UNLOCK(&rbtdb->lock); if (version == NULL) return (ISC_R_NOMEMORY); *versionp = version; return (ISC_R_SUCCESS);}static voidattachversion(dns_db_t *db, dns_dbversion_t *source, dns_dbversion_t **targetp){ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; rbtdb_version_t *rbtversion = source; REQUIRE(VALID_RBTDB(rbtdb)); LOCK(&rbtdb->lock); INSIST(rbtversion->references > 0); rbtversion->references++; INSIST(rbtversion->references != 0); UNLOCK(&rbtdb->lock); *targetp = rbtversion;}static rbtdb_changed_t *add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version, dns_rbtnode_t *node){ rbtdb_changed_t *changed; /* * Caller must be holding the node lock. */ changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed)); LOCK(&rbtdb->lock); REQUIRE(version->writer); if (changed != NULL) { INSIST(node->references > 0); node->references++; INSIST(node->references != 0); changed->node = node; changed->dirty = ISC_FALSE; ISC_LIST_INITANDAPPEND(version->changed_list, changed, link); } else version->commit_ok = ISC_FALSE; UNLOCK(&rbtdb->lock); return (changed);}static inline voidfree_noqname(isc_mem_t *mctx, struct noqname **noqname) { if (dns_name_dynamic(&(*noqname)->name)) dns_name_free(&(*noqname)->name, mctx); if ((*noqname)->nsec != NULL) isc_mem_put(mctx, (*noqname)->nsec, dns_rdataslab_size((*noqname)->nsec, 0)); if ((*noqname)->nsec != NULL) isc_mem_put(mctx, (*noqname)->nsecsig, dns_rdataslab_size((*noqname)->nsecsig, 0)); isc_mem_put(mctx, *noqname, sizeof(**noqname)); *noqname = NULL;}static inline voidfree_rdataset(isc_mem_t *mctx, rdatasetheader_t *rdataset) { unsigned int size; if (rdataset->noqname != NULL) free_noqname(mctx, &rdataset->noqname); if ((rdataset->attributes & RDATASET_ATTR_NONEXISTENT) != 0) size = sizeof(*rdataset); else size = dns_rdataslab_size((unsigned char *)rdataset, sizeof(*rdataset)); isc_mem_put(mctx, rdataset, size);}static inline voidrollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) { rdatasetheader_t *header, *dcurrent; isc_boolean_t make_dirty = ISC_FALSE; /* * Caller must hold the node lock. */ /* * We set the IGNORE attribute on rdatasets with serial number * 'serial'. When the reference count goes to zero, these rdatasets * will be cleaned up; until that time, they will be ignored. */ for (header = node->data; header != NULL; header = header->next) { if (header->serial == serial) { header->attributes |= RDATASET_ATTR_IGNORE; make_dirty = ISC_TRUE; } for (dcurrent = header->down; dcurrent != NULL; dcurrent = dcurrent->down) { if (dcurrent->serial == serial) { dcurrent->attributes |= RDATASET_ATTR_IGNORE; make_dirty = ISC_TRUE; } } } if (make_dirty) node->dirty = 1;}static inline voidclean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { rdatasetheader_t *current, *dcurrent, *top_prev, *top_next, *down_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; dcurrent = current->down; if (dcurrent != NULL) { do { down_next = dcurrent->down; free_rdataset(mctx, dcurrent); dcurrent = down_next; } while (dcurrent != NULL); current->down = NULL; } /* * 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;}static inline voidnew_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { if (node->references == 0) { rbtdb->node_locks[node->locknum].references++; INSIST(rbtdb->node_locks[node->locknum].references != 0); } node->references++; INSIST(node->references != 0);}static voidno_references(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rbtdb_serial_t least_serial, isc_rwlocktype_t lock){ isc_result_t result; isc_boolean_t write_locked; unsigned int locknum; /* * Caller must be holding the node lock. */ REQUIRE(node->references == 0); if (node->dirty) { if (IS_CACHE(rbtdb)) clean_cache_node(rbtdb, node); else { if (least_serial == 0) { /* * Caller doesn't know the least serial. * Get it. */ LOCK(&rbtdb->lock); least_serial = rbtdb->least_serial; UNLOCK(&rbtdb->lock); } clean_zone_node(rbtdb, node, least_serial); } } locknum = node->locknum; INSIST(rbtdb->node_locks[locknum].references > 0); rbtdb->node_locks[locknum].references--; /* * XXXDCL should this only be done for cache zones? */ if (node->data != NULL || node->down != NULL) return; /* * XXXDCL need to add a deferred delete method for ISC_R_LOCKBUSY. */ if (lock != 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 (lock == 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) { 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), "no_references: 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, "no_references: dns_rbt_deletenode: %s", isc_result_totext(result)); } /* * Relock a read lock, or unlock the write lock if no lock was held. */ if (lock == isc_rwlocktype_none) if (write_locked) RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); if (lock == isc_rwlocktype_read) if (write_locked) isc_rwlock_downgrade(&rbtdb->tree_lock);}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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -