]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- subrequests without parent store in cache and stop.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 5 Jun 2007 10:51:47 +0000 (10:51 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 5 Jun 2007 10:51:47 +0000 (10:51 +0000)
       - worker slumber list for ongoing promiscuous queries.
       - subrequest error handling.

git-svn-id: file:///svn/unbound/trunk@368 be551aaa-1e26-0410-a405-d3ace91eadb9

daemon/worker.c
daemon/worker.h
doc/Changelog
iterator/iterator.c
util/module.c
util/module.h

index 33003e1f808a8d4465d1795160bcdc53d90df98f..9c3ede1cd73168a447b4f398815fe5de8cb34545 100644 (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) {
-               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);
index 557c123193c974a5f7531f9a62d1a777c1e7f259..2f6b847b7b11657ba605bf0a0b9d3a73c8d1f4ab 100644 (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;
index 10476d320a447e86a36eb3bfd93ba2769f3cf4d7..61436008ab019302c35854ce254bf6b7a60dc783 100644 (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.
index 601c226381fbafbac97082a0e9ac85e00bb80310..7357d912a6d5de7cf6dd351f1f66fc2dd2b0561f 100644 (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 */
index 37d981512c07095d0d1260358fa2e2679a818ec9..7d53b735944056559f5ba3ce06a44c3ce25b9cc8 100644 (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;
+}
index 65fcc9a73deaf6473261b30a05c0ff3e5a6f44fc..12ac880df35e5d8eab63b98d0c5d7a93e3d5a720 100644 (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 */