Blame nscd/mem.c

Packit Service 82fcde
/* Cache memory handling.
Packit Service 82fcde
   Copyright (C) 2004-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
Packit Service 82fcde
Packit Service 82fcde
   This program is free software; you can redistribute it and/or modify
Packit Service 82fcde
   it under the terms of the GNU General Public License as published
Packit Service 82fcde
   by the Free Software Foundation; version 2 of the License, or
Packit Service 82fcde
   (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   This program is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 82fcde
   GNU General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU General Public License
Packit Service 82fcde
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <error.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <inttypes.h>
Packit Service 82fcde
#include <libintl.h>
Packit Service 82fcde
#include <limits.h>
Packit Service 82fcde
#include <obstack.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/mman.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
Packit Service 82fcde
#include "dbg_log.h"
Packit Service 82fcde
#include "nscd.h"
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
sort_he (const void *p1, const void *p2)
Packit Service 82fcde
{
Packit Service 82fcde
  struct hashentry *h1 = *(struct hashentry **) p1;
Packit Service 82fcde
  struct hashentry *h2 = *(struct hashentry **) p2;
Packit Service 82fcde
Packit Service 82fcde
  if (h1 < h2)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
  if (h1 > h2)
Packit Service 82fcde
    return 1;
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
sort_he_data (const void *p1, const void *p2)
Packit Service 82fcde
{
Packit Service 82fcde
  struct hashentry *h1 = *(struct hashentry **) p1;
Packit Service 82fcde
  struct hashentry *h2 = *(struct hashentry **) p2;
Packit Service 82fcde
Packit Service 82fcde
  if (h1->packet < h2->packet)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
  if (h1->packet > h2->packet)
Packit Service 82fcde
    return 1;
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Basic definitions for the bitmap implementation.  Only BITMAP_T
Packit Service 82fcde
   needs to be changed to choose a different word size.  */
Packit Service 82fcde
#define BITMAP_T uint8_t
Packit Service 82fcde
#define BITS (CHAR_BIT * sizeof (BITMAP_T))
Packit Service 82fcde
#define ALLBITS ((((BITMAP_T) 1) << BITS) - 1)
Packit Service 82fcde
#define HIGHBIT (((BITMAP_T) 1) << (BITS - 1))
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
markrange (BITMAP_T *mark, ref_t start, size_t len)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Adjust parameters for block alignment.  */
Packit Service 82fcde
  assert ((start & BLOCK_ALIGN_M1) == 0);
Packit Service 82fcde
  start /= BLOCK_ALIGN;
Packit Service 82fcde
  len = (len + BLOCK_ALIGN_M1) / BLOCK_ALIGN;
Packit Service 82fcde
Packit Service 82fcde
  size_t elem = start / BITS;
Packit Service 82fcde
Packit Service 82fcde
  if (start % BITS != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (start % BITS + len <= BITS)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* All fits in the partial byte.  */
Packit Service 82fcde
	  mark[elem] |= (ALLBITS >> (BITS - len)) << (start % BITS);
Packit Service 82fcde
	  return;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      mark[elem++] |= ALLBITS << (start % BITS);
Packit Service 82fcde
      len -= BITS - (start % BITS);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  while (len >= BITS)
Packit Service 82fcde
    {
Packit Service 82fcde
      mark[elem++] = ALLBITS;
Packit Service 82fcde
      len -= BITS;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (len > 0)
Packit Service 82fcde
    mark[elem] |= ALLBITS >> (BITS - len);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
gc (struct database_dyn *db)
Packit Service 82fcde
{
Packit Service 82fcde
  /* We need write access.  */
Packit Service 82fcde
  pthread_rwlock_wrlock (&db->lock);
Packit Service 82fcde
Packit Service 82fcde
  /* And the memory handling lock.  */
Packit Service 82fcde
  pthread_mutex_lock (&db->memlock);
Packit Service 82fcde
Packit Service 82fcde
  /* We need an array representing the data area.  All memory
Packit Service 82fcde
     allocation is BLOCK_ALIGN aligned so this is the level at which
Packit Service 82fcde
     we have to look at the memory.  We use a mark and sweep algorithm
Packit Service 82fcde
     where the marks are placed in this array.  */
Packit Service 82fcde
  assert (db->head->first_free % BLOCK_ALIGN == 0);
Packit Service 82fcde
Packit Service 82fcde
  BITMAP_T *mark;
Packit Service 82fcde
  bool mark_use_malloc;
Packit Service 82fcde
  /* In prune_cache we are also using a dynamically allocated array.
Packit Service 82fcde
     If the array in the caller is too large we have malloc'ed it.  */
Packit Service 82fcde
  size_t stack_used = sizeof (bool) * db->head->module;
Packit Service 82fcde
  if (__glibc_unlikely (stack_used > MAX_STACK_USE))
Packit Service 82fcde
    stack_used = 0;
Packit Service 82fcde
  size_t nmark = (db->head->first_free / BLOCK_ALIGN + BITS - 1) / BITS;
Packit Service 82fcde
  size_t memory_needed = nmark * sizeof (BITMAP_T);
Packit Service 82fcde
  if (__glibc_likely (stack_used + memory_needed <= MAX_STACK_USE))
Packit Service 82fcde
    {
Packit Service 82fcde
      mark = (BITMAP_T *) alloca_account (memory_needed, stack_used);
Packit Service 82fcde
      mark_use_malloc = false;
Packit Service 82fcde
      memset (mark, '\0', memory_needed);
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      mark = (BITMAP_T *) xcalloc (1, memory_needed);
Packit Service 82fcde
      mark_use_malloc = true;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Create an array which can hold pointer to all the entries in hash
Packit Service 82fcde
     entries.  */
Packit Service 82fcde
  memory_needed = 2 * db->head->nentries * sizeof (struct hashentry *);
Packit Service 82fcde
  struct hashentry **he;
Packit Service 82fcde
  struct hashentry **he_data;
Packit Service 82fcde
  bool he_use_malloc;
Packit Service 82fcde
  if (__glibc_likely (stack_used + memory_needed <= MAX_STACK_USE))
Packit Service 82fcde
    {
Packit Service 82fcde
      he = alloca_account (memory_needed, stack_used);
Packit Service 82fcde
      he_use_malloc = false;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      he = xmalloc (memory_needed);
Packit Service 82fcde
      he_use_malloc = true;
Packit Service 82fcde
    }
Packit Service 82fcde
  he_data = &he[db->head->nentries];
Packit Service 82fcde
Packit Service 82fcde
  size_t cnt = 0;
Packit Service 82fcde
  for (size_t idx = 0; idx < db->head->module; ++idx)
Packit Service 82fcde
    {
Packit Service 82fcde
      ref_t *prevp = &db->head->array[idx];
Packit Service 82fcde
      ref_t run = *prevp;
Packit Service 82fcde
Packit Service 82fcde
      while (run != ENDREF)
Packit Service 82fcde
	{
Packit Service 82fcde
	  assert (cnt < db->head->nentries);
Packit Service 82fcde
	  he[cnt] = (struct hashentry *) (db->data + run);
Packit Service 82fcde
Packit Service 82fcde
	  he[cnt]->prevp = prevp;
Packit Service 82fcde
	  prevp = &he[cnt]->next;
Packit Service 82fcde
Packit Service 82fcde
	  /* This is the hash entry itself.  */
Packit Service 82fcde
	  markrange (mark, run, sizeof (struct hashentry));
Packit Service 82fcde
Packit Service 82fcde
	  /* Add the information for the data itself.  We do this
Packit Service 82fcde
	     only for the one special entry marked with FIRST.  */
Packit Service 82fcde
	  if (he[cnt]->first)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      struct datahead *dh
Packit Service 82fcde
		= (struct datahead *) (db->data + he[cnt]->packet);
Packit Service 82fcde
	      markrange (mark, he[cnt]->packet, dh->allocsize);
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  run = he[cnt]->next;
Packit Service 82fcde
Packit Service 82fcde
	  ++cnt;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  assert (cnt == db->head->nentries);
Packit Service 82fcde
Packit Service 82fcde
  /* Sort the entries by the addresses of the referenced data.  All
Packit Service 82fcde
     the entries pointing to the same DATAHEAD object will have the
Packit Service 82fcde
     same key.  Stability of the sorting is unimportant.  */
Packit Service 82fcde
  memcpy (he_data, he, cnt * sizeof (struct hashentry *));
Packit Service 82fcde
  qsort (he_data, cnt, sizeof (struct hashentry *), sort_he_data);
Packit Service 82fcde
Packit Service 82fcde
  /* Sort the entries by their address.  */
Packit Service 82fcde
  qsort (he, cnt, sizeof (struct hashentry *), sort_he);
Packit Service 82fcde
Packit Service 82fcde
#define obstack_chunk_alloc xmalloc
Packit Service 82fcde
#define obstack_chunk_free free
Packit Service 82fcde
  struct obstack ob;
Packit Service 82fcde
  obstack_init (&ob;;
Packit Service 82fcde
Packit Service 82fcde
  /* Determine the highest used address.  */
Packit Service 82fcde
  size_t high = nmark;
Packit Service 82fcde
  while (high > 0 && mark[high - 1] == 0)
Packit Service 82fcde
    --high;
Packit Service 82fcde
Packit Service 82fcde
  /* No memory used.  */
Packit Service 82fcde
  if (high == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      db->head->first_free = 0;
Packit Service 82fcde
      goto out;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Determine the highest offset.  */
Packit Service 82fcde
  BITMAP_T mask = HIGHBIT;
Packit Service 82fcde
  ref_t highref = (high * BITS - 1) * BLOCK_ALIGN;
Packit Service 82fcde
  while ((mark[high - 1] & mask) == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      mask >>= 1;
Packit Service 82fcde
      highref -= BLOCK_ALIGN;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Now we can iterate over the MARK array and find bits which are not
Packit Service 82fcde
     set.  These represent memory which can be recovered.  */
Packit Service 82fcde
  size_t byte = 0;
Packit Service 82fcde
  /* Find the first gap.  */
Packit Service 82fcde
  while (byte < high && mark[byte] == ALLBITS)
Packit Service 82fcde
    ++byte;
Packit Service 82fcde
Packit Service 82fcde
  if (byte == high
Packit Service 82fcde
      || (byte == high - 1 && (mark[byte] & ~(mask | (mask - 1))) == 0))
Packit Service 82fcde
    /* No gap.  */
Packit Service 82fcde
    goto out;
Packit Service 82fcde
Packit Service 82fcde
  mask = 1;
Packit Service 82fcde
  cnt = 0;
Packit Service 82fcde
  while ((mark[byte] & mask) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      ++cnt;
Packit Service 82fcde
      mask <<= 1;
Packit Service 82fcde
    }
Packit Service 82fcde
  ref_t off_free = (byte * BITS + cnt) * BLOCK_ALIGN;
Packit Service 82fcde
  assert (off_free <= db->head->first_free);
Packit Service 82fcde
Packit Service 82fcde
  struct hashentry **next_hash = he;
Packit Service 82fcde
  struct hashentry **next_data = he_data;
Packit Service 82fcde
Packit Service 82fcde
  /* Skip over the hash entries in the first block which does not get
Packit Service 82fcde
     moved.  */
Packit Service 82fcde
  while (next_hash < &he[db->head->nentries]
Packit Service 82fcde
	 && *next_hash < (struct hashentry *) (db->data + off_free))
Packit Service 82fcde
    ++next_hash;
Packit Service 82fcde
Packit Service 82fcde
  while (next_data < &he_data[db->head->nentries]
Packit Service 82fcde
	 && (*next_data)->packet < off_free)
Packit Service 82fcde
    ++next_data;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
  /* Now we start modifying the data.  Make sure all readers of the
Packit Service 82fcde
     data are aware of this and temporarily don't use the data.  */
Packit Service 82fcde
  ++db->head->gc_cycle;
Packit Service 82fcde
  assert ((db->head->gc_cycle & 1) == 1);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
  /* We do not perform the move operations right away since the
Packit Service 82fcde
     he_data array is not sorted by the address of the data.  */
Packit Service 82fcde
  struct moveinfo
Packit Service 82fcde
  {
Packit Service 82fcde
    void *from;
Packit Service 82fcde
    void *to;
Packit Service 82fcde
    size_t size;
Packit Service 82fcde
    struct moveinfo *next;
Packit Service 82fcde
  } *moves = NULL;
Packit Service 82fcde
Packit Service 82fcde
  while (byte < high)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Search for the next filled block.  BYTE is the index of the
Packit Service 82fcde
	 entry in MARK, MASK is the bit, and CNT is the bit number.
Packit Service 82fcde
	 OFF_FILLED is the corresponding offset.  */
Packit Service 82fcde
      if ((mark[byte] & ~(mask - 1)) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* No other bit set in the same element of MARK.  Search in the
Packit Service 82fcde
	     following memory.  */
Packit Service 82fcde
	  do
Packit Service 82fcde
	    ++byte;
Packit Service 82fcde
	  while (byte < high && mark[byte] == 0);
Packit Service 82fcde
Packit Service 82fcde
	  if (byte == high)
Packit Service 82fcde
	    /* That was it.  */
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  mask = 1;
Packit Service 82fcde
	  cnt = 0;
Packit Service 82fcde
	}
Packit Service 82fcde
      /* Find the exact bit.  */
Packit Service 82fcde
      while ((mark[byte] & mask) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  ++cnt;
Packit Service 82fcde
	  mask <<= 1;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      ref_t off_alloc = (byte * BITS + cnt) * BLOCK_ALIGN;
Packit Service 82fcde
      assert (off_alloc <= db->head->first_free);
Packit Service 82fcde
Packit Service 82fcde
      /* Find the end of the used area.  */
Packit Service 82fcde
      if ((mark[byte] & ~(mask - 1)) == (BITMAP_T) ~(mask - 1))
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* All other bits set.  Search the next bytes in MARK.  */
Packit Service 82fcde
	  do
Packit Service 82fcde
	    ++byte;
Packit Service 82fcde
	  while (byte < high && mark[byte] == ALLBITS);
Packit Service 82fcde
Packit Service 82fcde
	  mask = 1;
Packit Service 82fcde
	  cnt = 0;
Packit Service 82fcde
	}
Packit Service 82fcde
      if (byte < high)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Find the exact bit.  */
Packit Service 82fcde
	  while ((mark[byte] & mask) != 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      ++cnt;
Packit Service 82fcde
	      mask <<= 1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      ref_t off_allocend = (byte * BITS + cnt) * BLOCK_ALIGN;
Packit Service 82fcde
      assert (off_allocend <= db->head->first_free);
Packit Service 82fcde
      /* Now we know that we can copy the area from OFF_ALLOC to
Packit Service 82fcde
	 OFF_ALLOCEND (not included) to the memory starting at
Packit Service 82fcde
	 OFF_FREE.  First fix up all the entries for the
Packit Service 82fcde
	 displacement.  */
Packit Service 82fcde
      ref_t disp = off_alloc - off_free;
Packit Service 82fcde
Packit Service 82fcde
      struct moveinfo *new_move;
Packit Service 82fcde
      if (__builtin_expect (stack_used + sizeof (*new_move) <= MAX_STACK_USE,
Packit Service 82fcde
			    1))
Packit Service 82fcde
	new_move = alloca_account (sizeof (*new_move), stack_used);
Packit Service 82fcde
      else
Packit Service 82fcde
	new_move = obstack_alloc (&ob, sizeof (*new_move));
Packit Service 82fcde
      new_move->from = db->data + off_alloc;
Packit Service 82fcde
      new_move->to = db->data + off_free;
Packit Service 82fcde
      new_move->size = off_allocend - off_alloc;
Packit Service 82fcde
      /* Create a circular list to be always able to append at the end.  */
Packit Service 82fcde
      if (moves == NULL)
Packit Service 82fcde
	moves = new_move->next = new_move;
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  new_move->next = moves->next;
Packit Service 82fcde
	  moves = moves->next = new_move;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* The following loop will prepare to move this much data.  */
Packit Service 82fcde
      off_free += off_allocend - off_alloc;
Packit Service 82fcde
Packit Service 82fcde
      while (off_alloc < off_allocend)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Determine whether the next entry is for a hash entry or
Packit Service 82fcde
	     the data.  */
Packit Service 82fcde
	  if ((struct hashentry *) (db->data + off_alloc) == *next_hash)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* Just correct the forward reference.  */
Packit Service 82fcde
	      *(*next_hash++)->prevp -= disp;
Packit Service 82fcde
Packit Service 82fcde
	      off_alloc += ((sizeof (struct hashentry) + BLOCK_ALIGN_M1)
Packit Service 82fcde
			    & ~BLOCK_ALIGN_M1);
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      assert (next_data < &he_data[db->head->nentries]);
Packit Service 82fcde
	      assert ((*next_data)->packet == off_alloc);
Packit Service 82fcde
Packit Service 82fcde
	      struct datahead *dh = (struct datahead *) (db->data + off_alloc);
Packit Service 82fcde
	      do
Packit Service 82fcde
		{
Packit Service 82fcde
		  assert ((*next_data)->key >= (*next_data)->packet);
Packit Service 82fcde
		  assert ((*next_data)->key + (*next_data)->len
Packit Service 82fcde
			  <= (*next_data)->packet + dh->allocsize);
Packit Service 82fcde
Packit Service 82fcde
		  (*next_data)->packet -= disp;
Packit Service 82fcde
		  (*next_data)->key -= disp;
Packit Service 82fcde
		  ++next_data;
Packit Service 82fcde
		}
Packit Service 82fcde
	      while (next_data < &he_data[db->head->nentries]
Packit Service 82fcde
		     && (*next_data)->packet == off_alloc);
Packit Service 82fcde
Packit Service 82fcde
	      off_alloc += (dh->allocsize + BLOCK_ALIGN_M1) & ~BLOCK_ALIGN_M1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      assert (off_alloc == off_allocend);
Packit Service 82fcde
Packit Service 82fcde
      assert (off_alloc <= db->head->first_free);
Packit Service 82fcde
      if (off_alloc == db->head->first_free)
Packit Service 82fcde
	/* We are done, that was the last block.  */
Packit Service 82fcde
	break;
Packit Service 82fcde
    }
Packit Service 82fcde
  assert (next_hash == &he[db->head->nentries]);
Packit Service 82fcde
  assert (next_data == &he_data[db->head->nentries]);
Packit Service 82fcde
Packit Service 82fcde
  /* Now perform the actual moves.  */
Packit Service 82fcde
  if (moves != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct moveinfo *runp = moves->next;
Packit Service 82fcde
      do
Packit Service 82fcde
	{
Packit Service 82fcde
	  assert ((char *) runp->to >= db->data);
Packit Service 82fcde
	  assert ((char *) runp->to + runp->size
Packit Service 82fcde
		  <= db->data  + db->head->first_free);
Packit Service 82fcde
	  assert ((char *) runp->from >= db->data);
Packit Service 82fcde
	  assert ((char *) runp->from + runp->size
Packit Service 82fcde
		  <= db->data  + db->head->first_free);
Packit Service 82fcde
Packit Service 82fcde
	  /* The regions may overlap.  */
Packit Service 82fcde
	  memmove (runp->to, runp->from, runp->size);
Packit Service 82fcde
	  runp = runp->next;
Packit Service 82fcde
	}
Packit Service 82fcde
      while (runp != moves->next);
Packit Service 82fcde
Packit Service 82fcde
      if (__glibc_unlikely (debug_level >= 3))
Packit Service 82fcde
	dbg_log (_("freed %zu bytes in %s cache"),
Packit Service 82fcde
		 (size_t) (db->head->first_free
Packit Service 82fcde
			   - ((char *) moves->to + moves->size - db->data)),
Packit Service 82fcde
		 dbnames[db - dbs]);
Packit Service 82fcde
Packit Service 82fcde
      /* The byte past the end of the last copied block is the next
Packit Service 82fcde
	 available byte.  */
Packit Service 82fcde
      db->head->first_free = (char *) moves->to + moves->size - db->data;
Packit Service 82fcde
Packit Service 82fcde
      /* Consistency check.  */
Packit Service 82fcde
      if (__glibc_unlikely (debug_level >= 3))
Packit Service 82fcde
	{
Packit Service 82fcde
	  for (size_t idx = 0; idx < db->head->module; ++idx)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      ref_t run = db->head->array[idx];
Packit Service 82fcde
	      size_t cnt = 0;
Packit Service 82fcde
Packit Service 82fcde
	      while (run != ENDREF)
Packit Service 82fcde
		{
Packit Service 82fcde
		  if (run + sizeof (struct hashentry) > db->head->first_free)
Packit Service 82fcde
		    {
Packit Service 82fcde
		      dbg_log ("entry %zu in hash bucket %zu out of bounds: "
Packit Service 82fcde
			       "%" PRIu32 "+%zu > %zu\n",
Packit Service 82fcde
			       cnt, idx, run, sizeof (struct hashentry),
Packit Service 82fcde
			       (size_t) db->head->first_free);
Packit Service 82fcde
		      break;
Packit Service 82fcde
		    }
Packit Service 82fcde
Packit Service 82fcde
		  struct hashentry *he = (struct hashentry *) (db->data + run);
Packit Service 82fcde
Packit Service 82fcde
		  if (he->key + he->len > db->head->first_free)
Packit Service 82fcde
		    dbg_log ("key of entry %zu in hash bucket %zu out of "
Packit Service 82fcde
			     "bounds: %" PRIu32 "+%zu > %zu\n",
Packit Service 82fcde
			     cnt, idx, he->key, (size_t) he->len,
Packit Service 82fcde
			     (size_t) db->head->first_free);
Packit Service 82fcde
Packit Service 82fcde
		  if (he->packet + sizeof (struct datahead)
Packit Service 82fcde
		      > db->head->first_free)
Packit Service 82fcde
		    dbg_log ("packet of entry %zu in hash bucket %zu out of "
Packit Service 82fcde
			     "bounds: %" PRIu32 "+%zu > %zu\n",
Packit Service 82fcde
			     cnt, idx, he->packet, sizeof (struct datahead),
Packit Service 82fcde
			     (size_t) db->head->first_free);
Packit Service 82fcde
		  else
Packit Service 82fcde
		    {
Packit Service 82fcde
		      struct datahead *dh = (struct datahead *) (db->data
Packit Service 82fcde
								 + he->packet);
Packit Service 82fcde
		      if (he->packet + dh->allocsize
Packit Service 82fcde
			  > db->head->first_free)
Packit Service 82fcde
			dbg_log ("full key of entry %zu in hash bucket %zu "
Packit Service 82fcde
				 "out of bounds: %" PRIu32 "+%zu > %zu",
Packit Service 82fcde
				 cnt, idx, he->packet, (size_t) dh->allocsize,
Packit Service 82fcde
				 (size_t) db->head->first_free);
Packit Service 82fcde
		    }
Packit Service 82fcde
Packit Service 82fcde
		  run = he->next;
Packit Service 82fcde
		  ++cnt;
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the data on disk is updated.  */
Packit Service 82fcde
  if (db->persistent)
Packit Service 82fcde
    msync (db->head, db->data + db->head->first_free - (char *) db->head,
Packit Service 82fcde
	   MS_ASYNC);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
  /* Now we are done modifying the data.  */
Packit Service 82fcde
  ++db->head->gc_cycle;
Packit Service 82fcde
  assert ((db->head->gc_cycle & 1) == 0);
Packit Service 82fcde
Packit Service 82fcde
  /* We are done.  */
Packit Service 82fcde
 out:
Packit Service 82fcde
  pthread_mutex_unlock (&db->memlock);
Packit Service 82fcde
  pthread_rwlock_unlock (&db->lock);
Packit Service 82fcde
Packit Service 82fcde
  if (he_use_malloc)
Packit Service 82fcde
    free (he);
Packit Service 82fcde
  if (mark_use_malloc)
Packit Service 82fcde
    free (mark);
Packit Service 82fcde
Packit Service 82fcde
  obstack_free (&ob, NULL);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void *
Packit Service 82fcde
mempool_alloc (struct database_dyn *db, size_t len, int data_alloc)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Make sure LEN is a multiple of our maximum alignment so we can
Packit Service 82fcde
     keep track of used memory is multiples of this alignment value.  */
Packit Service 82fcde
  if ((len & BLOCK_ALIGN_M1) != 0)
Packit Service 82fcde
    len += BLOCK_ALIGN - (len & BLOCK_ALIGN_M1);
Packit Service 82fcde
Packit Service 82fcde
  if (data_alloc)
Packit Service 82fcde
    pthread_rwlock_rdlock (&db->lock);
Packit Service 82fcde
Packit Service 82fcde
  pthread_mutex_lock (&db->memlock);
Packit Service 82fcde
Packit Service 82fcde
  assert ((db->head->first_free & BLOCK_ALIGN_M1) == 0);
Packit Service 82fcde
Packit Service 82fcde
  bool tried_resize = false;
Packit Service 82fcde
  void *res;
Packit Service 82fcde
 retry:
Packit Service 82fcde
  res = db->data + db->head->first_free;
Packit Service 82fcde
Packit Service 82fcde
  if (__glibc_unlikely (db->head->first_free + len > db->head->data_size))
Packit Service 82fcde
    {
Packit Service 82fcde
      if (! tried_resize)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Try to resize the database.  Grow size of 1/8th.  */
Packit Service 82fcde
	  size_t oldtotal = (sizeof (struct database_pers_head)
Packit Service 82fcde
			     + roundup (db->head->module * sizeof (ref_t),
Packit Service 82fcde
					ALIGN)
Packit Service 82fcde
			     + db->head->data_size);
Packit Service 82fcde
	  size_t new_data_size = (db->head->data_size
Packit Service 82fcde
				  + MAX (2 * len, db->head->data_size / 8));
Packit Service 82fcde
	  size_t newtotal = (sizeof (struct database_pers_head)
Packit Service 82fcde
			     + roundup (db->head->module * sizeof (ref_t), ALIGN)
Packit Service 82fcde
			     + new_data_size);
Packit Service 82fcde
	  if (newtotal > db->max_db_size)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      new_data_size -= newtotal - db->max_db_size;
Packit Service 82fcde
	      newtotal = db->max_db_size;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (db->mmap_used && newtotal > oldtotal
Packit Service 82fcde
	      /* We only have to adjust the file size.  The new pages
Packit Service 82fcde
		 become magically available.  */
Packit Service 82fcde
	      && TEMP_FAILURE_RETRY_VAL (posix_fallocate (db->wr_fd, oldtotal,
Packit Service 82fcde
							  newtotal
Packit Service 82fcde
							  - oldtotal)) == 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      db->head->data_size = new_data_size;
Packit Service 82fcde
	      tried_resize = true;
Packit Service 82fcde
	      goto retry;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (data_alloc)
Packit Service 82fcde
	pthread_rwlock_unlock (&db->lock);
Packit Service 82fcde
Packit Service 82fcde
      if (! db->last_alloc_failed)
Packit Service 82fcde
	{
Packit Service 82fcde
	  dbg_log (_("no more memory for database '%s'"), dbnames[db - dbs]);
Packit Service 82fcde
Packit Service 82fcde
	  db->last_alloc_failed = true;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      ++db->head->addfailed;
Packit Service 82fcde
Packit Service 82fcde
      /* No luck.  */
Packit Service 82fcde
      res = NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      db->head->first_free += len;
Packit Service 82fcde
Packit Service 82fcde
      db->last_alloc_failed = false;
Packit Service 82fcde
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  pthread_mutex_unlock (&db->memlock);
Packit Service 82fcde
Packit Service 82fcde
  return res;
Packit Service 82fcde
}