[v2,05/23] nscd: Use a real read-write lock for the client mapping
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
Commit Message
The old code had half of an implementation of a rwlock, but did
not have code to wait in case the mapping was used by another
thread, so it performed busy waiting in some cases. Switch
to a proper rwlock instead.
Simplify the memory management by pre-allocating the struct
mapped_database structures. Consolidate all struct definitions
in an array in nscd_helper.c. This will make it possible to add
fork safety later, in a centralized fashion.
Simplify the error reporting and consolidate the retry logic.
There is still no proper software TM protocol, the code is just
simplified while retaining the old (incorrect) GC checks.
Assume that SCM_RIGHTS and MSG_CMSG_CLOEXEC are always available
(Linux and Hurd have them).
I converted nscd_getpw_r.c manually, and guided automation to
update the other files. Adjusting __nscd_get_nl_timestamp required
additional instructions (use __nscd_get_map_ref, despite
the potential loss of efficiency). Auto-generation should be
fine here because the changes are mechanical and they mostly delete
code.
The definition of MAPPED_DATABASE_INITIALIZER is a slight layering
violation, but both htl and ntpl use pthread rwlocks directly.
---
include/set-freeres.h | 5 --
malloc/set-freeres.c | 15 ----
nscd/nscd-client.h | 90 ++++++----------------
nscd/nscd_getai.c | 39 +++-------
nscd/nscd_getgr_r.c | 52 +++----------
nscd/nscd_gethst_r.c | 85 +++++---------------
nscd/nscd_getpw_r.c | 46 ++---------
nscd/nscd_getserv_r.c | 55 +++----------
nscd/nscd_helper.c | 171 +++++++++++++++++++++++++----------------
nscd/nscd_initgroups.c | 32 ++------
nscd/nscd_netgroup.c | 72 ++++-------------
11 files changed, 208 insertions(+), 454 deletions(-)
Comments
On 3/20/26 4:41 PM, Florian Weimer wrote:
> The old code had half of an implementation of a rwlock, but did
> not have code to wait in case the mapping was used by another
> thread, so it performed busy waiting in some cases. Switch
> to a proper rwlock instead.
...
> -struct locked_map_ptr
> -{
> - int lock;
> - struct mapped_database *mapped;
> + /* Use of the mapping must acquire a read lock. If the mapping is
> + changed, acquire a write lock. */
> + __libc_rwlock_define (, lock);
Does this work when you have a 64-bit nscd with both 64-bit and 32-bit clients?
* Carlos O'Donell:
> On 3/20/26 4:41 PM, Florian Weimer wrote:
>> The old code had half of an implementation of a rwlock, but did
>> not have code to wait in case the mapping was used by another
>> thread, so it performed busy waiting in some cases. Switch
>> to a proper rwlock instead.
>
> ...
>
>> -struct locked_map_ptr
>> -{
>> - int lock;
>> - struct mapped_database *mapped;
>> + /* Use of the mapping must acquire a read lock. If the mapping is
>> + changed, acquire a write lock. */
>> + __libc_rwlock_define (, lock);
>
> Does this work when you have a 64-bit nscd with both 64-bit and 32-bit clients?
This is a libc-internal data structure, not part of the shared mapping.
See the “*” on the deleted line. That changed between different word
sizes before.
Thanks,
Florian
On 3/23/26 12:01 PM, Florian Weimer wrote:
> * Carlos O'Donell:
>
>> On 3/20/26 4:41 PM, Florian Weimer wrote:
>>> The old code had half of an implementation of a rwlock, but did
>>> not have code to wait in case the mapping was used by another
>>> thread, so it performed busy waiting in some cases. Switch
>>> to a proper rwlock instead.
>>
>> ...
>>
>>> -struct locked_map_ptr
>>> -{
>>> - int lock;
>>> - struct mapped_database *mapped;
>>> + /* Use of the mapping must acquire a read lock. If the mapping is
>>> + changed, acquire a write lock. */
>>> + __libc_rwlock_define (, lock);
>>
>> Does this work when you have a 64-bit nscd with both 64-bit and 32-bit clients?
>
> This is a libc-internal data structure, not part of the shared mapping.
> See the “*” on the deleted line. That changed between different word
> sizes before.
Perfect. Thanks. I'll keep reviewing and probably send out quick questions
as I think of them while reviewing the series.
On 3/20/26 4:41 PM, Florian Weimer wrote:
> The old code had half of an implementation of a rwlock, but did
> not have code to wait in case the mapping was used by another
> thread, so it performed busy waiting in some cases. Switch
> to a proper rwlock instead.
Agreed. This looks better.
> Simplify the memory management by pre-allocating the struct
> mapped_database structures. Consolidate all struct definitions
> in an array in nscd_helper.c. This will make it possible to add
> fork safety later, in a centralized fashion.
>
> Simplify the error reporting and consolidate the retry logic.
> There is still no proper software TM protocol, the code is just
> simplified while retaining the old (incorrect) GC checks.
OK.
> Assume that SCM_RIGHTS and MSG_CMSG_CLOEXEC are always available
> (Linux and Hurd have them).
>
> I converted nscd_getpw_r.c manually, and guided automation to
> update the other files. Adjusting __nscd_get_nl_timestamp required
> additional instructions (use __nscd_get_map_ref, despite
> the potential loss of efficiency). Auto-generation should be
> fine here because the changes are mechanical and they mostly delete
> code.
Agreed.
> The definition of MAPPED_DATABASE_INITIALIZER is a slight layering
> violation, but both htl and ntpl use pthread rwlocks directly.
LGTM.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> include/set-freeres.h | 5 --
> malloc/set-freeres.c | 15 ----
> nscd/nscd-client.h | 90 ++++++----------------
> nscd/nscd_getai.c | 39 +++-------
> nscd/nscd_getgr_r.c | 52 +++----------
> nscd/nscd_gethst_r.c | 85 +++++---------------
> nscd/nscd_getpw_r.c | 46 ++---------
> nscd/nscd_getserv_r.c | 55 +++----------
> nscd/nscd_helper.c | 171 +++++++++++++++++++++++++----------------
> nscd/nscd_initgroups.c | 32 ++------
> nscd/nscd_netgroup.c | 72 ++++-------------
> 11 files changed, 208 insertions(+), 454 deletions(-)
>
> diff --git a/include/set-freeres.h b/include/set-freeres.h
> index 470ce00791..55093aa89d 100644
> --- a/include/set-freeres.h
> +++ b/include/set-freeres.h
> @@ -46,11 +46,6 @@ extern void __gconv_dl_freemem (void) attribute_hidden;
> extern void __intl_freemem (void) attribute_hidden;
> extern void __libio_freemem (void) attribute_hidden;
> extern void __libc_fstab_freemem (void) attribute_hidden;
> -extern void __nscd_gr_map_freemem (void) attribute_hidden;
> -extern void __nscd_hst_map_freemem (void) attribute_hidden;
> -extern void __nscd_pw_map_freemem (void) attribute_hidden;
> -extern void __nscd_serv_map_freemem (void) attribute_hidden;
> -extern void __nscd_group_map_freemem (void) attribute_hidden;
> extern void __libc_regcomp_freemem (void) attribute_hidden;
> extern void __libc_atfork_freemem (void) attribute_hidden;
> extern void __libc_resolv_conf_freemem (void) attribute_hidden;
> diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
> index ab2998db89..39fd7d4d27 100644
> --- a/malloc/set-freeres.c
> +++ b/malloc/set-freeres.c
> @@ -36,13 +36,6 @@
> # pragma weak __intl_freemem
> # pragma weak __libio_freemem
> # pragma weak __libc_fstab_freemem
> -#ifdef USE_NSCD
> -# pragma weak __nscd_gr_map_freemem
> -# pragma weak __nscd_hst_map_freemem
> -# pragma weak __nscd_pw_map_freemem
> -# pragma weak __nscd_serv_map_freemem
> -# pragma weak __nscd_group_map_freemem
> -#endif
> # pragma weak __libc_regcomp_freemem
> # pragma weak __libc_atfork_freemem
> # pragma weak __res_thread_freeres
> @@ -144,14 +137,6 @@ __libc_freeres (void)
> call_function_static_weak (__libio_freemem);
> call_function_static_weak (__libc_fstab_freemem);
>
> -#ifdef USE_NSCD
> - call_function_static_weak (__nscd_gr_map_freemem);
> - call_function_static_weak (__nscd_hst_map_freemem);
> - call_function_static_weak (__nscd_pw_map_freemem);
> - call_function_static_weak (__nscd_serv_map_freemem);
> - call_function_static_weak (__nscd_group_map_freemem);
> -#endif
> -
> call_function_static_weak (__libc_regcomp_freemem);
> call_function_static_weak (__libc_atfork_freemem);
> /* __res_thread_freeres deallocates the per-thread resolv_context);
> diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
> index 0906de2929..aff303b757 100644
> --- a/nscd/nscd-client.h
> +++ b/nscd/nscd-client.h
> @@ -29,7 +29,7 @@
> #include <atomic.h>
> #include <nscd-types.h>
> #include <sys/uio.h>
> -
> +#include <libc-lock.h>
>
> /* Version number of the daemon interface */
> #define NSCD_VERSION 2
> @@ -351,83 +351,43 @@ struct mapped_database
> {
> const struct database_pers_head *head;
> const char *data;
> - size_t mapsize;
> - int counter; /* > 0 indicates it is usable. */
> + size_t mapsize; /* Zero means not in use. */
OK. Drop local counter and use mapsize.
> size_t datasize;
> -};
> -#define NO_MAPPING ((struct mapped_database *) -1l)
>
> -struct locked_map_ptr
> -{
> - int lock;
> - struct mapped_database *mapped;
> + /* Use of the mapping must acquire a read lock. If the mapping is
> + changed, acquire a write lock. */
> + __libc_rwlock_define (, lock);
OK. Change the internal lock to an internal rwlock.
> };
> -#define libc_locked_map_ptr(class, name) class struct locked_map_ptr name
> -
> -/* Try acquiring lock for mapptr, returns true if it succeeds, false
> - if not. */
> -static inline bool
> -__nscd_acquire_maplock (volatile struct locked_map_ptr *mapptr)
> -{
> - int cnt = 0;
> - while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
> - 1, 0) != 0, 0))
> - {
> - // XXX Best number of rounds?
> - if (__glibc_unlikely (++cnt > 5))
> - return false;
> -
> - atomic_spin_nop ();
> - }
> -
> - return true;
> -}
> -
OK. Remove __nscd_acquire_lock.
>
> /* Open socket connection to nscd server. */
> extern int __nscd_open_socket (const char *key, size_t keylen,
> request_type type, void *response,
> size_t responselen) attribute_hidden;
>
> -/* Try to get a file descriptor for the shared memory segment
> - containing the database. */
> -extern struct mapped_database *__nscd_get_mapping (request_type type,
> - const char *key,
> - struct mapped_database **mappedp) attribute_hidden;
> -
> -/* Get reference of mapping. */
> -extern struct mapped_database *__nscd_get_map_ref (request_type type,
> - const char *name,
> - volatile struct locked_map_ptr *mapptr,
> - int *gc_cyclep)
> - attribute_hidden;
> +/* Acquire reference to the mapping for DB (see <nscd-dbtype.h>). On
> + success, return a pointer to the mapping descriptor, and lock the
> + mapping.
> +
> + Errors are not fatal (socket fallback should be used,
> + rather than reporting an error immediately to the caller).
>
> -/* Unmap database. */
> -extern void __nscd_unmap (struct mapped_database *mapped)
> + To release the map reference, call __nscd_map_ref_retry_or_drop below. */
> +struct mapped_database *__nscd_get_map_ref (unsigned int db, int *gc_cyclep)
> attribute_hidden;
>
> -/* Drop reference of mapping. */
> -static int
> -__attribute__ ((unused))
> -__nscd_drop_map_ref (struct mapped_database *map, int *gc_cycle)
> -{
> - if (map != NO_MAPPING)
> - {
> - int now_cycle = map->head->gc_cycle;
> - if (__glibc_unlikely (now_cycle != *gc_cycle))
> - {
> - /* We might have read inconsistent data. */
> - *gc_cycle = now_cycle;
> - return -1;
> - }
> -
> - if (atomic_fetch_add_relaxed (&map->counter, -1) == 1)
> - __nscd_unmap (map);
> - }
> -
> - return 0;
> -}
> +/* If *MAPPED is not null, check that *GC_CYCLE matches the current cycle in
> + **MAPPED. If not, the function may return true to indicate another
> + attempt, and increments *NRETRIES. If the retry counter is
> + exceeded, set *MAPPED to null, unlock the mapping, and return
> + true to retry once more without the mapping.
> +
> + If no retries are needed, unlock the mapping (for non-null *MAPPED), and
> + return false. This always happens if retval == -1.
>
> + Must be called after __nscd_get_map_ref. */
> +bool __nscd_map_ref_retry_or_drop (struct mapped_database **mapped,
> + int *gc_cycle, int *nretries, int retval)
> + attribute_hidden;
>
> /* Search the mapped database. */
> extern struct datahead *__nscd_cache_search (request_type type,
> diff --git a/nscd/nscd_getai.c b/nscd/nscd_getai.c
> index 4aca0a18c4..a2e5da6b1c 100644
> --- a/nscd/nscd_getai.c
> +++ b/nscd/nscd_getai.c
> @@ -24,16 +24,13 @@
> #include <not-cancel.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
>
> /* Define in nscd_gethst_r.c. */
> extern int __nss_not_use_nscd_hosts;
>
> -
> -/* We use the mapping from nscd_gethst. */
> -libc_locked_map_ptr (extern, __hst_map_handle) attribute_hidden;
> -
> /* Defined in nscd_gethst_r.c. */
> extern int __nss_have_localdomain attribute_hidden;
>
> @@ -58,9 +55,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
> - &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
>
> retry:;
> struct nscd_ai_result *resultbuf = NULL;
> @@ -70,10 +65,10 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
> int sock = -1;
> ai_response_header ai_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
> - mapped, sizeof ai_resp);
> + map, sizeof ai_resp);
> if (found != NULL)
> {
> respdata = (char *) (&found->data[0].aidata + 1);
> @@ -81,7 +76,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
> recend = (const char *) found->data + found->recsize;
> /* Now check if we can trust ai_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -157,7 +152,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
> && resultbuf->canon[ai_resp.canonlen - 1] != '\0')
> /* We cannot use the database. */
> {
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> retval = -2;
> else
> free (resultbuf);
> @@ -190,25 +185,11 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - {
> - *result = NULL;
> - free (resultbuf);
> - goto retry;
> - }
> + *result = NULL;
> + free (resultbuf);
> + goto retry;
> }
>
> return retval;
> diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
> index db3c0ab464..746b11d792 100644
> --- a/nscd/nscd_getgr_r.c
> +++ b/nscd/nscd_getgr_r.c
> @@ -33,6 +33,7 @@
> #include <scratch_buffer.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
> int __nss_not_use_nscd_group;
> @@ -63,23 +64,6 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
> buffer, buflen, result);
> }
>
> -
> -libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
> -/* Note that we only free the structure if necessary. The memory
> - mapping is not removed since it is not visible to the malloc
> - handling. */
> -void
> -__nscd_gr_map_freemem (void)
> -{
> - if (__gr_map_handle.mapped != NO_MAPPING)
> - {
> - void *p = __gr_map_handle.mapped;
> - __gr_map_handle.mapped = NO_MAPPING;
> - free (p);
> - }
> -}
> -
> -
> static int
> nscd_getgr_r (const char *key, size_t keylen, request_type type,
> struct group *resultbuf, char *buffer, size_t buflen,
> @@ -93,9 +77,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
> - &__gr_map_handle,
> - &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (grpdb, &gc_cycle);
> retry:;
> const char *gr_name = NULL;
> size_t gr_name_len = 0;
> @@ -103,9 +85,9 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> const char *recend = (const char *) ~UINTMAX_C (0);
> gr_response_header gr_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> - struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
> + struct datahead *found = __nscd_cache_search (type, key, keylen, map,
> sizeof gr_resp);
> if (found != NULL)
> {
> @@ -117,7 +99,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> recend = (const char *) found->data + found->recsize;
> /* Now check if we can trust gr_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -239,7 +221,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> {
> /* len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (gr_name != NULL && map->head->gc_cycle != gc_cycle)
> retval = -2;
> goto out_close;
> }
> @@ -247,7 +229,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> {
> /* len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (gr_name != NULL && map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out_close;
> @@ -286,7 +268,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> cnt < gr_resp.gr_mem_cnt; }))
> {
> /* We cannot use the database. */
> - retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
> + retval = map != NULL && map->head->gc_cycle != gc_cycle ? -2 : -1;
> goto out_close;
> }
>
> @@ -305,22 +287,8 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> scratch_buffer_free (&lenbuf);
>
> diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
> index 9291039008..15d747fc33 100644
> --- a/nscd/nscd_gethst_r.c
> +++ b/nscd/nscd_gethst_r.c
> @@ -24,6 +24,7 @@
> #include <not-cancel.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
> int __nss_not_use_nscd_hosts;
> @@ -77,52 +78,22 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
> }
>
>
> -libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
> -/* Note that we only free the structure if necessary. The memory
> - mapping is not removed since it is not visible to the malloc
> - handling. */
> -void
> -__nscd_hst_map_freemem (void)
> -{
> - if (__hst_map_handle.mapped != NO_MAPPING)
> - {
> - void *p = __hst_map_handle.mapped;
> - __hst_map_handle.mapped = NO_MAPPING;
> - free (p);
> - }
> -}
> -
> -
> uint32_t
> __nscd_get_nl_timestamp (void)
> {
> - uint32_t retval;
> if (__nss_not_use_nscd_hosts != 0)
> return 0;
>
> - /* __nscd_get_mapping can change hst_map_handle.mapped to NO_MAPPING.
> - However, __nscd_get_mapping assumes the prior value was not NO_MAPPING.
> - Thus we have to acquire the lock to prevent this thread from changing
> - hst_map_handle.mapped to NO_MAPPING while another thread is inside
> - __nscd_get_mapping. */
> - if (!__nscd_acquire_maplock (&__hst_map_handle))
> - return 0;
> -
> - struct mapped_database *map = __hst_map_handle.mapped;
> -
> - if (map == NULL
> - || (map != NO_MAPPING
> - && map->head->nscd_certainly_running == 0
> - && map->head->timestamp + MAPPING_TIMEOUT < time64_now ()))
> - map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
> -
> - if (map == NO_MAPPING)
> - retval = 0;
> + int gc_cycle;
> + uint32_t retval;
> + struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
> + if (map != NULL)
> + {
> + retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
> + __libc_rwlock_unlock (map->lock);
> + }
> else
> - retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
> -
> - /* Release the lock. */
> - __hst_map_handle.lock = 0;
> + retval = 0;
>
> return retval;
> }
> @@ -151,9 +122,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
> - &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
>
> retry:;
> const char *h_name = NULL;
> @@ -164,11 +133,11 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> const char *recend = (const char *) ~UINTMAX_C (0);
> int sock = -1;
> hst_response_header hst_resp;
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> /* No const qualifier, as it can change during garbage collection. */
> - struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
> - sizeof hst_resp);
> + struct datahead *found = __nscd_cache_search (type, key, keylen,
> + map, sizeof hst_resp);
> if (found != NULL)
> {
> h_name = (char *) (&found->data[0].hstdata + 1);
> @@ -180,7 +149,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> recend = (const char *) found->data + found->recsize;
> /* Now check if we can trust hst_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -364,7 +333,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> {
> /* aliases_len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (addr_list != NULL && map->head->gc_cycle != gc_cycle)
> retval = -2;
> goto out_close;
> }
> @@ -373,7 +342,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> {
> /* aliases_len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (addr_list != NULL && map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out_close;
> @@ -406,7 +375,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> cnt < hst_resp.h_aliases_cnt; }))
> {
> /* We cannot use the database. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> retval = -2;
> goto out_close;
> }
> @@ -430,22 +399,8 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> return retval;
> }
> diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
> index f19b7a271c..938eac9c70 100644
> --- a/nscd/nscd_getpw_r.c
> +++ b/nscd/nscd_getpw_r.c
> @@ -31,6 +31,7 @@
> #include <_itoa.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
> int __nss_not_use_nscd_passwd;
> @@ -63,22 +64,6 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
> }
>
>
> -libc_locked_map_ptr (static, map_handle);
> -/* Note that we only free the structure if necessary. The memory
> - mapping is not removed since it is not visible to the malloc
> - handling. */
> -void
> -__nscd_pw_map_freemem (void)
> -{
> - if (map_handle.mapped != NO_MAPPING)
> - {
> - void *p = map_handle.mapped;
> - map_handle.mapped = NO_MAPPING;
> - free (p);
> - }
> -}
> -
> -
> static int
> nscd_getpw_r (const char *key, size_t keylen, request_type type,
> struct passwd *resultbuf, char *buffer, size_t buflen,
> @@ -89,8 +74,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (pwddb, &gc_cycle);
>
> retry:;
> const char *pw_name = NULL;
> @@ -98,9 +82,9 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
> const char *recend = (const char *) ~UINTMAX_C (0);
> pw_response_header pw_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> - struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
> + struct datahead *found = __nscd_cache_search (type, key, keylen, map,
> sizeof pw_resp);
> if (found != NULL)
> {
> @@ -109,7 +93,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
> recend = (const char *) found->data + found->recsize;
> /* Now check if we can trust pw_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -199,7 +183,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
> || resultbuf->pw_shell[pw_resp.pw_shell_len - 1] != '\0')
> {
> /* We cannot use the database. */
> - retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
> + retval = map->head->gc_cycle != gc_cycle ? -2 : -1;
> goto out_close;
> }
>
> @@ -218,22 +202,8 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> return retval;
> }
> diff --git a/nscd/nscd_getserv_r.c b/nscd/nscd_getserv_r.c
> index 63cd6d5860..519078d8fe 100644
> --- a/nscd/nscd_getserv_r.c
> +++ b/nscd/nscd_getserv_r.c
> @@ -23,6 +23,7 @@
> #include <stdint.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
>
> @@ -58,22 +59,6 @@ __nscd_getservbyport_r (int port, const char *proto,
> }
>
>
> -libc_locked_map_ptr (, __serv_map_handle) attribute_hidden;
> -/* Note that we only free the structure if necessary. The memory
> - mapping is not removed since it is not visible to the malloc
> - handling. */
> -void
> -__nscd_serv_map_freemem (void)
> -{
> - if (__serv_map_handle.mapped != NO_MAPPING)
> - {
> - void *p = __serv_map_handle.mapped;
> - __serv_map_handle.mapped = NO_MAPPING;
> - free (p);
> - }
> -}
> -
> -
> static int
> nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> request_type type, struct servent *resultbuf,
> @@ -85,9 +70,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDSERV, "services", &__serv_map_handle,
> - &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (servdb, &gc_cycle);
> size_t protolen = proto == NULL ? 0 : strlen (proto);
> size_t keylen = critlen + 1 + protolen + 1;
> int alloca_key = __libc_use_alloca (keylen);
> @@ -114,9 +97,9 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> int sock = -1;
> serv_response_header serv_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> - struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
> + struct datahead *found = __nscd_cache_search (type, key, keylen, map,
> sizeof serv_resp);
>
> if (found != NULL)
> @@ -131,7 +114,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> recend = (const char *) found->data + found->recsize;
> /* Now check if we can trust serv_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -291,7 +274,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> {
> /* aliases_len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (aliases_list != NULL && map->head->gc_cycle != gc_cycle)
> retval = -2;
> goto out_close;
> }
> @@ -301,7 +284,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> {
> /* aliases_len array might contain garbage during nscd GC cycle,
> retry rather than fail in that case. */
> - if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
> + if (aliases_list != NULL && map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out_close;
> @@ -334,7 +317,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> cnt < serv_resp.s_aliases_cnt; }))
> {
> /* We cannot use the database. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> retval = -2;
> goto out_close;
> }
> @@ -355,25 +338,11 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - {
> - if (!alloca_aliases_len)
> - free ((void *) aliases_len);
> - goto retry;
> - }
> + if (!alloca_aliases_len)
> + free ((void *) aliases_len);
> + goto retry;
> }
>
> if (!alloca_aliases_len)
> diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
> index 6319fde6f2..d3bfc208ad 100644
> --- a/nscd/nscd_helper.c
> +++ b/nscd/nscd_helper.c
> @@ -39,6 +39,7 @@
> #include <struct___timespec64.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
>
> /* Extra time we wait if the socket is still receiving data. This
> value is in milliseconds. Note that the other side is nscd on the
> @@ -241,32 +242,64 @@ open_socket (request_type type, const char *key, size_t keylen)
> return -1;
> }
>
> +#define MAPPED_DATABASE_INITIALIZER \
> + ((struct mapped_database) { .lock = PTHREAD_RWLOCK_INITIALIZER, })
>
> -void
> -__nscd_unmap (struct mapped_database *mapped)
> -{
> - assert (mapped->counter == 0);
> - __munmap ((void *) mapped->head, mapped->mapsize);
> - free (mapped);
> -}
> -
> +static struct mapped_database __nscd_mapped_databases[lastdb] =
> + {
> + [pwddb] = MAPPED_DATABASE_INITIALIZER,
> + [grpdb] = MAPPED_DATABASE_INITIALIZER,
> + [hstdb] = MAPPED_DATABASE_INITIALIZER,
> + [servdb] = MAPPED_DATABASE_INITIALIZER,
> + [netgrdb] = MAPPED_DATABASE_INITIALIZER,
> + };
>
> /* Try to get a file descriptor for the shared memory segment
> - containing the database. */
> -struct mapped_database *
> -__nscd_get_mapping (request_type type, const char *key,
> - struct mapped_database **mappedp)
> + containing the database. Lock the mapping, but mapsize might still
> + be 0 after return because there is no mapping. */
> +static void
> +__nscd_get_mapping (unsigned int db)
> {
> - struct mapped_database *result = NO_MAPPING;
> -#ifdef SCM_RIGHTS
> + struct mapped_database *mapped = &__nscd_mapped_databases[db];
> +
> + static const uint8_t requests[] =
> + {
> + [pwddb] = GETFDPW,
> + [grpdb] = GETFDGR,
> + [hstdb] = GETFDHST,
> + [servdb] = GETFDSERV,
> + [netgrdb] = GETFDNETGR,
> + };
> +
> + static const char keys[][9] =
> + {
> + [pwddb] = "passwd",
> + [grpdb] = "group",
> + [hstdb] = "hosts",
> + [servdb] = "services",
> + [netgrdb] = "netgroup"
> + };
> + const char *key = keys[db];
> const size_t keylen = strlen (key) + 1;
> +
> + __libc_rwlock_wrlock (mapped->lock);
> +
> + /* Remove any previously existing mapping. */
> + if (mapped->mapsize > 0)
> + {
> + __munmap ((void *) mapped->head, mapped->mapsize);
> + mapped->head = NULL;
> + mapped->data = NULL;
> + mapped->mapsize = 0;
> + }
> +
> int saved_errno = errno;
>
> int mapfd = -1;
> - char resdata[keylen];
> + char resdata[sizeof (keys[0])];
>
> /* Open a socket and send the request. */
> - int sock = open_socket (type, key, keylen);
> + int sock = open_socket (requests[db], key, keylen);
> if (sock < 0)
> goto out;
>
> @@ -302,9 +335,6 @@ __nscd_get_mapping (request_type type, const char *key,
> if (wait_on_socket (sock, 5 * 1000) <= 0)
> goto out_close2;
>
> -# ifndef MSG_CMSG_CLOEXEC
> -# define MSG_CMSG_CLOEXEC 0
> -# endif
> ssize_t n = TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, MSG_CMSG_CLOEXEC));
>
> if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
> @@ -362,21 +392,11 @@ __nscd_get_mapping (request_type type, const char *key,
> if (__glibc_unlikely (mapsize < size))
> goto out_unmap;
>
> - /* Allocate a record for the mapping. */
> - struct mapped_database *newp = malloc (sizeof (*newp));
> - if (newp == NULL)
> - /* Ugh, after all we went through the memory allocation failed. */
> - goto out_unmap;
> -
> - newp->head = mapping;
> - newp->data = ((char *) mapping + head->header_size
> + mapped->head = mapping;
> + mapped->data = ((char *) mapping + head->header_size
> + roundup (head->module * sizeof (ref_t), ALIGN));
> - newp->mapsize = size;
> - newp->datasize = head->data_size;
> - /* Set counter to 1 to show it is usable. */
> - newp->counter = 1;
> -
> - result = newp;
> + mapped->mapsize = size;
> + mapped->datasize = head->data_size;
> }
>
> out_close:
> @@ -385,56 +405,71 @@ __nscd_get_mapping (request_type type, const char *key,
> __close (sock);
> out:
> __set_errno (saved_errno);
> -#endif /* SCM_RIGHTS */
> -
> - struct mapped_database *oldval = *mappedp;
> - *mappedp = result;
>
> - if (oldval != NULL && atomic_fetch_add_relaxed (&oldval->counter, -1) == 1)
> - __nscd_unmap (oldval);
> -
> - return result;
> + /* Downgrade lock to a read lock. The mapping may go away between
> + the two calls, but the caller has to handle the no-mapping case
> + anyway. */
> + __libc_rwlock_unlock (mapped->lock);
> + __libc_rwlock_rdlock (mapped->lock);
> }
>
> struct mapped_database *
> -__nscd_get_map_ref (request_type type, const char *name,
> - volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
> +__nscd_get_map_ref (unsigned int db, int *gc_cyclep)
> {
> - struct mapped_database *cur = mapptr->mapped;
> - if (cur == NO_MAPPING)
> - return cur;
> + assert (db < lastdb);
> + struct mapped_database *mapped = &__nscd_mapped_databases[db];
> +
> + __libc_rwlock_rdlock (mapped->lock);
OK. Lock.
> + if (mapped->mapsize == 0
> + || (mapped->head->nscd_certainly_running == 0
> + && mapped->head->timestamp + MAPPING_TIMEOUT < time_now ())
> + || mapped->head->data_size > mapped->datasize)
> + {
> + __libc_rwlock_unlock (mapped->lock);
OK. Unlock.
> + __nscd_get_mapping (db);
> + }
>
> - if (!__nscd_acquire_maplock (mapptr))
> - return NO_MAPPING;
> + if (mapped->mapsize > 0)
> + {
> + *gc_cyclep = mapped->head->gc_cycle;
> + if ((*gc_cyclep & 1) == 0)
> + return mapped;
> + }
>
> - cur = mapptr->mapped;
> + __libc_rwlock_unlock (mapped->lock);
OK. Unlock.
> + return NULL;
> +}
> +
> +bool
> +__nscd_map_ref_retry_or_drop (struct mapped_database **mapped,
> + int *gc_cycle, int *nretries, int retval)
> +{
> + if (*mapped == NULL)
> + return false;
>
> - if (__glibc_likely (cur != NO_MAPPING))
> + int now_cycle = (*mapped)->head->gc_cycle;
> + if (__glibc_unlikely (now_cycle != *gc_cycle))
> {
> - /* If not mapped or timestamp not updated, request new map. */
> - if (cur == NULL
> - || (cur->head->nscd_certainly_running == 0
> - && cur->head->timestamp + MAPPING_TIMEOUT < time_now ())
> - || cur->head->data_size > cur->datasize)
> - cur = __nscd_get_mapping (type, name,
> - (struct mapped_database **) &mapptr->mapped);
> -
> - if (__glibc_likely (cur != NO_MAPPING))
> + /* When we come here this means there has been a GC cycle while we
> + were looking for the data. This means the data might have been
> + inconsistent. Retry if possible. */
> +
> + *gc_cycle = now_cycle;
> + if ((*gc_cycle & 1) != 0 || ++*nretries == 5)
> {
> - if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
> - 0))
> - cur = NO_MAPPING;
> - else
> - atomic_fetch_add_relaxed (&cur->counter, 1);
> + __libc_rwlock_unlock ((*mapped)->lock);
> + *mapped = NULL;
> + return true;
> }
> - }
>
> - mapptr->lock = 0;
> + if (retval != -1)
> + return true;
> + }
>
> - return cur;
> + __libc_rwlock_unlock ((*mapped)->lock);
> + return false;
> }
>
> -
> /* Using sizeof (hashentry) is not always correct to determine the size of
> the data structure as found in the nscd cache. The program could be
> a 64-bit process and nscd could be a 32-bit process. In this case
> diff --git a/nscd/nscd_initgroups.c b/nscd/nscd_initgroups.c
> index 71ad7c7f96..f1e821b8d0 100644
> --- a/nscd/nscd_initgroups.c
> +++ b/nscd/nscd_initgroups.c
> @@ -24,13 +24,10 @@
> #include <not-cancel.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
>
> -/* We use the same mapping as in nscd_getgr. */
> -libc_locked_map_ptr (extern, __gr_map_handle) attribute_hidden;
> -
> -
> int
> __nscd_getgrouplist (const char *user, gid_t group, long int *size,
> gid_t **groupsp, long int limit)
> @@ -41,8 +38,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (grpdb, &gc_cycle);
>
> retry:;
> char *respdata = NULL;
> @@ -50,10 +46,10 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
> int sock = -1;
> initgr_response_header initgr_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> struct datahead *found = __nscd_cache_search (INITGROUPS, user,
> - userlen, mapped,
> + userlen, map,
> sizeof initgr_resp);
> if (found != NULL)
> {
> @@ -63,7 +59,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
>
> /* Now check if we can trust initgr_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -158,22 +154,8 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> return retval;
> }
> diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
> index 990d071cc0..53649adc82 100644
> --- a/nscd/nscd_netgroup.c
> +++ b/nscd/nscd_netgroup.c
> @@ -22,27 +22,11 @@
> #include <not-cancel.h>
>
> #include "nscd-client.h"
> +#include "nscd-dbtype.h"
> #include "nscd_proto.h"
>
> int __nss_not_use_nscd_netgroup;
>
> -
> -libc_locked_map_ptr (static, map_handle);
> -/* Note that we only free the structure if necessary. The memory
> - mapping is not removed since it is not visible to the malloc
> - handling. */
> -void
> -__nscd_group_map_freemem (void)
> -{
> - if (map_handle.mapped != NO_MAPPING)
> - {
> - void *p = map_handle.mapped;
> - map_handle.mapped = NO_MAPPING;
> - free (p);
> - }
> -}
> -
> -
> int
> __nscd_setnetgrent (const char *group, struct __netgrent *datap)
> {
> @@ -52,18 +36,17 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
>
> /* If the mapping is available, try to search there instead of
> communicating with the nscd. */
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (netgrdb, &gc_cycle);
>
> retry:;
> char *respdata = NULL;
> int retval = -1;
> netgroup_response_header netgroup_resp;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> struct datahead *found = __nscd_cache_search (GETNETGRENT, group,
> - group_len, mapped,
> + group_len, map,
> sizeof netgroup_resp);
> if (found != NULL)
> {
> @@ -71,7 +54,7 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
> netgroup_resp = found->data[0].netgroupdata;
> /* Now check if we can trust pw_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -141,22 +124,8 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> return retval;
> }
> @@ -206,25 +175,24 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
> communicating with the nscd. */
> int gc_cycle;
> int nretries = 0;
> - struct mapped_database *mapped;
> - mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
> + struct mapped_database *map = __nscd_get_map_ref (netgrdb, &gc_cycle);
>
> retry:;
> int retval = -1;
> innetgroup_response_header innetgroup_resp;
> int sock = -1;
>
> - if (mapped != NO_MAPPING)
> + if (map != NULL)
> {
> struct datahead *found = __nscd_cache_search (INNETGR, key,
> - key_len, mapped,
> + key_len, map,
> sizeof innetgroup_resp);
> if (found != NULL)
> {
> innetgroup_resp = found->data[0].innetgroupdata;
> /* Now check if we can trust pw_resp fields. If GC is
> in progress, it can contain anything. */
> - if (mapped->head->gc_cycle != gc_cycle)
> + if (map->head->gc_cycle != gc_cycle)
> {
> retval = -2;
> goto out;
> @@ -265,22 +233,8 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
> if (sock != -1)
> __close_nocancel_nostatus (sock);
> out:
> - if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
> - {
> - /* When we come here this means there has been a GC cycle while we
> - were looking for the data. This means the data might have been
> - inconsistent. Retry if possible. */
> - if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
> - {
> - /* nscd is just running gc now. Disable using the mapping. */
> - if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
> - __nscd_unmap (mapped);
> - mapped = NO_MAPPING;
> - }
> -
> - if (retval != -1)
> - goto retry;
> - }
> + if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
> + goto retry;
>
> if (! use_alloca)
> free (key);
@@ -46,11 +46,6 @@ extern void __gconv_dl_freemem (void) attribute_hidden;
extern void __intl_freemem (void) attribute_hidden;
extern void __libio_freemem (void) attribute_hidden;
extern void __libc_fstab_freemem (void) attribute_hidden;
-extern void __nscd_gr_map_freemem (void) attribute_hidden;
-extern void __nscd_hst_map_freemem (void) attribute_hidden;
-extern void __nscd_pw_map_freemem (void) attribute_hidden;
-extern void __nscd_serv_map_freemem (void) attribute_hidden;
-extern void __nscd_group_map_freemem (void) attribute_hidden;
extern void __libc_regcomp_freemem (void) attribute_hidden;
extern void __libc_atfork_freemem (void) attribute_hidden;
extern void __libc_resolv_conf_freemem (void) attribute_hidden;
@@ -36,13 +36,6 @@
# pragma weak __intl_freemem
# pragma weak __libio_freemem
# pragma weak __libc_fstab_freemem
-#ifdef USE_NSCD
-# pragma weak __nscd_gr_map_freemem
-# pragma weak __nscd_hst_map_freemem
-# pragma weak __nscd_pw_map_freemem
-# pragma weak __nscd_serv_map_freemem
-# pragma weak __nscd_group_map_freemem
-#endif
# pragma weak __libc_regcomp_freemem
# pragma weak __libc_atfork_freemem
# pragma weak __res_thread_freeres
@@ -144,14 +137,6 @@ __libc_freeres (void)
call_function_static_weak (__libio_freemem);
call_function_static_weak (__libc_fstab_freemem);
-#ifdef USE_NSCD
- call_function_static_weak (__nscd_gr_map_freemem);
- call_function_static_weak (__nscd_hst_map_freemem);
- call_function_static_weak (__nscd_pw_map_freemem);
- call_function_static_weak (__nscd_serv_map_freemem);
- call_function_static_weak (__nscd_group_map_freemem);
-#endif
-
call_function_static_weak (__libc_regcomp_freemem);
call_function_static_weak (__libc_atfork_freemem);
/* __res_thread_freeres deallocates the per-thread resolv_context);
@@ -29,7 +29,7 @@
#include <atomic.h>
#include <nscd-types.h>
#include <sys/uio.h>
-
+#include <libc-lock.h>
/* Version number of the daemon interface */
#define NSCD_VERSION 2
@@ -351,83 +351,43 @@ struct mapped_database
{
const struct database_pers_head *head;
const char *data;
- size_t mapsize;
- int counter; /* > 0 indicates it is usable. */
+ size_t mapsize; /* Zero means not in use. */
size_t datasize;
-};
-#define NO_MAPPING ((struct mapped_database *) -1l)
-struct locked_map_ptr
-{
- int lock;
- struct mapped_database *mapped;
+ /* Use of the mapping must acquire a read lock. If the mapping is
+ changed, acquire a write lock. */
+ __libc_rwlock_define (, lock);
};
-#define libc_locked_map_ptr(class, name) class struct locked_map_ptr name
-
-/* Try acquiring lock for mapptr, returns true if it succeeds, false
- if not. */
-static inline bool
-__nscd_acquire_maplock (volatile struct locked_map_ptr *mapptr)
-{
- int cnt = 0;
- while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
- 1, 0) != 0, 0))
- {
- // XXX Best number of rounds?
- if (__glibc_unlikely (++cnt > 5))
- return false;
-
- atomic_spin_nop ();
- }
-
- return true;
-}
-
/* Open socket connection to nscd server. */
extern int __nscd_open_socket (const char *key, size_t keylen,
request_type type, void *response,
size_t responselen) attribute_hidden;
-/* Try to get a file descriptor for the shared memory segment
- containing the database. */
-extern struct mapped_database *__nscd_get_mapping (request_type type,
- const char *key,
- struct mapped_database **mappedp) attribute_hidden;
-
-/* Get reference of mapping. */
-extern struct mapped_database *__nscd_get_map_ref (request_type type,
- const char *name,
- volatile struct locked_map_ptr *mapptr,
- int *gc_cyclep)
- attribute_hidden;
+/* Acquire reference to the mapping for DB (see <nscd-dbtype.h>). On
+ success, return a pointer to the mapping descriptor, and lock the
+ mapping.
+
+ Errors are not fatal (socket fallback should be used,
+ rather than reporting an error immediately to the caller).
-/* Unmap database. */
-extern void __nscd_unmap (struct mapped_database *mapped)
+ To release the map reference, call __nscd_map_ref_retry_or_drop below. */
+struct mapped_database *__nscd_get_map_ref (unsigned int db, int *gc_cyclep)
attribute_hidden;
-/* Drop reference of mapping. */
-static int
-__attribute__ ((unused))
-__nscd_drop_map_ref (struct mapped_database *map, int *gc_cycle)
-{
- if (map != NO_MAPPING)
- {
- int now_cycle = map->head->gc_cycle;
- if (__glibc_unlikely (now_cycle != *gc_cycle))
- {
- /* We might have read inconsistent data. */
- *gc_cycle = now_cycle;
- return -1;
- }
-
- if (atomic_fetch_add_relaxed (&map->counter, -1) == 1)
- __nscd_unmap (map);
- }
-
- return 0;
-}
+/* If *MAPPED is not null, check that *GC_CYCLE matches the current cycle in
+ **MAPPED. If not, the function may return true to indicate another
+ attempt, and increments *NRETRIES. If the retry counter is
+ exceeded, set *MAPPED to null, unlock the mapping, and return
+ true to retry once more without the mapping.
+
+ If no retries are needed, unlock the mapping (for non-null *MAPPED), and
+ return false. This always happens if retval == -1.
+ Must be called after __nscd_get_map_ref. */
+bool __nscd_map_ref_retry_or_drop (struct mapped_database **mapped,
+ int *gc_cycle, int *nretries, int retval)
+ attribute_hidden;
/* Search the mapped database. */
extern struct datahead *__nscd_cache_search (request_type type,
@@ -24,16 +24,13 @@
#include <not-cancel.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
/* Define in nscd_gethst_r.c. */
extern int __nss_not_use_nscd_hosts;
-
-/* We use the mapping from nscd_gethst. */
-libc_locked_map_ptr (extern, __hst_map_handle) attribute_hidden;
-
/* Defined in nscd_gethst_r.c. */
extern int __nss_have_localdomain attribute_hidden;
@@ -58,9 +55,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
- &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
retry:;
struct nscd_ai_result *resultbuf = NULL;
@@ -70,10 +65,10 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
int sock = -1;
ai_response_header ai_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
- mapped, sizeof ai_resp);
+ map, sizeof ai_resp);
if (found != NULL)
{
respdata = (char *) (&found->data[0].aidata + 1);
@@ -81,7 +76,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust ai_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -157,7 +152,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
&& resultbuf->canon[ai_resp.canonlen - 1] != '\0')
/* We cannot use the database. */
{
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
retval = -2;
else
free (resultbuf);
@@ -190,25 +185,11 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
{
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- {
- *result = NULL;
- free (resultbuf);
- goto retry;
- }
+ *result = NULL;
+ free (resultbuf);
+ goto retry;
}
return retval;
@@ -33,6 +33,7 @@
#include <scratch_buffer.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
int __nss_not_use_nscd_group;
@@ -63,23 +64,6 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
buffer, buflen, result);
}
-
-libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
-/* Note that we only free the structure if necessary. The memory
- mapping is not removed since it is not visible to the malloc
- handling. */
-void
-__nscd_gr_map_freemem (void)
-{
- if (__gr_map_handle.mapped != NO_MAPPING)
- {
- void *p = __gr_map_handle.mapped;
- __gr_map_handle.mapped = NO_MAPPING;
- free (p);
- }
-}
-
-
static int
nscd_getgr_r (const char *key, size_t keylen, request_type type,
struct group *resultbuf, char *buffer, size_t buflen,
@@ -93,9 +77,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
- &__gr_map_handle,
- &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (grpdb, &gc_cycle);
retry:;
const char *gr_name = NULL;
size_t gr_name_len = 0;
@@ -103,9 +85,9 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
const char *recend = (const char *) ~UINTMAX_C (0);
gr_response_header gr_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
- struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+ struct datahead *found = __nscd_cache_search (type, key, keylen, map,
sizeof gr_resp);
if (found != NULL)
{
@@ -117,7 +99,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust gr_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -239,7 +221,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
{
/* len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (gr_name != NULL && map->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
@@ -247,7 +229,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
{
/* len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (gr_name != NULL && map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out_close;
@@ -286,7 +268,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
cnt < gr_resp.gr_mem_cnt; }))
{
/* We cannot use the database. */
- retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
+ retval = map != NULL && map->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close;
}
@@ -305,22 +287,8 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
scratch_buffer_free (&lenbuf);
@@ -24,6 +24,7 @@
#include <not-cancel.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
int __nss_not_use_nscd_hosts;
@@ -77,52 +78,22 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
}
-libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
-/* Note that we only free the structure if necessary. The memory
- mapping is not removed since it is not visible to the malloc
- handling. */
-void
-__nscd_hst_map_freemem (void)
-{
- if (__hst_map_handle.mapped != NO_MAPPING)
- {
- void *p = __hst_map_handle.mapped;
- __hst_map_handle.mapped = NO_MAPPING;
- free (p);
- }
-}
-
-
uint32_t
__nscd_get_nl_timestamp (void)
{
- uint32_t retval;
if (__nss_not_use_nscd_hosts != 0)
return 0;
- /* __nscd_get_mapping can change hst_map_handle.mapped to NO_MAPPING.
- However, __nscd_get_mapping assumes the prior value was not NO_MAPPING.
- Thus we have to acquire the lock to prevent this thread from changing
- hst_map_handle.mapped to NO_MAPPING while another thread is inside
- __nscd_get_mapping. */
- if (!__nscd_acquire_maplock (&__hst_map_handle))
- return 0;
-
- struct mapped_database *map = __hst_map_handle.mapped;
-
- if (map == NULL
- || (map != NO_MAPPING
- && map->head->nscd_certainly_running == 0
- && map->head->timestamp + MAPPING_TIMEOUT < time64_now ()))
- map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
-
- if (map == NO_MAPPING)
- retval = 0;
+ int gc_cycle;
+ uint32_t retval;
+ struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
+ if (map != NULL)
+ {
+ retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
+ __libc_rwlock_unlock (map->lock);
+ }
else
- retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
-
- /* Release the lock. */
- __hst_map_handle.lock = 0;
+ retval = 0;
return retval;
}
@@ -151,9 +122,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
- &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (hstdb, &gc_cycle);
retry:;
const char *h_name = NULL;
@@ -164,11 +133,11 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
const char *recend = (const char *) ~UINTMAX_C (0);
int sock = -1;
hst_response_header hst_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
/* No const qualifier, as it can change during garbage collection. */
- struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
- sizeof hst_resp);
+ struct datahead *found = __nscd_cache_search (type, key, keylen,
+ map, sizeof hst_resp);
if (found != NULL)
{
h_name = (char *) (&found->data[0].hstdata + 1);
@@ -180,7 +149,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust hst_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -364,7 +333,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
{
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (addr_list != NULL && map->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
@@ -373,7 +342,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
{
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (addr_list != NULL && map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out_close;
@@ -406,7 +375,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
cnt < hst_resp.h_aliases_cnt; }))
{
/* We cannot use the database. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
@@ -430,22 +399,8 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
return retval;
}
@@ -31,6 +31,7 @@
#include <_itoa.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
int __nss_not_use_nscd_passwd;
@@ -63,22 +64,6 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
}
-libc_locked_map_ptr (static, map_handle);
-/* Note that we only free the structure if necessary. The memory
- mapping is not removed since it is not visible to the malloc
- handling. */
-void
-__nscd_pw_map_freemem (void)
-{
- if (map_handle.mapped != NO_MAPPING)
- {
- void *p = map_handle.mapped;
- map_handle.mapped = NO_MAPPING;
- free (p);
- }
-}
-
-
static int
nscd_getpw_r (const char *key, size_t keylen, request_type type,
struct passwd *resultbuf, char *buffer, size_t buflen,
@@ -89,8 +74,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (pwddb, &gc_cycle);
retry:;
const char *pw_name = NULL;
@@ -98,9 +82,9 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
const char *recend = (const char *) ~UINTMAX_C (0);
pw_response_header pw_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
- struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+ struct datahead *found = __nscd_cache_search (type, key, keylen, map,
sizeof pw_resp);
if (found != NULL)
{
@@ -109,7 +93,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust pw_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -199,7 +183,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
|| resultbuf->pw_shell[pw_resp.pw_shell_len - 1] != '\0')
{
/* We cannot use the database. */
- retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
+ retval = map->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close;
}
@@ -218,22 +202,8 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
return retval;
}
@@ -23,6 +23,7 @@
#include <stdint.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
@@ -58,22 +59,6 @@ __nscd_getservbyport_r (int port, const char *proto,
}
-libc_locked_map_ptr (, __serv_map_handle) attribute_hidden;
-/* Note that we only free the structure if necessary. The memory
- mapping is not removed since it is not visible to the malloc
- handling. */
-void
-__nscd_serv_map_freemem (void)
-{
- if (__serv_map_handle.mapped != NO_MAPPING)
- {
- void *p = __serv_map_handle.mapped;
- __serv_map_handle.mapped = NO_MAPPING;
- free (p);
- }
-}
-
-
static int
nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
request_type type, struct servent *resultbuf,
@@ -85,9 +70,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDSERV, "services", &__serv_map_handle,
- &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (servdb, &gc_cycle);
size_t protolen = proto == NULL ? 0 : strlen (proto);
size_t keylen = critlen + 1 + protolen + 1;
int alloca_key = __libc_use_alloca (keylen);
@@ -114,9 +97,9 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
int sock = -1;
serv_response_header serv_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
- struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
+ struct datahead *found = __nscd_cache_search (type, key, keylen, map,
sizeof serv_resp);
if (found != NULL)
@@ -131,7 +114,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
recend = (const char *) found->data + found->recsize;
/* Now check if we can trust serv_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -291,7 +274,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
{
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (aliases_list != NULL && map->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
@@ -301,7 +284,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
{
/* aliases_len array might contain garbage during nscd GC cycle,
retry rather than fail in that case. */
- if (aliases_list != NULL && mapped->head->gc_cycle != gc_cycle)
+ if (aliases_list != NULL && map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out_close;
@@ -334,7 +317,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
cnt < serv_resp.s_aliases_cnt; }))
{
/* We cannot use the database. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
retval = -2;
goto out_close;
}
@@ -355,25 +338,11 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
{
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- {
- if (!alloca_aliases_len)
- free ((void *) aliases_len);
- goto retry;
- }
+ if (!alloca_aliases_len)
+ free ((void *) aliases_len);
+ goto retry;
}
if (!alloca_aliases_len)
@@ -39,6 +39,7 @@
#include <struct___timespec64.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
/* Extra time we wait if the socket is still receiving data. This
value is in milliseconds. Note that the other side is nscd on the
@@ -241,32 +242,64 @@ open_socket (request_type type, const char *key, size_t keylen)
return -1;
}
+#define MAPPED_DATABASE_INITIALIZER \
+ ((struct mapped_database) { .lock = PTHREAD_RWLOCK_INITIALIZER, })
-void
-__nscd_unmap (struct mapped_database *mapped)
-{
- assert (mapped->counter == 0);
- __munmap ((void *) mapped->head, mapped->mapsize);
- free (mapped);
-}
-
+static struct mapped_database __nscd_mapped_databases[lastdb] =
+ {
+ [pwddb] = MAPPED_DATABASE_INITIALIZER,
+ [grpdb] = MAPPED_DATABASE_INITIALIZER,
+ [hstdb] = MAPPED_DATABASE_INITIALIZER,
+ [servdb] = MAPPED_DATABASE_INITIALIZER,
+ [netgrdb] = MAPPED_DATABASE_INITIALIZER,
+ };
/* Try to get a file descriptor for the shared memory segment
- containing the database. */
-struct mapped_database *
-__nscd_get_mapping (request_type type, const char *key,
- struct mapped_database **mappedp)
+ containing the database. Lock the mapping, but mapsize might still
+ be 0 after return because there is no mapping. */
+static void
+__nscd_get_mapping (unsigned int db)
{
- struct mapped_database *result = NO_MAPPING;
-#ifdef SCM_RIGHTS
+ struct mapped_database *mapped = &__nscd_mapped_databases[db];
+
+ static const uint8_t requests[] =
+ {
+ [pwddb] = GETFDPW,
+ [grpdb] = GETFDGR,
+ [hstdb] = GETFDHST,
+ [servdb] = GETFDSERV,
+ [netgrdb] = GETFDNETGR,
+ };
+
+ static const char keys[][9] =
+ {
+ [pwddb] = "passwd",
+ [grpdb] = "group",
+ [hstdb] = "hosts",
+ [servdb] = "services",
+ [netgrdb] = "netgroup"
+ };
+ const char *key = keys[db];
const size_t keylen = strlen (key) + 1;
+
+ __libc_rwlock_wrlock (mapped->lock);
+
+ /* Remove any previously existing mapping. */
+ if (mapped->mapsize > 0)
+ {
+ __munmap ((void *) mapped->head, mapped->mapsize);
+ mapped->head = NULL;
+ mapped->data = NULL;
+ mapped->mapsize = 0;
+ }
+
int saved_errno = errno;
int mapfd = -1;
- char resdata[keylen];
+ char resdata[sizeof (keys[0])];
/* Open a socket and send the request. */
- int sock = open_socket (type, key, keylen);
+ int sock = open_socket (requests[db], key, keylen);
if (sock < 0)
goto out;
@@ -302,9 +335,6 @@ __nscd_get_mapping (request_type type, const char *key,
if (wait_on_socket (sock, 5 * 1000) <= 0)
goto out_close2;
-# ifndef MSG_CMSG_CLOEXEC
-# define MSG_CMSG_CLOEXEC 0
-# endif
ssize_t n = TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, MSG_CMSG_CLOEXEC));
if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
@@ -362,21 +392,11 @@ __nscd_get_mapping (request_type type, const char *key,
if (__glibc_unlikely (mapsize < size))
goto out_unmap;
- /* Allocate a record for the mapping. */
- struct mapped_database *newp = malloc (sizeof (*newp));
- if (newp == NULL)
- /* Ugh, after all we went through the memory allocation failed. */
- goto out_unmap;
-
- newp->head = mapping;
- newp->data = ((char *) mapping + head->header_size
+ mapped->head = mapping;
+ mapped->data = ((char *) mapping + head->header_size
+ roundup (head->module * sizeof (ref_t), ALIGN));
- newp->mapsize = size;
- newp->datasize = head->data_size;
- /* Set counter to 1 to show it is usable. */
- newp->counter = 1;
-
- result = newp;
+ mapped->mapsize = size;
+ mapped->datasize = head->data_size;
}
out_close:
@@ -385,56 +405,71 @@ __nscd_get_mapping (request_type type, const char *key,
__close (sock);
out:
__set_errno (saved_errno);
-#endif /* SCM_RIGHTS */
-
- struct mapped_database *oldval = *mappedp;
- *mappedp = result;
- if (oldval != NULL && atomic_fetch_add_relaxed (&oldval->counter, -1) == 1)
- __nscd_unmap (oldval);
-
- return result;
+ /* Downgrade lock to a read lock. The mapping may go away between
+ the two calls, but the caller has to handle the no-mapping case
+ anyway. */
+ __libc_rwlock_unlock (mapped->lock);
+ __libc_rwlock_rdlock (mapped->lock);
}
struct mapped_database *
-__nscd_get_map_ref (request_type type, const char *name,
- volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
+__nscd_get_map_ref (unsigned int db, int *gc_cyclep)
{
- struct mapped_database *cur = mapptr->mapped;
- if (cur == NO_MAPPING)
- return cur;
+ assert (db < lastdb);
+ struct mapped_database *mapped = &__nscd_mapped_databases[db];
+
+ __libc_rwlock_rdlock (mapped->lock);
+ if (mapped->mapsize == 0
+ || (mapped->head->nscd_certainly_running == 0
+ && mapped->head->timestamp + MAPPING_TIMEOUT < time_now ())
+ || mapped->head->data_size > mapped->datasize)
+ {
+ __libc_rwlock_unlock (mapped->lock);
+ __nscd_get_mapping (db);
+ }
- if (!__nscd_acquire_maplock (mapptr))
- return NO_MAPPING;
+ if (mapped->mapsize > 0)
+ {
+ *gc_cyclep = mapped->head->gc_cycle;
+ if ((*gc_cyclep & 1) == 0)
+ return mapped;
+ }
- cur = mapptr->mapped;
+ __libc_rwlock_unlock (mapped->lock);
+ return NULL;
+}
+
+bool
+__nscd_map_ref_retry_or_drop (struct mapped_database **mapped,
+ int *gc_cycle, int *nretries, int retval)
+{
+ if (*mapped == NULL)
+ return false;
- if (__glibc_likely (cur != NO_MAPPING))
+ int now_cycle = (*mapped)->head->gc_cycle;
+ if (__glibc_unlikely (now_cycle != *gc_cycle))
{
- /* If not mapped or timestamp not updated, request new map. */
- if (cur == NULL
- || (cur->head->nscd_certainly_running == 0
- && cur->head->timestamp + MAPPING_TIMEOUT < time_now ())
- || cur->head->data_size > cur->datasize)
- cur = __nscd_get_mapping (type, name,
- (struct mapped_database **) &mapptr->mapped);
-
- if (__glibc_likely (cur != NO_MAPPING))
+ /* When we come here this means there has been a GC cycle while we
+ were looking for the data. This means the data might have been
+ inconsistent. Retry if possible. */
+
+ *gc_cycle = now_cycle;
+ if ((*gc_cycle & 1) != 0 || ++*nretries == 5)
{
- if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
- 0))
- cur = NO_MAPPING;
- else
- atomic_fetch_add_relaxed (&cur->counter, 1);
+ __libc_rwlock_unlock ((*mapped)->lock);
+ *mapped = NULL;
+ return true;
}
- }
- mapptr->lock = 0;
+ if (retval != -1)
+ return true;
+ }
- return cur;
+ __libc_rwlock_unlock ((*mapped)->lock);
+ return false;
}
-
/* Using sizeof (hashentry) is not always correct to determine the size of
the data structure as found in the nscd cache. The program could be
a 64-bit process and nscd could be a 32-bit process. In this case
@@ -24,13 +24,10 @@
#include <not-cancel.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
-/* We use the same mapping as in nscd_getgr. */
-libc_locked_map_ptr (extern, __gr_map_handle) attribute_hidden;
-
-
int
__nscd_getgrouplist (const char *user, gid_t group, long int *size,
gid_t **groupsp, long int limit)
@@ -41,8 +38,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (grpdb, &gc_cycle);
retry:;
char *respdata = NULL;
@@ -50,10 +46,10 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
int sock = -1;
initgr_response_header initgr_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
struct datahead *found = __nscd_cache_search (INITGROUPS, user,
- userlen, mapped,
+ userlen, map,
sizeof initgr_resp);
if (found != NULL)
{
@@ -63,7 +59,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
/* Now check if we can trust initgr_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -158,22 +154,8 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
return retval;
}
@@ -22,27 +22,11 @@
#include <not-cancel.h>
#include "nscd-client.h"
+#include "nscd-dbtype.h"
#include "nscd_proto.h"
int __nss_not_use_nscd_netgroup;
-
-libc_locked_map_ptr (static, map_handle);
-/* Note that we only free the structure if necessary. The memory
- mapping is not removed since it is not visible to the malloc
- handling. */
-void
-__nscd_group_map_freemem (void)
-{
- if (map_handle.mapped != NO_MAPPING)
- {
- void *p = map_handle.mapped;
- map_handle.mapped = NO_MAPPING;
- free (p);
- }
-}
-
-
int
__nscd_setnetgrent (const char *group, struct __netgrent *datap)
{
@@ -52,18 +36,17 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
/* If the mapping is available, try to search there instead of
communicating with the nscd. */
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (netgrdb, &gc_cycle);
retry:;
char *respdata = NULL;
int retval = -1;
netgroup_response_header netgroup_resp;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
struct datahead *found = __nscd_cache_search (GETNETGRENT, group,
- group_len, mapped,
+ group_len, map,
sizeof netgroup_resp);
if (found != NULL)
{
@@ -71,7 +54,7 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
netgroup_resp = found->data[0].netgroupdata;
/* Now check if we can trust pw_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -141,22 +124,8 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
return retval;
}
@@ -206,25 +175,24 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
communicating with the nscd. */
int gc_cycle;
int nretries = 0;
- struct mapped_database *mapped;
- mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+ struct mapped_database *map = __nscd_get_map_ref (netgrdb, &gc_cycle);
retry:;
int retval = -1;
innetgroup_response_header innetgroup_resp;
int sock = -1;
- if (mapped != NO_MAPPING)
+ if (map != NULL)
{
struct datahead *found = __nscd_cache_search (INNETGR, key,
- key_len, mapped,
+ key_len, map,
sizeof innetgroup_resp);
if (found != NULL)
{
innetgroup_resp = found->data[0].innetgroupdata;
/* Now check if we can trust pw_resp fields. If GC is
in progress, it can contain anything. */
- if (mapped->head->gc_cycle != gc_cycle)
+ if (map->head->gc_cycle != gc_cycle)
{
retval = -2;
goto out;
@@ -265,22 +233,8 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
if (sock != -1)
__close_nocancel_nostatus (sock);
out:
- if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
- {
- /* When we come here this means there has been a GC cycle while we
- were looking for the data. This means the data might have been
- inconsistent. Retry if possible. */
- if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
- {
- /* nscd is just running gc now. Disable using the mapping. */
- if (atomic_fetch_add_relaxed (&mapped->counter, -1) == 1)
- __nscd_unmap (mapped);
- mapped = NO_MAPPING;
- }
-
- if (retval != -1)
- goto retry;
- }
+ if (__nscd_map_ref_retry_or_drop (&map, &gc_cycle, &nretries, retval))
+ goto retry;
if (! use_alloca)
free (key);