- 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
This commit is contained in:
Wouter Wijngaards 2007-06-05 10:51:47 +00:00
parent 7a882802b6
commit e98df72b44
6 changed files with 205 additions and 49 deletions

View file

@ -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; i<worker->daemon->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) {
if(w) {
replyerror(LDNS_RCODE_SERVFAIL, w);
req_release(w);
}
qstate_free(worker, qstate);
return;
}
if(s == module_finished) {
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);
qstate_free(worker, qstate);
req_release(w);
}
qstate_free(worker, qstate);
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);

View file

@ -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;

View file

@ -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.

View file

@ -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 */

View file

@ -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;
}

View file

@ -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 */