4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
28 * Cache routines for nscd
40 #include <sys/types.h>
44 #include <nss_common.h>
52 #include "nscd_door.h"
54 #include "nscd_config.h"
55 #include "nscd_frontend.h"
56 #include "nscd_switch.h"
60 #define SERVERERROR -2
64 static nsc_db_t
*nsc_get_db(nsc_ctx_t
*, int);
65 static nscd_rc_t
lookup_cache(nsc_lookup_args_t
*, nscd_cfg_cache_t
*,
66 nss_XbyY_args_t
*, char *, nsc_entry_t
**);
67 static uint_t
reap_cache(nsc_ctx_t
*, uint_t
, uint_t
);
68 static void delete_entry(nsc_db_t
*, nsc_ctx_t
*, nsc_entry_t
*);
69 static void print_stats(nscd_cfg_stat_cache_t
*);
70 static void print_cfg(nscd_cfg_cache_t
*);
71 static int lookup_int(nsc_lookup_args_t
*, int);
74 static void print_entry(nsc_db_t
*, time_t, nsc_entry_t
*);
75 static void avl_dump(nsc_db_t
*, time_t);
76 static void hash_dump(nsc_db_t
*, time_t);
77 #endif /* NSCD_DEBUG */
78 static nsc_entry_t
*hash_find(nsc_db_t
*, nsc_entry_t
*, uint_t
*, nscd_bool_t
);
80 static void queue_adjust(nsc_db_t
*, nsc_entry_t
*);
81 static void queue_remove(nsc_db_t
*, nsc_entry_t
*);
83 static void queue_dump(nsc_db_t
*, time_t);
84 #endif /* NSCD_DEBUG */
86 static int launch_update(nsc_lookup_args_t
*);
87 static void do_update(nsc_lookup_args_t
*);
88 static void getxy_keepalive(nsc_ctx_t
*, nsc_db_t
*, int, int);
90 static void ctx_info(nsc_ctx_t
*);
91 static void ctx_info_nolock(nsc_ctx_t
*);
92 static void ctx_invalidate(nsc_ctx_t
*);
94 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
95 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
96 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
98 static int nsc_db_cis_key_compar(const void *, const void *);
99 static int nsc_db_ces_key_compar(const void *, const void *);
100 static int nsc_db_int_key_compar(const void *, const void *);
102 static uint_t
nsc_db_cis_key_gethash(nss_XbyY_key_t
*, int);
103 static uint_t
nsc_db_ces_key_gethash(nss_XbyY_key_t
*, int);
104 static uint_t
nsc_db_int_key_gethash(nss_XbyY_key_t
*, int);
106 static umem_cache_t
*nsc_entry_cache
;
108 static nsc_ctx_t
*init_cache_ctx(int);
109 static void reaper(nsc_ctx_t
*);
110 static void revalidate(nsc_ctx_t
*);
113 dup_packed_buffer(void *src
, void *dst
) {
114 nsc_lookup_args_t
*s
= (nsc_lookup_args_t
*)src
;
115 nsc_entry_t
*d
= (nsc_entry_t
*)dst
;
116 nss_pheader_t
*sphdr
= (nss_pheader_t
*)s
->buffer
;
117 nss_pheader_t
*dphdr
= (nss_pheader_t
*)d
->buffer
;
118 int slen
, new_pbufsiz
= 0;
120 if (NSCD_GET_STATUS(sphdr
) != NSS_SUCCESS
) {
122 /* no result, copy header only (status, errno, etc) */
123 slen
= sphdr
->data_off
;
126 * lookup result returned, data to copy is the packed
127 * header plus result (add 1 for the terminating NULL
130 slen
= sphdr
->data_off
+ sphdr
->data_len
+ 1;
133 /* allocate cache packed buffer */
134 if (dphdr
!= NULL
&& d
->bufsize
<= slen
&& d
->bufsize
!= 0) {
135 /* old buffer too small, free it */
143 dphdr
= calloc(1, slen
+ 1);
147 d
->bufsize
= slen
+ 1;
148 new_pbufsiz
= slen
+ 1;
151 (void) memcpy(dphdr
, sphdr
, slen
);
152 if (new_pbufsiz
!= 0)
153 dphdr
->pbufsiz
= new_pbufsiz
;
155 return (NSS_SUCCESS
);
158 char *cache_name
[CACHE_CTX_COUNT
] = {
170 NSS_DBNAM_BOOTPARAMS
,
180 typedef void (*cache_init_ctx_t
)(nsc_ctx_t
*);
181 static cache_init_ctx_t cache_init_ctx
[CACHE_CTX_COUNT
] = {
203 nsc_ctx_t
*cache_ctx_p
[CACHE_CTX_COUNT
] = { 0 };
204 static nscd_cfg_stat_cache_t null_stats
= { 0 };
205 static nscd_cfg_global_cache_t global_cfg
;
208 * Given database name 'dbname' find cache index
211 get_cache_idx(char *dbname
) {
215 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
216 nsc_name
= cache_name
[i
];
217 if (strcmp(nsc_name
, dbname
) == 0)
224 * Given database name 'dbname' retrieve cache context,
225 * if not created yet, allocate and initialize it.
228 get_cache_ctx(char *dbname
, nsc_ctx_t
**ctx
) {
233 i
= get_cache_idx(dbname
);
235 return (NSCD_INVALID_ARGUMENT
);
236 if ((*ctx
= cache_ctx_p
[i
]) == NULL
) {
237 *ctx
= init_cache_ctx(i
);
239 return (NSCD_NO_MEMORY
);
242 return (NSCD_SUCCESS
);
246 * Generate a log string to identify backend operation in debug logs
249 nsc_db_str_key_getlogstr(char *name
, char *whoami
, size_t len
,
250 nss_XbyY_args_t
*argp
) {
251 (void) snprintf(whoami
, len
, "%s [key=%s]", name
, argp
->key
.name
);
256 nsc_db_int_key_getlogstr(char *name
, char *whoami
, size_t len
,
257 nss_XbyY_args_t
*argp
) {
258 (void) snprintf(whoami
, len
, "%s [key=%d]", name
, argp
->key
.number
);
263 nsc_db_any_key_getlogstr(char *name
, char *whoami
, size_t len
,
264 nss_XbyY_args_t
*argp
) {
265 (void) snprintf(whoami
, len
, "%s", name
);
270 * Returns cache based on dbop
273 nsc_get_db(nsc_ctx_t
*ctx
, int dbop
) {
276 for (i
= 0; i
< ctx
->db_count
; i
++) {
277 if (ctx
->nsc_db
[i
] && dbop
== ctx
->nsc_db
[i
]->dbop
)
278 return (ctx
->nsc_db
[i
]);
285 * integer compare routine for _NSC_DB_INT_KEY
288 nsc_db_int_key_compar(const void *n1
, const void *n2
) {
289 nsc_entry_t
*e1
, *e2
;
291 e1
= (nsc_entry_t
*)n1
;
292 e2
= (nsc_entry_t
*)n2
;
293 return (_NSC_INT_KEY_CMP(e1
->key
.number
, e2
->key
.number
));
298 * case sensitive name compare routine for _NSC_DB_CES_KEY
301 nsc_db_ces_key_compar(const void *n1
, const void *n2
) {
302 nsc_entry_t
*e1
, *e2
;
305 e1
= (nsc_entry_t
*)n1
;
306 e2
= (nsc_entry_t
*)n2
;
307 l1
= strlen(e1
->key
.name
);
308 l2
= strlen(e2
->key
.name
);
309 res
= strncmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
310 return (_NSC_INT_KEY_CMP(res
, 0));
315 * case insensitive name compare routine _NSC_DB_CIS_KEY
318 nsc_db_cis_key_compar(const void *n1
, const void *n2
) {
319 nsc_entry_t
*e1
, *e2
;
322 e1
= (nsc_entry_t
*)n1
;
323 e2
= (nsc_entry_t
*)n2
;
324 l1
= strlen(e1
->key
.name
);
325 l2
= strlen(e2
->key
.name
);
326 res
= strncasecmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
327 return (_NSC_INT_KEY_CMP(res
, 0));
331 * macro used to generate elf hashes for strings
333 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
337 hval = (hval << 4) + func(*str++); \
338 if ((g = (hval & 0xf0000000)) != 0) \
349 cis_gethash(const char *key
, int htsize
) {
353 _NSC_ELF_STR_GETHASH(tolower
, key
, htsize
, hval
);
362 ces_gethash(const char *key
, int htsize
) {
366 _NSC_ELF_STR_GETHASH(, key
, htsize
, hval
);
372 * one-at-a-time hash function
375 db_gethash(const void *key
, int len
, int htsize
) {
377 const char *str
= key
;
382 for (hval
= 0, i
= 0; i
< len
; i
++) {
384 hval
+= (hval
<< 10);
388 hval
^= (hval
>> 11);
389 hval
+= (hval
<< 15);
390 return (hval
% htsize
);
395 * case insensitive name gethash routine _NSC_DB_CIS_KEY
398 nsc_db_cis_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
399 return (cis_gethash(key
->name
, htsize
));
404 * case sensitive name gethash routine _NSC_DB_CES_KEY
407 nsc_db_ces_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
408 return (ces_gethash(key
->name
, htsize
));
413 * integer gethash routine _NSC_DB_INT_KEY
416 nsc_db_int_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
417 return (db_gethash(&key
->number
, sizeof (key
->number
), htsize
));
422 * Find entry in the hash table
423 * if cmp == nscd_true)
424 * return entry only if the keys match
426 * return entry in the hash location without checking the keys
430 hash_find(nsc_db_t
*nscdb
, nsc_entry_t
*entry
, uint_t
*hash
,
433 nsc_entry_t
*hashentry
;
436 *hash
= nscdb
->gethash(&entry
->key
, nscdb
->htsize
);
440 hashentry
= nscdb
->htable
[*hash
];
441 if (cmp
== nscd_false
|| hashentry
== NULL
)
444 if (nscdb
->compar(entry
, hashentry
) == 0)
451 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
452 if (nscdb->htable) { \
453 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
454 nscdb->htable[hash] = NULL; \
458 #define HASH_INSERT(nscdb, entry, hash, cmp) \
459 if (nscdb->htable) { \
460 (void) hash_find(nscdb, entry, &hash, cmp); \
461 nscdb->htable[hash] = entry; \
467 print_entry(nsc_db_t
*nscdb
, time_t now
, nsc_entry_t
*entry
) {
468 nss_XbyY_args_t args
;
471 switch (entry
->stats
.status
) {
473 (void) fprintf(stdout
, gettext("\t status: new entry\n"));
475 case ST_UPDATE_PENDING
:
476 (void) fprintf(stdout
, gettext("\t status: update pending\n"));
478 case ST_LOOKUP_PENDING
:
479 (void) fprintf(stdout
, gettext("\t status: lookup pending\n"));
482 (void) fprintf(stdout
, gettext("\t status: discarded entry\n"));
485 if (entry
->stats
.timestamp
< now
)
486 (void) fprintf(stdout
,
487 gettext("\t status: expired (%d seconds ago)\n"),
488 now
- entry
->stats
.timestamp
);
490 (void) fprintf(stdout
,
491 gettext("\t status: valid (expiry in %d seconds)\n"),
492 entry
->stats
.timestamp
- now
);
495 (void) fprintf(stdout
, gettext("\t hits: %u\n"), entry
->stats
.hits
);
496 args
.key
= entry
->key
;
497 (void) nscdb
->getlogstr(nscdb
->name
, whoami
, sizeof (whoami
), &args
);
498 (void) fprintf(stdout
, "\t %s\n", whoami
);
500 #endif /* NSCD_DEBUG */
503 print_stats(nscd_cfg_stat_cache_t
*statsp
) {
505 (void) fprintf(stdout
, gettext("\n\t STATISTICS:\n"));
506 (void) fprintf(stdout
, gettext("\t positive hits: %lu\n"),
508 (void) fprintf(stdout
, gettext("\t negative hits: %lu\n"),
510 (void) fprintf(stdout
, gettext("\t positive misses: %lu\n"),
512 (void) fprintf(stdout
, gettext("\t negative misses: %lu\n"),
514 (void) fprintf(stdout
, gettext("\t total entries: %lu\n"),
516 (void) fprintf(stdout
, gettext("\t queries queued: %lu\n"),
518 (void) fprintf(stdout
, gettext("\t queries dropped: %lu\n"),
520 (void) fprintf(stdout
, gettext("\t cache invalidations: %lu\n"),
521 statsp
->invalidate_count
);
523 _NSC_GET_HITRATE(statsp
);
524 (void) fprintf(stdout
, gettext("\t cache hit rate: %10.1f\n"),
530 print_cfg(nscd_cfg_cache_t
*cfgp
) {
531 (void) fprintf(stdout
, gettext("\n\t CONFIG:\n"));
532 (void) fprintf(stdout
, gettext("\t enabled: %s\n"),
533 yes_no(cfgp
->enable
));
534 (void) fprintf(stdout
, gettext("\t per user cache: %s\n"),
535 yes_no(cfgp
->per_user
));
536 (void) fprintf(stdout
, gettext("\t avoid name service: %s\n"),
537 yes_no(cfgp
->avoid_ns
));
538 (void) fprintf(stdout
, gettext("\t check file: %s\n"),
539 yes_no(cfgp
->check_files
));
540 (void) fprintf(stdout
, gettext("\t check file interval: %d\n"),
541 cfgp
->check_interval
);
542 (void) fprintf(stdout
, gettext("\t positive ttl: %d\n"),
544 (void) fprintf(stdout
, gettext("\t negative ttl: %d\n"),
546 (void) fprintf(stdout
, gettext("\t keep hot count: %d\n"),
548 (void) fprintf(stdout
, gettext("\t hint size: %d\n"),
550 (void) fprintf(stdout
, gettext("\t max entries: %lu%s"),
552 cfgp
->maxentries
?"\n":" (unlimited)\n");
558 hash_dump(nsc_db_t
*nscdb
, time_t now
) {
562 (void) fprintf(stdout
, gettext("\n\nHASH TABLE:\n"));
563 for (i
= 0; i
< nscdb
->htsize
; i
++) {
564 if ((entry
= nscdb
->htable
[i
]) != NULL
) {
565 (void) fprintf(stdout
, "hash[%d]:\n", i
);
566 print_entry(nscdb
, now
, entry
);
570 #endif /* NSCD_DEBUG */
575 avl_dump(nsc_db_t
*nscdb
, time_t now
) {
579 (void) fprintf(stdout
, gettext("\n\nAVL TREE:\n"));
580 for (entry
= avl_first(&nscdb
->tree
), i
= 0; entry
!= NULL
;
581 entry
= avl_walk(&nscdb
->tree
, entry
, AVL_AFTER
)) {
582 (void) fprintf(stdout
, "avl node[%d]:\n", i
++);
583 print_entry(nscdb
, now
, entry
);
586 #endif /* NSCD_DEBUG */
591 queue_dump(nsc_db_t
*nscdb
, time_t now
) {
595 (void) fprintf(stdout
,
596 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
597 nscdb
->name
, avl_numnodes(&nscdb
->tree
));
599 (void) fprintf(stdout
,
600 gettext("Starting with the most recently accessed:\n"));
602 for (entry
= nscdb
->qtail
, i
= 0; entry
; entry
= entry
->qnext
) {
603 (void) fprintf(stdout
, "entry[%d]:\n", i
++);
604 print_entry(nscdb
, now
, entry
);
607 #endif /* NSCD_DEBUG */
610 queue_remove(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
612 if (nscdb
->qtail
== entry
)
613 nscdb
->qtail
= entry
->qnext
;
615 entry
->qprev
->qnext
= entry
->qnext
;
617 if (nscdb
->qhead
== entry
)
618 nscdb
->qhead
= entry
->qprev
;
620 entry
->qnext
->qprev
= entry
->qprev
;
622 if (nscdb
->reap_node
== entry
)
623 nscdb
->reap_node
= entry
->qnext
;
624 entry
->qnext
= entry
->qprev
= NULL
;
629 queue_adjust(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
632 assert(nscdb
->qtail
|| entry
->qnext
== NULL
&&
633 entry
->qprev
== NULL
);
635 assert(nscdb
->qtail
&& nscdb
->qhead
||
636 nscdb
->qtail
== NULL
&& nscdb
->qhead
== NULL
);
638 assert(entry
->qprev
|| entry
->qnext
== NULL
||
639 nscdb
->qtail
== entry
);
640 #endif /* NSCD_DEBUG */
642 /* already in the desired position */
643 if (nscdb
->qtail
== entry
)
647 if (nscdb
->qtail
== NULL
) {
648 nscdb
->qhead
= nscdb
->qtail
= entry
;
652 /* new entry (prev == NULL AND tail != entry) */
653 if (entry
->qprev
== NULL
) {
654 nscdb
->qtail
->qprev
= entry
;
655 entry
->qnext
= nscdb
->qtail
;
656 nscdb
->qtail
= entry
;
661 if (nscdb
->reap_node
== entry
)
662 nscdb
->reap_node
= entry
->qnext
;
663 if (nscdb
->qhead
== entry
)
664 nscdb
->qhead
= entry
->qprev
;
666 entry
->qnext
->qprev
= entry
->qprev
;
667 entry
->qprev
->qnext
= entry
->qnext
;
669 entry
->qnext
= nscdb
->qtail
;
670 nscdb
->qtail
->qprev
= entry
;
671 nscdb
->qtail
= entry
;
679 init_cache(int debug_level
) {
682 cflags
= (debug_level
> 0)?0:UMC_NODEBUG
;
683 nsc_entry_cache
= umem_cache_create("nsc_entry_cache",
684 sizeof (nsc_entry_t
), 0, NULL
, NULL
, NULL
,
686 if (nsc_entry_cache
== NULL
)
687 return (NSCD_NO_MEMORY
);
688 return (NSCD_SUCCESS
);
696 make_cache(enum db_type dbtype
, int dbop
, char *name
,
697 int (*compar
) (const void *, const void *),
698 void (*getlogstr
)(char *, char *, size_t, nss_XbyY_args_t
*),
699 uint_t (*gethash
)(nss_XbyY_key_t
*, int),
700 enum hash_type httype
, int htsize
)
703 char *me
= "make_cache";
705 nscdb
= (nsc_db_t
*)malloc(sizeof (*nscdb
));
707 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
708 (me
, "%s: memory allocation failure\n", name
);
711 (void) memset(nscdb
, 0, sizeof (*nscdb
));
715 nscdb
->db_type
= dbtype
;
717 /* Assign compare routine */
718 if (compar
== NULL
) {
719 if (_NSC_DB_CES_KEY(nscdb
))
720 nscdb
->compar
= nsc_db_ces_key_compar
;
721 else if (_NSC_DB_CIS_KEY(nscdb
))
722 nscdb
->compar
= nsc_db_cis_key_compar
;
723 else if (_NSC_DB_INT_KEY(nscdb
))
724 nscdb
->compar
= nsc_db_int_key_compar
;
728 nscdb
->compar
= compar
;
731 /* The cache is an AVL tree */
732 avl_create(&nscdb
->tree
, nscdb
->compar
, sizeof (nsc_entry_t
),
733 offsetof(nsc_entry_t
, avl_link
));
735 /* Assign log routine */
736 if (getlogstr
== NULL
) {
737 if (_NSC_DB_STR_KEY(nscdb
))
738 nscdb
->getlogstr
= nsc_db_str_key_getlogstr
;
739 else if (_NSC_DB_INT_KEY(nscdb
))
740 nscdb
->getlogstr
= nsc_db_int_key_getlogstr
;
742 nscdb
->getlogstr
= nsc_db_any_key_getlogstr
;
744 nscdb
->getlogstr
= getlogstr
;
747 /* The AVL tree based cache uses a hash table for quick access */
749 /* Determine hash table size based on type */
750 nscdb
->hash_type
= httype
;
754 htsize
= _NSC_INIT_HTSIZE_POWER2
;
759 htsize
= _NSC_INIT_HTSIZE_PRIME
;
762 nscdb
->htsize
= htsize
;
764 /* Create the hash table */
765 nscdb
->htable
= calloc(htsize
, sizeof (*(nscdb
->htable
)));
766 if (nscdb
->htable
== NULL
) {
767 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
768 (me
, "%s: memory allocation failure\n", name
);
772 /* Assign gethash routine */
773 if (gethash
== NULL
) {
774 if (_NSC_DB_CES_KEY(nscdb
))
775 nscdb
->gethash
= nsc_db_ces_key_gethash
;
776 else if (_NSC_DB_CIS_KEY(nscdb
))
777 nscdb
->gethash
= nsc_db_cis_key_gethash
;
778 else if (_NSC_DB_INT_KEY(nscdb
))
779 nscdb
->gethash
= nsc_db_int_key_gethash
;
783 nscdb
->gethash
= gethash
;
787 (void) mutex_init(&nscdb
->db_mutex
, USYNC_THREAD
, NULL
);
804 _nscd_cfg_cache_verify(
806 struct nscd_cfg_param_desc
*pdesc
,
807 nscd_cfg_id_t
*nswdb
,
808 nscd_cfg_flag_t dflag
,
809 nscd_cfg_error_t
**errorp
,
813 return (NSCD_SUCCESS
);
821 _nscd_cfg_cache_notify(
823 struct nscd_cfg_param_desc
*pdesc
,
824 nscd_cfg_id_t
*nswdb
,
825 nscd_cfg_flag_t dflag
,
826 nscd_cfg_error_t
**errorp
,
834 if (_nscd_cfg_flag_is_set(dflag
, NSCD_CFG_DFLAG_GROUP
)) {
835 if (_nscd_cfg_flag_is_set(pdesc
->pflag
,
836 NSCD_CFG_PFLAG_GLOBAL
)) {
838 global_cfg
= *(nscd_cfg_global_cache_t
*)data
;
839 } else if (_nscd_cfg_flag_is_set(dflag
,
840 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
841 /* non-global config for all dbs */
842 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
843 ctx
= cache_ctx_p
[i
];
845 return (NSCD_CTX_NOT_FOUND
);
846 (void) rw_wrlock(&ctx
->cfg_rwlp
);
847 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
848 ctx
->cfg_mtime
= time(NULL
);
849 (void) rw_unlock(&ctx
->cfg_rwlp
);
852 /* non-global config for a specific db */
854 /* ignore non-caching databases */
855 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
856 return (NSCD_SUCCESS
);
857 (void) rw_wrlock(&ctx
->cfg_rwlp
);
858 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
859 ctx
->cfg_mtime
= time(NULL
);
860 (void) rw_unlock(&ctx
->cfg_rwlp
);
862 return (NSCD_SUCCESS
);
865 /* individual data */
866 if (_nscd_cfg_flag_is_set(pdesc
->pflag
, NSCD_CFG_PFLAG_GLOBAL
)) {
868 dp
= (char *)&global_cfg
+ pdesc
->p_offset
;
869 (void) memcpy(dp
, data
, pdesc
->p_size
);
870 } else if (_nscd_cfg_flag_is_set(dflag
,
871 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
872 /* non-global config for all dbs */
873 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
874 ctx
= cache_ctx_p
[i
];
876 return (NSCD_CTX_NOT_FOUND
);
877 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
878 (void) rw_wrlock(&ctx
->cfg_rwlp
);
879 (void) memcpy(dp
, data
, pdesc
->p_size
);
880 ctx
->cfg_mtime
= time(NULL
);
881 (void) rw_unlock(&ctx
->cfg_rwlp
);
884 /* non-global config for a specific db */
886 /* ignore non-caching databases */
887 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
888 return (NSCD_SUCCESS
);
889 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
890 (void) rw_wrlock(&ctx
->cfg_rwlp
);
891 (void) memcpy(dp
, data
, pdesc
->p_size
);
892 ctx
->cfg_mtime
= time(NULL
);
893 (void) rw_unlock(&ctx
->cfg_rwlp
);
895 return (NSCD_SUCCESS
);
904 _nscd_cfg_cache_get_stat(
906 struct nscd_cfg_stat_desc
*sdesc
,
907 nscd_cfg_id_t
*nswdb
,
908 nscd_cfg_flag_t
*dflag
,
909 void (**free_stat
)(void *stat
),
910 nscd_cfg_error_t
**errorp
)
912 nscd_cfg_stat_cache_t
*statsp
, stats
;
917 statsp
= calloc(1, sizeof (*statsp
));
919 return (NSCD_NO_MEMORY
);
921 if (_nscd_cfg_flag_is_set(sdesc
->sflag
, NSCD_CFG_SFLAG_GLOBAL
)) {
922 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
923 if (cache_ctx_p
[i
] == NULL
)
926 (void) mutex_lock(&cache_ctx_p
[i
]->stats_mutex
);
927 stats
= cache_ctx_p
[i
]->stats
;
929 &cache_ctx_p
[i
]->stats_mutex
);
931 statsp
->pos_hits
+= stats
.pos_hits
;
932 statsp
->neg_hits
+= stats
.neg_hits
;
933 statsp
->pos_misses
+= stats
.pos_misses
;
934 statsp
->neg_misses
+= stats
.neg_misses
;
935 statsp
->entries
+= stats
.entries
;
936 statsp
->drop_count
+= stats
.drop_count
;
937 statsp
->wait_count
+= stats
.wait_count
;
938 statsp
->invalidate_count
+=
939 stats
.invalidate_count
;
942 if ((rc
= get_cache_ctx(nswdb
->name
, &ctx
)) != NSCD_SUCCESS
) {
946 (void) mutex_lock(&ctx
->stats_mutex
);
947 *statsp
= ctx
->stats
;
948 (void) mutex_unlock(&ctx
->stats_mutex
);
951 _NSC_GET_HITRATE(statsp
);
953 return (NSCD_SUCCESS
);
957 * This function should only be called when nscd is
961 nsc_info(nsc_ctx_t
*ctx
, char *dbname
, nscd_cfg_cache_t cfg
[],
962 nscd_cfg_stat_cache_t stats
[])
965 char *me
= "nsc_info";
976 rc
= get_cache_ctx(dbname
, &ctx1
);
977 if (rc
== NSCD_INVALID_ARGUMENT
) {
978 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
979 (me
, "%s: no cache context found\n", dbname
);
981 } else if (rc
== NSCD_NO_MEMORY
) {
982 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
983 (me
, "%s: unable to create cache context - no memory\n",
991 if (cfg
== NULL
|| stats
== NULL
)
994 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
996 ctx2
.dbname
= cache_name
[i
];
998 ctx2
.stats
= stats
[i
];
999 ctx_info_nolock(&ctx2
);
1004 ctx_info_nolock(nsc_ctx_t
*ctx
) {
1005 nscd_cfg_cache_t cfg
;
1006 nscd_cfg_stat_cache_t stats
;
1009 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1010 (void) print_cfg(&cfg
);
1012 if (cfg
.enable
== nscd_false
)
1016 (void) print_stats(&stats
);
1020 ctx_info(nsc_ctx_t
*ctx
) {
1021 nscd_cfg_cache_t cfg
;
1022 nscd_cfg_stat_cache_t stats
;
1024 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1026 (void) rw_unlock(&ctx
->cfg_rwlp
);
1027 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1028 (void) print_cfg(&cfg
);
1030 if (cfg
.enable
== nscd_false
)
1033 (void) mutex_lock(&ctx
->stats_mutex
);
1035 (void) mutex_unlock(&ctx
->stats_mutex
);
1036 (void) print_stats(&stats
);
1041 * This function should only be called when nscd is
1045 nsc_dump(char *dbname
, int dbop
)
1049 nscd_bool_t enabled
;
1051 char *me
= "nsc_dump";
1054 if ((i
= get_cache_idx(dbname
)) == -1) {
1055 (void) fprintf(stdout
, gettext("invalid cache name\n"));
1057 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1058 (me
, "%s: invalid cache name\n", dbname
);
1059 return (NSCD_CACHE_INVALID_CACHE_NAME
);
1062 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1063 (void) fprintf(stdout
, gettext("no cache context\n"));
1065 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1066 (me
, "%s: no cache context\n", dbname
);
1067 return (NSCD_CACHE_NO_CACHE_CTX
);
1071 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1072 enabled
= ctx
->cfg
.enable
;
1073 (void) rw_unlock(&ctx
->cfg_rwlp
);
1075 if (enabled
== nscd_false
)
1076 return (NSCD_CACHE_DISABLED
);
1078 nscdb
= nsc_get_db(ctx
, dbop
);
1079 if (nscdb
== NULL
) {
1080 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1081 (me
, "%s:%d: no cache found\n", dbname
, dbop
);
1082 return (NSCD_CACHE_NO_CACHE_FOUND
);
1085 (void) mutex_lock(&nscdb
->db_mutex
);
1086 (void) queue_dump(nscdb
, now
);
1087 (void) hash_dump(nscdb
, now
);
1088 (void) avl_dump(nscdb
, now
);
1089 (void) mutex_unlock(&nscdb
->db_mutex
);
1090 return (NSCD_SUCCESS
);
1092 #endif /* NSCD_DEBUG */
1095 * These macros are for exclusive use of nsc_lookup
1097 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1098 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1102 nsc_lookup_no_cache(nsc_lookup_args_t
*largs
, const char *str
)
1104 char *me
= "nsc_lookup_no_cache";
1105 nss_status_t status
;
1107 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1108 (me
, "%s: name service lookup (bypassing cache)\n", str
);
1109 nss_psearch(largs
->buffer
, largs
->bufsize
);
1110 status
= NSCD_GET_STATUS(largs
->buffer
);
1111 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1112 (me
, "%s: name service lookup status = %d\n", str
, status
);
1113 if (status
== NSS_SUCCESS
) {
1115 } else if (status
== NSS_NOTFOUND
) {
1118 return (SERVERERROR
);
1123 * This function starts the revalidation and reaper threads
1127 start_threads(nsc_ctx_t
*ctx
)
1130 char *me
= "start_threads";
1133 * kick off the revalidate thread (if necessary)
1135 if (ctx
->revalidate_on
!= nscd_true
) {
1136 if (thr_create(NULL
, NULL
, (void *(*)(void *))revalidate
,
1137 ctx
, 0, NULL
) != 0) {
1139 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1140 (me
, "thr_create (revalidate thread for %s): %s\n",
1141 ctx
->dbname
, strerror(errnum
));
1144 ctx
->revalidate_on
= nscd_true
;
1148 * kick off the reaper thread (if necessary)
1150 if (ctx
->reaper_on
!= nscd_true
) {
1151 if (thr_create(NULL
, NULL
, (void *(*)(void *))reaper
,
1152 ctx
, 0, NULL
) != 0) {
1154 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1155 (me
, "thr_create (reaper thread for %s): %s\n",
1156 ctx
->dbname
, strerror(errnum
));
1159 ctx
->reaper_on
= nscd_true
;
1164 * Examine the packed buffer, see if the front-end parameters
1165 * indicate that the caller specified nsswitch config should be
1166 * used for the lookup. Return 1 if yes, otherwise 0.
1169 nsw_config_in_phdr(void *buf
)
1171 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buf
;
1174 char *me
= "nsw_config_in_phdr";
1176 off
= pbuf
->dbd_off
;
1179 pdbd
= (nss_dbd_t
*)((void *)((char *)pbuf
+ off
));
1180 if (pdbd
->o_default_config
== 0)
1183 if ((enum nss_dbp_flags
)pdbd
->flags
& NSS_USE_DEFAULT_CONFIG
) {
1184 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1185 (me
, "use caller specified nsswitch config\n");
1192 copy_result(void *rbuf
, void *cbuf
)
1194 nss_pheader_t
*rphdr
= (nss_pheader_t
*)rbuf
;
1195 nss_pheader_t
*cphdr
= (nss_pheader_t
*)cbuf
;
1196 char *me
= "copy_result";
1198 /* return NSS_ERROR if not enough room to copy result */
1199 if (cphdr
->data_len
+ 1 > rphdr
->data_len
) {
1200 NSCD_SET_STATUS(rphdr
, NSS_ERROR
, ERANGE
);
1205 if (cphdr
->data_len
== 0)
1206 return (NSS_SUCCESS
);
1208 dst
= (char *)rphdr
+ rphdr
->data_off
;
1209 (void) memcpy(dst
, (char *)cphdr
+ cphdr
->data_off
,
1211 rphdr
->data_len
= cphdr
->data_len
;
1212 /* some frontend code expects a terminating NULL char */
1213 *(dst
+ rphdr
->data_len
) = '\0';
1215 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1216 (me
, "cache data (len = %lld): >>%s<<\n",
1217 cphdr
->data_len
, (char *)cphdr
+ cphdr
->data_off
);
1219 return (NSS_SUCCESS
);
1224 get_dns_ttl(void *pbuf
, char *dbname
)
1226 nss_pheader_t
*phdr
= (nss_pheader_t
*)pbuf
;
1228 char *me
= "get_dns_ttl";
1230 /* if returned, dns ttl is stored in the extended data area */
1231 if (phdr
->ext_off
== 0)
1234 if (strcmp(dbname
, NSS_DBNAM_HOSTS
) != 0 &&
1235 strcmp(dbname
, NSS_DBNAM_IPNODES
) != 0)
1238 ttl
= *(nssuint_t
*)((void *)((char *)pbuf
+ phdr
->ext_off
));
1240 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1241 (me
, "dns ttl is %d seconds\n", ttl
);
1247 check_config(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
1248 char *whoami
, int flag
)
1252 char *me
= "check_config";
1255 nscdb
= largs
->nscdb
;
1257 /* see if the cached config needs update */
1258 if (nscdb
->cfg_mtime
!= ctx
->cfg_mtime
) {
1259 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1260 nscdb
->cfg
= ctx
->cfg
;
1261 nscdb
->cfg_mtime
= ctx
->cfg_mtime
;
1262 (void) rw_unlock(&ctx
->cfg_rwlp
);
1263 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1264 (me
, "config for context %s, database %s updated\n",
1265 ctx
->dbname
, nscdb
->name
);
1269 if (cfgp
->enable
== nscd_false
) {
1270 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1271 (me
, "%s: cache disabled\n", ctx
->dbname
);
1273 if (UPDATEBIT
& flag
)
1276 return (nsc_lookup_no_cache(largs
, whoami
));
1280 * if caller requests lookup using its
1281 * own nsswitch config, bypass cache
1283 if (nsw_config_in_phdr(largs
->buffer
))
1284 return (nsc_lookup_no_cache(largs
, whoami
));
1286 /* no need of cache if we are dealing with 0 ttls */
1287 if (cfgp
->pos_ttl
<= 0 && cfgp
->neg_ttl
<= 0) {
1288 if (flag
& UPDATEBIT
)
1290 else if (cfgp
->avoid_ns
== nscd_true
)
1291 return (SERVERERROR
);
1292 return (nsc_lookup_no_cache(largs
, whoami
));
1299 * Invalidate cache if database file has been modified.
1300 * See check_files config param for details.
1303 check_db_file(nsc_ctx_t
*ctx
, nscd_cfg_cache_t cfg
,
1304 char *whoami
, time_t now
)
1307 nscd_bool_t file_modified
= nscd_false
;
1308 char *me
= "check_db_file";
1310 if (cfg
.check_interval
!= 0 &&
1311 (now
- ctx
->file_chktime
) < cfg
.check_interval
)
1314 ctx
->file_chktime
= now
;
1315 if (stat(ctx
->file_name
, &buf
) == 0) {
1316 if (ctx
->file_mtime
== 0) {
1317 (void) mutex_lock(&ctx
->file_mutex
);
1318 if (ctx
->file_mtime
== 0) {
1319 ctx
->file_mtime
= buf
.st_mtime
;
1320 ctx
->file_size
= buf
.st_size
;
1321 ctx
->file_ino
= buf
.st_ino
;
1323 (void) mutex_unlock(&ctx
->file_mutex
);
1324 } else if (ctx
->file_mtime
< buf
.st_mtime
||
1325 ctx
->file_size
!= buf
.st_size
||
1326 ctx
->file_ino
!= buf
.st_ino
) {
1327 (void) mutex_lock(&ctx
->file_mutex
);
1328 if (ctx
->file_mtime
< buf
.st_mtime
||
1329 ctx
->file_size
!= buf
.st_size
||
1330 ctx
->file_ino
!= buf
.st_ino
) {
1331 file_modified
= nscd_true
;
1332 ctx
->file_mtime
= buf
.st_mtime
;
1333 ctx
->file_size
= buf
.st_size
;
1334 ctx
->file_ino
= buf
.st_ino
;
1336 (void) mutex_unlock(&ctx
->file_mutex
);
1340 if (file_modified
== nscd_true
) {
1341 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1342 (me
, "%s: file %s has been modified - invalidating cache\n",
1343 whoami
, ctx
->file_name
);
1344 ctx_invalidate(ctx
);
1349 lookup_int(nsc_lookup_args_t
*largs
, int flag
)
1353 nscd_cfg_cache_t cfg
;
1354 nsc_entry_t
*this_entry
;
1355 nsc_entry_stat_t
*this_stats
;
1356 nsc_action_t next_action
;
1357 nss_status_t status
;
1363 nss_XbyY_args_t args
;
1365 time_t now
= time(NULL
); /* current time */
1366 char *me
= "lookup_int";
1368 /* extract dbop, dbname, key and cred */
1369 status
= nss_packed_getkey(largs
->buffer
, largs
->bufsize
, &dbname
,
1371 if (status
!= NSS_SUCCESS
) {
1372 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1373 (me
, "nss_packed_getkey failure (%d)\n", status
);
1374 return (SERVERERROR
);
1377 /* get the cache context */
1378 if (largs
->ctx
== NULL
) {
1379 if (get_cache_ctx(dbname
, &largs
->ctx
) != NSCD_SUCCESS
) {
1380 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1381 (me
, "%s: no cache context found\n", dbname
);
1383 if (UPDATEBIT
& flag
)
1386 return (nsc_lookup_no_cache(largs
, dbname
));
1391 if (largs
->nscdb
== NULL
) {
1392 if ((largs
->nscdb
= nsc_get_db(ctx
, dbop
)) == NULL
) {
1393 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1394 (me
, "%s:%d: no cache found\n",
1397 if (UPDATEBIT
& flag
)
1400 return (nsc_lookup_no_cache(largs
, dbname
));
1404 nscdb
= largs
->nscdb
;
1406 _NSCD_LOG_IF(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ALL
) {
1407 (void) nscdb
->getlogstr(nscdb
->name
, whoami
,
1408 sizeof (whoami
), &args
);
1411 if (UPDATEBIT
& flag
) {
1412 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1413 (me
, "%s: refresh start\n", whoami
);
1415 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1416 (me
, "%s: lookup start\n", whoami
);
1419 cfg_rc
= check_config(largs
, &cfg
, whoami
, flag
);
1420 if (cfg_rc
!= CONTINUE
)
1424 * Invalidate cache if file has been modified.
1426 if (cfg
.check_files
== nscd_true
)
1427 check_db_file(ctx
, cfg
, whoami
, now
);
1429 (void) mutex_lock(&nscdb
->db_mutex
);
1431 /* Lookup the cache table */
1433 delete = nscd_false
;
1434 rc
= lookup_cache(largs
, &cfg
, &args
, whoami
, &this_entry
);
1435 if (rc
!= NSCD_SUCCESS
) {
1436 (void) mutex_unlock(&nscdb
->db_mutex
);
1438 /* Either no entry and avoid name service */
1439 if (rc
== NSCD_DB_ENTRY_NOT_FOUND
||
1440 rc
== NSCD_INVALID_ARGUMENT
)
1443 /* OR memory error */
1444 return (SERVERERROR
);
1447 /* get the stats from the entry */
1448 this_stats
= &this_entry
->stats
;
1451 * What should we do next ?
1453 switch (this_stats
->status
) {
1456 next_action
= _NSC_NSLOOKUP
;
1458 case ST_UPDATE_PENDING
:
1459 if (flag
& UPDATEBIT
) {
1460 (void) mutex_unlock(&nscdb
->db_mutex
);
1462 } else if (this_stats
->timestamp
< now
)
1463 next_action
= _NSC_WAIT
;
1465 next_action
= _NSC_USECACHED
;
1467 case ST_LOOKUP_PENDING
:
1468 if (flag
& UPDATEBIT
) {
1469 (void) mutex_unlock(&nscdb
->db_mutex
);
1472 next_action
= _NSC_WAIT
;
1475 if (cfg
.avoid_ns
== nscd_true
) {
1476 (void) mutex_unlock(&nscdb
->db_mutex
);
1479 /* otherwise reuse the entry */
1480 (void) memset(this_stats
, 0, sizeof (*this_stats
));
1481 next_action
= _NSC_NSLOOKUP
;
1484 if (cfg
.avoid_ns
== nscd_true
)
1485 next_action
= _NSC_USECACHED
;
1486 else if ((flag
& UPDATEBIT
) ||
1487 (this_stats
->timestamp
< now
)) {
1488 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1489 (me
, "%s: cached entry needs to be updated\n",
1491 next_action
= _NSC_NSLOOKUP
;
1493 next_action
= _NSC_USECACHED
;
1497 if (next_action
== _NSC_WAIT
) {
1498 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1499 (me
, "%s: need to wait\n", whoami
);
1501 /* do we have clearance ? */
1502 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1504 (void) mutex_lock(&ctx
->stats_mutex
);
1505 ctx
->stats
.drop_count
++;
1506 (void) mutex_unlock(&ctx
->stats_mutex
);
1507 _NSCD_LOG(NSCD_LOG_CACHE
,
1508 NSCD_LOG_LEVEL_DEBUG_6
)
1509 (me
, "%s: throttling load\n", whoami
);
1510 (void) mutex_unlock(&nscdb
->db_mutex
);
1511 NSC_LOOKUP_LOG(WARNING
,
1512 "%s: no clearance to wait\n");
1516 (void) nscd_wait(ctx
, nscdb
, this_entry
);
1517 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1525 if (!(UPDATEBIT
& flag
))
1526 this_stats
->hits
++; /* update hit count */
1528 if (next_action
== _NSC_NSLOOKUP
) {
1530 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1531 (me
, "%s: name service lookup required\n", whoami
);
1533 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1534 if (delete == nscd_true
)
1535 delete_entry(nscdb
, ctx
, this_entry
);
1537 this_stats
->status
= ST_DISCARD
;
1538 (void) mutex_lock(&ctx
->stats_mutex
);
1539 ctx
->stats
.drop_count
++;
1540 (void) mutex_unlock(&ctx
->stats_mutex
);
1541 (void) mutex_unlock(&nscdb
->db_mutex
);
1542 NSC_LOOKUP_LOG(WARNING
,
1543 "%s: no clearance for lookup\n");
1547 /* block any threads accessing this entry */
1548 this_stats
->status
= (flag
& UPDATEBIT
) ?
1549 ST_UPDATE_PENDING
: ST_LOOKUP_PENDING
;
1551 /* release lock and do name service lookup */
1552 (void) mutex_unlock(&nscdb
->db_mutex
);
1553 nss_psearch(largs
->buffer
, largs
->bufsize
);
1554 status
= NSCD_GET_STATUS(largs
->buffer
);
1555 (void) mutex_lock(&nscdb
->db_mutex
);
1556 this_stats
->status
= 0;
1557 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1559 /* signal waiting threads */
1560 (void) nscd_signal(ctx
, nscdb
, this_entry
);
1562 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1563 (me
, "%s: name service lookup status = %d\n",
1566 if (status
== NSS_SUCCESS
) {
1570 * data found in name service
1573 status
= dup_packed_buffer(largs
, this_entry
);
1574 if (status
!= NSS_SUCCESS
) {
1575 delete_entry(nscdb
, ctx
, this_entry
);
1576 (void) mutex_unlock(&nscdb
->db_mutex
);
1577 NSC_LOOKUP_LOG(ERROR
,
1578 "%s: failed to update cache\n");
1579 return (SERVERERROR
);
1583 * store unpacked key in cache
1585 status
= nss_packed_getkey(this_entry
->buffer
,
1586 this_entry
->bufsize
,
1587 &dbname
, &dbop
, &args
);
1588 if (status
!= NSS_SUCCESS
) {
1589 delete_entry(nscdb
, ctx
, this_entry
);
1590 (void) mutex_unlock(&nscdb
->db_mutex
);
1591 NSC_LOOKUP_LOG(ERROR
,
1592 "%s: failed to extract key\n");
1593 return (SERVERERROR
);
1595 this_entry
->key
= args
.key
; /* struct copy */
1597 /* update +ve miss count */
1598 if (!(UPDATEBIT
& flag
)) {
1599 (void) mutex_lock(&ctx
->stats_mutex
);
1600 ctx
->stats
.pos_misses
++;
1601 (void) mutex_unlock(&ctx
->stats_mutex
);
1604 /* update +ve ttl */
1605 ttl
= get_dns_ttl(largs
->buffer
, dbname
);
1606 /* honor the dns ttl less than postive ttl */
1607 if (ttl
< 0 || ttl
> cfg
.pos_ttl
)
1609 this_stats
->timestamp
= time(NULL
) + ttl
;
1612 * start the revalidation and reaper threads
1613 * if not already started
1617 (void) mutex_unlock(&nscdb
->db_mutex
);
1618 NSC_LOOKUP_LOG(DEBUG
,
1619 "%s: cache updated with positive entry\n");
1621 } else if (status
== NSS_NOTFOUND
) {
1623 * data not found in name service
1626 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1627 (me
, "%s: name service lookup failed\n", whoami
);
1629 if (NSCD_GET_ERRNO(largs
->buffer
) == ERANGE
) {
1630 delete_entry(nscdb
, ctx
, this_entry
);
1631 (void) mutex_unlock(&nscdb
->db_mutex
);
1632 NSC_LOOKUP_LOG(DEBUG
,
1633 "%s: ERANGE, cache not updated "
1634 "with negative entry\n");
1638 status
= dup_packed_buffer(largs
, this_entry
);
1639 if (status
!= NSS_SUCCESS
) {
1640 delete_entry(nscdb
, ctx
, this_entry
);
1641 (void) mutex_unlock(&nscdb
->db_mutex
);
1642 NSC_LOOKUP_LOG(ERROR
,
1643 "%s: failed to update cache\n");
1644 return (SERVERERROR
);
1647 /* store unpacked key in cache */
1648 status
= nss_packed_getkey(this_entry
->buffer
,
1649 this_entry
->bufsize
,
1650 &dbname
, &dbop
, &args
);
1651 if (status
!= NSS_SUCCESS
) {
1652 delete_entry(nscdb
, ctx
, this_entry
);
1653 (void) mutex_unlock(&nscdb
->db_mutex
);
1654 NSC_LOOKUP_LOG(ERROR
,
1655 "%s: failed to extract key\n");
1656 return (SERVERERROR
);
1658 this_entry
->key
= args
.key
; /* struct copy */
1660 /* update -ve ttl */
1661 this_stats
->timestamp
= time(NULL
) + cfg
.neg_ttl
;
1663 /* update -ve miss count */
1664 if (!(UPDATEBIT
& flag
)) {
1665 (void) mutex_lock(&ctx
->stats_mutex
);
1666 ctx
->stats
.neg_misses
++;
1667 (void) mutex_unlock(&ctx
->stats_mutex
);
1671 * start the revalidation and reaper threads
1672 * if not already started
1676 (void) mutex_unlock(&nscdb
->db_mutex
);
1677 NSC_LOOKUP_LOG(DEBUG
,
1678 "%s: cache updated with negative entry\n");
1682 * name service lookup failed
1684 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1685 (me
, "%s: name service lookup failed\n", whoami
);
1687 errnum
= NSCD_GET_ERRNO(largs
->buffer
);
1688 if (delete == nscd_true
)
1689 delete_entry(nscdb
, ctx
, this_entry
);
1691 this_stats
->status
= ST_DISCARD
;
1693 (void) mutex_unlock(&nscdb
->db_mutex
);
1694 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1695 (me
, "%s: name service lookup failed "
1696 "(status=%d, errno=%d)\n",
1697 whoami
, status
, errnum
);
1699 return (SERVERERROR
);
1701 } else if (next_action
== _NSC_USECACHED
) {
1703 * found entry in cache
1705 if (UPDATEBIT
& flag
) {
1706 (void) mutex_unlock(&nscdb
->db_mutex
);
1707 NSC_LOOKUP_LOG(DEBUG
, "%s: no need to update\n");
1711 if (NSCD_GET_STATUS((nss_pheader_t
*)this_entry
->buffer
) ==
1714 (void) mutex_lock(&ctx
->stats_mutex
);
1715 ctx
->stats
.pos_hits
++;
1716 (void) mutex_unlock(&ctx
->stats_mutex
);
1718 /* update response buffer */
1719 if (copy_result(largs
->buffer
,
1720 this_entry
->buffer
) != NSS_SUCCESS
) {
1721 (void) mutex_unlock(&nscdb
->db_mutex
);
1722 NSC_LOOKUP_LOG(ERROR
,
1723 "%s: response buffer insufficient\n");
1724 return (SERVERERROR
);
1727 (void) mutex_unlock(&nscdb
->db_mutex
);
1728 NSC_LOOKUP_LOG(DEBUG
,
1729 "%s: positive entry in cache\n");
1733 (void) mutex_lock(&ctx
->stats_mutex
);
1734 ctx
->stats
.neg_hits
++;
1735 (void) mutex_unlock(&ctx
->stats_mutex
);
1737 NSCD_SET_STATUS((nss_pheader_t
*)largs
->buffer
,
1738 NSCD_GET_STATUS(this_entry
->buffer
),
1739 NSCD_GET_ERRNO(this_entry
->buffer
));
1740 NSCD_SET_HERRNO((nss_pheader_t
*)largs
->buffer
,
1741 NSCD_GET_HERRNO(this_entry
->buffer
));
1743 (void) mutex_unlock(&nscdb
->db_mutex
);
1744 NSC_LOOKUP_LOG(DEBUG
,
1745 "%s: negative entry in cache\n");
1750 (void) mutex_unlock(&nscdb
->db_mutex
);
1751 NSC_LOOKUP_LOG(ERROR
, "%s: cache backend failure\n");
1752 return (SERVERERROR
);
1756 * NSCD cache backend lookup function
1760 nsc_lookup(nsc_lookup_args_t
*largs
, int flag
) {
1762 nss_pheader_t
*phdr
= (nss_pheader_t
*)largs
->buffer
;
1765 rc
= lookup_int(largs
, 0);
1767 if (NSCD_GET_STATUS(phdr
) == NSS_TRYLOCAL
)
1773 NSCD_SET_STATUS(phdr
, NSS_SUCCESS
, 0);
1777 NSCD_SET_STATUS(phdr
, NSS_NOTFOUND
, -1);
1782 * status and errno should have been set in the phdr,
1783 * if not, set status to NSS_ERROR
1785 if (NSCD_STATUS_IS_OK(phdr
)) {
1786 NSCD_SET_STATUS(phdr
, NSS_ERROR
, 0);
1791 NSCD_SET_STATUS(phdr
, NSS_TRYLOCAL
, -1);
1798 init_cache_ctx(int i
) {
1801 ctx
= calloc(1, sizeof (nsc_ctx_t
));
1805 /* init locks and semaphores */
1806 (void) mutex_init(&ctx
->file_mutex
, USYNC_THREAD
, NULL
);
1807 (void) rwlock_init(&ctx
->cfg_rwlp
, USYNC_THREAD
, NULL
);
1808 (void) mutex_init(&ctx
->stats_mutex
, USYNC_THREAD
, NULL
);
1809 (void) _nscd_init_cache_sema(&ctx
->throttle_sema
, cache_name
[i
]);
1810 cache_init_ctx
[i
](ctx
);
1811 cache_ctx_p
[i
] = ctx
;
1818 revalidate(nsc_ctx_t
*ctx
)
1821 int i
, slp
, interval
, count
;
1823 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1824 slp
= ctx
->cfg
.pos_ttl
;
1825 count
= ctx
->cfg
.keephot
;
1826 (void) rw_unlock(&ctx
->cfg_rwlp
);
1831 interval
= (slp
/2)/count
;
1834 (void) sleep(slp
*2/3);
1835 for (i
= 0; i
< ctx
->db_count
; i
++) {
1836 getxy_keepalive(ctx
, ctx
->nsc_db
[i
],
1847 getxy_keepalive(nsc_ctx_t
*ctx
, nsc_db_t
*nscdb
, int keep
, int interval
)
1849 nsc_keephot_t
*table
;
1850 nsc_entry_t
*entry
, *ptr
;
1852 nsc_lookup_args_t
*largs
;
1853 nss_pheader_t
*phdr
;
1855 char *me
= "getxy_keepalive";
1857 /* we won't be here if keep == 0 so need to check that */
1859 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1860 (me
, "%s: keep alive\n", nscdb
->name
);
1862 if ((table
= maken(keep
)) == NULL
) {
1863 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1864 (me
, "memory allocation failure\n");
1868 (void) mutex_lock(&nscdb
->db_mutex
);
1869 entry
= nscdb
->qtail
;
1870 while (entry
!= NULL
) {
1871 /* leave pending calls alone */
1872 if (!(entry
->stats
.status
& ST_PENDING
)) {
1874 (void) insertn(table
, entry
->stats
.hits
, entry
);
1876 entry
= entry
->qnext
;
1878 for (i
= 1; i
<= keep
; i
++) {
1879 if (table
[i
].ptr
== NULL
)
1881 ptr
= (nsc_entry_t
*)table
[i
].ptr
;
1882 phdr
= (nss_pheader_t
*)ptr
->buffer
;
1883 if (NSCD_GET_STATUS(phdr
) == NSS_SUCCESS
)
1885 * for positive cache, in addition to the packed
1886 * header size, allocate twice the size of the
1887 * existing result (in case the result grows
1888 * larger) plus 2K (for the file/compat backend to
1889 * process a possible large entry in the /etc files)
1891 bufsiz
= phdr
->data_off
+ 2 * phdr
->data_len
+ 2048;
1894 * for negative cache, allocate 8K buffer to
1895 * hold result in case the next lookup may
1896 * return something (in addition to the
1897 * packed header size)
1899 bufsiz
= phdr
->data_off
+ 8096;
1900 table
[i
].ptr
= malloc(bufsiz
);
1901 if (table
[i
].ptr
== NULL
) {
1902 (void) mutex_unlock(&nscdb
->db_mutex
);
1903 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1904 (me
, "memory allocation failure\n");
1907 (void) memcpy(table
[i
].ptr
, ptr
->buffer
, ptr
->bufsize
);
1908 ((nss_pheader_t
*)table
[i
].ptr
)->pbufsiz
= bufsiz
;
1909 table
[i
].num
= bufsiz
;
1911 (void) mutex_unlock(&nscdb
->db_mutex
);
1913 /* launch update thread for each keep hot entry */
1914 for (i
= keep
; i
> 0; i
--) {
1915 if (table
[i
].ptr
== NULL
)
1916 continue; /* unused slot in table */
1917 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1918 (me
, "%s: launching update\n", nscdb
->name
);
1919 largs
= (nsc_lookup_args_t
*)malloc(sizeof (*largs
));
1920 if (largs
== NULL
) {
1921 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1922 (me
, "memory allocation failure\n");
1925 largs
->buffer
= table
[i
].ptr
;
1926 largs
->bufsize
= table
[i
].num
;
1928 largs
->nscdb
= nscdb
;
1929 if (launch_update(largs
) < 0)
1931 (void) sleep(interval
);
1935 * The update thread will handle freeing of buffer and largs.
1936 * Free the table here.
1943 launch_update(nsc_lookup_args_t
*in
)
1945 char *me
= "launch_update";
1948 errnum
= thr_create(NULL
, NULL
, (void *(*)(void*))do_update
,
1949 in
, 0|THR_DETACHED
, NULL
);
1951 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1952 (me
, "%s: thread creation failure (%d)\n",
1953 in
->nscdb
->name
, errnum
);
1961 do_update(nsc_lookup_args_t
*in
) {
1962 nss_pheader_t
*phdr
= (nss_pheader_t
*)in
->buffer
;
1964 /* update the length of the data buffer */
1965 phdr
->data_len
= phdr
->pbufsiz
- phdr
->data_off
;
1967 (void) lookup_int(in
, UPDATEBIT
);
1978 nsc_invalidate(nsc_ctx_t
*ctx
, char *dbname
, nsc_ctx_t
**ctxs
)
1981 char *me
= "nsc_invalidate";
1984 ctx_invalidate(ctx
);
1989 if ((i
= get_cache_idx(dbname
)) == -1) {
1990 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1991 (me
, "%s: invalid cache name\n", dbname
);
1994 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1995 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1996 (me
, "%s: no cache context found\n",
2000 ctx_invalidate(ctx
);
2007 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
2008 if (ctxs
[i
] != NULL
)
2009 ctx_invalidate(ctxs
[i
]);
2015 * Invalidate cache by context
2018 ctx_invalidate(nsc_ctx_t
*ctx
)
2022 char *me
= "ctx_invalidate";
2024 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2025 (me
, "%s: invalidate cache\n", ctx
->dbname
);
2027 for (i
= 0; i
< ctx
->db_count
; i
++) {
2028 if (ctx
->nsc_db
[i
] == NULL
)
2030 (void) mutex_lock(&ctx
->nsc_db
[i
]->db_mutex
);
2031 entry
= ctx
->nsc_db
[i
]->qtail
;
2032 while (entry
!= NULL
) {
2033 /* leave pending calls alone */
2034 if (!(entry
->stats
.status
& ST_PENDING
))
2035 entry
->stats
.status
= ST_DISCARD
;
2036 entry
= entry
->qnext
;
2038 (void) mutex_unlock(&ctx
->nsc_db
[i
]->db_mutex
);
2041 (void) mutex_lock(&ctx
->stats_mutex
);
2042 ctx
->stats
.invalidate_count
++;
2043 (void) mutex_unlock(&ctx
->stats_mutex
);
2051 * nscdb->db_mutex lock must be held before calling this function
2054 delete_entry(nsc_db_t
*nscdb
, nsc_ctx_t
*ctx
, nsc_entry_t
*entry
) {
2057 avl_remove(&nscdb
->tree
, entry
);
2058 HASH_REMOVE(nscdb
, entry
, hash
, nscd_false
);
2059 queue_remove(nscdb
, entry
);
2060 if (entry
->buffer
!= NULL
) {
2061 free(entry
->buffer
);
2062 entry
->buffer
= NULL
;
2064 umem_cache_free(nsc_entry_cache
, entry
);
2065 (void) mutex_lock(&ctx
->stats_mutex
);
2066 ctx
->stats
.entries
--;
2067 (void) mutex_unlock(&ctx
->stats_mutex
);
2072 lookup_cache(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
2073 nss_XbyY_args_t
*argp
, char *whoami
, nsc_entry_t
**entry
)
2080 nsc_entry_t find_entry
, *node
;
2081 char *me
= "lookup_cache";
2084 nscdb
= largs
->nscdb
;
2086 /* set the search key */
2087 find_entry
.key
= argp
->key
; /* struct copy (not deep) */
2089 /* lookup the hash table ==> O(1) */
2090 if (nscdb
->htable
) {
2091 *entry
= hash_find(nscdb
, &find_entry
, &hash
, nscd_true
);
2092 if (*entry
!= NULL
) {
2093 (void) queue_adjust(nscdb
, *entry
);
2094 return (NSCD_SUCCESS
);
2098 /* if not found, lookup the AVL tree ==> O(log n) */
2099 *entry
= (nsc_entry_t
*)avl_find(&nscdb
->tree
, &find_entry
, &pos
);
2100 if (*entry
!= NULL
) {
2101 (void) queue_adjust(nscdb
, *entry
);
2102 /* move it to the hash table */
2103 if (nscdb
->htable
) {
2104 if (nscdb
->htable
[hash
] == NULL
||
2105 (*entry
)->stats
.hits
>=
2106 nscdb
->htable
[hash
]->stats
.hits
) {
2107 nscdb
->htable
[hash
] = *entry
;
2110 return (NSCD_SUCCESS
);
2113 /* entry not found in the cache */
2114 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2115 (me
, "%s: cache miss\n", whoami
);
2117 if (cfgp
->avoid_ns
== nscd_true
) {
2118 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2119 (me
, "%s: avoid name service\n", whoami
);
2120 return (NSCD_DB_ENTRY_NOT_FOUND
);
2123 /* allocate memory for new entry (stub) */
2124 *entry
= (nsc_entry_t
*)umem_cache_alloc(nsc_entry_cache
,
2126 if (*entry
== NULL
) {
2127 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2128 (me
, "%s: memory allocation failure\n", whoami
);
2129 return (NSCD_NO_MEMORY
);
2131 (void) memset(*entry
, 0, sizeof (**entry
));
2134 * Note that the actual data for the key is stored within
2135 * the largs->buffer (input buffer to nsc_lookup).
2136 * find_entry.key only contains pointers to this data.
2138 * If largs->buffer will be re-allocated by nss_psearch
2139 * then (*entry)->key will have dangling pointers.
2140 * In such case, the following assignment needs to be
2141 * replaced by code that duplicates the key.
2143 (*entry
)->key
= find_entry
.key
;
2146 * Add the entry to the cache.
2148 avl_insert(&nscdb
->tree
, *entry
, pos
); /* O(log n) */
2149 (void) queue_adjust(nscdb
, *entry
); /* constant */
2150 if (nscdb
->htable
) /* constant */
2151 nscdb
->htable
[hash
] = *entry
;
2152 (*entry
)->stats
.status
= ST_NEW_ENTRY
;
2154 (void) mutex_lock(&ctx
->stats_mutex
);
2155 nentries
= ++(ctx
->stats
.entries
);
2156 (void) mutex_unlock(&ctx
->stats_mutex
);
2158 /* Have we exceeded max entries ? */
2159 if (cfgp
->maxentries
> 0 && nentries
> cfgp
->maxentries
) {
2160 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2161 (me
, "%s: maximum entries exceeded -- "
2162 "deleting least recently used entry\n",
2165 node
= nscdb
->qhead
;
2166 while (node
!= NULL
&& node
!= *entry
) {
2167 if (node
->stats
.status
== ST_DISCARD
||
2168 !(node
->stats
.status
& ST_PENDING
)) {
2169 delete_entry(nscdb
, ctx
, node
);
2176 * It's okay if we were not able to find one to delete.
2177 * The reaper (when invoked) will return the cache to a
2182 return (NSCD_SUCCESS
);
2186 reaper(nsc_ctx_t
*ctx
)
2188 uint_t ttl
, extra_sleep
, total_sleep
, intervals
;
2189 uint_t nodes_per_interval
, seconds_per_interval
;
2190 ulong_t nsc_entries
;
2191 char *me
= "reaper";
2194 (void) mutex_lock(&ctx
->stats_mutex
);
2195 nsc_entries
= ctx
->stats
.entries
;
2196 (void) mutex_unlock(&ctx
->stats_mutex
);
2198 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2199 ttl
= ctx
->cfg
.pos_ttl
;
2200 (void) rw_unlock(&ctx
->cfg_rwlp
);
2202 if (nsc_entries
== 0) {
2203 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2204 (me
, "%s: nothing to reap\n", ctx
->dbname
);
2206 /* sleep for atleast 60 seconds */
2209 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2210 (me
, "%s: sleep %d\n", ctx
->dbname
, ttl
);
2215 if (ttl
< 32) ttl
= 32;
2216 if (ttl
> (1<<28)) ttl
= 1<<28;
2219 * minimum nodes_per_interval = 256 or 1<<8
2220 * maximum nodes_per_interval = nsc_entries
2221 * minimum seconds_per_interval = 32 or 1<<5
2222 * maximum_seconds_per_interval = ttl
2224 if (nsc_entries
<= ttl
) {
2225 intervals
= (nsc_entries
>> 8) + 1;
2226 seconds_per_interval
= ttl
/ intervals
;
2227 nodes_per_interval
= 256;
2229 intervals
= (ttl
>> 5) + 1;
2230 seconds_per_interval
= 32;
2231 nodes_per_interval
= nsc_entries
/ intervals
;
2232 if (nodes_per_interval
< 256)
2233 nodes_per_interval
= 256;
2236 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2237 (me
, "%s: total entries = %d, "
2238 "seconds per interval = %d, "
2239 "nodes per interval = %d\n",
2240 ctx
->dbname
, nsc_entries
, seconds_per_interval
,
2241 nodes_per_interval
);
2242 total_sleep
= reap_cache(ctx
, nodes_per_interval
,
2243 seconds_per_interval
);
2244 extra_sleep
= 1 + ttl
- total_sleep
;
2245 if (extra_sleep
> 0)
2246 (void) sleep(extra_sleep
);
2252 reap_cache(nsc_ctx_t
*ctx
, uint_t nodes_per_interval
,
2253 uint_t seconds_per_interval
)
2255 uint_t nodes_togo
, total_sleep
;
2257 nsc_entry_t
*node
, *next_node
;
2259 uint_t primes
[] = {_NSC_HTSIZE_PRIMES
};
2260 ulong_t count
, nentries
, maxentries
;
2261 int i
, slot
, value
, newhtsize
;
2262 char *me
= "reap_cache";
2266 nodes_togo
= nodes_per_interval
;
2269 for (i
= 0; i
< ctx
->db_count
; i
++) {
2270 nscdb
= ctx
->nsc_db
[i
];
2271 (void) mutex_lock(&nscdb
->db_mutex
);
2272 nscdb
->reap_node
= nscdb
->qtail
;
2273 while (nscdb
->reap_node
!= NULL
) {
2274 if (nodes_togo
== 0) {
2275 (void) mutex_unlock(&nscdb
->db_mutex
);
2276 (void) sleep(seconds_per_interval
);
2277 total_sleep
+= seconds_per_interval
;
2278 nodes_togo
= nodes_per_interval
;
2280 (void) mutex_lock(&nscdb
->db_mutex
);
2282 /* delete ST_DISCARD and expired nodes */
2283 if ((node
= nscdb
->reap_node
) == NULL
)
2285 if (node
->stats
.status
== ST_DISCARD
||
2286 (!(node
->stats
.status
& ST_PENDING
) &&
2287 node
->stats
.timestamp
< now
)) {
2289 * Delete entry if its discard flag is
2290 * set OR if it has expired. Entries
2291 * with pending updates are not
2293 * nscdb->reap_node will be adjusted
2296 delete_entry(nscdb
, ctx
, node
);
2299 nscdb
->reap_node
= node
->qnext
;
2304 if (nscdb
->htsize
== 0) {
2305 (void) mutex_unlock(&nscdb
->db_mutex
);
2310 * Dynamic adjustment of hash table size.
2312 * Hash table size is roughly 1/8th of the
2313 * total entries. However the size is changed
2314 * only when the number of entries double or
2317 nentries
= avl_numnodes(&nscdb
->tree
);
2318 for (slot
= 0, value
= _NSC_INIT_HTSIZE_SLOT_VALUE
;
2319 slot
< _NSC_HTSIZE_NUM_SLOTS
&& nentries
> value
;
2320 value
= (value
<< 1) + 1, slot
++)
2322 if (nscdb
->hash_type
== nsc_ht_power2
)
2323 newhtsize
= _NSC_INIT_HTSIZE_POWER2
<< slot
;
2325 newhtsize
= primes
[slot
];
2327 /* Recommended size is same as the current size. Done */
2328 if (nscdb
->htsize
== newhtsize
) {
2329 (void) mutex_unlock(&nscdb
->db_mutex
);
2333 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2334 (me
, "%s: resizing hash table from %d to %d\n",
2335 nscdb
->name
, nscdb
->htsize
, newhtsize
);
2338 * Dump old hashes because it would be time
2339 * consuming to rehash them.
2341 (void) free(nscdb
->htable
);
2342 nscdb
->htable
= calloc(newhtsize
, sizeof (*(nscdb
->htable
)));
2343 if (nscdb
->htable
== NULL
) {
2344 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2345 (me
, "%s: memory allocation failure\n",
2347 /* -1 to try later */
2350 nscdb
->htsize
= newhtsize
;
2352 (void) mutex_unlock(&nscdb
->db_mutex
);
2355 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2356 (me
, "%s: reaped %lu entries\n", ctx
->dbname
, count
);
2359 * if cache is almost full then reduce it to a safe level by
2360 * evicting LRU entries
2363 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2364 maxentries
= ctx
->cfg
.maxentries
;
2365 (void) rw_unlock(&ctx
->cfg_rwlp
);
2367 /* No limit on number of entries. Done */
2368 if (maxentries
== 0)
2371 (void) mutex_lock(&ctx
->stats_mutex
);
2372 nentries
= ctx
->stats
.entries
;
2373 (void) mutex_unlock(&ctx
->stats_mutex
);
2375 /* what is the percentage of cache used ? */
2376 value
= (nentries
* 100) / maxentries
;
2377 if (value
< _NSC_EVICTION_START_LEVEL
)
2381 * cache needs to be reduced to a safe level
2383 value
-= _NSC_EVICTION_SAFE_LEVEL
;
2384 for (i
= 0, count
= 0; i
< ctx
->db_count
; i
++) {
2386 * Reduce each subcache by 'value' percent
2388 nscdb
= ctx
->nsc_db
[i
];
2389 (void) mutex_lock(&nscdb
->db_mutex
);
2390 nodes_togo
= (value
* avl_numnodes(&nscdb
->tree
)) / 100;
2392 /* Start from LRU entry i.e queue head */
2393 next_node
= nscdb
->qhead
;
2394 while (nodes_togo
> 0 && next_node
!= NULL
) {
2396 next_node
= next_node
->qprev
;
2397 if (node
->stats
.status
== ST_DISCARD
||
2398 !(node
->stats
.status
& ST_PENDING
)) {
2399 /* Leave nodes with pending updates alone */
2400 delete_entry(nscdb
, ctx
, node
);
2405 (void) mutex_unlock(&nscdb
->db_mutex
);
2408 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2409 (me
, "%s: evicted %lu LRU entries\n", ctx
->dbname
, count
);
2412 return (total_sleep
);