[PATCH v2 12/23] nscd: Rewrite __nscd_cache_search using <concurrent_buffer.h>

Florian Weimer fweimer@redhat.com
Fri Mar 20 20:42:23 GMT 2026


This makes it somewhat clearer what is going on.  This still
does not implement a proper software TM snapshot.
---
 nscd/nscd_helper.c | 85 ++++++++++++++++++----------------------------
 1 file changed, 33 insertions(+), 52 deletions(-)

diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index 2a60c3572e..9bd0e7818e 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -41,6 +41,7 @@
 #include "nscd-client.h"
 #include "nscd-dbtype.h"
 #include "nscd_proto.h"
+#include "concurrent_buffer.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
@@ -527,71 +528,51 @@ __nscd_cache_search (request_type type, const char *key, size_t keylen,
 		     const struct mapped_database *mapped, size_t datalen)
 {
   unsigned long int hash = __nss_hash (key, keylen) % mapped->head->module;
-  size_t datasize = mapped->datasize;
+
+  struct concurrent_buffer cb = cb_create (mapped->data, mapped->datasize);
 
   ref_t trail = mapped->head->array[hash];
   ref_t work = trail;
-  size_t loop_cnt = datasize / (MINIMUM_HASHENTRY_SIZE
-				+ offsetof (struct datahead, data) / 2);
+  size_t loop_cnt = mapped->datasize / (MINIMUM_HASHENTRY_SIZE
+					+ offsetof (struct datahead, data) / 2);
   int tick = 0;
 
-  while (work != ENDREF && work + MINIMUM_HASHENTRY_SIZE <= datasize)
+  while (work != ENDREF)
     {
-      struct hashentry *here = (struct hashentry *) (mapped->data + work);
-      ref_t here_key, here_packet;
-
-      /* Although during garbage collection when moving struct hashentry
-	 records around we first copy from old to new location and then
-	 adjust pointer from previous hashentry to it, there is no barrier
-	 between those memory writes!!! This is extremely risky on any
-	 modern CPU which can reorder memory accesses very aggressively.
-	 Check alignment, both as a partial consistency check and to avoid
-	 crashes on targets which require atomic loads to be aligned.  */
-      if ((uintptr_t) here & (__alignof__ (*here) - 1))
-	return NULL;
-
-      if (type == here->type
-	  && keylen == here->len
-	  && (here_key = atomic_load_relaxed (&here->key)) + keylen <= datasize
-	  && memcmp (key, mapped->data + here_key, keylen) == 0
-	  && ((here_packet = atomic_load_relaxed (&here->packet))
-	      + sizeof (struct datahead) <= datasize))
+      /* First compare type and key.  */
+      if (type == cb_field (&cb, work, struct hashentry, type)
+	  && keylen == cb_field (&cb, work, struct hashentry, len))
 	{
-	  /* We found the entry.  Increment the appropriate counter.  */
-	  struct datahead *dh
-	    = (struct datahead *) (mapped->data + here_packet);
-
-	  if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
-	    return NULL;
-
-	  /* See whether we must ignore the entry or whether something
-	     is wrong because garbage collection is in progress.  */
-	  if (dh->usable
-	      && here_packet + dh->allocsize <= datasize
-	      && (here_packet + offsetof (struct datahead, data) + datalen
-		  <= datasize))
-	    return dh;
+	  ref_t here_key = cb_field (&cb, work, struct hashentry, key);
+	  if (cb_memeq (&cb, here_key, key, keylen))
+	    {
+	      /* Data found.  Now validate the data reference.  */
+	      ref_t here_packet = cb_field (&cb, work,
+					    struct hashentry, packet);
+	      if ((here_packet & (__alignof__ (struct datahead) - 1)) == 0
+		  && cb_field (&cb, here_packet, struct datahead, usable))
+		{
+		  size_t allocsize = cb_field (&cb, here_packet,
+					       struct datahead, allocsize);
+		  if (cb_available (&cb, here_packet, allocsize))
+		      /* We found the entry.  */
+		      return (struct datahead *) (mapped->data + here_packet);
+		}
+	    }
 	}
 
-      work = atomic_load_relaxed (&here->next);
+      work = cb_field (&cb, work, struct hashentry, next);
+      if (cb_has_failed (&cb))
+	return NULL;
+
       /* Prevent endless loops.  This should never happen but perhaps
-	 the database got corrupted, accidentally or deliberately.  */
+	 the database got corrupted, accidentally or deliberately.
+	 Use both a loop count and a Floyd-style cycle detector
+	 (work is the hare, and trail is the tortoise).  */
       if (work == trail || loop_cnt-- == 0)
 	break;
       if (tick)
-	{
-	  struct hashentry *trailelem;
-	  trailelem = (struct hashentry *) (mapped->data + trail);
-
-	  /* We have to redo the checks.  Maybe the data changed.  */
-	  if ((uintptr_t) trailelem & (__alignof__ (*trailelem) - 1))
-	    return NULL;
-
-	  if (trail + MINIMUM_HASHENTRY_SIZE > datasize)
-	    return NULL;
-
-	  trail = atomic_load_relaxed (&trailelem->next);
-	}
+	trail = cb_field (&cb, trail, struct hashentry, next);
       tick = 1 - tick;
     }
 
-- 
2.53.0




More information about the Libc-alpha mailing list