📄 rbtdb.c
字号:
isc_boolean_t paused; isc_boolean_t new_origin; isc_rwlocktype_t tree_locked; isc_result_t result; dns_fixedname_t name; dns_fixedname_t origin; dns_rbtnodechain_t chain; dns_rbtnode_t *node; dns_rbtnode_t *deletions[DELETION_BATCH_MAX]; int delete;} rbtdb_dbiterator_t;#define IS_STUB(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_STUB) != 0)#define IS_CACHE(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_CACHE) != 0)static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event);/* * Locking * * If a routine is going to lock more than one lock in this module, then * the locking must be done in the following order: * * Tree Lock * * Node Lock (Only one from the set may be locked at one time by * any caller) * * Database Lock * * Failure to follow this hierarchy can result in deadlock. *//* * Deleting Nodes * * Currently there is no deletion of nodes from the database, except when * the database is being destroyed. * * If node deletion is added in the future, then for zone databases the node * for the origin of the zone MUST NOT be deleted. *//* * DB Routines */static voidattach(dns_db_t *source, dns_db_t **targetp) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source; REQUIRE(VALID_RBTDB(rbtdb)); isc_refcount_increment(&rbtdb->references, NULL); *targetp = source;}static voidfree_rbtdb_callback(isc_task_t *task, isc_event_t *event) { dns_rbtdb_t *rbtdb = event->ev_arg; UNUSED(task); free_rbtdb(rbtdb, ISC_TRUE, event);}/*% * Work out how many nodes can be deleted in the time between two * requests to the nameserver. Smooth the resulting number and use it * as a estimate for the number of nodes to be deleted in the next * iteration. */static unsigned intadjust_quantum(unsigned int old, isc_time_t *start) { unsigned int pps = dns_pps; /* packets per second */ unsigned int interval; isc_uint64_t usecs; isc_time_t end; unsigned int new; if (pps < 100) pps = 100; isc_time_now(&end); interval = 1000000 / pps; /* interval in usec */ if (interval == 0) interval = 1; usecs = isc_time_microdiff(&end, start); if (usecs == 0) { /* * We were unable to measure the amount of time taken. * Double the nodes deleted next time. */ old *= 2; if (old > 1000) old = 1000; return (old); } new = old * interval; new /= (unsigned int)usecs; if (new == 0) new = 1; else if (new > 1000) new = 1000; /* Smooth */ new = (new + old * 3) / 4; isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "adjust_quantum -> %d", new); return (new);} static voidfree_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { unsigned int i; isc_ondestroy_t ondest; isc_result_t result; char buf[DNS_NAME_FORMATSIZE]; isc_time_t start; REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions)); REQUIRE(rbtdb->future_version == NULL); if (rbtdb->current_version != NULL) { unsigned int refs; isc_refcount_decrement(&rbtdb->current_version->references, &refs); INSIST(refs == 0); UNLINK(rbtdb->open_versions, rbtdb->current_version, link); isc_refcount_destroy(&rbtdb->current_version->references); isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, sizeof(rbtdb_version_t)); } if (event == NULL) rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0; again: if (rbtdb->tree != NULL) { isc_time_now(&start); result = dns_rbt_destroy2(&rbtdb->tree, rbtdb->quantum); if (result == ISC_R_QUOTA) { INSIST(rbtdb->task != NULL); if (rbtdb->quantum != 0) rbtdb->quantum = adjust_quantum(rbtdb->quantum, &start); if (event == NULL) event = isc_event_allocate(rbtdb->common.mctx, NULL, DNS_EVENT_FREESTORAGE, free_rbtdb_callback, rbtdb, sizeof(isc_event_t)); if (event == NULL) goto again; isc_task_send(rbtdb->task, &event); return; } INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL); } if (event != NULL) isc_event_free(&event); if (log) { if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_format(&rbtdb->common.origin, buf, sizeof(buf)); else strcpy(buf, "<UNKNOWN>"); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "done free_rbtdb(%s)", buf); } if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx); for (i = 0; i < rbtdb->node_lock_count; i++) { isc_refcount_destroy(&rbtdb->node_locks[i].references); NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); } isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); isc_rwlock_destroy(&rbtdb->tree_lock); isc_refcount_destroy(&rbtdb->references); if (rbtdb->task != NULL) isc_task_detach(&rbtdb->task); RBTDB_DESTROYLOCK(&rbtdb->lock); rbtdb->common.magic = 0; rbtdb->common.impmagic = 0; ondest = rbtdb->common.ondest; isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb)); isc_ondestroy_notify(&ondest, rbtdb);}static inline voidmaybe_free_rbtdb(dns_rbtdb_t *rbtdb) { isc_boolean_t want_free = ISC_FALSE; unsigned int i; unsigned int inactive = 0; /* XXX check for open versions here */ if (rbtdb->soanode != NULL) dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode); if (rbtdb->nsnode != NULL) dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode); /* * Even though there are no external direct references, there still * may be nodes in use. */ for (i = 0; i < rbtdb->node_lock_count; i++) { NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); rbtdb->node_locks[i].exiting = ISC_TRUE; NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write); if (isc_refcount_current(&rbtdb->node_locks[i].references) == 0) { inactive++; } } if (inactive != 0) { RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); rbtdb->active -= inactive; if (rbtdb->active == 0) want_free = ISC_TRUE; RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); if (want_free) { char buf[DNS_NAME_FORMATSIZE]; if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_format(&rbtdb->common.origin, buf, sizeof(buf)); else strcpy(buf, "<UNKNOWN>"); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "calling free_rbtdb(%s)", buf); free_rbtdb(rbtdb, ISC_TRUE, NULL); } }}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; unsigned int refs; REQUIRE(VALID_RBTDB(rbtdb)); RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); version = rbtdb->current_version; isc_refcount_increment(&version->references, &refs); RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); *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){ isc_result_t result; rbtdb_version_t *version; version = isc_mem_get(mctx, sizeof(*version)); if (version == NULL) return (NULL); version->serial = serial; result = isc_refcount_init(&version->references, references); if (result != ISC_R_SUCCESS) { isc_mem_put(mctx, version, sizeof(*version)); return (NULL); } 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); RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); 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; } RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); 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; unsigned int refs; REQUIRE(VALID_RBTDB(rbtdb)); isc_refcount_increment(&rbtversion->references, &refs); INSIST(refs > 1); *targetp = rbtversion;}static rbtdb_changed_t *add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version, dns_rbtnode_t *node){ rbtdb_changed_t *changed; unsigned int refs; /* * Caller must be holding the node lock if its reference must be * protected by the lock. */ changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed)); RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write); REQUIRE(version->writer); if (changed != NULL) { dns_rbtnode_refincrement(node, &refs); INSIST(refs != 0); changed->node = node; changed->dirty = ISC_FALSE; ISC_LIST_INITANDAPPEND(version->changed_list, changed, link); } else version->commit_ok = ISC_FALSE; RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write); return (changed);}static voidfree_acachearray(isc_mem_t *mctx, rdatasetheader_t *header, acachectl_t *array){ unsigned int count; unsigned int i; unsigned char *raw; /* RDATASLAB */ /* * The caller must be holding the corresponding node lock. */ if (array == NULL) return; raw = (unsigned char *)header + sizeof(*header); count = raw[0] * 256 + raw[1]; /* * Sanity check: since an additional cache entry has a reference to * the original DB node (in the callback arg), there should be no * acache entries when the node can be freed. */ for (i = 0; i < count; i++) INSIST(array[i].entry == NULL && array[i].cbarg == NULL); isc_mem_put(mctx, array, count * sizeof(acachectl_t));}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); free_acachearray(mctx, rdataset, rdataset->additional_auth); free_acachearray(mctx, rdataset, rdataset->additional_glue); 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_stale_headers(isc_mem_t *mctx, rdatasetheader_t *top) { rdatasetheader_t *d, *down_next; for (d = top->down; d != NULL; d = down_next) { down_next = d->down; free_rdataset(mctx, d); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -