diff --git a/services/authload.c b/services/authload.c index 6531e7648..1d63cb085 100644 --- a/services/authload.c +++ b/services/authload.c @@ -880,6 +880,15 @@ struct auth_load_general_info* auth_load_info_create(void) lock_protect(&auth_load_info->lock, &auth_load_info->num_auth_load_threads, sizeof(auth_load_info->num_auth_load_threads)); + lock_protect(&auth_load_info->lock, + &auth_load_info->num_auth_transfers, + sizeof(auth_load_info->num_auth_transfers)); + lock_protect(&auth_load_info->lock, + &auth_load_info->wait_transfer_list, + sizeof(auth_load_info->wait_transfer_list)); + lock_protect(&auth_load_info->lock, + &auth_load_info->wait_transfer_last, + sizeof(auth_load_info->wait_transfer_last)); return auth_load_info; } @@ -919,3 +928,122 @@ void auth_load_info_release_thread(struct module_env* env) } lock_basic_unlock(&auth_load_info->lock); } + +int auth_load_info_grab_transfer_in_progress(struct module_env* env) +{ + struct auth_load_general_info* auth_load_info = + env->worker->daemon->auth_load_info; + struct config_file* cfg = env->cfg; + int ret = 0; + lock_basic_lock(&auth_load_info->lock); + if(auth_load_info->num_auth_transfers < cfg->auth_task_threads) { + ret = 1; + auth_load_info->num_auth_transfers++; + } + lock_basic_unlock(&auth_load_info->lock); + return ret; +} + +void auth_load_info_release_transfer_in_progress(struct module_env* env) +{ + struct auth_load_general_info* auth_load_info = + env->worker->daemon->auth_load_info; + lock_basic_lock(&auth_load_info->lock); + if(auth_load_info->num_auth_transfers == 0) { + verbose(VERB_ALGO, "release of auth load transfer, but " + "num_auth_transfers not > 0."); + } else { + auth_load_info->num_auth_transfers--; + } + lock_basic_unlock(&auth_load_info->lock); +} + +int +xfr_transfer_grab_active(struct auth_xfer* xfr) +{ + if(xfr->task_transfer->active_transfer) { + char zname[LDNS_MAX_DOMAINLEN]; + dname_str(xfr->name, zname); + log_err("task transfer already active when activated transfer " + "%s", zname); + return 1; + } + xfr->task_transfer->active_transfer = 1; + return auth_load_info_grab_transfer_in_progress( + xfr->task_transfer->env); +} + +/** Schedule pick up of waiting transfers. */ +static void +xfr_transfer_schedule_waiting_pickup(struct auth_xfer* xfr) +{ + struct auth_load_general_info* auth_load_info = + env->worker->daemon->auth_load_info; + int waiting = 0; + lock_basic_lock(&auth_load_info->lock); + if(auth_load_info->wait_transfer_list) + waiting = 1; + lock_basic_unlock(&auth_load_info->lock); + + if(waiting) { + } +} + +void +xfr_transfer_release_active(struct auth_xfer* xfr) +{ + if(!xfr->task_transfer->active_transfer) + return; + auth_load_info_release_transfer_in_progress(xfr->task_transfer->env); + xfr->task_transfer->active_transfer = 0; + + /* Since a transfer is no longer in progress, see if there are + * waiting transfers. If so, schedule them to get picked up. */ + xfr_transfer_schedule_waiting_pickup(xfr); +} + +void +xfr_transfer_wait_active(struct auth_xfer* xfr) +{ + struct auth_load_general_info* auth_load_info = + xfr->task_transfer->env->worker->daemon->auth_load_info; + if(xfr->task_transfer->on_wait_transfer_list) + return; + lock_basic_lock(&auth_load_info->lock); + xfr->task_transfer->wait_transfer_prev = + auth_load_info->wait_transfer_last; + xfr->task_transfer->wait_transfer_next = NULL; + if(auth_load_info->wait_transfer_last) + auth_load_info->wait_transfer_last->task_transfer-> + wait_transfer_next = xfr; + else auth_load_info->wait_transfer_list = xfr; + auth_load_info->wait_transfer_last = xfr; + lock_basic_unlock(&auth_load_info->lock); + xfr->task_transfer->on_wait_transfer_list = 1; +} + +void +xfr_transfer_remove_wait_transfer_list(struct auth_xfer* xfr) +{ + struct auth_load_general_info* auth_load_info = + xfr->task_transfer->env->worker->daemon->auth_load_info; + if(!xfr->task_transfer->on_wait_transfer_list) + return; + lock_basic_lock(&auth_load_info->lock); + if(xfr->task_transfer->wait_transfer_prev) + xfr->task_transfer->wait_transfer_prev->task_transfer-> + wait_transfer_next = + xfr->task_transfer->wait_transfer_next; + else auth_load_info->wait_transfer_list = + xfr->task_transfer->wait_transfer_next; + if(xfr->task_transfer->wait_transfer_next) + xfr->task_transfer->wait_transfer_next->task_transfer-> + wait_transfer_prev = + xfr->task_transfer->wait_transfer_prev; + else auth_load_info->wait_transfer_last = + xfr->task_transfer->wait_transfer_prev; + lock_basic_unlock(&auth_load_info->lock); + xfr->task_transfer->wait_transfer_prev = NULL; + xfr->task_transfer->wait_transfer_next = NULL; + xfr->task_transfer->on_wait_transfer_list = 0; +} diff --git a/services/authload.h b/services/authload.h index f501c3d51..e2081c991 100644 --- a/services/authload.h +++ b/services/authload.h @@ -57,6 +57,14 @@ struct auth_load_general_info { lock_basic_type lock; /** The number of active auth load threads. */ int num_auth_load_threads; + /** The number of active downloads, zone transfers in progress. + * These are going to need an auth load thread when done. The counter + * is decremented when the thread and transfer have completed. */ + int num_auth_transfers; + /** The auth zone xfer transfer wait list */ + struct auth_xfer* wait_transfer_list; + /** The last item of the wait_transfer list. */ + struct auth_xfer* wait_transfer_last; }; /** @@ -200,4 +208,43 @@ int auth_load_info_grab_thread(struct module_env* env); */ void auth_load_info_release_thread(struct module_env* env); +/** + * Grab a new transfer in progress, this is limited by the auth load count. + * @param env: with auth_load_info with the active transfer number. + * and config file, with configured maximum. + * @return false on failure, like too many active, true if successful. + */ +int auth_load_info_grab_transfer_in_progress(struct module_env* env); + +/** + * Release transfer from active count. It is done. + * @param env: with auth_load_info with the active transfer count. + */ +void auth_load_info_release_transfer_in_progress(struct module_env* env); + +/** + * Make transfer active in the in progress transfer count. + * @param xfr: the xfr is in progress + * @return false if not possible. + */ +int xfr_transfer_grab_active(struct auth_xfer* xfr); + +/** + * Release active transfer from in progress transfer count. + * @param xfr: the xfr is no longer in progress. + */ +void xfr_transfer_release_active(struct auth_xfer* xfr); + +/** + * Add transfer to wait_transfer list. It can not become active right now. + * @param xfr: the xfr is put on the wait_transfer list. + */ +void xfr_transfer_wait_active(struct auth_xfer* xfr); + +/** + * Remove xfr from wait_transfer list + * @param xfr: the xfr is removed from the wait_transfer list. + */ +void xfr_transfer_remove_wait_transfer_list(struct auth_xfer* xfr); + #endif /* SERVICES_AUTHLOAD_H */ diff --git a/services/authzone.c b/services/authzone.c index 38fb3ec3c..2ff96952d 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -5716,6 +5716,8 @@ xfr_transfer_disown(struct auth_xfer* xfr) xfr->task_transfer->lookup_aaaa, xfr->dclass, xfr->task_transfer->env->mesh, &auth_xfer_transfer_lookup_callback, xfr); + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); /* we don't own this item anymore */ xfr->task_transfer->worker = NULL; xfr->task_transfer->env = NULL; @@ -5934,11 +5936,26 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env) /* and set timeout on it */ while(!xfr_transfer_end_of_list(xfr)) { xfr->task_transfer->master = xfr_transfer_current_master(xfr); + if(xfr->task_transfer->master && + !xfr->task_transfer->master->allow_notify && + env->cfg->auth_task_threads != 0) { + if(!xfr_transfer_grab_active(xfr)) { + /* Can not get another transfer to have in + * progress, wait to do this later, when other + * transfers are done. */ + xfr_transfer_wait_active(xfr); + lock_basic_unlock(&xfr->lock); + return; + } + } if(xfr_transfer_init_fetch(xfr, env)) { /* successfully started, wait for callback */ lock_basic_unlock(&xfr->lock); return; } + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); + /* failed to fetch, next master */ xfr_transfer_nextmaster(xfr); } @@ -6522,6 +6539,8 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env) } /* when done, delete data from list */ auth_chunks_delete(xfr->task_transfer); + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); xfr_process_transfer_failed(xfr, env, ixfr_fail); } @@ -6674,6 +6693,8 @@ void xfr_process_load_end_transfer(struct auth_xfer* xfr, /* the zone is gone from the authzones. */ lock_basic_unlock(&xfr->lock); auth_chunks_delete(xfr->task_transfer); + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); return; } } @@ -6689,6 +6710,8 @@ void xfr_process_load_end_transfer(struct auth_xfer* xfr, } /* The transfer failed */ verbose(VERB_ALGO, "xfr_process_load_end_transfer: failed"); + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); xfr_process_transfer_failed(xfr, env, ixfr_fail); } @@ -6785,6 +6808,8 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, auth_chunks_delete(xfr->task_transfer); comm_point_delete(xfr->task_transfer->cp); xfr->task_transfer->cp = NULL; + xfr_transfer_remove_wait_transfer_list(xfr); + xfr_transfer_release_active(xfr); if(gonextonfail) xfr_transfer_nextmaster(xfr); xfr_transfer_nexttarget_or_end(xfr, env); diff --git a/services/authzone.h b/services/authzone.h index c34538c38..0f1e8110f 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -405,6 +405,15 @@ struct auth_transfer { size_t chunks_total; /** start time of the transfer */ struct timeval start_time; + /** if the transfer is active and in the in progress count. */ + int active_transfer; + /** if the transfer is on the wait_transfer list (it waits to + * become active). */ + int on_wait_transfer_list; + /** If on the wait_transfer list, the previous list item. */ + struct auth_xfer* wait_transfer_prev; + /** If on the wait_transfer list, the next list item. */ + struct auth_xfer* wait_transfer_next; /** list of upstream masters for this zone, from config */ struct auth_master* masters;