From 41d9a503716ed6f3291f4cae097dca08d459429c Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 8 Apr 2014 15:43:41 -0400 Subject: [PATCH] Add support for deleting per-network DNS resolver caches. This is required when the possible range of NetIds is large to prevent netd consuming excessive amounts of memory. This required replacing the per-cache locks in favor of a single global lock to prevent accesses to deleted caches. Change-Id: I99d058bafea5de743e56075dbed74031da4df63f --- libc/dns/include/resolv_cache.h | 11 +-- libc/dns/include/resolv_netid.h | 3 + libc/dns/resolv/res_cache.c | 128 ++++++++++++++++++++------------ libc/dns/resolv/res_send.c | 31 ++++---- 4 files changed, 100 insertions(+), 73 deletions(-) diff --git a/libc/dns/include/resolv_cache.h b/libc/dns/include/resolv_cache.h index 16f3e43a1..e049d951f 100644 --- a/libc/dns/include/resolv_cache.h +++ b/libc/dns/include/resolv_cache.h @@ -32,11 +32,6 @@ #include struct __res_state; -struct resolv_cache; /* forward */ - -/* Gets the cache for a network. Returned cache might be NULL. */ -__LIBC_HIDDEN__ -extern struct resolv_cache* __get_res_cache(unsigned netid); /* sets the name server addresses to the provided res_state structure. The * name servers are retrieved from the cache which is associated @@ -53,7 +48,7 @@ typedef enum { __LIBC_HIDDEN__ extern ResolvCacheStatus -_resolv_cache_lookup( struct resolv_cache* cache, +_resolv_cache_lookup( unsigned netid, const void* query, int querylen, void* answer, @@ -65,7 +60,7 @@ _resolv_cache_lookup( struct resolv_cache* cache, */ __LIBC_HIDDEN__ extern void -_resolv_cache_add( struct resolv_cache* cache, +_resolv_cache_add( unsigned netid, const void* query, int querylen, const void* answer, @@ -74,7 +69,7 @@ _resolv_cache_add( struct resolv_cache* cache, /* Notify the cache a request failed */ __LIBC_HIDDEN__ extern void -_resolv_cache_query_failed( struct resolv_cache* cache, +_resolv_cache_query_failed( unsigned netid, const void* query, int querylen); diff --git a/libc/dns/include/resolv_netid.h b/libc/dns/include/resolv_netid.h index 991a0bf13..006a6c237 100644 --- a/libc/dns/include/resolv_netid.h +++ b/libc/dns/include/resolv_netid.h @@ -65,6 +65,9 @@ extern void _resolv_set_nameservers_for_net(unsigned netid, /* flush the cache associated with a certain network */ extern void _resolv_flush_cache_for_net(unsigned netid); +/* delete the cache associated with a certain network */ +extern void _resolv_delete_cache_for_net(unsigned netid); + __END_DECLS #endif /* _RESOLV_NETID_H */ diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c index 9df97cdfc..70e8340c3 100644 --- a/libc/dns/resolv/res_cache.c +++ b/libc/dns/resolv/res_cache.c @@ -1218,7 +1218,6 @@ typedef struct resolv_cache { int max_entries; int num_entries; Entry mru_list; - pthread_mutex_t lock; int last_id; Entry* entries; PendingReqInfo pending_requests; @@ -1236,6 +1235,15 @@ struct resolv_cache_info { #define HTABLE_VALID(x) ((x) != NULL && (x) != HTABLE_DELETED) +static pthread_once_t _res_cache_once = PTHREAD_ONCE_INIT; +static void _res_cache_init(void); + +// lock protecting everything in the _resolve_cache_info structs (next ptr, etc) +static pthread_mutex_t _res_cache_list_lock; + +/* gets cache associated with a network, or NULL if none exists */ +static struct resolv_cache* _find_named_cache_locked(unsigned netid); + static void _cache_flush_pending_requests_locked( struct resolv_cache* cache ) { @@ -1256,18 +1264,18 @@ _cache_flush_pending_requests_locked( struct resolv_cache* cache ) } } -/* return 0 if no pending request is found matching the key - * if a matching request is found the calling thread will wait - * and return 1 when released */ +/* Return 0 if no pending request is found matching the key. + * If a matching request is found the calling thread will wait until + * the matching request completes, then update *cache and return 1. */ static int -_cache_check_pending_request_locked( struct resolv_cache* cache, Entry* key ) +_cache_check_pending_request_locked( struct resolv_cache** cache, Entry* key, unsigned netid ) { struct pending_req_info *ri, *prev; int exist = 0; - if (cache && key) { - ri = cache->pending_requests.next; - prev = &cache->pending_requests; + if (*cache && key) { + ri = (*cache)->pending_requests.next; + prev = &(*cache)->pending_requests; while (ri) { if (ri->hash == key->hash) { exist = 1; @@ -1288,7 +1296,9 @@ _cache_check_pending_request_locked( struct resolv_cache* cache, Entry* key ) struct timespec ts = {0,0}; XLOG("Waiting for previous request"); ts.tv_sec = _time_now() + PENDING_REQUEST_TIMEOUT; - pthread_cond_timedwait(&ri->cond, &cache->lock, &ts); + pthread_cond_timedwait(&ri->cond, &_res_cache_list_lock, &ts); + /* Must update *cache as it could have been deleted. */ + *cache = _find_named_cache_locked(netid); } } @@ -1325,17 +1335,25 @@ _cache_notify_waiting_tid_locked( struct resolv_cache* cache, Entry* key ) /* notify the cache that the query failed */ void -_resolv_cache_query_failed( struct resolv_cache* cache, +_resolv_cache_query_failed( unsigned netid, const void* query, int querylen) { Entry key[1]; + Cache* cache; - if (cache && entry_init_key(key, query, querylen)) { - pthread_mutex_lock(&cache->lock); + if (!entry_init_key(key, query, querylen)) + return; + + pthread_mutex_lock(&_res_cache_list_lock); + + cache = _find_named_cache_locked(netid); + + if (cache) { _cache_notify_waiting_tid_locked(cache, key); - pthread_mutex_unlock(&cache->lock); } + + pthread_mutex_unlock(&_res_cache_list_lock); } static void @@ -1391,7 +1409,6 @@ _resolv_cache_create( void ) cache->max_entries = _res_cache_get_max_entries(); cache->entries = calloc(sizeof(*cache->entries), cache->max_entries); if (cache->entries) { - pthread_mutex_init( &cache->lock, NULL ); cache->mru_list.mru_prev = cache->mru_list.mru_next = &cache->mru_list; XLOG("%s: cache created\n", __FUNCTION__); } else { @@ -1586,7 +1603,7 @@ static void _cache_remove_expired(Cache* cache) { } ResolvCacheStatus -_resolv_cache_lookup( struct resolv_cache* cache, +_resolv_cache_lookup( unsigned netid, const void* query, int querylen, void* answer, @@ -1597,6 +1614,7 @@ _resolv_cache_lookup( struct resolv_cache* cache, Entry** lookup; Entry* e; time_t now; + Cache* cache; ResolvCacheStatus result = RESOLV_CACHE_NOTFOUND; @@ -1609,7 +1627,14 @@ _resolv_cache_lookup( struct resolv_cache* cache, return RESOLV_CACHE_UNSUPPORTED; } /* lookup cache */ - pthread_mutex_lock( &cache->lock ); + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + cache = _find_named_cache_locked(netid); + if (cache == NULL) { + result = RESOLV_CACHE_UNSUPPORTED; + goto Exit; + } /* see the description of _lookup_p to understand this. * the function always return a non-NULL pointer. @@ -1621,7 +1646,7 @@ _resolv_cache_lookup( struct resolv_cache* cache, XLOG( "NOT IN CACHE"); // calling thread will wait if an outstanding request is found // that matching this query - if (!_cache_check_pending_request_locked(cache, key)) { + if (!_cache_check_pending_request_locked(&cache, key, netid) || cache == NULL) { goto Exit; } else { lookup = _cache_lookup_p(cache, key); @@ -1662,13 +1687,13 @@ _resolv_cache_lookup( struct resolv_cache* cache, result = RESOLV_CACHE_FOUND; Exit: - pthread_mutex_unlock( &cache->lock ); + pthread_mutex_unlock(&_res_cache_list_lock); return result; } void -_resolv_cache_add( struct resolv_cache* cache, +_resolv_cache_add( unsigned netid, const void* query, int querylen, const void* answer, @@ -1678,6 +1703,7 @@ _resolv_cache_add( struct resolv_cache* cache, Entry* e; Entry** lookup; u_long ttl; + Cache* cache = NULL; /* don't assume that the query has already been cached */ @@ -1686,7 +1712,12 @@ _resolv_cache_add( struct resolv_cache* cache, return; } - pthread_mutex_lock( &cache->lock ); + pthread_mutex_lock(&_res_cache_list_lock); + + cache = _find_named_cache_locked(netid); + if (cache == NULL) { + goto Exit; + } XLOG( "%s: query:", __FUNCTION__ ); XLOG_QUERY(query,querylen); @@ -1732,8 +1763,10 @@ _resolv_cache_add( struct resolv_cache* cache, _cache_dump_mru(cache); #endif Exit: - _cache_notify_waiting_tid_locked(cache, key); - pthread_mutex_unlock( &cache->lock ); + if (cache != NULL) { + _cache_notify_waiting_tid_locked(cache, key); + } + pthread_mutex_unlock(&_res_cache_list_lock); } /****************************************************************************/ @@ -1744,20 +1777,13 @@ Exit: /****************************************************************************/ /****************************************************************************/ -static pthread_once_t _res_cache_once = PTHREAD_ONCE_INIT; - // Head of the list of caches. Protected by _res_cache_list_lock. static struct resolv_cache_info _res_cache_list; -// lock protecting everything in the _resolve_cache_info structs (next ptr, etc) -static pthread_mutex_t _res_cache_list_lock; - /* insert resolv_cache_info into the list of resolv_cache_infos */ static void _insert_cache_info_locked(struct resolv_cache_info* cache_info); /* creates a resolv_cache_info */ static struct resolv_cache_info* _create_cache_info( void ); -/* gets cache associated with a network, or NULL if none exists */ -static struct resolv_cache* _find_named_cache_locked(unsigned netid); /* gets a resolv_cache_info associated with a network, or NULL if not found */ static struct resolv_cache_info* _find_cache_info_locked(unsigned netid); /* look up the named cache, and creates one if needed */ @@ -1785,22 +1811,6 @@ _res_cache_init(void) pthread_mutex_init(&_res_cache_list_lock, NULL); } -struct resolv_cache* -__get_res_cache(unsigned netid) -{ - struct resolv_cache *cache; - - pthread_once(&_res_cache_once, _res_cache_init); - pthread_mutex_lock(&_res_cache_list_lock); - - /* Does NOT create a cache if it does not exist. */ - cache = _find_named_cache_locked(netid); - - pthread_mutex_unlock(&_res_cache_list_lock); - XLOG("%s: netid=%u, cache=%p\n", __FUNCTION__, netid, cache); - return cache; -} - static struct resolv_cache* _get_res_cache_for_net_locked(unsigned netid) { @@ -1837,12 +1847,36 @@ _flush_cache_for_net_locked(unsigned netid) { struct resolv_cache* cache = _find_named_cache_locked(netid); if (cache) { - pthread_mutex_lock(&cache->lock); _cache_flush_locked(cache); - pthread_mutex_unlock(&cache->lock); } } +void _resolv_delete_cache_for_net(unsigned netid) +{ + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + struct resolv_cache_info* prev_cache_info = &_res_cache_list; + + while (prev_cache_info->next) { + struct resolv_cache_info* cache_info = prev_cache_info->next; + + if (cache_info->netid == netid) { + prev_cache_info->next = cache_info->next; + _cache_flush_locked(cache_info->cache); + free(cache_info->cache->entries); + free(cache_info->cache); + _free_nameservers_locked(cache_info); + free(cache_info); + break; + } + + prev_cache_info = prev_cache_info->next; + } + + pthread_mutex_unlock(&_res_cache_list_lock); +} + static struct resolv_cache_info* _create_cache_info(void) { diff --git a/libc/dns/resolv/res_send.c b/libc/dns/resolv/res_send.c index 9b36f5575..972e143a9 100644 --- a/libc/dns/resolv/res_send.c +++ b/libc/dns/resolv/res_send.c @@ -367,7 +367,6 @@ res_nsend(res_state statp, int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; char abuf[NI_MAXHOST]; #if USE_RESOLV_CACHE - struct resolv_cache* cache; ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED; #endif @@ -389,21 +388,17 @@ res_nsend(res_state statp, terrno = ETIMEDOUT; #if USE_RESOLV_CACHE - // get the cache associated with the network - cache = __get_res_cache(statp->netid); - if (cache != NULL) { - int anslen = 0; - cache_status = _resolv_cache_lookup( - cache, buf, buflen, - ans, anssiz, &anslen); + int anslen = 0; + cache_status = _resolv_cache_lookup( + statp->netid, buf, buflen, + ans, anssiz, &anslen); - if (cache_status == RESOLV_CACHE_FOUND) { - return anslen; - } else { - // had a cache miss for a known network, so populate the thread private - // data so the normal resolve path can do its thing - _resolv_populate_res_for_net(statp); - } + if (cache_status == RESOLV_CACHE_FOUND) { + return anslen; + } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) { + // had a cache miss for a known network, so populate the thread private + // data so the normal resolve path can do its thing + _resolv_populate_res_for_net(statp); } if (statp->nscount == 0) { @@ -602,7 +597,7 @@ res_nsend(res_state statp, #if USE_RESOLV_CACHE if (cache_status == RESOLV_CACHE_NOTFOUND) { - _resolv_cache_add(cache, buf, buflen, + _resolv_cache_add(statp->netid, buf, buflen, ans, resplen); } #endif @@ -658,13 +653,13 @@ res_nsend(res_state statp, errno = terrno; #if USE_RESOLV_CACHE - _resolv_cache_query_failed(cache, buf, buflen); + _resolv_cache_query_failed(statp->netid, buf, buflen); #endif return (-1); fail: #if USE_RESOLV_CACHE - _resolv_cache_query_failed(cache, buf, buflen); + _resolv_cache_query_failed(statp->netid, buf, buflen); #endif res_nclose(statp); return (-1);