📄 rbtdb.c
字号:
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_rdataset(isc_mem_t *mctx, rdatasetheader_t *rdataset) { unsigned int size; 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 ((current->attributes & RDATASET_ATTR_NONEXISTENT) != 0) { 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); if (!changed->dirty) { UNLINK(version->changed_list, changed, link); APPEND(*cleanup_list, changed, link); } }}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; isc_mutex_t *lock; REQUIRE(VALID_RBTDB(rbtdb)); version = (rbtdb_version_t *)*versionp; cleanup_version = NULL; ISC_LIST_INIT(cleanup_list); LOCK(&rbtdb->lock); INSIST(version->references > 0); INSIST(!version->writer || !(commit && version->references > 1)); version->references--; serial = version->serial; if (version->references == 0) { if (version->writer) { if (commit) { INSIST(version->commit_ok); INSIST(version == rbtdb->future_version); 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. */ cleanup_nondirty(version, &cleanup_list); } /* * If the (soon to be former) current version * isn't being used by anyone, we can clean * it up. */ if (rbtdb->current_version->references == 0) cleanup_version = rbtdb->current_version; /* * Become the current version. */ version->writer = ISC_FALSE; rbtdb->current_version = version; rbtdb->current_serial = version->serial; rbtdb->future_version = NULL; } else { /* * We're rolling back this transaction. */ cleanup_list = version->changed_list; rollback = ISC_TRUE; cleanup_version = version; rbtdb->future_version = NULL; } } else { if (version != rbtdb->current_version) { /* * There are no external or internal references * to this version and it can be cleaned up. */ cleanup_version = version; /* * Find the version with the least serial * number greater than ours. */ least_greater = PREV(version, link); if (least_greater == NULL) least_greater = rbtdb->current_version; /* * Is this the least open version? */ if (version->serial == rbtdb->least_serial) { /* * Yes. Install the new least open * version. */ make_least_version(rbtdb, least_greater, &cleanup_list); } else { /* * Add any unexecuted cleanups to * those of the least greater version. */ APPENDLIST(least_greater->changed_list, version->changed_list, link); } } UNLINK(rbtdb->open_versions, version, link); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -