[v2,08/23] nscd: Fix data races in client retry counters (bug 33654)

Message ID 925c2367bd945a00033d4ddcd283e86d3b77c5b0.1774037705.git.fweimer@redhat.com (mailing list archive)
State Failed CI
Headers
Series NSS, nscd updates (for group merging and more) |

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
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed

Commit Message

Florian Weimer March 20, 2026, 8:42 p.m. UTC
  Store the skip counters inside struct mapped_database.  Introduce
helper functions __nscd_use_database, __nscd_defer_database,
__nscd_disable_database to access these counters.

Teach the NSS code to translate to nscd database indices when
calling these functions.

Remove the special check from getaddrinfo (in get_nscd_addresses)
that avoided fallback to an in-process operation for nscd protocol
errors.  Errors from service modules still produce a response
from the nscd, so this check only avoided fallback if there were
certain nscd communication errors.  Checking the skip counters
in this way for nscd usage was not reliable because another thread
might have updated the skip counters.
---
 nscd/nscd-client.h     |  7 +++++++
 nscd/nscd-dbtype.h     |  3 +++
 nscd/nscd_getai.c      |  9 +++------
 nscd/nscd_getgr_r.c    |  6 ++----
 nscd/nscd_gethst_r.c   | 10 ++++------
 nscd/nscd_getpw_r.c    |  6 ++----
 nscd/nscd_getserv_r.c  |  7 ++-----
 nscd/nscd_helper.c     | 41 +++++++++++++++++++++++++++++++++++++++++
 nscd/nscd_initgroups.c |  5 +++--
 nscd/nscd_netgroup.c   | 10 ++++------
 nscd/nscd_proto.h      | 15 +++++++++------
 nss/getXXbyYY_r.c      | 11 +++--------
 nss/getaddrinfo.c      | 19 +++++--------------
 nss/getnetgrent_r.c    | 15 +++------------
 nss/initgroups.c       | 10 ++--------
 nss/nss_database.c     | 23 ++++++++++++++++++++++-
 nss/nss_module.c       |  8 +++-----
 nss/nsswitch.c         |  5 -----
 nss/nsswitch.h         | 20 ++++++++------------
 19 files changed, 126 insertions(+), 104 deletions(-)
  

Comments

Carlos O'Donell March 24, 2026, 5:31 p.m. UTC | #1
On 3/20/26 4:42 PM, Florian Weimer wrote:
> Store the skip counters inside struct mapped_database.  Introduce
> helper functions __nscd_use_database, __nscd_defer_database,
> __nscd_disable_database to access these counters.
> 
> Teach the NSS code to translate to nscd database indices when
> calling these functions.
> 
> Remove the special check from getaddrinfo (in get_nscd_addresses)
> that avoided fallback to an in-process operation for nscd protocol
> errors.  Errors from service modules still produce a response
> from the nscd, so this check only avoided fallback if there were
> certain nscd communication errors.  Checking the skip counters
> in this way for nscd usage was not reliable because another thread
> might have updated the skip counters.

Great cleanup!

You can keep my RB if you accept my comment suggestion (or something
similar in your own words), otherwise we should discuss why you
used relaxed MO.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> ---
>   nscd/nscd-client.h     |  7 +++++++
>   nscd/nscd-dbtype.h     |  3 +++
>   nscd/nscd_getai.c      |  9 +++------
>   nscd/nscd_getgr_r.c    |  6 ++----
>   nscd/nscd_gethst_r.c   | 10 ++++------
>   nscd/nscd_getpw_r.c    |  6 ++----
>   nscd/nscd_getserv_r.c  |  7 ++-----
>   nscd/nscd_helper.c     | 41 +++++++++++++++++++++++++++++++++++++++++
>   nscd/nscd_initgroups.c |  5 +++--
>   nscd/nscd_netgroup.c   | 10 ++++------
>   nscd/nscd_proto.h      | 15 +++++++++------
>   nss/getXXbyYY_r.c      | 11 +++--------
>   nss/getaddrinfo.c      | 19 +++++--------------
>   nss/getnetgrent_r.c    | 15 +++------------
>   nss/initgroups.c       | 10 ++--------
>   nss/nss_database.c     | 23 ++++++++++++++++++++++-
>   nss/nss_module.c       |  8 +++-----
>   nss/nsswitch.c         |  5 -----
>   nss/nsswitch.h         | 20 ++++++++------------
>   19 files changed, 126 insertions(+), 104 deletions(-)
> 
> diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
> index aff303b757..dd7421c5d1 100644
> --- a/nscd/nscd-client.h
> +++ b/nscd/nscd-client.h
> @@ -354,6 +354,13 @@ struct mapped_database
>     size_t mapsize;		/* Zero means not in use.  */
>     size_t datasize;
>   
> +  /* This is set to 1 by nscd client functions to request not using nscd
> +     for a bit.  The main NSS code increments them until NSS_NSCD_RETRY
> +     is reached, at which point nscd is attempted again.  If the counter
> +     is 0 (the default), use of nscd is attempted.  If it is -1, nscd is
> +     never used for this database.  */
> +  int skip_counter;

OK. Interesting design.

> +
>     /* Use of the mapping must acquire a read lock.  If the mapping is
>        changed, acquire a write lock.  */
>     __libc_rwlock_define (, lock);
> diff --git a/nscd/nscd-dbtype.h b/nscd/nscd-dbtype.h
> index 7d553d020a..262c96fd78 100644
> --- a/nscd/nscd-dbtype.h
> +++ b/nscd/nscd-dbtype.h
> @@ -19,6 +19,9 @@
>   #ifndef NSCD_DBTYPE_H
>   #define NSCD_DBTYPE_H
>   
> +/* Note: If you add new database types here, you must add another call
> +   to __nscd_disable_database in __nss_configure_lookup.  */
> +
>   typedef enum
>   {
>     pwddb,
> diff --git a/nscd/nscd_getai.c b/nscd/nscd_getai.c
> index a2e5da6b1c..65970b59ee 100644
> --- a/nscd/nscd_getai.c
> +++ b/nscd/nscd_getai.c
> @@ -28,9 +28,6 @@
>   #include "nscd_proto.h"
>   
>   
> -/* Define in nscd_gethst_r.c.  */
> -extern int __nss_not_use_nscd_hosts;
> -
>   /* Defined in nscd_gethst_r.c.  */
>   extern int __nss_have_localdomain attribute_hidden;
>   
> @@ -44,7 +41,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
>   	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
>         if (__nss_have_localdomain > 0)
>   	{
> -	  __nss_not_use_nscd_hosts = 1;
> +	  __nscd_defer_database (hstdb);
>   	  return -1;
>   	}
>       }
> @@ -93,7 +90,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
>         if (sock == -1)
>   	{
>   	  /* nscd not running or wrong version.  */
> -	  __nss_not_use_nscd_hosts = 1;
> +	  __nscd_defer_database (hstdb);
>   	  goto out;
>   	}
>       }
> @@ -168,7 +165,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
>         if (__glibc_unlikely (ai_resp.found == -1))
>   	{
>   	  /* The daemon does not cache this database.  */
> -	  __nss_not_use_nscd_hosts = 1;
> +	  __nscd_defer_database (hstdb);
>   	  goto out_close;
>   	}
>   
> diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
> index 746b11d792..f77650b25f 100644
> --- a/nscd/nscd_getgr_r.c
> +++ b/nscd/nscd_getgr_r.c
> @@ -36,8 +36,6 @@
>   #include "nscd-dbtype.h"
>   #include "nscd_proto.h"
>   
> -int __nss_not_use_nscd_group;
> -
>   static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
>   			 struct group *resultbuf, char *buffer,
>   			 size_t buflen, struct group **result);
> @@ -117,7 +115,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
>   				 sizeof (gr_resp));
>         if (sock == -1)
>   	{
> -	  __nss_not_use_nscd_group = 1;
> +	  __nscd_defer_database (grpdb);
>   	  goto out;
>   	}
>       }
> @@ -128,7 +126,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
>     if (__glibc_unlikely (gr_resp.found == -1))
>       {
>         /* The daemon does not cache this database.  */
> -      __nss_not_use_nscd_group = 1;
> +      __nscd_defer_database (grpdb);
>         goto out_close;
>       }
>   
> diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
> index 15d747fc33..9bb52614b7 100644
> --- a/nscd/nscd_gethst_r.c
> +++ b/nscd/nscd_gethst_r.c
> @@ -27,8 +27,6 @@
>   #include "nscd-dbtype.h"
>   #include "nscd_proto.h"
>   
> -int __nss_not_use_nscd_hosts;
> -
>   static int nscd_gethst_r (const char *key, size_t keylen, request_type type,
>   			  struct hostent *resultbuf, char *buffer,
>   			  size_t buflen, struct hostent **result,
> @@ -81,7 +79,7 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
>   uint32_t
>   __nscd_get_nl_timestamp (void)
>   {
> -  if (__nss_not_use_nscd_hosts != 0)
> +  if (!__nscd_use_database (hstdb))
>       return 0;
>   
>     int gc_cycle;
> @@ -112,7 +110,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
>   	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
>         if (__nss_have_localdomain > 0)
>   	{
> -	  __nss_not_use_nscd_hosts = 1;
> +	  __nscd_defer_database (hstdb);
>   	  return -1;
>   	}
>       }
> @@ -186,7 +184,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
>   				 sizeof (hst_resp));
>         if (sock == -1)
>   	{
> -	  __nss_not_use_nscd_hosts = 1;
> +	  __nscd_defer_database (hstdb);
>   	  goto out;
>   	}
>       }
> @@ -197,7 +195,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
>     if (__glibc_unlikely (hst_resp.found == -1))
>       {
>         /* The daemon does not cache this database.  */
> -      __nss_not_use_nscd_hosts = 1;
> +      __nscd_defer_database (hstdb);
>         goto out_close;
>       }
>   
> diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
> index 938eac9c70..8ce1009fe0 100644
> --- a/nscd/nscd_getpw_r.c
> +++ b/nscd/nscd_getpw_r.c
> @@ -34,8 +34,6 @@
>   #include "nscd-dbtype.h"
>   #include "nscd_proto.h"
>   
> -int __nss_not_use_nscd_passwd;
> -
>   static int nscd_getpw_r (const char *key, size_t keylen, request_type type,
>   			 struct passwd *resultbuf, char *buffer,
>   			 size_t buflen, struct passwd **result);
> @@ -108,7 +106,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
>   				 sizeof (pw_resp));
>         if (sock == -1)
>   	{
> -	  __nss_not_use_nscd_passwd = 1;
> +	  __nscd_defer_database (pwddb);
>   	  goto out;
>   	}
>       }
> @@ -119,7 +117,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
>     if (__glibc_unlikely (pw_resp.found == -1))
>       {
>         /* The daemon does not cache this database.  */
> -      __nss_not_use_nscd_passwd = 1;
> +      __nscd_defer_database (pwddb);
>         goto out_close;
>       }
>   
> diff --git a/nscd/nscd_getserv_r.c b/nscd/nscd_getserv_r.c
> index 519078d8fe..8f0c010118 100644
> --- a/nscd/nscd_getserv_r.c
> +++ b/nscd/nscd_getserv_r.c
> @@ -27,9 +27,6 @@
>   #include "nscd_proto.h"
>   
>   
> -int __nss_not_use_nscd_services;
> -
> -
>   static int nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
>   			   request_type type, struct servent *resultbuf,
>   			   char *buf, size_t buflen, struct servent **result);
> @@ -162,7 +159,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
>   				 sizeof (serv_resp));
>         if (sock == -1)
>   	{
> -	  __nss_not_use_nscd_services = 1;
> +	  __nscd_defer_database (servdb);
>   	  goto out;
>   	}
>       }
> @@ -173,7 +170,7 @@ nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
>     if (__glibc_unlikely (serv_resp.found == -1))
>       {
>         /* The daemon does not cache this database.  */
> -      __nss_not_use_nscd_services = 1;
> +      __nscd_defer_database (servdb);
>         goto out_close;
>       }
>   
> diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
> index d3bfc208ad..2a60c3572e 100644
> --- a/nscd/nscd_helper.c
> +++ b/nscd/nscd_helper.c
> @@ -40,6 +40,7 @@
>   
>   #include "nscd-client.h"
>   #include "nscd-dbtype.h"
> +#include "nscd_proto.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
> @@ -413,6 +414,46 @@ __nscd_get_mapping (unsigned int db)
>     __libc_rwlock_rdlock (mapped->lock);
>   }
>   
> +bool
> +__nscd_use_database (unsigned int db)

This should have a comment explaining what is going on here from a P&C
perspective.

Suggestion:

/* Determine if nscd should be used with the database at index DB.
    There are three possible scenarios:
    * nscd should NOT be used - skip_counter == -1
    * nscd should ALWAYS be usd - skip_counter == 0
    * nscd use should be DEFERRED - skip_counter == [1, NSS_NSCD_RETRY)
    In the last instance we either atomically increment skip_counter
    or atomically reset it to zero.  Relaxed MO is fine because we don't
    care about seeing the effect of any other writes; this is just a
    counter.  */

> +{
> +  assert (db < lastdb);
> +
> +  int *pcounter = &__nscd_mapped_databases[db].skip_counter;
> +  int counter;
> +  while (true)
> +    {
> +      counter = atomic_load_relaxed (pcounter);
> +      if (counter <= 0)
> +        break;
> +      else if (counter > 0)
> +        {
> +          int old_counter = counter;
> +          ++counter;
> +          if (counter > NSS_NSCD_RETRY)
> +            counter = 0;
> +          if (atomic_compare_exchange_weak_relaxed
> +	      (pcounter, &old_counter, counter))
> +            break;
> +        }
> +    }
> +  return counter == 0;
> +}
> +
> +void
> +__nscd_defer_database (unsigned int db)
> +{
> +  assert (db < lastdb);
> +  atomic_store_relaxed (&__nscd_mapped_databases[db].skip_counter, 1);
> +}
> +
> +void
> +__nscd_disable_database (unsigned int db)
> +{
> +  assert (db < lastdb);
> +  atomic_store_relaxed (&__nscd_mapped_databases[db].skip_counter, -1);
> +}
> +
>   struct mapped_database *
>   __nscd_get_map_ref (unsigned int db, int *gc_cyclep)
>   {
> diff --git a/nscd/nscd_initgroups.c b/nscd/nscd_initgroups.c
> index f1e821b8d0..acc799cf55 100644
> --- a/nscd/nscd_initgroups.c
> +++ b/nscd/nscd_initgroups.c
> @@ -16,6 +16,7 @@
>      <https://www.gnu.org/licenses/>.  */
>   
>   #include <assert.h>
> +#include <atomic.h>
>   #include <errno.h>
>   #include <grp.h>
>   #include <stdlib.h>
> @@ -79,7 +80,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
>         if (sock == -1)
>   	{
>   	  /* nscd not running or wrong version.  */
> -	  __nss_not_use_nscd_group = 1;
> +	  __nscd_disable_database (grpdb);
>   	  goto out;
>   	}
>       }
> @@ -128,7 +129,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
>         if (__glibc_unlikely (initgr_resp.found == -1))
>   	{
>   	  /* The daemon does not cache this database.  */
> -	  __nss_not_use_nscd_group = 1;
> +	  __nscd_disable_database (grpdb);
>   	  goto out_close;
>   	}
>   
> diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
> index 53649adc82..22c7cdcc9c 100644
> --- a/nscd/nscd_netgroup.c
> +++ b/nscd/nscd_netgroup.c
> @@ -25,8 +25,6 @@
>   #include "nscd-dbtype.h"
>   #include "nscd_proto.h"
>   
> -int __nss_not_use_nscd_netgroup;
> -
>   int
>   __nscd_setnetgrent (const char *group, struct __netgrent *datap)
>   {
> @@ -70,7 +68,7 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
>         if (sock == -1)
>   	{
>   	  /* nscd not running or wrong version.  */
> -	  __nss_not_use_nscd_netgroup = 1;
> +	  __nscd_defer_database (netgrdb);
>   	  goto out;
>   	}
>       }
> @@ -110,7 +108,7 @@ __nscd_setnetgrent (const char *group, struct __netgrent *datap)
>         if (__glibc_unlikely (netgroup_resp.found == -1))
>   	{
>   	  /* The daemon does not cache this database.  */
> -	  __nss_not_use_nscd_netgroup = 1;
> +	  __nscd_defer_database (netgrdb);
>   	  goto out_close;
>   	}
>   
> @@ -207,7 +205,7 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
>     if (sock == -1)
>       {
>         /* nscd not running or wrong version.  */
> -      __nss_not_use_nscd_netgroup = 1;
> +      __nscd_defer_database (netgrdb);
>         goto out;
>       }
>   
> @@ -219,7 +217,7 @@ __nscd_innetgr (const char *netgroup, const char *host, const char *user,
>         if (__glibc_unlikely (innetgroup_resp.found == -1))
>   	{
>   	  /* The daemon does not cache this database.  */
> -	  __nss_not_use_nscd_netgroup = 1;
> +	  __nscd_defer_database (netgrdb);
>   	  goto out_close;
>   	}
>   
> diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
> index 917324aebd..6c4a318bc0 100644
> --- a/nscd/nscd_proto.h
> +++ b/nscd/nscd_proto.h
> @@ -28,13 +28,16 @@
>   /* Type needed in the interfaces.  */
>   struct nscd_ai_result;
>   
> +/* Update the internal per-database counters if necessary and return
> +   true if nscd should be used for DB for the next lookup.
> +   DB is defined in <nscd-dbtype.h>.  */
> +_Bool __nscd_use_database (unsigned int db) attribute_hidden;
>   
> -/* Variables for communication between NSCD handler functions and NSS.  */
> -extern int __nss_not_use_nscd_passwd attribute_hidden;
> -extern int __nss_not_use_nscd_group attribute_hidden;
> -extern int __nss_not_use_nscd_hosts attribute_hidden;
> -extern int __nss_not_use_nscd_services attribute_hidden;
> -extern int __nss_not_use_nscd_netgroup attribute_hidden;
> +/* Stop using DB until NSS_NSCD_RETRY lookups have been performed.  */
> +void __nscd_defer_database (unsigned int db) attribute_hidden;
> +
> +/* Completely stop using DB.  */
> +void __nscd_disable_database (unsigned int db) attribute_hidden;
>   
>   extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
>   			      char *buffer, size_t buflen,
> diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
> index 21e82886b0..9527b6722d 100644
> --- a/nss/getXXbyYY_r.c
> +++ b/nss/getXXbyYY_r.c
> @@ -23,6 +23,7 @@
>   #include "sysdep.h"
>   #ifdef USE_NSCD
>   # include <nscd/nscd_proto.h>
> +# include <nscd/nscd-dbtype.h>
>   #endif
>   #ifdef NEED__RES
>   # include <resolv/resolv_context.h>
> @@ -78,9 +79,7 @@
>   # define NSCD_NAME ADD_NSCD (REENTRANT_NAME)
>   # define ADD_NSCD(name) ADD_NSCD1 (name)
>   # define ADD_NSCD1(name) __nscd_##name
> -# define NOT_USENSCD_NAME ADD_NOT_NSCDUSE (DATABASE_NAME)
> -# define ADD_NOT_NSCDUSE(name) ADD_NOT_NSCDUSE1 (name)
> -# define ADD_NOT_NSCDUSE1(name) __nss_not_use_nscd_##name
> +# define NSS_DATABASE_INDEX CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)
>   # define CONCAT2(arg1, arg2) CONCAT2_2 (arg1, arg2)
>   # define CONCAT2_2(arg1, arg2) arg1##arg2
>   #endif
> @@ -235,11 +234,7 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
>   #endif
>   
>   #ifdef USE_NSCD
> -  if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
> -    NOT_USENSCD_NAME = 0;
> -
> -  if (!NOT_USENSCD_NAME
> -      && !__nss_database_custom[CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)])
> +  if (__nscd_use_database (NSS_DATABASE_INDEX))
>       {
>         nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
>   			       H_ERRNO_VAR);
> diff --git a/nss/getaddrinfo.c b/nss/getaddrinfo.c
> index 4f6ac3358a..233801052e 100644
> --- a/nss/getaddrinfo.c
> +++ b/nss/getaddrinfo.c
> @@ -81,7 +81,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>   #include <libc-lock.h>
>   #include <not-cancel.h>
>   #include <nscd/nscd-client.h>
> -#include <nscd/nscd_proto.h>
> +#ifdef USE_NSCD
> +# include <nscd/nscd_proto.h>
> +# include <nscd/nscd-dbtype.h>
> +#endif
>   #include <scratch_buffer.h>
>   #include <inet/net-internal.h>
>   
> @@ -485,13 +488,9 @@ static int
>   get_nscd_addresses (const char *name, const struct addrinfo *req,
>   		    struct gaih_result *res)
>   {
> -  if (__nss_not_use_nscd_hosts > 0
> -      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
> -    __nss_not_use_nscd_hosts = 0;
> -
>     res->at = NULL;
>   
> -  if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
> +  if (!__nscd_use_database (hstdb))
>       return 0;
>   
>     /* Try to use nscd.  */
> @@ -508,14 +507,6 @@ get_nscd_addresses (const char *name, const struct addrinfo *req,
>   	    return -EAI_AGAIN;
>   	  return -EAI_NONAME;
>   	}
> -      if (__nss_not_use_nscd_hosts == 0)
> -	{
> -	  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
> -	    return -EAI_MEMORY;
> -	  if (h_errno == TRY_AGAIN)
> -	    return -EAI_AGAIN;
> -	  return -EAI_SYSTEM;
> -	}
>         return 0;
>       }
>   
> diff --git a/nss/getnetgrent_r.c b/nss/getnetgrent_r.c
> index d391e2ac66..569087d3b0 100644
> --- a/nss/getnetgrent_r.c
> +++ b/nss/getnetgrent_r.c
> @@ -27,6 +27,7 @@
>   #include "nsswitch.h"
>   #include <sysdep.h>
>   #include <nscd/nscd_proto.h>
> +#include <nscd/nscd-dbtype.h>
>   
>   
>   /* Protect above variable against multiple uses at the same time.  */
> @@ -150,12 +151,7 @@ static int
>   nscd_setnetgrent (const char *group)
>   {
>   #ifdef USE_NSCD
> -  if (__nss_not_use_nscd_netgroup > 0
> -      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
> -    __nss_not_use_nscd_netgroup = 0;
> -
> -  if (!__nss_not_use_nscd_netgroup
> -      && !__nss_database_custom[NSS_DBSIDX_netgroup])
> +  if (__nscd_use_database (netgrdb))
>       return __nscd_setnetgrent (group, &dataset);
>   #endif
>     return -1;
> @@ -357,12 +353,7 @@ innetgr (const char *netgroup, const char *host, const char *user,
>   	 const char *domain)
>   {
>   #ifdef USE_NSCD
> -  if (__nss_not_use_nscd_netgroup > 0
> -      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
> -    __nss_not_use_nscd_netgroup = 0;
> -
> -  if (!__nss_not_use_nscd_netgroup
> -      && !__nss_database_custom[NSS_DBSIDX_netgroup])
> +  if (__nscd_use_database (netgrdb))
>       {
>         int result = __nscd_innetgr (netgroup, host, user, domain);
>         if (result >= 0)
> diff --git a/nss/initgroups.c b/nss/initgroups.c
> index f4106226ec..95691542fb 100644
> --- a/nss/initgroups.c
> +++ b/nss/initgroups.c
> @@ -29,6 +29,7 @@
>   #include <config.h>
>   
>   #include "../nscd/nscd-client.h"
> +#include "../nscd/nscd-dbtype.h"
>   #include "../nscd/nscd_proto.h"
>   
>   /* Type of the lookup function.  */
> @@ -47,18 +48,11 @@ internal_getgrouplist (const char *user, gid_t group, long int *size,
>   		       gid_t **groupsp, long int limit)
>   {
>   #ifdef USE_NSCD
> -  if (__nss_not_use_nscd_group > 0
> -      && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
> -    __nss_not_use_nscd_group = 0;
> -  if (!__nss_not_use_nscd_group
> -      && !__nss_database_custom[NSS_DBSIDX_group])
> +  if (__nscd_use_database (grpdb))
>       {
>         int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
>         if (n >= 0)
>   	return n;
> -
> -      /* nscd is not usable.  */
> -      __nss_not_use_nscd_group = 1;
>       }
>   #endif
>   
> diff --git a/nss/nss_database.c b/nss/nss_database.c
> index 076d5a63fe..5861454467 100644
> --- a/nss/nss_database.c
> +++ b/nss/nss_database.c
> @@ -28,6 +28,10 @@
>   #include <netdb.h>
>   #include <stdio_ext.h>
>   #include <string.h>
> +#ifdef USE_NSCD
> +# include <nscd/nscd_proto.h>
> +# include <nscd/nscd-dbtype.h>
> +#endif
>   
>   struct nss_database_state
>   {
> @@ -256,7 +260,24 @@ __nss_configure_lookup (const char *dbname, const char *service_line)
>     local->data.services[db] = result;
>   
>   #ifdef USE_NSCD
> -  __nss_database_custom[db] = true;
> +  switch (db)
> +    {
> +    case nss_database_group:
> +      __nscd_disable_database (grpdb);
> +      break;
> +    case nss_database_hosts:
> +      __nscd_disable_database (hstdb);
> +      break;
> +    case nss_database_netgroup:
> +      __nscd_disable_database (netgrdb);
> +      break;
> +    case nss_database_passwd:
> +      __nscd_disable_database (pwddb);
> +      break;
> +    case nss_database_services:
> +      __nscd_disable_database (servdb);
> +      break;
> +    }
>   #endif
>   
>     return 0;
> diff --git a/nss/nss_module.c b/nss/nss_module.c
> index cb8fde38b4..fbbc6a266a 100644
> --- a/nss/nss_module.c
> +++ b/nss/nss_module.c
> @@ -33,6 +33,7 @@
>   #include <stdlib.h>
>   #include <string.h>
>   #include <pointer_guard.h>
> +#include <nscd/nscd-dbtype.h>
>   
>   /* Suffix after .so of NSS service modules.  This is a bit of magic,
>      but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we
> @@ -395,11 +396,8 @@ __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
>     cb1 (netgrdb, &netgr_traced_file.file);
>   
>     /* Disable all uses of NSCD.  */
> -  __nss_not_use_nscd_passwd = -1;
> -  __nss_not_use_nscd_group = -1;
> -  __nss_not_use_nscd_hosts = -1;
> -  __nss_not_use_nscd_services = -1;
> -  __nss_not_use_nscd_netgroup = -1;
> +  for (int i = 0; i < lastdb; ++i)
> +    __nscd_disable_database (i);
>   }
>   #endif
>   
> diff --git a/nss/nsswitch.c b/nss/nsswitch.c
> index 97cedff826..ff69ede948 100644
> --- a/nss/nsswitch.c
> +++ b/nss/nsswitch.c
> @@ -42,11 +42,6 @@
>   #include <sysdep.h>
>   #include <config.h>
>   
> -#ifdef USE_NSCD
> -/* Flags whether custom rules for database is set.  */
> -bool __nss_database_custom[NSS_DBSIDX_max];
> -#endif
> -
>   /*__libc_lock_define_initialized (static, lock)*/
>   
>   /* -1 == not found
> diff --git a/nss/nsswitch.h b/nss/nsswitch.h
> index 1b18794fee..b7cfb4f602 100644
> --- a/nss/nsswitch.h
> +++ b/nss/nsswitch.h
> @@ -62,19 +62,15 @@ typedef struct
>   /* To access the action based on the status value use this macro.  */
>   #define nss_next_action(ni, status) nss_action_get (ni, status)
>   
> -
>   #ifdef USE_NSCD
> -/* Indices into DATABASES in nsswitch.c and __NSS_DATABASE_CUSTOM.  */
> -enum
> -  {
> -# define DEFINE_DATABASE(arg) NSS_DBSIDX_##arg,
> -# include "databases.def"
> -# undef DEFINE_DATABASE
> -    NSS_DBSIDX_max
> -  };
> -
> -/* Flags whether custom rules for database is set.  */
> -extern bool __nss_database_custom[NSS_DBSIDX_max] attribute_hidden;
> +/* Compile-time mapping of NSS databases to nscd dbtype values from
> +   <nscd/nscd-dbtype.h>.  This is used to update nscd usage counters
> +   in nscd-enabled builds if getXbyY_r routines are called.  */
> +# define NSS_DBSIDX_group grpdb
> +# define NSS_DBSIDX_hosts hstdb
> +# define NSS_DBSIDX_netgroup netgrdb
> +# define NSS_DBSIDX_passwd pwddb
> +# define NSS_DBSIDX_services servdb
>   #endif
>   
>   /* Warning for NSS functions, which don't require dlopen if glibc
  
Carlos O'Donell March 24, 2026, 5:51 p.m. UTC | #2
On 3/20/26 4:42 PM, Florian Weimer wrote:
> Store the skip counters inside struct mapped_database.  Introduce
> helper functions __nscd_use_database, __nscd_defer_database,
> __nscd_disable_database to access these counters.
> 
> Teach the NSS code to translate to nscd database indices when
> calling these functions.
> 
> Remove the special check from getaddrinfo (in get_nscd_addresses)
> that avoided fallback to an in-process operation for nscd protocol
> errors.  Errors from service modules still produce a response
> from the nscd, so this check only avoided fallback if there were
> certain nscd communication errors.  Checking the skip counters
> in this way for nscd usage was not reliable because another thread
> might have updated the skip counters.
> ---

A quick P&C note from my review with Claude Code v2.1.81 (Sonnet 4.5):

> +bool
> +__nscd_use_database (unsigned int db)
> +{
> +  assert (db < lastdb);
> +
> +  int *pcounter = &__nscd_mapped_databases[db].skip_counter;
> +  int counter;
> +  while (true)
> +    {
> +      counter = atomic_load_relaxed (pcounter);
> +      if (counter <= 0)
> +        break;
> +      else if (counter > 0)
> +        {
> +          int old_counter = counter;
> +          ++counter;
> +          if (counter > NSS_NSCD_RETRY)
> +            counter = 0;
> +          if (atomic_compare_exchange_weak_relaxed
> +	      (pcounter, &old_counter, counter))

The weakened CAS has performance benefits.

It is also possible for this to fail spuriously.

That doesn't change my review, but I thought it was worth mentioning.

We might increment the counter without any use of the database.

We have an outer loop to treat this as-if another thread had changed the value.

It just may not yield the expected number of calls, which should be fine.


> +            break;
> +        }
> +    }
> +  return counter == 0;
> +}
  

Patch

diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index aff303b757..dd7421c5d1 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -354,6 +354,13 @@  struct mapped_database
   size_t mapsize;		/* Zero means not in use.  */
   size_t datasize;
 
+  /* This is set to 1 by nscd client functions to request not using nscd
+     for a bit.  The main NSS code increments them until NSS_NSCD_RETRY
+     is reached, at which point nscd is attempted again.  If the counter
+     is 0 (the default), use of nscd is attempted.  If it is -1, nscd is
+     never used for this database.  */
+  int skip_counter;
+
   /* Use of the mapping must acquire a read lock.  If the mapping is
      changed, acquire a write lock.  */
   __libc_rwlock_define (, lock);
diff --git a/nscd/nscd-dbtype.h b/nscd/nscd-dbtype.h
index 7d553d020a..262c96fd78 100644
--- a/nscd/nscd-dbtype.h
+++ b/nscd/nscd-dbtype.h
@@ -19,6 +19,9 @@ 
 #ifndef NSCD_DBTYPE_H
 #define NSCD_DBTYPE_H
 
+/* Note: If you add new database types here, you must add another call
+   to __nscd_disable_database in __nss_configure_lookup.  */
+
 typedef enum
 {
   pwddb,
diff --git a/nscd/nscd_getai.c b/nscd/nscd_getai.c
index a2e5da6b1c..65970b59ee 100644
--- a/nscd/nscd_getai.c
+++ b/nscd/nscd_getai.c
@@ -28,9 +28,6 @@ 
 #include "nscd_proto.h"
 
 
-/* Define in nscd_gethst_r.c.  */
-extern int __nss_not_use_nscd_hosts;
-
 /* Defined in nscd_gethst_r.c.  */
 extern int __nss_have_localdomain attribute_hidden;
 
@@ -44,7 +41,7 @@  __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
 	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
       if (__nss_have_localdomain > 0)
 	{
-	  __nss_not_use_nscd_hosts = 1;
+	  __nscd_defer_database (hstdb);
 	  return -1;
 	}
     }
@@ -93,7 +90,7 @@  __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
       if (sock == -1)
 	{
 	  /* nscd not running or wrong version.  */
-	  __nss_not_use_nscd_hosts = 1;
+	  __nscd_defer_database (hstdb);
 	  goto out;
 	}
     }
@@ -168,7 +165,7 @@  __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
       if (__glibc_unlikely (ai_resp.found == -1))
 	{
 	  /* The daemon does not cache this database.  */
-	  __nss_not_use_nscd_hosts = 1;
+	  __nscd_defer_database (hstdb);
 	  goto out_close;
 	}
 
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
index 746b11d792..f77650b25f 100644
--- a/nscd/nscd_getgr_r.c
+++ b/nscd/nscd_getgr_r.c
@@ -36,8 +36,6 @@ 
 #include "nscd-dbtype.h"
 #include "nscd_proto.h"
 
-int __nss_not_use_nscd_group;
-
 static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
 			 struct group *resultbuf, char *buffer,
 			 size_t buflen, struct group **result);
@@ -117,7 +115,7 @@  nscd_getgr_r (const char *key, size_t keylen, request_type type,
 				 sizeof (gr_resp));
       if (sock == -1)
 	{
-	  __nss_not_use_nscd_group = 1;
+	  __nscd_defer_database (grpdb);
 	  goto out;
 	}
     }
@@ -128,7 +126,7 @@  nscd_getgr_r (const char *key, size_t keylen, request_type type,
   if (__glibc_unlikely (gr_resp.found == -1))
     {
       /* The daemon does not cache this database.  */
-      __nss_not_use_nscd_group = 1;
+      __nscd_defer_database (grpdb);
       goto out_close;
     }
 
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index 15d747fc33..9bb52614b7 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -27,8 +27,6 @@ 
 #include "nscd-dbtype.h"
 #include "nscd_proto.h"
 
-int __nss_not_use_nscd_hosts;
-
 static int nscd_gethst_r (const char *key, size_t keylen, request_type type,
 			  struct hostent *resultbuf, char *buffer,
 			  size_t buflen, struct hostent **result,
@@ -81,7 +79,7 @@  __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
 uint32_t
 __nscd_get_nl_timestamp (void)
 {
-  if (__nss_not_use_nscd_hosts != 0)
+  if (!__nscd_use_database (hstdb))
     return 0;
 
   int gc_cycle;
@@ -112,7 +110,7 @@  nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	__nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
       if (__nss_have_localdomain > 0)
 	{
-	  __nss_not_use_nscd_hosts = 1;
+	  __nscd_defer_database (hstdb);
 	  return -1;
 	}
     }
@@ -186,7 +184,7 @@  nscd_gethst_r (const char *key, size_t keylen, request_type type,
 				 sizeof (hst_resp));
       if (sock == -1)
 	{
-	  __nss_not_use_nscd_hosts = 1;
+	  __nscd_defer_database (hstdb);
 	  goto out;
 	}
     }
@@ -197,7 +195,7 @@  nscd_gethst_r (const char *key, size_t keylen, request_type type,
   if (__glibc_unlikely (hst_resp.found == -1))
     {
       /* The daemon does not cache this database.  */
-      __nss_not_use_nscd_hosts = 1;
+      __nscd_defer_database (hstdb);
       goto out_close;
     }
 
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
index 938eac9c70..8ce1009fe0 100644
--- a/nscd/nscd_getpw_r.c
+++ b/nscd/nscd_getpw_r.c
@@ -34,8 +34,6 @@ 
 #include "nscd-dbtype.h"
 #include "nscd_proto.h"
 
-int __nss_not_use_nscd_passwd;
-
 static int nscd_getpw_r (const char *key, size_t keylen, request_type type,
 			 struct passwd *resultbuf, char *buffer,
 			 size_t buflen, struct passwd **result);
@@ -108,7 +106,7 @@  nscd_getpw_r (const char *key, size_t keylen, request_type type,
 				 sizeof (pw_resp));
       if (sock == -1)
 	{
-	  __nss_not_use_nscd_passwd = 1;
+	  __nscd_defer_database (pwddb);
 	  goto out;
 	}
     }
@@ -119,7 +117,7 @@  nscd_getpw_r (const char *key, size_t keylen, request_type type,
   if (__glibc_unlikely (pw_resp.found == -1))
     {
       /* The daemon does not cache this database.  */
-      __nss_not_use_nscd_passwd = 1;
+      __nscd_defer_database (pwddb);
       goto out_close;
     }
 
diff --git a/nscd/nscd_getserv_r.c b/nscd/nscd_getserv_r.c
index 519078d8fe..8f0c010118 100644
--- a/nscd/nscd_getserv_r.c
+++ b/nscd/nscd_getserv_r.c
@@ -27,9 +27,6 @@ 
 #include "nscd_proto.h"
 
 
-int __nss_not_use_nscd_services;
-
-
 static int nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
 			   request_type type, struct servent *resultbuf,
 			   char *buf, size_t buflen, struct servent **result);
@@ -162,7 +159,7 @@  nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
 				 sizeof (serv_resp));
       if (sock == -1)
 	{
-	  __nss_not_use_nscd_services = 1;
+	  __nscd_defer_database (servdb);
 	  goto out;
 	}
     }
@@ -173,7 +170,7 @@  nscd_getserv_r (const char *crit, size_t critlen, const char *proto,
   if (__glibc_unlikely (serv_resp.found == -1))
     {
       /* The daemon does not cache this database.  */
-      __nss_not_use_nscd_services = 1;
+      __nscd_defer_database (servdb);
       goto out_close;
     }
 
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index d3bfc208ad..2a60c3572e 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -40,6 +40,7 @@ 
 
 #include "nscd-client.h"
 #include "nscd-dbtype.h"
+#include "nscd_proto.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
@@ -413,6 +414,46 @@  __nscd_get_mapping (unsigned int db)
   __libc_rwlock_rdlock (mapped->lock);
 }
 
+bool
+__nscd_use_database (unsigned int db)
+{
+  assert (db < lastdb);
+
+  int *pcounter = &__nscd_mapped_databases[db].skip_counter;
+  int counter;
+  while (true)
+    {
+      counter = atomic_load_relaxed (pcounter);
+      if (counter <= 0)
+        break;
+      else if (counter > 0)
+        {
+          int old_counter = counter;
+          ++counter;
+          if (counter > NSS_NSCD_RETRY)
+            counter = 0;
+          if (atomic_compare_exchange_weak_relaxed
+	      (pcounter, &old_counter, counter))
+            break;
+        }
+    }
+  return counter == 0;
+}
+
+void
+__nscd_defer_database (unsigned int db)
+{
+  assert (db < lastdb);
+  atomic_store_relaxed (&__nscd_mapped_databases[db].skip_counter, 1);
+}
+
+void
+__nscd_disable_database (unsigned int db)
+{
+  assert (db < lastdb);
+  atomic_store_relaxed (&__nscd_mapped_databases[db].skip_counter, -1);
+}
+
 struct mapped_database *
 __nscd_get_map_ref (unsigned int db, int *gc_cyclep)
 {
diff --git a/nscd/nscd_initgroups.c b/nscd/nscd_initgroups.c
index f1e821b8d0..acc799cf55 100644
--- a/nscd/nscd_initgroups.c
+++ b/nscd/nscd_initgroups.c
@@ -16,6 +16,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
+#include <atomic.h>
 #include <errno.h>
 #include <grp.h>
 #include <stdlib.h>
@@ -79,7 +80,7 @@  __nscd_getgrouplist (const char *user, gid_t group, long int *size,
       if (sock == -1)
 	{
 	  /* nscd not running or wrong version.  */
-	  __nss_not_use_nscd_group = 1;
+	  __nscd_disable_database (grpdb);
 	  goto out;
 	}
     }
@@ -128,7 +129,7 @@  __nscd_getgrouplist (const char *user, gid_t group, long int *size,
       if (__glibc_unlikely (initgr_resp.found == -1))
 	{
 	  /* The daemon does not cache this database.  */
-	  __nss_not_use_nscd_group = 1;
+	  __nscd_disable_database (grpdb);
 	  goto out_close;
 	}
 
diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
index 53649adc82..22c7cdcc9c 100644
--- a/nscd/nscd_netgroup.c
+++ b/nscd/nscd_netgroup.c
@@ -25,8 +25,6 @@ 
 #include "nscd-dbtype.h"
 #include "nscd_proto.h"
 
-int __nss_not_use_nscd_netgroup;
-
 int
 __nscd_setnetgrent (const char *group, struct __netgrent *datap)
 {
@@ -70,7 +68,7 @@  __nscd_setnetgrent (const char *group, struct __netgrent *datap)
       if (sock == -1)
 	{
 	  /* nscd not running or wrong version.  */
-	  __nss_not_use_nscd_netgroup = 1;
+	  __nscd_defer_database (netgrdb);
 	  goto out;
 	}
     }
@@ -110,7 +108,7 @@  __nscd_setnetgrent (const char *group, struct __netgrent *datap)
       if (__glibc_unlikely (netgroup_resp.found == -1))
 	{
 	  /* The daemon does not cache this database.  */
-	  __nss_not_use_nscd_netgroup = 1;
+	  __nscd_defer_database (netgrdb);
 	  goto out_close;
 	}
 
@@ -207,7 +205,7 @@  __nscd_innetgr (const char *netgroup, const char *host, const char *user,
   if (sock == -1)
     {
       /* nscd not running or wrong version.  */
-      __nss_not_use_nscd_netgroup = 1;
+      __nscd_defer_database (netgrdb);
       goto out;
     }
 
@@ -219,7 +217,7 @@  __nscd_innetgr (const char *netgroup, const char *host, const char *user,
       if (__glibc_unlikely (innetgroup_resp.found == -1))
 	{
 	  /* The daemon does not cache this database.  */
-	  __nss_not_use_nscd_netgroup = 1;
+	  __nscd_defer_database (netgrdb);
 	  goto out_close;
 	}
 
diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
index 917324aebd..6c4a318bc0 100644
--- a/nscd/nscd_proto.h
+++ b/nscd/nscd_proto.h
@@ -28,13 +28,16 @@ 
 /* Type needed in the interfaces.  */
 struct nscd_ai_result;
 
+/* Update the internal per-database counters if necessary and return
+   true if nscd should be used for DB for the next lookup.
+   DB is defined in <nscd-dbtype.h>.  */
+_Bool __nscd_use_database (unsigned int db) attribute_hidden;
 
-/* Variables for communication between NSCD handler functions and NSS.  */
-extern int __nss_not_use_nscd_passwd attribute_hidden;
-extern int __nss_not_use_nscd_group attribute_hidden;
-extern int __nss_not_use_nscd_hosts attribute_hidden;
-extern int __nss_not_use_nscd_services attribute_hidden;
-extern int __nss_not_use_nscd_netgroup attribute_hidden;
+/* Stop using DB until NSS_NSCD_RETRY lookups have been performed.  */
+void __nscd_defer_database (unsigned int db) attribute_hidden;
+
+/* Completely stop using DB.  */
+void __nscd_disable_database (unsigned int db) attribute_hidden;
 
 extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
 			      char *buffer, size_t buflen,
diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
index 21e82886b0..9527b6722d 100644
--- a/nss/getXXbyYY_r.c
+++ b/nss/getXXbyYY_r.c
@@ -23,6 +23,7 @@ 
 #include "sysdep.h"
 #ifdef USE_NSCD
 # include <nscd/nscd_proto.h>
+# include <nscd/nscd-dbtype.h>
 #endif
 #ifdef NEED__RES
 # include <resolv/resolv_context.h>
@@ -78,9 +79,7 @@ 
 # define NSCD_NAME ADD_NSCD (REENTRANT_NAME)
 # define ADD_NSCD(name) ADD_NSCD1 (name)
 # define ADD_NSCD1(name) __nscd_##name
-# define NOT_USENSCD_NAME ADD_NOT_NSCDUSE (DATABASE_NAME)
-# define ADD_NOT_NSCDUSE(name) ADD_NOT_NSCDUSE1 (name)
-# define ADD_NOT_NSCDUSE1(name) __nss_not_use_nscd_##name
+# define NSS_DATABASE_INDEX CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)
 # define CONCAT2(arg1, arg2) CONCAT2_2 (arg1, arg2)
 # define CONCAT2_2(arg1, arg2) arg1##arg2
 #endif
@@ -235,11 +234,7 @@  INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
 #endif
 
 #ifdef USE_NSCD
-  if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
-    NOT_USENSCD_NAME = 0;
-
-  if (!NOT_USENSCD_NAME
-      && !__nss_database_custom[CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)])
+  if (__nscd_use_database (NSS_DATABASE_INDEX))
     {
       nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
 			       H_ERRNO_VAR);
diff --git a/nss/getaddrinfo.c b/nss/getaddrinfo.c
index 4f6ac3358a..233801052e 100644
--- a/nss/getaddrinfo.c
+++ b/nss/getaddrinfo.c
@@ -81,7 +81,10 @@  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <libc-lock.h>
 #include <not-cancel.h>
 #include <nscd/nscd-client.h>
-#include <nscd/nscd_proto.h>
+#ifdef USE_NSCD
+# include <nscd/nscd_proto.h>
+# include <nscd/nscd-dbtype.h>
+#endif
 #include <scratch_buffer.h>
 #include <inet/net-internal.h>
 
@@ -485,13 +488,9 @@  static int
 get_nscd_addresses (const char *name, const struct addrinfo *req,
 		    struct gaih_result *res)
 {
-  if (__nss_not_use_nscd_hosts > 0
-      && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
-    __nss_not_use_nscd_hosts = 0;
-
   res->at = NULL;
 
-  if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
+  if (!__nscd_use_database (hstdb))
     return 0;
 
   /* Try to use nscd.  */
@@ -508,14 +507,6 @@  get_nscd_addresses (const char *name, const struct addrinfo *req,
 	    return -EAI_AGAIN;
 	  return -EAI_NONAME;
 	}
-      if (__nss_not_use_nscd_hosts == 0)
-	{
-	  if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
-	    return -EAI_MEMORY;
-	  if (h_errno == TRY_AGAIN)
-	    return -EAI_AGAIN;
-	  return -EAI_SYSTEM;
-	}
       return 0;
     }
 
diff --git a/nss/getnetgrent_r.c b/nss/getnetgrent_r.c
index d391e2ac66..569087d3b0 100644
--- a/nss/getnetgrent_r.c
+++ b/nss/getnetgrent_r.c
@@ -27,6 +27,7 @@ 
 #include "nsswitch.h"
 #include <sysdep.h>
 #include <nscd/nscd_proto.h>
+#include <nscd/nscd-dbtype.h>
 
 
 /* Protect above variable against multiple uses at the same time.  */
@@ -150,12 +151,7 @@  static int
 nscd_setnetgrent (const char *group)
 {
 #ifdef USE_NSCD
-  if (__nss_not_use_nscd_netgroup > 0
-      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
-    __nss_not_use_nscd_netgroup = 0;
-
-  if (!__nss_not_use_nscd_netgroup
-      && !__nss_database_custom[NSS_DBSIDX_netgroup])
+  if (__nscd_use_database (netgrdb))
     return __nscd_setnetgrent (group, &dataset);
 #endif
   return -1;
@@ -357,12 +353,7 @@  innetgr (const char *netgroup, const char *host, const char *user,
 	 const char *domain)
 {
 #ifdef USE_NSCD
-  if (__nss_not_use_nscd_netgroup > 0
-      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
-    __nss_not_use_nscd_netgroup = 0;
-
-  if (!__nss_not_use_nscd_netgroup
-      && !__nss_database_custom[NSS_DBSIDX_netgroup])
+  if (__nscd_use_database (netgrdb))
     {
       int result = __nscd_innetgr (netgroup, host, user, domain);
       if (result >= 0)
diff --git a/nss/initgroups.c b/nss/initgroups.c
index f4106226ec..95691542fb 100644
--- a/nss/initgroups.c
+++ b/nss/initgroups.c
@@ -29,6 +29,7 @@ 
 #include <config.h>
 
 #include "../nscd/nscd-client.h"
+#include "../nscd/nscd-dbtype.h"
 #include "../nscd/nscd_proto.h"
 
 /* Type of the lookup function.  */
@@ -47,18 +48,11 @@  internal_getgrouplist (const char *user, gid_t group, long int *size,
 		       gid_t **groupsp, long int limit)
 {
 #ifdef USE_NSCD
-  if (__nss_not_use_nscd_group > 0
-      && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
-    __nss_not_use_nscd_group = 0;
-  if (!__nss_not_use_nscd_group
-      && !__nss_database_custom[NSS_DBSIDX_group])
+  if (__nscd_use_database (grpdb))
     {
       int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
       if (n >= 0)
 	return n;
-
-      /* nscd is not usable.  */
-      __nss_not_use_nscd_group = 1;
     }
 #endif
 
diff --git a/nss/nss_database.c b/nss/nss_database.c
index 076d5a63fe..5861454467 100644
--- a/nss/nss_database.c
+++ b/nss/nss_database.c
@@ -28,6 +28,10 @@ 
 #include <netdb.h>
 #include <stdio_ext.h>
 #include <string.h>
+#ifdef USE_NSCD
+# include <nscd/nscd_proto.h>
+# include <nscd/nscd-dbtype.h>
+#endif
 
 struct nss_database_state
 {
@@ -256,7 +260,24 @@  __nss_configure_lookup (const char *dbname, const char *service_line)
   local->data.services[db] = result;
 
 #ifdef USE_NSCD
-  __nss_database_custom[db] = true;
+  switch (db)
+    {
+    case nss_database_group:
+      __nscd_disable_database (grpdb);
+      break;
+    case nss_database_hosts:
+      __nscd_disable_database (hstdb);
+      break;
+    case nss_database_netgroup:
+      __nscd_disable_database (netgrdb);
+      break;
+    case nss_database_passwd:
+      __nscd_disable_database (pwddb);
+      break;
+    case nss_database_services:
+      __nscd_disable_database (servdb);
+      break;
+    }
 #endif
 
   return 0;
diff --git a/nss/nss_module.c b/nss/nss_module.c
index cb8fde38b4..fbbc6a266a 100644
--- a/nss/nss_module.c
+++ b/nss/nss_module.c
@@ -33,6 +33,7 @@ 
 #include <stdlib.h>
 #include <string.h>
 #include <pointer_guard.h>
+#include <nscd/nscd-dbtype.h>
 
 /* Suffix after .so of NSS service modules.  This is a bit of magic,
    but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we
@@ -395,11 +396,8 @@  __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
   cb1 (netgrdb, &netgr_traced_file.file);
 
   /* Disable all uses of NSCD.  */
-  __nss_not_use_nscd_passwd = -1;
-  __nss_not_use_nscd_group = -1;
-  __nss_not_use_nscd_hosts = -1;
-  __nss_not_use_nscd_services = -1;
-  __nss_not_use_nscd_netgroup = -1;
+  for (int i = 0; i < lastdb; ++i)
+    __nscd_disable_database (i);
 }
 #endif
 
diff --git a/nss/nsswitch.c b/nss/nsswitch.c
index 97cedff826..ff69ede948 100644
--- a/nss/nsswitch.c
+++ b/nss/nsswitch.c
@@ -42,11 +42,6 @@ 
 #include <sysdep.h>
 #include <config.h>
 
-#ifdef USE_NSCD
-/* Flags whether custom rules for database is set.  */
-bool __nss_database_custom[NSS_DBSIDX_max];
-#endif
-
 /*__libc_lock_define_initialized (static, lock)*/
 
 /* -1 == not found
diff --git a/nss/nsswitch.h b/nss/nsswitch.h
index 1b18794fee..b7cfb4f602 100644
--- a/nss/nsswitch.h
+++ b/nss/nsswitch.h
@@ -62,19 +62,15 @@  typedef struct
 /* To access the action based on the status value use this macro.  */
 #define nss_next_action(ni, status) nss_action_get (ni, status)
 
-
 #ifdef USE_NSCD
-/* Indices into DATABASES in nsswitch.c and __NSS_DATABASE_CUSTOM.  */
-enum
-  {
-# define DEFINE_DATABASE(arg) NSS_DBSIDX_##arg,
-# include "databases.def"
-# undef DEFINE_DATABASE
-    NSS_DBSIDX_max
-  };
-
-/* Flags whether custom rules for database is set.  */
-extern bool __nss_database_custom[NSS_DBSIDX_max] attribute_hidden;
+/* Compile-time mapping of NSS databases to nscd dbtype values from
+   <nscd/nscd-dbtype.h>.  This is used to update nscd usage counters
+   in nscd-enabled builds if getXbyY_r routines are called.  */
+# define NSS_DBSIDX_group grpdb
+# define NSS_DBSIDX_hosts hstdb
+# define NSS_DBSIDX_netgroup netgrdb
+# define NSS_DBSIDX_passwd pwddb
+# define NSS_DBSIDX_services servdb
 #endif
 
 /* Warning for NSS functions, which don't require dlopen if glibc