From: Wouter Wijngaards Date: Tue, 5 Jun 2007 10:51:47 +0000 (+0000) Subject: - subrequests without parent store in cache and stop. X-Git-Tag: release-0.4~98 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e98df72b444b1da21d705c586f8c6ca5d4c4dbe1;p=thirdparty%2Funbound.git - subrequests without parent store in cache and stop. - worker slumber list for ongoing promiscuous queries. - subrequest error handling. git-svn-id: file:///svn/unbound/trunk@368 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 33003e1f8..9c3ede1cd 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -79,16 +79,11 @@ worker_send_cmd(struct worker* worker, ldns_buffer* buffer, /** delete subrequest */ static void -qstate_free(struct worker* worker, struct module_qstate* qstate) +qstate_cleanup(struct worker* worker, struct module_qstate* qstate) { int i; if(!qstate) return; - /* remove subqueries */ - while(qstate->subquery_first) { - qstate_free(worker, qstate->subquery_first); - } - log_assert(qstate->subquery_first == NULL); /* call de-init while all is OK */ for(i=0; idaemon->num_modules; i++) (*worker->daemon->modfunc[i]->clear)(qstate, i); @@ -96,12 +91,55 @@ qstate_free(struct worker* worker, struct module_qstate* qstate) region_free_all(qstate->region); query_info_clear(&qstate->qinfo); if(qstate->parent) { - module_subreq_remove(qstate); + /* subquery of parent */ + module_subreq_remove(&qstate->parent->subquery_first, qstate); + region_destroy(qstate->region); + free(qstate); + } else if (!qstate->work_info) { + /* slumbering query */ + module_subreq_remove(&worker->slumber_list, qstate); region_destroy(qstate->region); free(qstate); + verbose(VERB_ALGO, "cleanup: slumber list has %d entries", + module_subreq_num(worker->slumber_list)); + } +} + +/** delete subrequest recursively */ +static void +qstate_free_recurs_list(struct worker* worker, struct module_qstate* list) +{ + struct module_qstate* n; + /* remove subqueries */ + while(list) { + n = list->subquery_next; + qstate_free_recurs_list(worker, list->subquery_first); + qstate_cleanup(worker, list); + list = n; } } +/** delete subrequest */ +static void +qstate_free(struct worker* worker, struct module_qstate* qstate) +{ + if(!qstate) + return; + while(qstate->subquery_first) { + /* put subqueries on slumber list */ + struct module_qstate* s = qstate->subquery_first; + module_subreq_remove(&qstate->subquery_first, s); + s->parent = NULL; + s->work_info = NULL; + s->subquery_next = worker->slumber_list; + s->subquery_prev = NULL; + worker->slumber_list = s; + } + verbose(VERB_ALGO, "worker: slumber list has %d entries", + module_subreq_num(worker->slumber_list)); + qstate_cleanup(worker, qstate); +} + /** release workrequest back to the freelist, * note that the w->qinfo still needs to be cleared after this. */ @@ -237,7 +275,7 @@ worker_process_query(struct worker* worker, struct work_query* w, qstate_free(worker, qstate); qstate = up; entry = NULL; - event = module_event_error; + event = module_event_subq_error; continue; } if(s == module_finished && qstate->parent) { @@ -261,16 +299,21 @@ worker_process_query(struct worker* worker, struct work_query* w, } /* request done */ if(s == module_error) { - replyerror(LDNS_RCODE_SERVFAIL, w); + if(w) { + replyerror(LDNS_RCODE_SERVFAIL, w); + req_release(w); + } qstate_free(worker, qstate); return; } if(s == module_finished) { - memcpy(ldns_buffer_begin(w->query_reply.c->buffer), - &w->query_id, sizeof(w->query_id)); - comm_point_send_reply(&w->query_reply); + if(w) { + memcpy(ldns_buffer_begin(w->query_reply.c->buffer), + &w->query_id, sizeof(w->query_id)); + comm_point_send_reply(&w->query_reply); + req_release(w); + } qstate_free(worker, qstate); - req_release(w); return; } /* suspend, waits for wakeup callback */ @@ -312,7 +355,7 @@ worker_handle_service_reply(struct comm_point* c, void* arg, int error, { struct outbound_entry* e = (struct outbound_entry*)arg; struct work_query* w = e->qstate->work_info; - struct worker* worker = w->state.env->worker; + struct worker* worker = e->qstate->env->worker; e->qstate->reply = reply_info; if(error != 0) { @@ -656,7 +699,8 @@ reqs_delete(struct worker* worker) n = q->all_next; log_assert(q->state.env->worker == worker); /* comm_reply closed in outside_network_delete */ - qstate_free(worker, &q->state); + qstate_free_recurs_list(worker, q->state.subquery_first); + qstate_cleanup(worker, &q->state); region_destroy(q->state.region); free(q); q = n; @@ -751,6 +795,7 @@ worker_init(struct worker* worker, struct config_file *cfg, worker_delete(worker); return 0; } + worker->slumber_list = NULL; server_stats_init(&worker->stats); alloc_init(&worker->alloc, &worker->daemon->superalloc, @@ -775,6 +820,7 @@ worker_delete(struct worker* worker) return; server_stats_log(&worker->stats, worker->thread_num); reqs_delete(worker); + qstate_free_recurs_list(worker, worker->slumber_list); listen_delete(worker->front); outside_network_delete(worker->back); comm_signal_delete(worker->comsig); diff --git a/daemon/worker.h b/daemon/worker.h index 557c12319..2f6b847b7 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -116,6 +116,8 @@ struct worker { struct work_query* free_queries; /** list of all working queries */ struct work_query* all_queries; + /** list of slumbering states, with promiscuous queries */ + struct module_qstate* slumber_list; /** random() table for this worker. */ struct ub_randstate* rndstate; diff --git a/doc/Changelog b/doc/Changelog index 10476d320..61436008a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,8 @@ 5 June 2007: Wouter - iterator state finished. + - subrequests without parent store in cache and stop. + - worker slumber list for ongoing promiscuous queries. + - subrequest error handling. 4 June 2007: Wouter - random selection of equally preferred nameserver targets. diff --git a/iterator/iterator.c b/iterator/iterator.c index 601c22638..7357d912a 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -253,11 +253,30 @@ final_state(struct module_qstate* qstate, struct iter_qstate* iq) static int error_response(struct module_qstate* qstate, int id, int rcode) { + struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id]; log_info("err response %s", ldns_lookup_by_id(ldns_rcodes, rcode)? ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??"); + if(iq && iq->orig_qname) { + /* encode original query */ + qstate->qinfo.qname = iq->orig_qname; + qstate->qinfo.qname_len = iq->orig_qnamelen; + qstate->query_flags = iq->orig_qflags; + } qinfo_query_encode(qstate->buf, &qstate->qinfo); LDNS_RCODE_SET(ldns_buffer_begin(qstate->buf), rcode); + LDNS_RA_SET(ldns_buffer_begin(qstate->buf)); LDNS_QR_SET(ldns_buffer_begin(qstate->buf)); + if((qstate->query_flags & BIT_RD)) + LDNS_RD_SET(ldns_buffer_begin(qstate->buf)); + if((qstate->query_flags & BIT_CD)) + LDNS_CD_SET(ldns_buffer_begin(qstate->buf)); + + if(qstate->parent) { + /* return subquery error module event to parent */ + qstate->ext_state[id] = module_error; + return 0; + } + /* return to client */ qstate->ext_state[id] = module_finished; return 0; } @@ -468,6 +487,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, subq->work_info = qstate->work_info; subq->parent = qstate; subq->subquery_next = qstate->subquery_first; + subq->subquery_prev = NULL; qstate->subquery_first = subq; subiq = (struct iter_qstate*)subq->minfo[id]; @@ -571,7 +591,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, QUERYTARGETS_STATE, PRIME_RESP_STATE); if(!subq) { log_err("out of memory priming stub"); - qstate->ext_state[id] = module_error; + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return 1; /* return 1 to make module stop, with error */ } subiq = (struct iter_qstate*)subq->minfo[id]; @@ -844,7 +864,10 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq, subiq = (struct iter_qstate*)subq->minfo[id]; subiq->dp = delegpt_copy(iq->dp, subq->region); if(!subiq->dp) { - subq->ext_state[id] = module_error; + module_subreq_remove(&qstate->subquery_first, subq); + region_destroy(subq->region); + free(subq->qinfo.qname); + free(subq); return 0; } return 1; @@ -1082,7 +1105,6 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, { enum response_type type; iq->num_current_queries--; - qstate->ext_state[id] = module_error; /* debug, must be overridden */ if(iq->response == NULL) { verbose(VERB_ALGO, "query response was timeout"); return next_state(qstate, iq, QUERYTARGETS_STATE); @@ -1214,7 +1236,14 @@ processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq, struct delegpt* dp = NULL; enum response_type type = response_type_from_server(iq->response, &qstate->qinfo, iq->dp); - log_assert(qstate->parent); /* this is a subrequest of another */ + + /* This event is finished. */ + qstate->ext_state[id] = module_finished; + + if(!qstate->parent) { + /* no more parent - it is not interested anymore */ + return 0; + } if(type == RESPONSE_TYPE_ANSWER) { /* Convert our response to a delegation point */ dp = delegpt_from_message(iq->response, forq->region); @@ -1224,23 +1253,17 @@ processPrimeResponse(struct module_qstate* qstate, struct iter_qstate* iq, * the ANSWER type was (presumably) a negative answer. */ verbose(VERB_ALGO, "prime response was not a positive " "ANSWER; failing"); - /* note that this will call the forevent with event error. */ - qstate->ext_state[id] = module_error; - return 0; + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } log_nametypeclass("priming successful for", qstate->qinfo.qname, qstate->qinfo.qtype, qstate->qinfo.qclass); - /* This event is finished. */ - qstate->ext_state[id] = module_finished; foriq = (struct iter_qstate*)forq->minfo[id]; foriq->dp = dp; foriq->response = dns_copy_msg(iq->response, forq->region); if(!foriq->response) { log_err("copy prime response: out of memory"); - /* note that this will call the forevent with event error. */ - qstate->ext_state[id] = module_error; - return 0; + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } /* root priming responses go to init stage 2, priming stub @@ -1274,9 +1297,14 @@ processTargetResponse(struct module_qstate* qstate, struct iter_qstate* iq, struct delegpt_ns* dpns; struct module_qstate* forq = qstate->parent; struct iter_qstate* foriq; - log_assert(qstate->parent); /* fetch targets for a parent */ - foriq = (struct iter_qstate*)forq->minfo[id]; + qstate->ext_state[id] = module_finished; + if(!qstate->parent) { + /* no parent, it is not interested anymore */ + return 0; + } + + foriq = (struct iter_qstate*)forq->minfo[id]; /* check to see if parent event is still interested. */ if(iq->orig_qname) @@ -1460,7 +1488,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, if(event != module_event_reply || !qstate->reply) { log_err("Bad event combined with response"); outbound_list_remove(&iq->outlist, outbound); - qstate->ext_state[id] = module_error; + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return; } @@ -1496,6 +1524,48 @@ handle_it: outbound_list_remove(&iq->outlist, outbound); iter_handle(qstate, iq, ie, id); } +/** + * Handles subquery errors. Checks if query is still relevant, and adjusts + * the state. + * @param qstate: query state. + * @param ie: iterator shared global environment. + * @param iq: iterator query state. + * @param id: module id. + */ +static void +process_subq_error(struct module_qstate* qstate, struct iter_qstate* iq, + struct iter_env* ie, int id) +{ + struct query_info errinf; + struct delegpt_ns* dpns = NULL; + if(!query_info_parse(&errinf, qstate->buf)) { + log_err("Could not parse error from sub module"); + return; + } + if(errinf.qtype == LDNS_RR_TYPE_NS) { + /* a priming query has failed. */ + iter_handle(qstate, iq, ie, id); + return; + } + if(errinf.qtype != LDNS_RR_TYPE_A && + errinf.qtype != LDNS_RR_TYPE_AAAA) { + log_err("Bad error from sub module"); + return; + } + /* see if we are still interested in this subquery result */ + + if(!iq->dp) + dpns = delegpt_find_ns(iq->dp, errinf.qname, + errinf.qname_len); + if(!dpns) { + /* not interested */ + return; + } + dpns->resolved = 1; /* mark as failed */ + iq->num_target_queries--; /* and the query is finished */ + iq->state = QUERYTARGETS_STATE; /* evaluate targets again */ + iter_handle(qstate, iq, ie, id); +} /** iterator operate on a query */ static void @@ -1514,7 +1584,7 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id, if(event == module_event_new && iq == NULL) { log_info("iter state machine"); if(!iter_new(qstate, id)) { - qstate->ext_state[id] = module_error; + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return; } iq = (struct iter_qstate*)qstate->minfo[id]; @@ -1529,14 +1599,24 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id, process_response(qstate, iq, ie, id, outbound, event); return; } + if(event == module_event_subq_done) { + /* subquery has set our state correctly */ + iter_handle(qstate, iq, ie, id); + return; + } + if(event == module_event_subq_error) { + /* need to delist subquery and continue processing */ + process_subq_error(qstate, iq, ie, id); + return; + } if(event == module_event_error) { verbose(VERB_ALGO, "got called with event error, giving up"); - qstate->ext_state[id] = module_error; + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return; } log_err("bad event for iterator"); - qstate->ext_state[id] = module_error; + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); } /** iterator cleanup query state */ diff --git a/util/module.c b/util/module.c index 37d981512..7d53b7359 100644 --- a/util/module.c +++ b/util/module.c @@ -64,28 +64,26 @@ strmodulevent(enum module_ev e) case module_event_timeout: return "module_event_timeout"; case module_event_mod_done: return "module_event_mod_done"; case module_event_subq_done: return "module_event_subq_done"; + case module_event_subq_error: return "module_event_subq_error"; case module_event_error: return "module_event_error"; } return "bad_event_value"; } void -module_subreq_remove(struct module_qstate* sub) +module_subreq_remove(struct module_qstate** head, struct module_qstate* sub) { - struct module_qstate* p, *prev = NULL; - if(!sub || !sub->parent) + if(!sub || !head) return; - p = sub->parent->subquery_first; - while(p) { - if(p == sub) { - /* snip it off */ - if(prev) prev->subquery_next = p->subquery_next; - else sub->parent->subquery_first = p->subquery_next; - return; - } - prev = p; - p = p->subquery_next; - } + /* snip off double linked list */ + if(sub->subquery_prev) + sub->subquery_prev->subquery_next = sub->subquery_next; + else *head = sub->subquery_next; + if(sub->subquery_next) + sub->subquery_next->subquery_prev = sub->subquery_prev; + /* cleanup values for robustness */ + sub->subquery_next = NULL; + sub->subquery_prev = NULL; } int @@ -98,3 +96,14 @@ module_subreq_depth(struct module_qstate* sub) } return d; } + +int +module_subreq_num(struct module_qstate* q) +{ + int n = 0; + while(q) { + n++; + q = q->subquery_next; + } + return n; +} diff --git a/util/module.h b/util/module.h index 65fcc9a73..12ac880df 100644 --- a/util/module.h +++ b/util/module.h @@ -163,6 +163,8 @@ enum module_ev { module_event_mod_done, /** subquery finished */ module_event_subq_done, + /** subquery finished with error */ + module_event_subq_error, /** error */ module_event_error }; @@ -198,15 +200,19 @@ struct module_qstate { void* minfo[MAX_MODULE]; /** environment for this query */ struct module_env* env; - /** worker related state for this query */ + /** worker related state for this query. NULL for queries that do + * not need to have answers sent to a client. */ struct work_query* work_info; /** parent query, only nonNULL for subqueries */ struct module_qstate* parent; /** pointer to first subquery below this one; makes list with next */ struct module_qstate* subquery_first; + /** pointer to next sibling subquery (not above or below this one) */ struct module_qstate* subquery_next; + /** pointer to prev sibling subquery (not above or below this one) */ + struct module_qstate* subquery_prev; }; /** @@ -271,10 +277,13 @@ const char* strmodulevent(enum module_ev e); /** * Remove subqrequest from list. + * @param head: List head. pointer to start of subquery_next/prev sibling list. + * mostly reference to the parent subquery_first. * @param sub: subrequest. Parent pointer used to access list. * It is snipped off. */ -void module_subreq_remove(struct module_qstate* sub); +void module_subreq_remove(struct module_qstate** head, + struct module_qstate* sub); /** * Calculate depth of subrequest @@ -283,4 +292,11 @@ void module_subreq_remove(struct module_qstate* sub); */ int module_subreq_depth(struct module_qstate* sub); +/** + * Calculate number of queries in the query list. + * @param q: the start of the list, pass subquery_first. + * @return: number, 0 if q was NULL. + */ +int module_subreq_num(struct module_qstate* q); + #endif /* UTIL_MODULE_H */