From: Lennart Poettering Date: Sun, 13 Nov 2005 16:36:33 +0000 (+0000) Subject: * Make "NameAcquired" warning line disappear in avahi-client X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=da75d1898a45c106a994cd87f8da5b6bcb2b6450;p=catta * Make "NameAcquired" warning line disappear in avahi-client Scheduler tweaks: * Add some more comments * Remove scheduled queries from the query queue if the querier which issued them dies. This reduces traffic immensly when many short lived queries are made, e.g. during host name lookups. * Don't free a querier object immediately when it is no longer referenced. Instead keep it and try to recycle it in case someone else wants to do the same query later on. Free it at the latest moment possible: just before the next query is scheduled to be made. This reduces traffic immensly when many short lived queries are made. With these two changes we can minimize the traffic to zero or near zero for many simple lookups. * When responding records with the FLUSH_CACHE bit set, reply immediately only when all response record have this bit set. Prior to this change we replied imediately as soon as one record hat this bit set. This change should make us pass *all* Bonjour mDNS conformance tests without any exceptions. git-svn-id: file:///home/lennart/svn/public/avahi/trunk@954 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- diff --git a/avahi-client/client.c b/avahi-client/client.c index 1067456..2fd5123 100644 --- a/avahi-client/client.c +++ b/avahi-client/client.c @@ -115,7 +115,11 @@ static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message, avahi_client_set_errno(client, AVAHI_ERR_DISCONNECTED); goto fail; - } if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { + } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameAcquired")) { + + /* Ignore this message */ + + } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { char *name, *old, *new; if (!dbus_message_get_args( @@ -154,7 +158,7 @@ static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message, } else if (!avahi_client_is_connected(client)) { - /* Ignore messages, we get in unconnected state */ + /* Ignore messages we get in unconnected state */ } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged")) { int32_t state; diff --git a/avahi-core/cache.c b/avahi-core/cache.c index 5e87706..c26b6bb 100644 --- a/avahi-core/cache.c +++ b/avahi-core/cache.c @@ -211,11 +211,9 @@ static void elapse_func(AvahiTimeEvent *t, void *userdata) { assert(percent > 0); - /* Request a cache update, if we are subscribed to this entry */ - if (avahi_querier_exists(e->cache->interface, e->record->key)) { -/* avahi_log_debug("Requesting cache entry update at %i%% for %s.", percent, txt); */ - avahi_interface_post_query(e->cache->interface, e->record->key, 1); - } + /* Request a cache update if we are subscribed to this entry */ + if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key)) + avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL); /* Check again later */ next_expiry(e->cache, e, percent); diff --git a/avahi-core/iface.c b/avahi-core/iface.c index 0e3ef68..b466f48 100644 --- a/avahi-core/iface.c +++ b/avahi-core/iface.c @@ -583,16 +583,21 @@ void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { avahi_interface_send_packet_unicast(i, p, NULL, 0); } -int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately) { +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) { assert(i); assert(key); if (avahi_interface_is_relevant(i)) - return avahi_query_scheduler_post(i->query_scheduler, key, immediately); + return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id); return 0; } +int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) { + + return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id); +} + int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { assert(i); assert(record); diff --git a/avahi-core/iface.h b/avahi-core/iface.h index 30d3d22..4106ea7 100644 --- a/avahi-core/iface.h +++ b/avahi-core/iface.h @@ -166,7 +166,8 @@ int avahi_interface_is_relevant(AvahiInterface *i); void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port); -int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately); +int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id); +int avahi_interface_withraw_query(AvahiInterface *i, unsigned id); int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately); int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately); diff --git a/avahi-core/multicast-lookup.c b/avahi-core/multicast-lookup.c index fa8c859..8101df5 100644 --- a/avahi-core/multicast-lookup.c +++ b/avahi-core/multicast-lookup.c @@ -114,9 +114,10 @@ AvahiMulticastLookup *avahi_multicast_lookup_new( avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv); l->queriers_added = 1; - /* add a second */ + /* Add a second */ avahi_timeval_add(&tv, 1000000); + /* Issue the ALL_FOR_NOW event one second after the querier was initially created */ l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l); return l; diff --git a/avahi-core/querier.c b/avahi-core/querier.c index 41acc24..b1069d1 100644 --- a/avahi-core/querier.c +++ b/avahi-core/querier.c @@ -42,6 +42,9 @@ struct AvahiQuerier { AvahiTimeEvent *time_event; struct timeval creation_time; + + unsigned post_id; + int post_id_valid; AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); }; @@ -64,7 +67,24 @@ static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *us assert(q); - avahi_interface_post_query(q->interface, q->key, 0); + if (q->n_used <= 0) { + + /* We are not referenced by anyone anymore, so let's free + * ourselves. We should not send out any further queries from + * this querier object anymore. */ + + avahi_querier_free(q); + return; + } + + if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) { + + /* The queue accepted our query. We store the query id here, + * that allows us to drop the query at a later point if the + * query is very short-lived. */ + + q->post_id_valid = 1; + } q->sec_delay *= 2; @@ -83,10 +103,13 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti assert(key); if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { + /* Someone is already browsing for records of this RR key */ q->n_used++; - /* Return the creation time */ + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was + * initially created. */ if (ret_ctime) *ret_ctime = q->creation_time; return; @@ -100,10 +123,12 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti q->interface = i; q->n_used = 1; q->sec_delay = 1; + q->post_id_valid = 0; gettimeofday(&q->creation_time, NULL); /* Do the initial query */ - avahi_interface_post_query(i, key, 0); + if (avahi_interface_post_query(i, key, 0, &q->post_id)) + q->post_id_valid = 1; /* Schedule next queries */ q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); @@ -111,7 +136,9 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); avahi_hashmap_insert(i->queriers_by_key, q->key, q); - /* Return the creation time */ + /* Return the creation time. This is used for generating the + * ALL_FOR_NOW event one second after the querier was initially + * created. */ if (ret_ctime) *ret_ctime = q->creation_time; } @@ -119,16 +146,29 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { AvahiQuerier *q; - if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) { - /* The was no querier for this RR key */ - avahi_log_warn(__FILE__": querier_remove() called but no querier to remove"); + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) { + /* There was no querier for this RR key, or it wasn't referenced by anyone */ + avahi_log_warn(__FILE__": querier_remove() called but no querier to remove."); return; } - assert(q->n_used >= 1); + if ((--q->n_used) <= 0) { - if ((--q->n_used) <= 0) - avahi_querier_free(q); + /* Nobody references us anymore. */ + + if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) { + + /* We succeeded in withdrawing our query from the queue, + * so let's drop dead. */ + + avahi_querier_free(q); + } + + /* If we failed to withdraw our query from the queue, we stay + * alive, in case someone else might recycle our querier at a + * later point. We are freed at our next expiry, in case + * nobody recycled us. */ + } } static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { @@ -183,14 +223,42 @@ void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol p avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); } -int avahi_querier_exists(AvahiInterface *i, AvahiKey *key) { +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + assert(i); assert(key); - if (avahi_hashmap_lookup(i->queriers_by_key, key)) - return 1; + /* Called by the cache maintainer */ - return 0; + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) + /* This key is currently not subscribed at all, so no cache + * refresh is needed */ + return 0; + + if (q->n_used <= 0) { + + /* If this is an entry nobody references right now, don't + * consider it "existing". */ + + /* Remove this querier since it is referenced by nobody + * and the cached data will soon be out of date */ + avahi_querier_free(q); + + /* Tell the cache that no refresh is needed */ + return 0; + + } else { + struct timeval tv; + + /* We can defer our query a little, since the cache will now + * issue a refresh query anyway. */ + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); + + /* Tell the cache that a refresh should be issued */ + return 1; + } } void avahi_querier_free_all(AvahiInterface *i) { diff --git a/avahi-core/querier.h b/avahi-core/querier.h index 3ec4564..3f4eead 100644 --- a/avahi-core/querier.h +++ b/avahi-core/querier.h @@ -45,6 +45,6 @@ void avahi_querier_free(AvahiQuerier *q); void avahi_querier_free_all(AvahiInterface *i); /** Return 1 if there is a querier for the specified key on the specified interface */ -int avahi_querier_exists(AvahiInterface *i, AvahiKey *key); +int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key); #endif diff --git a/avahi-core/query-sched.c b/avahi-core/query-sched.c index 0096838..f875411 100644 --- a/avahi-core/query-sched.c +++ b/avahi-core/query-sched.c @@ -36,6 +36,9 @@ typedef struct AvahiQueryJob AvahiQueryJob; typedef struct AvahiKnownAnswer AvahiKnownAnswer; struct AvahiQueryJob { + unsigned id; + int n_posted; + AvahiQueryScheduler *scheduler; AvahiTimeEvent *time_event; @@ -44,6 +47,14 @@ struct AvahiQueryJob { AvahiKey *key; + /* Jobs are stored in a simple linked list. It might turn out in + * the future that this list grows too long and we must switch to + * some other kind of data structure. This needs further + * investigation. I expect the list to be very short (< 20 + * entries) most of the time, but this might be a wrong + * assumption, especially on setups where traffic reflection is + * involved. */ + AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs); }; @@ -58,6 +69,8 @@ struct AvahiQueryScheduler { AvahiInterface *interface; AvahiTimeEventQueue *time_event_queue; + unsigned next_id; + AVAHI_LLIST_HEAD(AvahiQueryJob, jobs); AVAHI_LLIST_HEAD(AvahiQueryJob, history); AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers); @@ -77,6 +90,8 @@ static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) { qj->scheduler = s; qj->key = avahi_key_ref(key); qj->time_event = NULL; + qj->n_posted = 1; + qj->id = s->next_id++; if ((qj->done = done)) AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj); @@ -144,6 +159,7 @@ AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) { s->interface = i; s->time_event_queue = i->monitor->server->time_event_queue; + s->next_id = 0; AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs); AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history); @@ -335,25 +351,21 @@ static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) { return NULL; } -int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately) { +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) { struct timeval tv; AvahiQueryJob *qj; assert(s); assert(key); - if ((qj = find_history_job(s, key))) { -/* avahi_log_debug("Query suppressed by local duplicate suppression (history)"); */ + if ((qj = find_history_job(s, key))) return 0; - } avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0); if ((qj = find_scheduled_job(s, key))) { /* Duplicate questions suppression */ -/* avahi_log_debug("Query suppressed by local duplicate suppression (scheduled)"); */ - if (avahi_timeval_compare(&tv, &qj->delivery) < 0) { /* If the new entry should be scheduled earlier, * update the old entry */ @@ -361,18 +373,21 @@ int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immedi avahi_time_event_update(qj->time_event, &qj->delivery); } - return 1; + qj->n_posted++; + } else { -/* avahi_log_debug("Accepted new query job."); */ if (!(qj = job_new(s, key, 0))) return 0; /* OOM */ qj->delivery = tv; qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj); - - return 1; } + + if (ret_id) + *ret_id = qj->id; + + return 1; } void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) { @@ -382,19 +397,54 @@ void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) { assert(key); /* This function is called whenever an incoming query was - * receieved. We drop scheduled queries that match. The keyword is + * received. We drop scheduled queries that match. The keyword is * "DUPLICATE QUESTION SUPPRESION". */ if ((qj = find_scheduled_job(s, key))) { -/* avahi_log_debug("Query suppressed by distributed duplicate suppression"); */ job_mark_done(s, qj); return; } - - if (!(qj = job_new(s, key, 1))) - return; /* OOM */ + + /* Look if there's a history job for this key. If there is, just + * update the elapse time */ + if (!(qj = find_history_job(s, key))) + if (!(qj = job_new(s, key, 1))) + return; /* OOM */ gettimeofday(&qj->delivery, NULL); job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0); } +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) { + AvahiQueryJob *qj; + + assert(s); + + /* Very short lived queries can withdraw an already scheduled item + * from the queue using this function, simply by passing the id + * returned by avahi_query_scheduler_post(). */ + + for (qj = s->jobs; qj; qj = qj->jobs_next) { + assert(!qj->done); + + if (qj->id == id) { + /* Entry found */ + + assert(qj->n_posted >= 1); + + if (--qj->n_posted <= 0) { + + /* We withdraw this job only if the calling object was + * the only remaining poster. (Usually this is the + * case since there should exist only one querier per + * key, but there are exceptions, notably reflected + * traffic.) */ + + job_free(s, qj); + return 1; + } + } + } + + return 0; +} diff --git a/avahi-core/query-sched.h b/avahi-core/query-sched.h index 7b281b6..5238558 100644 --- a/avahi-core/query-sched.h +++ b/avahi-core/query-sched.h @@ -31,7 +31,8 @@ AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i); void avahi_query_scheduler_free(AvahiQueryScheduler *s); void avahi_query_scheduler_clear(AvahiQueryScheduler *s); -int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately); +int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id); +int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id); void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key); #endif diff --git a/avahi-core/rrlist.c b/avahi-core/rrlist.c index 9d76684..915ecbb 100644 --- a/avahi-core/rrlist.c +++ b/avahi-core/rrlist.c @@ -43,10 +43,11 @@ struct AvahiRecordListItem { AVAHI_LLIST_FIELDS(AvahiRecordListItem, items); }; - struct AvahiRecordList { AVAHI_LLIST_HEAD(AvahiRecordListItem, read); AVAHI_LLIST_HEAD(AvahiRecordListItem, unread); + + int all_flush_cache; }; AvahiRecordList *avahi_record_list_new(void) { @@ -59,6 +60,8 @@ AvahiRecordList *avahi_record_list_new(void) { AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read); AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread); + + l->all_flush_cache = 1; return l; } @@ -89,9 +92,11 @@ void avahi_record_list_flush(AvahiRecordList *l) { item_free(l, l->read); while (l->unread) item_free(l, l->unread); + + l->all_flush_cache = 1; } -AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *unicast_response, int *auxiliary) { +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) { AvahiRecord *r; AvahiRecordListItem *i; @@ -101,12 +106,12 @@ AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *u assert(!i->read); r = avahi_record_ref(i->record); - if (unicast_response) - *unicast_response = i->unicast_response; - if (flush_cache) - *flush_cache = i->flush_cache; - if (auxiliary) - *auxiliary = i->auxiliary; + if (ret_unicast_response) + *ret_unicast_response = i->unicast_response; + if (ret_flush_cache) + *ret_flush_cache = i->flush_cache; + if (ret_auxiliary) + *ret_auxiliary = i->auxiliary; AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i); AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i); @@ -153,6 +158,8 @@ void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, i->record = avahi_record_ref(r); i->read = 0; + l->all_flush_cache = l->all_flush_cache && flush_cache; + AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i); } @@ -173,3 +180,11 @@ int avahi_record_list_is_empty(AvahiRecordList *l) { return !l->unread && !l->read; } + +int avahi_record_list_all_flush_cache(AvahiRecordList *l) { + assert(l); + + /* Return TRUE if all entries in this list have flush_cache set */ + + return l->all_flush_cache; +} diff --git a/avahi-core/rrlist.h b/avahi-core/rrlist.h index 200dae8..9c07ecd 100644 --- a/avahi-core/rrlist.h +++ b/avahi-core/rrlist.h @@ -31,10 +31,12 @@ AvahiRecordList *avahi_record_list_new(void); void avahi_record_list_free(AvahiRecordList *l); void avahi_record_list_flush(AvahiRecordList *l); -AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *unicast_response, int *auxiliary); +AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary); void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary); void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r); +int avahi_record_list_all_flush_cache(AvahiRecordList *l); + int avahi_record_list_is_empty(AvahiRecordList *l); #endif diff --git a/avahi-core/server.c b/avahi-core/server.c index eb04ec1..5790bee 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -404,15 +404,25 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC); while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) { - - if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) { - append_aux_records_to_list(s, i, r, unicast_response); - + int im = immediately; + + /* Only send the response immediately if it contains a + * unique entry AND it is not in reply to a truncated + * packet AND it is not an auxiliary record AND all other + * responses for this record are unique too. */ + + if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list)) + im = 1; + + if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) { + /* Due to some reasons the record has not been scheduled. * The client requested an unicast response in that * case. Therefore we prepare such a response */ + append_aux_records_to_list(s, i, r, unicast_response); + for (;;) { if (!reply) { @@ -517,7 +527,7 @@ static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) { for (j = s->monitor->interfaces; j; j = j->interface_next) if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) { /* Post the query to other networks */ - avahi_interface_post_query(j, k, 1); + avahi_interface_post_query(j, k, 1, NULL); /* Reply from caches of other network. This is needed to * "work around" known answer suppression. */