From 67479a700e3bd2e52980c00ac35c888589ac0a36 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 18 Oct 1998 15:16:22 +0000 Subject: Update. 1998-10-18 Ulrich Drepper * resolv/nss_dns/dns-host.c: Add missing errnop parameter to the NSS functions. * resolv/nss_dns/dns-network.c: Likewise. * grp/Makefile: Don't search for linuxhtreads in add-ons, use have-thread-library to determine whether threads are available. * pwd/Makefile: Remove wrong comment. * inet/Makefile: Define CFLAGS-gethstbyad_r.c, CFLAGS-gethstbynm_r.c, and CFLAGS-gethstbynm2_r.c to -DUSE_NSCD=1. * locale/C-messages.c: Define default strings for YESTR and NOSTR. * nss/Versions: Add __nss_hosts_lookup. * nss/getXXbyYY.c: Remove unneeded assignment. * nss/getXXbyYY_r.c: Include nscd/nscd_proto.h only if needed. Almost complete rewrite of the NSCD to make it smaller, faster, add more functionnality and make it easier to extend. * nscd/Makfile (routines): Add nscd_gethst_r. (nscd-modules): Add hstcache, gethstbyad_r, gethstbynm2_r, and cache. * nscd/cache.c: New file. * nscd/gethstbyad_r.c: New file. * nscd/gethstbynm2_r.c: New file. * nscd/hstcache.c: New file. * nscd/nscd_gethst_r.c: New file. * nscd/connections.c: Rewritten. Don't start new thread for every new connection. Use a fixed set of threads which handle all connections and also the cache cleanup. * nscd/grpcache.c: Rewritten to use generic cache handling functions in cache.c. * nscd/nscd.c: Recognize new parameter nthreads. Adjust initialization for rewrite. Remove handle_requests function. * nscd/nscd.h (NSCD_VERSION): Bump to 2. Define new data structure for the new unified cache and the host database entries. * nscd/nscd_conf.c: Rewrite parsing partly to allow adding of more databases easily. Recognize check-files and threads definitions. * nscd/nscd.conf: Add definition of enable-cache and check-files to passwd and group definitions. Add new set of definitions for hosts. * nscd/nscd_getgr_r.c: Rewrite for new protocol. * nscd/nscd_getpw_r.c: Likewise. * nscd/nscd_proto.h: Add prototype for host database functions. * nscd/nscd_stat.c: Rewrite to simplify printing of information for many databases. * nscd/dbg_log.c: Remove unnecessary variable initializations. Global variable debug_flag is renamed to dbg_level. * nscd/dbg_log.h: Declare set_logfile. --- nscd/cache.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 nscd/cache.c (limited to 'nscd/cache.c') diff --git a/nscd/cache.c b/nscd/cache.c new file mode 100644 index 0000000000..e957a577c0 --- /dev/null +++ b/nscd/cache.c @@ -0,0 +1,247 @@ +/* Copyright (c) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nscd.h" +#include "dbg_log.h" + +/* Search the cache for a matching entry and return it when found. If + this fails search the negative cache and return (void *) -1 if this + search was successful. Otherwise return NULL. + + This function must be called with the read-lock held. */ +struct hashentry * +cache_search (int type, void *key, size_t len, struct database *table) +{ + unsigned long int hash = __nis_hash (key, len) % table->module; + struct hashentry *work; + + work = table->array[hash]; + + while (work != NULL) + { + if (type == work->type + && len == work->len && memcmp (key, work->key, len) == 0) + { + /* We found the entry. Increment the appropriate counter. */ + if (work->data == (void *) -1) + ++table->neghit; + else + ++table->poshit; + + return work; + } + + work = work->next; + } + + return NULL; +} + +/* Add a new entry to the cache. The return value is zero if the function + call was successful. + + This function must be called with the read-lock held. + + We modify the table but we nevertheless only acquire a read-lock. + This is ok since we use operations which would be safe even without + locking, given that the `prune_cache' function never runs. Using + the readlock reduces the chance of conflicts. */ +void +cache_add (int type, void *key, size_t len, const void *packet, size_t total, + void *data, int last, time_t t, struct database *table) +{ + unsigned long int hash = __nis_hash (key, len) % table->module; + struct hashentry *newp; + + newp = malloc (sizeof (struct hashentry)); + if (newp == NULL) + error (EXIT_FAILURE, errno, _("while allocating hash table entry")); + + newp->type = type; + newp->len = len; + newp->key = key; + newp->data = data; + newp->timeout = t; + newp->packet = packet; + newp->total = total; + + newp->last = last; + + /* Put the new entry in the first position. */ + do + newp->next = table->array[hash]; + while (! compare_and_swap ((volatile long int *) &table->array[hash], + (long int) newp->next, (long int) newp)); + + /* Update the statistics. */ + if (data == (void *) -1) + ++table->negmiss; + else if (last) + ++table->posmiss; +} + +/* Walk through the table and remove all entries which lifetime ended. + + We have a problem here. To actually remove the entries we must get + the write-lock. But since we want to keep the time we have the + lock as short as possible we cannot simply acquire the lock when we + start looking for timedout entries. + + Therefore we do it in two stages: first we look for entries which + must be invalidated and remember them. Then we get the lock and + actually remove them. This is complicated by the way we have to + free the data structures since some hash table entries share the same + data. + + This function must be called with the write-lock held. */ +void +prune_cache (struct database *table, time_t now) +{ + size_t cnt = table->module; + int mark[cnt]; + int anything = 0; + size_t first = cnt + 1; + size_t last = 0; + + /* If we check for the modification of the underlying file we invalidate + the entries also in this case. */ + if (table->check_file) + { + struct stat st; + + if (stat (table->filename, &st) < 0) + { + char buf[128]; + /* We cannot stat() the file, disable file checking. */ + dbg_log (_("cannot stat() file `%s': %s"), + table->filename, strerror_r (errno, buf, sizeof (buf))); + table->check_file = 0; + } + else + { + if (st.st_mtime != table->file_mtime) + /* The file changed. Invalidate all entries. */ + now = LONG_MAX; + } + } + + /* We run through the table and find values which are not valid anymore. + + Note that for the initial step, finding the entries to be removed, + we don't need to get any lock. It is at all timed assured that the + linked lists are set up correctly and that no second thread prunes + the cache. */ + do + { + struct hashentry *runp = table->array[--cnt]; + + mark[cnt] = 0; + + while (runp != NULL) + { + if (runp->timeout < now) + { + ++mark[cnt]; + anything = 1; + first = MIN (first, cnt); + last = MAX (last, cnt); + } + runp = runp->next; + } + } + while (cnt > 0); + + if (anything) + { + struct hashentry *head = NULL; + + /* Now we have to get the write lock since we are about to modify + the table. */ + pthread_rwlock_wrlock (&table->lock); + + while (first <= last) + { + if (mark[first] > 0) + { + struct hashentry *runp; + + while (table->array[first]->timeout < now) + { + table->array[first]->dellist = head; + head = table->array[first]; + table->array[first] = head->next; + if (--mark[first] == 0) + break; + } + + runp = table->array[first]; + while (mark[first] > 0) + { + if (runp->next->timeout < now) + { + runp->next->dellist = head; + head = runp->next; + runp->next = head->next; + --mark[first]; + } + else + runp = runp->next; + } + } + ++first; + } + + /* It's all done. */ + pthread_rwlock_unlock (&table->lock); + + /* And another run to free the data. */ + do + { + struct hashentry *old = head; + + if (debug_level > 0) + dbg_log ("remove %s entry \"%s\"", + serv2str[old->type], + old->last + ? old->key : old->data == (void *) -1 ? old->key : "???"); + + /* Free the data structures. */ + if (old->data == (void *) -1) + free (old->key); + else if (old->last) + free (old->data); + + head = head->dellist; + + free (old); + } + while (head != NULL); + } +} -- cgit 1.4.1