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.
27 * Cache routines for nscd
39 #include <sys/types.h>
43 #include <nss_common.h>
51 #include "nscd_door.h"
53 #include "nscd_config.h"
54 #include "nscd_frontend.h"
55 #include "nscd_switch.h"
59 #define SERVERERROR -2
63 static nsc_db_t
*nsc_get_db(nsc_ctx_t
*, int);
64 static nscd_rc_t
lookup_cache(nsc_lookup_args_t
*, nscd_cfg_cache_t
*,
65 nss_XbyY_args_t
*, char *, nsc_entry_t
**);
66 static uint_t
reap_cache(nsc_ctx_t
*, uint_t
, uint_t
);
67 static void delete_entry(nsc_db_t
*, nsc_ctx_t
*, nsc_entry_t
*);
68 static void print_stats(nscd_cfg_stat_cache_t
*);
69 static void print_cfg(nscd_cfg_cache_t
*);
70 static int lookup_int(nsc_lookup_args_t
*, int);
73 static void print_entry(nsc_db_t
*, time_t, nsc_entry_t
*);
74 static void avl_dump(nsc_db_t
*, time_t);
75 static void hash_dump(nsc_db_t
*, time_t);
76 #endif /* NSCD_DEBUG */
77 static nsc_entry_t
*hash_find(nsc_db_t
*, nsc_entry_t
*, uint_t
*, nscd_bool_t
);
79 static void queue_adjust(nsc_db_t
*, nsc_entry_t
*);
80 static void queue_remove(nsc_db_t
*, nsc_entry_t
*);
82 static void queue_dump(nsc_db_t
*, time_t);
83 #endif /* NSCD_DEBUG */
85 static int launch_update(nsc_lookup_args_t
*);
86 static void do_update(nsc_lookup_args_t
*);
87 static void getxy_keepalive(nsc_ctx_t
*, nsc_db_t
*, int, int);
89 static void ctx_info(nsc_ctx_t
*);
90 static void ctx_info_nolock(nsc_ctx_t
*);
91 static void ctx_invalidate(nsc_ctx_t
*);
93 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
94 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
95 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
97 static int nsc_db_cis_key_compar(const void *, const void *);
98 static int nsc_db_ces_key_compar(const void *, const void *);
99 static int nsc_db_int_key_compar(const void *, const void *);
101 static uint_t
nsc_db_cis_key_gethash(nss_XbyY_key_t
*, int);
102 static uint_t
nsc_db_ces_key_gethash(nss_XbyY_key_t
*, int);
103 static uint_t
nsc_db_int_key_gethash(nss_XbyY_key_t
*, int);
105 static umem_cache_t
*nsc_entry_cache
;
107 static nsc_ctx_t
*init_cache_ctx(int);
108 static void reaper(nsc_ctx_t
*);
109 static void revalidate(nsc_ctx_t
*);
112 dup_packed_buffer(void *src
, void *dst
) {
113 nsc_lookup_args_t
*s
= (nsc_lookup_args_t
*)src
;
114 nsc_entry_t
*d
= (nsc_entry_t
*)dst
;
115 nss_pheader_t
*sphdr
= (nss_pheader_t
*)s
->buffer
;
116 nss_pheader_t
*dphdr
= (nss_pheader_t
*)d
->buffer
;
117 int slen
, new_pbufsiz
= 0;
119 if (NSCD_GET_STATUS(sphdr
) != NSS_SUCCESS
) {
121 /* no result, copy header only (status, errno, etc) */
122 slen
= sphdr
->data_off
;
125 * lookup result returned, data to copy is the packed
126 * header plus result (add 1 for the terminating NULL
129 slen
= sphdr
->data_off
+ sphdr
->data_len
+ 1;
132 /* allocate cache packed buffer */
133 if (dphdr
!= NULL
&& d
->bufsize
<= slen
&& d
->bufsize
!= 0) {
134 /* old buffer too small, free it */
142 dphdr
= calloc(1, slen
+ 1);
146 d
->bufsize
= slen
+ 1;
147 new_pbufsiz
= slen
+ 1;
150 (void) memcpy(dphdr
, sphdr
, slen
);
151 if (new_pbufsiz
!= 0)
152 dphdr
->pbufsiz
= new_pbufsiz
;
154 return (NSS_SUCCESS
);
157 char *cache_name
[CACHE_CTX_COUNT
] = {
169 NSS_DBNAM_BOOTPARAMS
,
177 typedef void (*cache_init_ctx_t
)(nsc_ctx_t
*);
178 static cache_init_ctx_t cache_init_ctx
[CACHE_CTX_COUNT
] = {
198 nsc_ctx_t
*cache_ctx_p
[CACHE_CTX_COUNT
] = { 0 };
199 static nscd_cfg_stat_cache_t null_stats
= { 0 };
200 static nscd_cfg_global_cache_t global_cfg
;
203 * Given database name 'dbname' find cache index
206 get_cache_idx(char *dbname
) {
210 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
211 nsc_name
= cache_name
[i
];
212 if (strcmp(nsc_name
, dbname
) == 0)
219 * Given database name 'dbname' retrieve cache context,
220 * if not created yet, allocate and initialize it.
223 get_cache_ctx(char *dbname
, nsc_ctx_t
**ctx
) {
228 i
= get_cache_idx(dbname
);
230 return (NSCD_INVALID_ARGUMENT
);
231 if ((*ctx
= cache_ctx_p
[i
]) == NULL
) {
232 *ctx
= init_cache_ctx(i
);
234 return (NSCD_NO_MEMORY
);
237 return (NSCD_SUCCESS
);
241 * Generate a log string to identify backend operation in debug logs
244 nsc_db_str_key_getlogstr(char *name
, char *whoami
, size_t len
,
245 nss_XbyY_args_t
*argp
) {
246 (void) snprintf(whoami
, len
, "%s [key=%s]", name
, argp
->key
.name
);
251 nsc_db_int_key_getlogstr(char *name
, char *whoami
, size_t len
,
252 nss_XbyY_args_t
*argp
) {
253 (void) snprintf(whoami
, len
, "%s [key=%d]", name
, argp
->key
.number
);
258 nsc_db_any_key_getlogstr(char *name
, char *whoami
, size_t len
,
259 nss_XbyY_args_t
*argp
) {
260 (void) snprintf(whoami
, len
, "%s", name
);
265 * Returns cache based on dbop
268 nsc_get_db(nsc_ctx_t
*ctx
, int dbop
) {
271 for (i
= 0; i
< ctx
->db_count
; i
++) {
272 if (ctx
->nsc_db
[i
] && dbop
== ctx
->nsc_db
[i
]->dbop
)
273 return (ctx
->nsc_db
[i
]);
280 * integer compare routine for _NSC_DB_INT_KEY
283 nsc_db_int_key_compar(const void *n1
, const void *n2
) {
284 nsc_entry_t
*e1
, *e2
;
286 e1
= (nsc_entry_t
*)n1
;
287 e2
= (nsc_entry_t
*)n2
;
288 return (_NSC_INT_KEY_CMP(e1
->key
.number
, e2
->key
.number
));
293 * case sensitive name compare routine for _NSC_DB_CES_KEY
296 nsc_db_ces_key_compar(const void *n1
, const void *n2
) {
297 nsc_entry_t
*e1
, *e2
;
300 e1
= (nsc_entry_t
*)n1
;
301 e2
= (nsc_entry_t
*)n2
;
302 l1
= strlen(e1
->key
.name
);
303 l2
= strlen(e2
->key
.name
);
304 res
= strncmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
305 return (_NSC_INT_KEY_CMP(res
, 0));
310 * case insensitive name compare routine _NSC_DB_CIS_KEY
313 nsc_db_cis_key_compar(const void *n1
, const void *n2
) {
314 nsc_entry_t
*e1
, *e2
;
317 e1
= (nsc_entry_t
*)n1
;
318 e2
= (nsc_entry_t
*)n2
;
319 l1
= strlen(e1
->key
.name
);
320 l2
= strlen(e2
->key
.name
);
321 res
= strncasecmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
322 return (_NSC_INT_KEY_CMP(res
, 0));
326 * macro used to generate elf hashes for strings
328 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
332 hval = (hval << 4) + func(*str++); \
333 if ((g = (hval & 0xf0000000)) != 0) \
344 cis_gethash(const char *key
, int htsize
) {
348 _NSC_ELF_STR_GETHASH(tolower
, key
, htsize
, hval
);
357 ces_gethash(const char *key
, int htsize
) {
361 _NSC_ELF_STR_GETHASH(, key
, htsize
, hval
);
367 * one-at-a-time hash function
370 db_gethash(const void *key
, int len
, int htsize
) {
372 const char *str
= key
;
377 for (hval
= 0, i
= 0; i
< len
; i
++) {
379 hval
+= (hval
<< 10);
383 hval
^= (hval
>> 11);
384 hval
+= (hval
<< 15);
385 return (hval
% htsize
);
390 * case insensitive name gethash routine _NSC_DB_CIS_KEY
393 nsc_db_cis_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
394 return (cis_gethash(key
->name
, htsize
));
399 * case sensitive name gethash routine _NSC_DB_CES_KEY
402 nsc_db_ces_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
403 return (ces_gethash(key
->name
, htsize
));
408 * integer gethash routine _NSC_DB_INT_KEY
411 nsc_db_int_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
412 return (db_gethash(&key
->number
, sizeof (key
->number
), htsize
));
417 * Find entry in the hash table
418 * if cmp == nscd_true)
419 * return entry only if the keys match
421 * return entry in the hash location without checking the keys
425 hash_find(nsc_db_t
*nscdb
, nsc_entry_t
*entry
, uint_t
*hash
,
428 nsc_entry_t
*hashentry
;
431 *hash
= nscdb
->gethash(&entry
->key
, nscdb
->htsize
);
435 hashentry
= nscdb
->htable
[*hash
];
436 if (cmp
== nscd_false
|| hashentry
== NULL
)
439 if (nscdb
->compar(entry
, hashentry
) == 0)
446 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
447 if (nscdb->htable) { \
448 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
449 nscdb->htable[hash] = NULL; \
453 #define HASH_INSERT(nscdb, entry, hash, cmp) \
454 if (nscdb->htable) { \
455 (void) hash_find(nscdb, entry, &hash, cmp); \
456 nscdb->htable[hash] = entry; \
462 print_entry(nsc_db_t
*nscdb
, time_t now
, nsc_entry_t
*entry
) {
463 nss_XbyY_args_t args
;
466 switch (entry
->stats
.status
) {
468 (void) fprintf(stdout
, gettext("\t status: new entry\n"));
470 case ST_UPDATE_PENDING
:
471 (void) fprintf(stdout
, gettext("\t status: update pending\n"));
473 case ST_LOOKUP_PENDING
:
474 (void) fprintf(stdout
, gettext("\t status: lookup pending\n"));
477 (void) fprintf(stdout
, gettext("\t status: discarded entry\n"));
480 if (entry
->stats
.timestamp
< now
)
481 (void) fprintf(stdout
,
482 gettext("\t status: expired (%d seconds ago)\n"),
483 now
- entry
->stats
.timestamp
);
485 (void) fprintf(stdout
,
486 gettext("\t status: valid (expiry in %d seconds)\n"),
487 entry
->stats
.timestamp
- now
);
490 (void) fprintf(stdout
, gettext("\t hits: %u\n"), entry
->stats
.hits
);
491 args
.key
= entry
->key
;
492 (void) nscdb
->getlogstr(nscdb
->name
, whoami
, sizeof (whoami
), &args
);
493 (void) fprintf(stdout
, "\t %s\n", whoami
);
495 #endif /* NSCD_DEBUG */
498 print_stats(nscd_cfg_stat_cache_t
*statsp
) {
500 (void) fprintf(stdout
, gettext("\n\t STATISTICS:\n"));
501 (void) fprintf(stdout
, gettext("\t positive hits: %lu\n"),
503 (void) fprintf(stdout
, gettext("\t negative hits: %lu\n"),
505 (void) fprintf(stdout
, gettext("\t positive misses: %lu\n"),
507 (void) fprintf(stdout
, gettext("\t negative misses: %lu\n"),
509 (void) fprintf(stdout
, gettext("\t total entries: %lu\n"),
511 (void) fprintf(stdout
, gettext("\t queries queued: %lu\n"),
513 (void) fprintf(stdout
, gettext("\t queries dropped: %lu\n"),
515 (void) fprintf(stdout
, gettext("\t cache invalidations: %lu\n"),
516 statsp
->invalidate_count
);
518 _NSC_GET_HITRATE(statsp
);
519 (void) fprintf(stdout
, gettext("\t cache hit rate: %10.1f\n"),
525 print_cfg(nscd_cfg_cache_t
*cfgp
) {
526 (void) fprintf(stdout
, gettext("\n\t CONFIG:\n"));
527 (void) fprintf(stdout
, gettext("\t enabled: %s\n"),
528 yes_no(cfgp
->enable
));
529 (void) fprintf(stdout
, gettext("\t per user cache: %s\n"),
530 yes_no(cfgp
->per_user
));
531 (void) fprintf(stdout
, gettext("\t avoid name service: %s\n"),
532 yes_no(cfgp
->avoid_ns
));
533 (void) fprintf(stdout
, gettext("\t check file: %s\n"),
534 yes_no(cfgp
->check_files
));
535 (void) fprintf(stdout
, gettext("\t check file interval: %d\n"),
536 cfgp
->check_interval
);
537 (void) fprintf(stdout
, gettext("\t positive ttl: %d\n"),
539 (void) fprintf(stdout
, gettext("\t negative ttl: %d\n"),
541 (void) fprintf(stdout
, gettext("\t keep hot count: %d\n"),
543 (void) fprintf(stdout
, gettext("\t hint size: %d\n"),
545 (void) fprintf(stdout
, gettext("\t max entries: %lu%s"),
547 cfgp
->maxentries
?"\n":" (unlimited)\n");
553 hash_dump(nsc_db_t
*nscdb
, time_t now
) {
557 (void) fprintf(stdout
, gettext("\n\nHASH TABLE:\n"));
558 for (i
= 0; i
< nscdb
->htsize
; i
++) {
559 if ((entry
= nscdb
->htable
[i
]) != NULL
) {
560 (void) fprintf(stdout
, "hash[%d]:\n", i
);
561 print_entry(nscdb
, now
, entry
);
565 #endif /* NSCD_DEBUG */
570 avl_dump(nsc_db_t
*nscdb
, time_t now
) {
574 (void) fprintf(stdout
, gettext("\n\nAVL TREE:\n"));
575 for (entry
= avl_first(&nscdb
->tree
), i
= 0; entry
!= NULL
;
576 entry
= avl_walk(&nscdb
->tree
, entry
, AVL_AFTER
)) {
577 (void) fprintf(stdout
, "avl node[%d]:\n", i
++);
578 print_entry(nscdb
, now
, entry
);
581 #endif /* NSCD_DEBUG */
586 queue_dump(nsc_db_t
*nscdb
, time_t now
) {
590 (void) fprintf(stdout
,
591 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
592 nscdb
->name
, avl_numnodes(&nscdb
->tree
));
594 (void) fprintf(stdout
,
595 gettext("Starting with the most recently accessed:\n"));
597 for (entry
= nscdb
->qtail
, i
= 0; entry
; entry
= entry
->qnext
) {
598 (void) fprintf(stdout
, "entry[%d]:\n", i
++);
599 print_entry(nscdb
, now
, entry
);
602 #endif /* NSCD_DEBUG */
605 queue_remove(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
607 if (nscdb
->qtail
== entry
)
608 nscdb
->qtail
= entry
->qnext
;
610 entry
->qprev
->qnext
= entry
->qnext
;
612 if (nscdb
->qhead
== entry
)
613 nscdb
->qhead
= entry
->qprev
;
615 entry
->qnext
->qprev
= entry
->qprev
;
617 if (nscdb
->reap_node
== entry
)
618 nscdb
->reap_node
= entry
->qnext
;
619 entry
->qnext
= entry
->qprev
= NULL
;
624 queue_adjust(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
627 assert(nscdb
->qtail
|| entry
->qnext
== NULL
&&
628 entry
->qprev
== NULL
);
630 assert(nscdb
->qtail
&& nscdb
->qhead
||
631 nscdb
->qtail
== NULL
&& nscdb
->qhead
== NULL
);
633 assert(entry
->qprev
|| entry
->qnext
== NULL
||
634 nscdb
->qtail
== entry
);
635 #endif /* NSCD_DEBUG */
637 /* already in the desired position */
638 if (nscdb
->qtail
== entry
)
642 if (nscdb
->qtail
== NULL
) {
643 nscdb
->qhead
= nscdb
->qtail
= entry
;
647 /* new entry (prev == NULL AND tail != entry) */
648 if (entry
->qprev
== NULL
) {
649 nscdb
->qtail
->qprev
= entry
;
650 entry
->qnext
= nscdb
->qtail
;
651 nscdb
->qtail
= entry
;
656 if (nscdb
->reap_node
== entry
)
657 nscdb
->reap_node
= entry
->qnext
;
658 if (nscdb
->qhead
== entry
)
659 nscdb
->qhead
= entry
->qprev
;
661 entry
->qnext
->qprev
= entry
->qprev
;
662 entry
->qprev
->qnext
= entry
->qnext
;
664 entry
->qnext
= nscdb
->qtail
;
665 nscdb
->qtail
->qprev
= entry
;
666 nscdb
->qtail
= entry
;
674 init_cache(int debug_level
) {
677 cflags
= (debug_level
> 0)?0:UMC_NODEBUG
;
678 nsc_entry_cache
= umem_cache_create("nsc_entry_cache",
679 sizeof (nsc_entry_t
), 0, NULL
, NULL
, NULL
,
681 if (nsc_entry_cache
== NULL
)
682 return (NSCD_NO_MEMORY
);
683 return (NSCD_SUCCESS
);
691 make_cache(enum db_type dbtype
, int dbop
, char *name
,
692 int (*compar
) (const void *, const void *),
693 void (*getlogstr
)(char *, char *, size_t, nss_XbyY_args_t
*),
694 uint_t (*gethash
)(nss_XbyY_key_t
*, int),
695 enum hash_type httype
, int htsize
)
698 char *me
= "make_cache";
700 nscdb
= (nsc_db_t
*)malloc(sizeof (*nscdb
));
702 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
703 (me
, "%s: memory allocation failure\n", name
);
706 (void) memset(nscdb
, 0, sizeof (*nscdb
));
710 nscdb
->db_type
= dbtype
;
712 /* Assign compare routine */
713 if (compar
== NULL
) {
714 if (_NSC_DB_CES_KEY(nscdb
))
715 nscdb
->compar
= nsc_db_ces_key_compar
;
716 else if (_NSC_DB_CIS_KEY(nscdb
))
717 nscdb
->compar
= nsc_db_cis_key_compar
;
718 else if (_NSC_DB_INT_KEY(nscdb
))
719 nscdb
->compar
= nsc_db_int_key_compar
;
723 nscdb
->compar
= compar
;
726 /* The cache is an AVL tree */
727 avl_create(&nscdb
->tree
, nscdb
->compar
, sizeof (nsc_entry_t
),
728 offsetof(nsc_entry_t
, avl_link
));
730 /* Assign log routine */
731 if (getlogstr
== NULL
) {
732 if (_NSC_DB_STR_KEY(nscdb
))
733 nscdb
->getlogstr
= nsc_db_str_key_getlogstr
;
734 else if (_NSC_DB_INT_KEY(nscdb
))
735 nscdb
->getlogstr
= nsc_db_int_key_getlogstr
;
737 nscdb
->getlogstr
= nsc_db_any_key_getlogstr
;
739 nscdb
->getlogstr
= getlogstr
;
742 /* The AVL tree based cache uses a hash table for quick access */
744 /* Determine hash table size based on type */
745 nscdb
->hash_type
= httype
;
749 htsize
= _NSC_INIT_HTSIZE_POWER2
;
754 htsize
= _NSC_INIT_HTSIZE_PRIME
;
757 nscdb
->htsize
= htsize
;
759 /* Create the hash table */
760 nscdb
->htable
= calloc(htsize
, sizeof (*(nscdb
->htable
)));
761 if (nscdb
->htable
== NULL
) {
762 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
763 (me
, "%s: memory allocation failure\n", name
);
767 /* Assign gethash routine */
768 if (gethash
== NULL
) {
769 if (_NSC_DB_CES_KEY(nscdb
))
770 nscdb
->gethash
= nsc_db_ces_key_gethash
;
771 else if (_NSC_DB_CIS_KEY(nscdb
))
772 nscdb
->gethash
= nsc_db_cis_key_gethash
;
773 else if (_NSC_DB_INT_KEY(nscdb
))
774 nscdb
->gethash
= nsc_db_int_key_gethash
;
778 nscdb
->gethash
= gethash
;
782 (void) mutex_init(&nscdb
->db_mutex
, USYNC_THREAD
, NULL
);
797 _nscd_cfg_cache_verify(
799 struct nscd_cfg_param_desc
*pdesc
,
800 nscd_cfg_id_t
*nswdb
,
801 nscd_cfg_flag_t dflag
,
802 nscd_cfg_error_t
**errorp
,
806 return (NSCD_SUCCESS
);
814 _nscd_cfg_cache_notify(
816 struct nscd_cfg_param_desc
*pdesc
,
817 nscd_cfg_id_t
*nswdb
,
818 nscd_cfg_flag_t dflag
,
819 nscd_cfg_error_t
**errorp
,
827 if (_nscd_cfg_flag_is_set(dflag
, NSCD_CFG_DFLAG_GROUP
)) {
828 if (_nscd_cfg_flag_is_set(pdesc
->pflag
,
829 NSCD_CFG_PFLAG_GLOBAL
)) {
831 global_cfg
= *(nscd_cfg_global_cache_t
*)data
;
832 } else if (_nscd_cfg_flag_is_set(dflag
,
833 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
834 /* non-global config for all dbs */
835 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
836 ctx
= cache_ctx_p
[i
];
838 return (NSCD_CTX_NOT_FOUND
);
839 (void) rw_wrlock(&ctx
->cfg_rwlp
);
840 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
841 ctx
->cfg_mtime
= time(NULL
);
842 (void) rw_unlock(&ctx
->cfg_rwlp
);
845 /* non-global config for a specific db */
847 /* ignore non-caching databases */
848 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
849 return (NSCD_SUCCESS
);
850 (void) rw_wrlock(&ctx
->cfg_rwlp
);
851 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
852 ctx
->cfg_mtime
= time(NULL
);
853 (void) rw_unlock(&ctx
->cfg_rwlp
);
855 return (NSCD_SUCCESS
);
858 /* individual data */
859 if (_nscd_cfg_flag_is_set(pdesc
->pflag
, NSCD_CFG_PFLAG_GLOBAL
)) {
861 dp
= (char *)&global_cfg
+ pdesc
->p_offset
;
862 (void) memcpy(dp
, data
, pdesc
->p_size
);
863 } else if (_nscd_cfg_flag_is_set(dflag
,
864 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
865 /* non-global config for all dbs */
866 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
867 ctx
= cache_ctx_p
[i
];
869 return (NSCD_CTX_NOT_FOUND
);
870 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
871 (void) rw_wrlock(&ctx
->cfg_rwlp
);
872 (void) memcpy(dp
, data
, pdesc
->p_size
);
873 ctx
->cfg_mtime
= time(NULL
);
874 (void) rw_unlock(&ctx
->cfg_rwlp
);
877 /* non-global config for a specific db */
879 /* ignore non-caching databases */
880 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
881 return (NSCD_SUCCESS
);
882 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
883 (void) rw_wrlock(&ctx
->cfg_rwlp
);
884 (void) memcpy(dp
, data
, pdesc
->p_size
);
885 ctx
->cfg_mtime
= time(NULL
);
886 (void) rw_unlock(&ctx
->cfg_rwlp
);
888 return (NSCD_SUCCESS
);
897 _nscd_cfg_cache_get_stat(
899 struct nscd_cfg_stat_desc
*sdesc
,
900 nscd_cfg_id_t
*nswdb
,
901 nscd_cfg_flag_t
*dflag
,
902 void (**free_stat
)(void *stat
),
903 nscd_cfg_error_t
**errorp
)
905 nscd_cfg_stat_cache_t
*statsp
, stats
;
910 statsp
= calloc(1, sizeof (*statsp
));
912 return (NSCD_NO_MEMORY
);
914 if (_nscd_cfg_flag_is_set(sdesc
->sflag
, NSCD_CFG_SFLAG_GLOBAL
)) {
915 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
916 if (cache_ctx_p
[i
] == NULL
)
919 (void) mutex_lock(&cache_ctx_p
[i
]->stats_mutex
);
920 stats
= cache_ctx_p
[i
]->stats
;
922 &cache_ctx_p
[i
]->stats_mutex
);
924 statsp
->pos_hits
+= stats
.pos_hits
;
925 statsp
->neg_hits
+= stats
.neg_hits
;
926 statsp
->pos_misses
+= stats
.pos_misses
;
927 statsp
->neg_misses
+= stats
.neg_misses
;
928 statsp
->entries
+= stats
.entries
;
929 statsp
->drop_count
+= stats
.drop_count
;
930 statsp
->wait_count
+= stats
.wait_count
;
931 statsp
->invalidate_count
+=
932 stats
.invalidate_count
;
935 if ((rc
= get_cache_ctx(nswdb
->name
, &ctx
)) != NSCD_SUCCESS
) {
939 (void) mutex_lock(&ctx
->stats_mutex
);
940 *statsp
= ctx
->stats
;
941 (void) mutex_unlock(&ctx
->stats_mutex
);
944 _NSC_GET_HITRATE(statsp
);
946 return (NSCD_SUCCESS
);
950 * This function should only be called when nscd is
954 nsc_info(nsc_ctx_t
*ctx
, char *dbname
, nscd_cfg_cache_t cfg
[],
955 nscd_cfg_stat_cache_t stats
[])
958 char *me
= "nsc_info";
969 rc
= get_cache_ctx(dbname
, &ctx1
);
970 if (rc
== NSCD_INVALID_ARGUMENT
) {
971 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
972 (me
, "%s: no cache context found\n", dbname
);
974 } else if (rc
== NSCD_NO_MEMORY
) {
975 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
976 (me
, "%s: unable to create cache context - no memory\n",
984 if (cfg
== NULL
|| stats
== NULL
)
987 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
989 ctx2
.dbname
= cache_name
[i
];
991 ctx2
.stats
= stats
[i
];
992 ctx_info_nolock(&ctx2
);
997 ctx_info_nolock(nsc_ctx_t
*ctx
) {
998 nscd_cfg_cache_t cfg
;
999 nscd_cfg_stat_cache_t stats
;
1002 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1003 (void) print_cfg(&cfg
);
1005 if (cfg
.enable
== nscd_false
)
1009 (void) print_stats(&stats
);
1013 ctx_info(nsc_ctx_t
*ctx
) {
1014 nscd_cfg_cache_t cfg
;
1015 nscd_cfg_stat_cache_t stats
;
1017 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1019 (void) rw_unlock(&ctx
->cfg_rwlp
);
1020 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1021 (void) print_cfg(&cfg
);
1023 if (cfg
.enable
== nscd_false
)
1026 (void) mutex_lock(&ctx
->stats_mutex
);
1028 (void) mutex_unlock(&ctx
->stats_mutex
);
1029 (void) print_stats(&stats
);
1034 * This function should only be called when nscd is
1038 nsc_dump(char *dbname
, int dbop
)
1042 nscd_bool_t enabled
;
1044 char *me
= "nsc_dump";
1047 if ((i
= get_cache_idx(dbname
)) == -1) {
1048 (void) fprintf(stdout
, gettext("invalid cache name\n"));
1050 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1051 (me
, "%s: invalid cache name\n", dbname
);
1052 return (NSCD_CACHE_INVALID_CACHE_NAME
);
1055 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1056 (void) fprintf(stdout
, gettext("no cache context\n"));
1058 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1059 (me
, "%s: no cache context\n", dbname
);
1060 return (NSCD_CACHE_NO_CACHE_CTX
);
1064 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1065 enabled
= ctx
->cfg
.enable
;
1066 (void) rw_unlock(&ctx
->cfg_rwlp
);
1068 if (enabled
== nscd_false
)
1069 return (NSCD_CACHE_DISABLED
);
1071 nscdb
= nsc_get_db(ctx
, dbop
);
1072 if (nscdb
== NULL
) {
1073 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1074 (me
, "%s:%d: no cache found\n", dbname
, dbop
);
1075 return (NSCD_CACHE_NO_CACHE_FOUND
);
1078 (void) mutex_lock(&nscdb
->db_mutex
);
1079 (void) queue_dump(nscdb
, now
);
1080 (void) hash_dump(nscdb
, now
);
1081 (void) avl_dump(nscdb
, now
);
1082 (void) mutex_unlock(&nscdb
->db_mutex
);
1083 return (NSCD_SUCCESS
);
1085 #endif /* NSCD_DEBUG */
1088 * These macros are for exclusive use of nsc_lookup
1090 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1091 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1095 nsc_lookup_no_cache(nsc_lookup_args_t
*largs
, const char *str
)
1097 char *me
= "nsc_lookup_no_cache";
1098 nss_status_t status
;
1100 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1101 (me
, "%s: name service lookup (bypassing cache)\n", str
);
1102 nss_psearch(largs
->buffer
, largs
->bufsize
);
1103 status
= NSCD_GET_STATUS(largs
->buffer
);
1104 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1105 (me
, "%s: name service lookup status = %d\n", str
, status
);
1106 if (status
== NSS_SUCCESS
) {
1108 } else if (status
== NSS_NOTFOUND
) {
1111 return (SERVERERROR
);
1116 * This function starts the revalidation and reaper threads
1120 start_threads(nsc_ctx_t
*ctx
)
1123 char *me
= "start_threads";
1126 * kick off the revalidate thread (if necessary)
1128 if (ctx
->revalidate_on
!= nscd_true
) {
1129 if (thr_create(NULL
, NULL
, (void *(*)(void *))revalidate
,
1130 ctx
, 0, NULL
) != 0) {
1132 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1133 (me
, "thr_create (revalidate thread for %s): %s\n",
1134 ctx
->dbname
, strerror(errnum
));
1137 ctx
->revalidate_on
= nscd_true
;
1141 * kick off the reaper thread (if necessary)
1143 if (ctx
->reaper_on
!= nscd_true
) {
1144 if (thr_create(NULL
, NULL
, (void *(*)(void *))reaper
,
1145 ctx
, 0, NULL
) != 0) {
1147 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1148 (me
, "thr_create (reaper thread for %s): %s\n",
1149 ctx
->dbname
, strerror(errnum
));
1152 ctx
->reaper_on
= nscd_true
;
1157 * Examine the packed buffer, see if the front-end parameters
1158 * indicate that the caller specified nsswitch config should be
1159 * used for the lookup. Return 1 if yes, otherwise 0.
1162 nsw_config_in_phdr(void *buf
)
1164 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buf
;
1167 char *me
= "nsw_config_in_phdr";
1169 off
= pbuf
->dbd_off
;
1172 pdbd
= (nss_dbd_t
*)((void *)((char *)pbuf
+ off
));
1173 if (pdbd
->o_default_config
== 0)
1176 if ((enum nss_dbp_flags
)pdbd
->flags
& NSS_USE_DEFAULT_CONFIG
) {
1177 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1178 (me
, "use caller specified nsswitch config\n");
1185 copy_result(void *rbuf
, void *cbuf
)
1187 nss_pheader_t
*rphdr
= (nss_pheader_t
*)rbuf
;
1188 nss_pheader_t
*cphdr
= (nss_pheader_t
*)cbuf
;
1189 char *me
= "copy_result";
1191 /* return NSS_ERROR if not enough room to copy result */
1192 if (cphdr
->data_len
+ 1 > rphdr
->data_len
) {
1193 NSCD_SET_STATUS(rphdr
, NSS_ERROR
, ERANGE
);
1198 if (cphdr
->data_len
== 0)
1199 return (NSS_SUCCESS
);
1201 dst
= (char *)rphdr
+ rphdr
->data_off
;
1202 (void) memcpy(dst
, (char *)cphdr
+ cphdr
->data_off
,
1204 rphdr
->data_len
= cphdr
->data_len
;
1205 /* some frontend code expects a terminating NULL char */
1206 *(dst
+ rphdr
->data_len
) = '\0';
1208 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1209 (me
, "cache data (len = %lld): >>%s<<\n",
1210 cphdr
->data_len
, (char *)cphdr
+ cphdr
->data_off
);
1212 return (NSS_SUCCESS
);
1217 get_dns_ttl(void *pbuf
, char *dbname
)
1219 nss_pheader_t
*phdr
= (nss_pheader_t
*)pbuf
;
1221 char *me
= "get_dns_ttl";
1223 /* if returned, dns ttl is stored in the extended data area */
1224 if (phdr
->ext_off
== 0)
1227 if (strcmp(dbname
, NSS_DBNAM_HOSTS
) != 0 &&
1228 strcmp(dbname
, NSS_DBNAM_IPNODES
) != 0)
1231 ttl
= *(nssuint_t
*)((void *)((char *)pbuf
+ phdr
->ext_off
));
1233 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1234 (me
, "dns ttl is %d seconds\n", ttl
);
1240 check_config(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
1241 char *whoami
, int flag
)
1245 char *me
= "check_config";
1248 nscdb
= largs
->nscdb
;
1250 /* see if the cached config needs update */
1251 if (nscdb
->cfg_mtime
!= ctx
->cfg_mtime
) {
1252 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1253 nscdb
->cfg
= ctx
->cfg
;
1254 nscdb
->cfg_mtime
= ctx
->cfg_mtime
;
1255 (void) rw_unlock(&ctx
->cfg_rwlp
);
1256 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1257 (me
, "config for context %s, database %s updated\n",
1258 ctx
->dbname
, nscdb
->name
);
1262 if (cfgp
->enable
== nscd_false
) {
1263 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1264 (me
, "%s: cache disabled\n", ctx
->dbname
);
1266 if (UPDATEBIT
& flag
)
1269 return (nsc_lookup_no_cache(largs
, whoami
));
1273 * if caller requests lookup using his
1274 * own nsswitch config, bypass cache
1276 if (nsw_config_in_phdr(largs
->buffer
))
1277 return (nsc_lookup_no_cache(largs
, whoami
));
1279 /* no need of cache if we are dealing with 0 ttls */
1280 if (cfgp
->pos_ttl
<= 0 && cfgp
->neg_ttl
<= 0) {
1281 if (flag
& UPDATEBIT
)
1283 else if (cfgp
->avoid_ns
== nscd_true
)
1284 return (SERVERERROR
);
1285 return (nsc_lookup_no_cache(largs
, whoami
));
1292 * Invalidate cache if database file has been modified.
1293 * See check_files config param for details.
1296 check_db_file(nsc_ctx_t
*ctx
, nscd_cfg_cache_t cfg
,
1297 char *whoami
, time_t now
)
1300 nscd_bool_t file_modified
= nscd_false
;
1301 char *me
= "check_db_file";
1303 if (cfg
.check_interval
!= 0 &&
1304 (now
- ctx
->file_chktime
) < cfg
.check_interval
)
1307 ctx
->file_chktime
= now
;
1308 if (stat(ctx
->file_name
, &buf
) == 0) {
1309 if (ctx
->file_mtime
== 0) {
1310 (void) mutex_lock(&ctx
->file_mutex
);
1311 if (ctx
->file_mtime
== 0) {
1312 ctx
->file_mtime
= buf
.st_mtime
;
1313 ctx
->file_size
= buf
.st_size
;
1314 ctx
->file_ino
= buf
.st_ino
;
1316 (void) mutex_unlock(&ctx
->file_mutex
);
1317 } else if (ctx
->file_mtime
< buf
.st_mtime
||
1318 ctx
->file_size
!= buf
.st_size
||
1319 ctx
->file_ino
!= buf
.st_ino
) {
1320 (void) mutex_lock(&ctx
->file_mutex
);
1321 if (ctx
->file_mtime
< buf
.st_mtime
||
1322 ctx
->file_size
!= buf
.st_size
||
1323 ctx
->file_ino
!= buf
.st_ino
) {
1324 file_modified
= nscd_true
;
1325 ctx
->file_mtime
= buf
.st_mtime
;
1326 ctx
->file_size
= buf
.st_size
;
1327 ctx
->file_ino
= buf
.st_ino
;
1329 (void) mutex_unlock(&ctx
->file_mutex
);
1333 if (file_modified
== nscd_true
) {
1334 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1335 (me
, "%s: file %s has been modified - invalidating cache\n",
1336 whoami
, ctx
->file_name
);
1337 ctx_invalidate(ctx
);
1342 lookup_int(nsc_lookup_args_t
*largs
, int flag
)
1346 nscd_cfg_cache_t cfg
;
1347 nsc_entry_t
*this_entry
;
1348 nsc_entry_stat_t
*this_stats
;
1349 nsc_action_t next_action
;
1350 nss_status_t status
;
1356 nss_XbyY_args_t args
;
1358 time_t now
= time(NULL
); /* current time */
1359 char *me
= "lookup_int";
1361 /* extract dbop, dbname, key and cred */
1362 status
= nss_packed_getkey(largs
->buffer
, largs
->bufsize
, &dbname
,
1364 if (status
!= NSS_SUCCESS
) {
1365 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1366 (me
, "nss_packed_getkey failure (%d)\n", status
);
1367 return (SERVERERROR
);
1370 /* get the cache context */
1371 if (largs
->ctx
== NULL
) {
1372 if (get_cache_ctx(dbname
, &largs
->ctx
) != NSCD_SUCCESS
) {
1373 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1374 (me
, "%s: no cache context found\n", dbname
);
1376 if (UPDATEBIT
& flag
)
1379 return (nsc_lookup_no_cache(largs
, dbname
));
1384 if (largs
->nscdb
== NULL
) {
1385 if ((largs
->nscdb
= nsc_get_db(ctx
, dbop
)) == NULL
) {
1386 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1387 (me
, "%s:%d: no cache found\n",
1390 if (UPDATEBIT
& flag
)
1393 return (nsc_lookup_no_cache(largs
, dbname
));
1397 nscdb
= largs
->nscdb
;
1399 _NSCD_LOG_IF(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ALL
) {
1400 (void) nscdb
->getlogstr(nscdb
->name
, whoami
,
1401 sizeof (whoami
), &args
);
1404 if (UPDATEBIT
& flag
) {
1405 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1406 (me
, "%s: refresh start\n", whoami
);
1408 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1409 (me
, "%s: lookup start\n", whoami
);
1412 cfg_rc
= check_config(largs
, &cfg
, whoami
, flag
);
1413 if (cfg_rc
!= CONTINUE
)
1417 * Invalidate cache if file has been modified.
1419 if (cfg
.check_files
== nscd_true
)
1420 check_db_file(ctx
, cfg
, whoami
, now
);
1422 (void) mutex_lock(&nscdb
->db_mutex
);
1424 /* Lookup the cache table */
1426 delete = nscd_false
;
1427 rc
= lookup_cache(largs
, &cfg
, &args
, whoami
, &this_entry
);
1428 if (rc
!= NSCD_SUCCESS
) {
1429 (void) mutex_unlock(&nscdb
->db_mutex
);
1431 /* Either no entry and avoid name service */
1432 if (rc
== NSCD_DB_ENTRY_NOT_FOUND
||
1433 rc
== NSCD_INVALID_ARGUMENT
)
1436 /* OR memory error */
1437 return (SERVERERROR
);
1440 /* get the stats from the entry */
1441 this_stats
= &this_entry
->stats
;
1444 * What should we do next ?
1446 switch (this_stats
->status
) {
1449 next_action
= _NSC_NSLOOKUP
;
1451 case ST_UPDATE_PENDING
:
1452 if (flag
& UPDATEBIT
) {
1453 (void) mutex_unlock(&nscdb
->db_mutex
);
1455 } else if (this_stats
->timestamp
< now
)
1456 next_action
= _NSC_WAIT
;
1458 next_action
= _NSC_USECACHED
;
1460 case ST_LOOKUP_PENDING
:
1461 if (flag
& UPDATEBIT
) {
1462 (void) mutex_unlock(&nscdb
->db_mutex
);
1465 next_action
= _NSC_WAIT
;
1468 if (cfg
.avoid_ns
== nscd_true
) {
1469 (void) mutex_unlock(&nscdb
->db_mutex
);
1472 /* otherwise reuse the entry */
1473 (void) memset(this_stats
, 0, sizeof (*this_stats
));
1474 next_action
= _NSC_NSLOOKUP
;
1477 if (cfg
.avoid_ns
== nscd_true
)
1478 next_action
= _NSC_USECACHED
;
1479 else if ((flag
& UPDATEBIT
) ||
1480 (this_stats
->timestamp
< now
)) {
1481 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1482 (me
, "%s: cached entry needs to be updated\n",
1484 next_action
= _NSC_NSLOOKUP
;
1486 next_action
= _NSC_USECACHED
;
1490 if (next_action
== _NSC_WAIT
) {
1491 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1492 (me
, "%s: need to wait\n", whoami
);
1494 /* do we have clearance ? */
1495 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1497 (void) mutex_lock(&ctx
->stats_mutex
);
1498 ctx
->stats
.drop_count
++;
1499 (void) mutex_unlock(&ctx
->stats_mutex
);
1500 _NSCD_LOG(NSCD_LOG_CACHE
,
1501 NSCD_LOG_LEVEL_DEBUG_6
)
1502 (me
, "%s: throttling load\n", whoami
);
1503 (void) mutex_unlock(&nscdb
->db_mutex
);
1504 NSC_LOOKUP_LOG(WARNING
,
1505 "%s: no clearance to wait\n");
1509 (void) nscd_wait(ctx
, nscdb
, this_entry
);
1510 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1518 if (!(UPDATEBIT
& flag
))
1519 this_stats
->hits
++; /* update hit count */
1521 if (next_action
== _NSC_NSLOOKUP
) {
1523 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1524 (me
, "%s: name service lookup required\n", whoami
);
1526 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1527 if (delete == nscd_true
)
1528 delete_entry(nscdb
, ctx
, this_entry
);
1530 this_stats
->status
= ST_DISCARD
;
1531 (void) mutex_lock(&ctx
->stats_mutex
);
1532 ctx
->stats
.drop_count
++;
1533 (void) mutex_unlock(&ctx
->stats_mutex
);
1534 (void) mutex_unlock(&nscdb
->db_mutex
);
1535 NSC_LOOKUP_LOG(WARNING
,
1536 "%s: no clearance for lookup\n");
1540 /* block any threads accessing this entry */
1541 this_stats
->status
= (flag
& UPDATEBIT
) ?
1542 ST_UPDATE_PENDING
: ST_LOOKUP_PENDING
;
1544 /* release lock and do name service lookup */
1545 (void) mutex_unlock(&nscdb
->db_mutex
);
1546 nss_psearch(largs
->buffer
, largs
->bufsize
);
1547 status
= NSCD_GET_STATUS(largs
->buffer
);
1548 (void) mutex_lock(&nscdb
->db_mutex
);
1549 this_stats
->status
= 0;
1550 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1552 /* signal waiting threads */
1553 (void) nscd_signal(ctx
, nscdb
, this_entry
);
1555 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1556 (me
, "%s: name service lookup status = %d\n",
1559 if (status
== NSS_SUCCESS
) {
1563 * data found in name service
1566 status
= dup_packed_buffer(largs
, this_entry
);
1567 if (status
!= NSS_SUCCESS
) {
1568 delete_entry(nscdb
, ctx
, this_entry
);
1569 (void) mutex_unlock(&nscdb
->db_mutex
);
1570 NSC_LOOKUP_LOG(ERROR
,
1571 "%s: failed to update cache\n");
1572 return (SERVERERROR
);
1576 * store unpacked key in cache
1578 status
= nss_packed_getkey(this_entry
->buffer
,
1579 this_entry
->bufsize
,
1580 &dbname
, &dbop
, &args
);
1581 if (status
!= NSS_SUCCESS
) {
1582 delete_entry(nscdb
, ctx
, this_entry
);
1583 (void) mutex_unlock(&nscdb
->db_mutex
);
1584 NSC_LOOKUP_LOG(ERROR
,
1585 "%s: failed to extract key\n");
1586 return (SERVERERROR
);
1588 this_entry
->key
= args
.key
; /* struct copy */
1590 /* update +ve miss count */
1591 if (!(UPDATEBIT
& flag
)) {
1592 (void) mutex_lock(&ctx
->stats_mutex
);
1593 ctx
->stats
.pos_misses
++;
1594 (void) mutex_unlock(&ctx
->stats_mutex
);
1597 /* update +ve ttl */
1598 ttl
= get_dns_ttl(largs
->buffer
, dbname
);
1599 /* honor the dns ttl less than postive ttl */
1600 if (ttl
< 0 || ttl
> cfg
.pos_ttl
)
1602 this_stats
->timestamp
= time(NULL
) + ttl
;
1605 * start the revalidation and reaper threads
1606 * if not already started
1610 (void) mutex_unlock(&nscdb
->db_mutex
);
1611 NSC_LOOKUP_LOG(DEBUG
,
1612 "%s: cache updated with positive entry\n");
1614 } else if (status
== NSS_NOTFOUND
) {
1616 * data not found in name service
1619 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1620 (me
, "%s: name service lookup failed\n", whoami
);
1622 if (NSCD_GET_ERRNO(largs
->buffer
) == ERANGE
) {
1623 delete_entry(nscdb
, ctx
, this_entry
);
1624 (void) mutex_unlock(&nscdb
->db_mutex
);
1625 NSC_LOOKUP_LOG(DEBUG
,
1626 "%s: ERANGE, cache not updated "
1627 "with negative entry\n");
1631 status
= dup_packed_buffer(largs
, this_entry
);
1632 if (status
!= NSS_SUCCESS
) {
1633 delete_entry(nscdb
, ctx
, this_entry
);
1634 (void) mutex_unlock(&nscdb
->db_mutex
);
1635 NSC_LOOKUP_LOG(ERROR
,
1636 "%s: failed to update cache\n");
1637 return (SERVERERROR
);
1640 /* store unpacked key in cache */
1641 status
= nss_packed_getkey(this_entry
->buffer
,
1642 this_entry
->bufsize
,
1643 &dbname
, &dbop
, &args
);
1644 if (status
!= NSS_SUCCESS
) {
1645 delete_entry(nscdb
, ctx
, this_entry
);
1646 (void) mutex_unlock(&nscdb
->db_mutex
);
1647 NSC_LOOKUP_LOG(ERROR
,
1648 "%s: failed to extract key\n");
1649 return (SERVERERROR
);
1651 this_entry
->key
= args
.key
; /* struct copy */
1653 /* update -ve ttl */
1654 this_stats
->timestamp
= time(NULL
) + cfg
.neg_ttl
;
1656 /* update -ve miss count */
1657 if (!(UPDATEBIT
& flag
)) {
1658 (void) mutex_lock(&ctx
->stats_mutex
);
1659 ctx
->stats
.neg_misses
++;
1660 (void) mutex_unlock(&ctx
->stats_mutex
);
1664 * start the revalidation and reaper threads
1665 * if not already started
1669 (void) mutex_unlock(&nscdb
->db_mutex
);
1670 NSC_LOOKUP_LOG(DEBUG
,
1671 "%s: cache updated with negative entry\n");
1675 * name service lookup failed
1677 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1678 (me
, "%s: name service lookup failed\n", whoami
);
1680 errnum
= NSCD_GET_ERRNO(largs
->buffer
);
1681 if (delete == nscd_true
)
1682 delete_entry(nscdb
, ctx
, this_entry
);
1684 this_stats
->status
= ST_DISCARD
;
1686 (void) mutex_unlock(&nscdb
->db_mutex
);
1687 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1688 (me
, "%s: name service lookup failed "
1689 "(status=%d, errno=%d)\n",
1690 whoami
, status
, errnum
);
1692 return (SERVERERROR
);
1694 } else if (next_action
== _NSC_USECACHED
) {
1696 * found entry in cache
1698 if (UPDATEBIT
& flag
) {
1699 (void) mutex_unlock(&nscdb
->db_mutex
);
1700 NSC_LOOKUP_LOG(DEBUG
, "%s: no need to update\n");
1704 if (NSCD_GET_STATUS((nss_pheader_t
*)this_entry
->buffer
) ==
1707 (void) mutex_lock(&ctx
->stats_mutex
);
1708 ctx
->stats
.pos_hits
++;
1709 (void) mutex_unlock(&ctx
->stats_mutex
);
1711 /* update response buffer */
1712 if (copy_result(largs
->buffer
,
1713 this_entry
->buffer
) != NSS_SUCCESS
) {
1714 (void) mutex_unlock(&nscdb
->db_mutex
);
1715 NSC_LOOKUP_LOG(ERROR
,
1716 "%s: response buffer insufficient\n");
1717 return (SERVERERROR
);
1720 (void) mutex_unlock(&nscdb
->db_mutex
);
1721 NSC_LOOKUP_LOG(DEBUG
,
1722 "%s: positive entry in cache\n");
1726 (void) mutex_lock(&ctx
->stats_mutex
);
1727 ctx
->stats
.neg_hits
++;
1728 (void) mutex_unlock(&ctx
->stats_mutex
);
1730 NSCD_SET_STATUS((nss_pheader_t
*)largs
->buffer
,
1731 NSCD_GET_STATUS(this_entry
->buffer
),
1732 NSCD_GET_ERRNO(this_entry
->buffer
));
1733 NSCD_SET_HERRNO((nss_pheader_t
*)largs
->buffer
,
1734 NSCD_GET_HERRNO(this_entry
->buffer
));
1736 (void) mutex_unlock(&nscdb
->db_mutex
);
1737 NSC_LOOKUP_LOG(DEBUG
,
1738 "%s: negative entry in cache\n");
1743 (void) mutex_unlock(&nscdb
->db_mutex
);
1744 NSC_LOOKUP_LOG(ERROR
, "%s: cache backend failure\n");
1745 return (SERVERERROR
);
1749 * NSCD cache backend lookup function
1753 nsc_lookup(nsc_lookup_args_t
*largs
, int flag
) {
1755 nss_pheader_t
*phdr
= (nss_pheader_t
*)largs
->buffer
;
1758 rc
= lookup_int(largs
, 0);
1760 if (NSCD_GET_STATUS(phdr
) == NSS_TRYLOCAL
)
1766 NSCD_SET_STATUS(phdr
, NSS_SUCCESS
, 0);
1770 NSCD_SET_STATUS(phdr
, NSS_NOTFOUND
, -1);
1775 * status and errno should have been set in the phdr,
1776 * if not, set status to NSS_ERROR
1778 if (NSCD_STATUS_IS_OK(phdr
)) {
1779 NSCD_SET_STATUS(phdr
, NSS_ERROR
, 0);
1784 NSCD_SET_STATUS(phdr
, NSS_TRYLOCAL
, -1);
1791 init_cache_ctx(int i
) {
1794 ctx
= calloc(1, sizeof (nsc_ctx_t
));
1798 /* init locks and semaphores */
1799 (void) mutex_init(&ctx
->file_mutex
, USYNC_THREAD
, NULL
);
1800 (void) rwlock_init(&ctx
->cfg_rwlp
, USYNC_THREAD
, NULL
);
1801 (void) mutex_init(&ctx
->stats_mutex
, USYNC_THREAD
, NULL
);
1802 (void) _nscd_init_cache_sema(&ctx
->throttle_sema
, cache_name
[i
]);
1803 cache_init_ctx
[i
](ctx
);
1804 cache_ctx_p
[i
] = ctx
;
1811 revalidate(nsc_ctx_t
*ctx
)
1814 int i
, slp
, interval
, count
;
1816 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1817 slp
= ctx
->cfg
.pos_ttl
;
1818 count
= ctx
->cfg
.keephot
;
1819 (void) rw_unlock(&ctx
->cfg_rwlp
);
1824 interval
= (slp
/2)/count
;
1827 (void) sleep(slp
*2/3);
1828 for (i
= 0; i
< ctx
->db_count
; i
++) {
1829 getxy_keepalive(ctx
, ctx
->nsc_db
[i
],
1840 getxy_keepalive(nsc_ctx_t
*ctx
, nsc_db_t
*nscdb
, int keep
, int interval
)
1842 nsc_keephot_t
*table
;
1843 nsc_entry_t
*entry
, *ptr
;
1845 nsc_lookup_args_t
*largs
;
1846 nss_pheader_t
*phdr
;
1848 char *me
= "getxy_keepalive";
1850 /* we won't be here if keep == 0 so need to check that */
1852 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1853 (me
, "%s: keep alive\n", nscdb
->name
);
1855 if ((table
= maken(keep
)) == NULL
) {
1856 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1857 (me
, "memory allocation failure\n");
1861 (void) mutex_lock(&nscdb
->db_mutex
);
1862 entry
= nscdb
->qtail
;
1863 while (entry
!= NULL
) {
1864 /* leave pending calls alone */
1865 if (!(entry
->stats
.status
& ST_PENDING
)) {
1867 (void) insertn(table
, entry
->stats
.hits
, entry
);
1869 entry
= entry
->qnext
;
1871 for (i
= 1; i
<= keep
; i
++) {
1872 if (table
[i
].ptr
== NULL
)
1874 ptr
= (nsc_entry_t
*)table
[i
].ptr
;
1875 phdr
= (nss_pheader_t
*)ptr
->buffer
;
1876 if (NSCD_GET_STATUS(phdr
) == NSS_SUCCESS
)
1878 * for positive cache, in addition to the packed
1879 * header size, allocate twice the size of the
1880 * existing result (in case the result grows
1881 * larger) plus 2K (for the file/compat backend to
1882 * process a possible large entry in the /etc files)
1884 bufsiz
= phdr
->data_off
+ 2 * phdr
->data_len
+ 2048;
1887 * for negative cache, allocate 8K buffer to
1888 * hold result in case the next lookup may
1889 * return something (in addition to the
1890 * packed header size)
1892 bufsiz
= phdr
->data_off
+ 8096;
1893 table
[i
].ptr
= malloc(bufsiz
);
1894 if (table
[i
].ptr
== NULL
) {
1895 (void) mutex_unlock(&nscdb
->db_mutex
);
1896 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1897 (me
, "memory allocation failure\n");
1900 (void) memcpy(table
[i
].ptr
, ptr
->buffer
, ptr
->bufsize
);
1901 ((nss_pheader_t
*)table
[i
].ptr
)->pbufsiz
= bufsiz
;
1902 table
[i
].num
= bufsiz
;
1904 (void) mutex_unlock(&nscdb
->db_mutex
);
1906 /* launch update thread for each keep hot entry */
1907 for (i
= keep
; i
> 0; i
--) {
1908 if (table
[i
].ptr
== NULL
)
1909 continue; /* unused slot in table */
1910 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1911 (me
, "%s: launching update\n", nscdb
->name
);
1912 largs
= (nsc_lookup_args_t
*)malloc(sizeof (*largs
));
1913 if (largs
== NULL
) {
1914 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1915 (me
, "memory allocation failure\n");
1918 largs
->buffer
= table
[i
].ptr
;
1919 largs
->bufsize
= table
[i
].num
;
1921 largs
->nscdb
= nscdb
;
1922 if (launch_update(largs
) < 0)
1924 (void) sleep(interval
);
1928 * The update thread will handle freeing of buffer and largs.
1929 * Free the table here.
1936 launch_update(nsc_lookup_args_t
*in
)
1938 char *me
= "launch_update";
1941 errnum
= thr_create(NULL
, NULL
, (void *(*)(void*))do_update
,
1942 in
, 0|THR_DETACHED
, NULL
);
1944 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1945 (me
, "%s: thread creation failure (%d)\n",
1946 in
->nscdb
->name
, errnum
);
1954 do_update(nsc_lookup_args_t
*in
) {
1955 nss_pheader_t
*phdr
= (nss_pheader_t
*)in
->buffer
;
1957 /* update the length of the data buffer */
1958 phdr
->data_len
= phdr
->pbufsiz
- phdr
->data_off
;
1960 (void) lookup_int(in
, UPDATEBIT
);
1970 nsc_invalidate(nsc_ctx_t
*ctx
, char *dbname
, nsc_ctx_t
**ctxs
)
1973 char *me
= "nsc_invalidate";
1976 ctx_invalidate(ctx
);
1981 if ((i
= get_cache_idx(dbname
)) == -1) {
1982 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1983 (me
, "%s: invalid cache name\n", dbname
);
1986 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1987 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1988 (me
, "%s: no cache context found\n",
1992 ctx_invalidate(ctx
);
1999 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
2000 if (ctxs
[i
] != NULL
)
2001 ctx_invalidate(ctxs
[i
]);
2007 * Invalidate cache by context
2010 ctx_invalidate(nsc_ctx_t
*ctx
)
2014 char *me
= "ctx_invalidate";
2016 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2017 (me
, "%s: invalidate cache\n", ctx
->dbname
);
2019 for (i
= 0; i
< ctx
->db_count
; i
++) {
2020 if (ctx
->nsc_db
[i
] == NULL
)
2022 (void) mutex_lock(&ctx
->nsc_db
[i
]->db_mutex
);
2023 entry
= ctx
->nsc_db
[i
]->qtail
;
2024 while (entry
!= NULL
) {
2025 /* leave pending calls alone */
2026 if (!(entry
->stats
.status
& ST_PENDING
))
2027 entry
->stats
.status
= ST_DISCARD
;
2028 entry
= entry
->qnext
;
2030 (void) mutex_unlock(&ctx
->nsc_db
[i
]->db_mutex
);
2033 (void) mutex_lock(&ctx
->stats_mutex
);
2034 ctx
->stats
.invalidate_count
++;
2035 (void) mutex_unlock(&ctx
->stats_mutex
);
2043 * nscdb->db_mutex lock must be held before calling this function
2046 delete_entry(nsc_db_t
*nscdb
, nsc_ctx_t
*ctx
, nsc_entry_t
*entry
) {
2049 avl_remove(&nscdb
->tree
, entry
);
2050 HASH_REMOVE(nscdb
, entry
, hash
, nscd_false
);
2051 queue_remove(nscdb
, entry
);
2052 if (entry
->buffer
!= NULL
) {
2053 free(entry
->buffer
);
2054 entry
->buffer
= NULL
;
2056 umem_cache_free(nsc_entry_cache
, entry
);
2057 (void) mutex_lock(&ctx
->stats_mutex
);
2058 ctx
->stats
.entries
--;
2059 (void) mutex_unlock(&ctx
->stats_mutex
);
2064 lookup_cache(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
2065 nss_XbyY_args_t
*argp
, char *whoami
, nsc_entry_t
**entry
)
2072 nsc_entry_t find_entry
, *node
;
2073 char *me
= "lookup_cache";
2076 nscdb
= largs
->nscdb
;
2078 /* set the search key */
2079 find_entry
.key
= argp
->key
; /* struct copy (not deep) */
2081 /* lookup the hash table ==> O(1) */
2082 if (nscdb
->htable
) {
2083 *entry
= hash_find(nscdb
, &find_entry
, &hash
, nscd_true
);
2084 if (*entry
!= NULL
) {
2085 (void) queue_adjust(nscdb
, *entry
);
2086 return (NSCD_SUCCESS
);
2090 /* if not found, lookup the AVL tree ==> O(log n) */
2091 *entry
= (nsc_entry_t
*)avl_find(&nscdb
->tree
, &find_entry
, &pos
);
2092 if (*entry
!= NULL
) {
2093 (void) queue_adjust(nscdb
, *entry
);
2094 /* move it to the hash table */
2095 if (nscdb
->htable
) {
2096 if (nscdb
->htable
[hash
] == NULL
||
2097 (*entry
)->stats
.hits
>=
2098 nscdb
->htable
[hash
]->stats
.hits
) {
2099 nscdb
->htable
[hash
] = *entry
;
2102 return (NSCD_SUCCESS
);
2105 /* entry not found in the cache */
2106 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2107 (me
, "%s: cache miss\n", whoami
);
2109 if (cfgp
->avoid_ns
== nscd_true
) {
2110 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2111 (me
, "%s: avoid name service\n", whoami
);
2112 return (NSCD_DB_ENTRY_NOT_FOUND
);
2115 /* allocate memory for new entry (stub) */
2116 *entry
= (nsc_entry_t
*)umem_cache_alloc(nsc_entry_cache
,
2118 if (*entry
== NULL
) {
2119 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2120 (me
, "%s: memory allocation failure\n", whoami
);
2121 return (NSCD_NO_MEMORY
);
2123 (void) memset(*entry
, 0, sizeof (**entry
));
2126 * Note that the actual data for the key is stored within
2127 * the largs->buffer (input buffer to nsc_lookup).
2128 * find_entry.key only contains pointers to this data.
2130 * If largs->buffer will be re-allocated by nss_psearch
2131 * then (*entry)->key will have dangling pointers.
2132 * In such case, the following assignment needs to be
2133 * replaced by code that duplicates the key.
2135 (*entry
)->key
= find_entry
.key
;
2138 * Add the entry to the cache.
2140 avl_insert(&nscdb
->tree
, *entry
, pos
); /* O(log n) */
2141 (void) queue_adjust(nscdb
, *entry
); /* constant */
2142 if (nscdb
->htable
) /* constant */
2143 nscdb
->htable
[hash
] = *entry
;
2144 (*entry
)->stats
.status
= ST_NEW_ENTRY
;
2146 (void) mutex_lock(&ctx
->stats_mutex
);
2147 nentries
= ++(ctx
->stats
.entries
);
2148 (void) mutex_unlock(&ctx
->stats_mutex
);
2150 /* Have we exceeded max entries ? */
2151 if (cfgp
->maxentries
> 0 && nentries
> cfgp
->maxentries
) {
2152 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2153 (me
, "%s: maximum entries exceeded -- "
2154 "deleting least recently used entry\n",
2157 node
= nscdb
->qhead
;
2158 while (node
!= NULL
&& node
!= *entry
) {
2159 if (node
->stats
.status
== ST_DISCARD
||
2160 !(node
->stats
.status
& ST_PENDING
)) {
2161 delete_entry(nscdb
, ctx
, node
);
2168 * It's okay if we were not able to find one to delete.
2169 * The reaper (when invoked) will return the cache to a
2174 return (NSCD_SUCCESS
);
2178 reaper(nsc_ctx_t
*ctx
)
2180 uint_t ttl
, extra_sleep
, total_sleep
, intervals
;
2181 uint_t nodes_per_interval
, seconds_per_interval
;
2182 ulong_t nsc_entries
;
2183 char *me
= "reaper";
2186 (void) mutex_lock(&ctx
->stats_mutex
);
2187 nsc_entries
= ctx
->stats
.entries
;
2188 (void) mutex_unlock(&ctx
->stats_mutex
);
2190 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2191 ttl
= ctx
->cfg
.pos_ttl
;
2192 (void) rw_unlock(&ctx
->cfg_rwlp
);
2194 if (nsc_entries
== 0) {
2195 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2196 (me
, "%s: nothing to reap\n", ctx
->dbname
);
2198 /* sleep for atleast 60 seconds */
2201 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2202 (me
, "%s: sleep %d\n", ctx
->dbname
, ttl
);
2207 if (ttl
< 32) ttl
= 32;
2208 if (ttl
> (1<<28)) ttl
= 1<<28;
2211 * minimum nodes_per_interval = 256 or 1<<8
2212 * maximum nodes_per_interval = nsc_entries
2213 * minimum seconds_per_interval = 32 or 1<<5
2214 * maximum_seconds_per_interval = ttl
2216 if (nsc_entries
<= ttl
) {
2217 intervals
= (nsc_entries
>> 8) + 1;
2218 seconds_per_interval
= ttl
/ intervals
;
2219 nodes_per_interval
= 256;
2221 intervals
= (ttl
>> 5) + 1;
2222 seconds_per_interval
= 32;
2223 nodes_per_interval
= nsc_entries
/ intervals
;
2224 if (nodes_per_interval
< 256)
2225 nodes_per_interval
= 256;
2228 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2229 (me
, "%s: total entries = %d, "
2230 "seconds per interval = %d, "
2231 "nodes per interval = %d\n",
2232 ctx
->dbname
, nsc_entries
, seconds_per_interval
,
2233 nodes_per_interval
);
2234 total_sleep
= reap_cache(ctx
, nodes_per_interval
,
2235 seconds_per_interval
);
2236 extra_sleep
= 1 + ttl
- total_sleep
;
2237 if (extra_sleep
> 0)
2238 (void) sleep(extra_sleep
);
2244 reap_cache(nsc_ctx_t
*ctx
, uint_t nodes_per_interval
,
2245 uint_t seconds_per_interval
)
2247 uint_t nodes_togo
, total_sleep
;
2249 nsc_entry_t
*node
, *next_node
;
2251 uint_t primes
[] = {_NSC_HTSIZE_PRIMES
};
2252 ulong_t count
, nentries
, maxentries
;
2253 int i
, slot
, value
, newhtsize
;
2254 char *me
= "reap_cache";
2258 nodes_togo
= nodes_per_interval
;
2261 for (i
= 0; i
< ctx
->db_count
; i
++) {
2262 nscdb
= ctx
->nsc_db
[i
];
2263 (void) mutex_lock(&nscdb
->db_mutex
);
2264 nscdb
->reap_node
= nscdb
->qtail
;
2265 while (nscdb
->reap_node
!= NULL
) {
2266 if (nodes_togo
== 0) {
2267 (void) mutex_unlock(&nscdb
->db_mutex
);
2268 (void) sleep(seconds_per_interval
);
2269 total_sleep
+= seconds_per_interval
;
2270 nodes_togo
= nodes_per_interval
;
2272 (void) mutex_lock(&nscdb
->db_mutex
);
2274 /* delete ST_DISCARD and expired nodes */
2275 if ((node
= nscdb
->reap_node
) == NULL
)
2277 if (node
->stats
.status
== ST_DISCARD
||
2278 (!(node
->stats
.status
& ST_PENDING
) &&
2279 node
->stats
.timestamp
< now
)) {
2281 * Delete entry if its discard flag is
2282 * set OR if it has expired. Entries
2283 * with pending updates are not
2285 * nscdb->reap_node will be adjusted
2288 delete_entry(nscdb
, ctx
, node
);
2291 nscdb
->reap_node
= node
->qnext
;
2296 if (nscdb
->htsize
== 0) {
2297 (void) mutex_unlock(&nscdb
->db_mutex
);
2302 * Dynamic adjustment of hash table size.
2304 * Hash table size is roughly 1/8th of the
2305 * total entries. However the size is changed
2306 * only when the number of entries double or
2309 nentries
= avl_numnodes(&nscdb
->tree
);
2310 for (slot
= 0, value
= _NSC_INIT_HTSIZE_SLOT_VALUE
;
2311 slot
< _NSC_HTSIZE_NUM_SLOTS
&& nentries
> value
;
2312 value
= (value
<< 1) + 1, slot
++)
2314 if (nscdb
->hash_type
== nsc_ht_power2
)
2315 newhtsize
= _NSC_INIT_HTSIZE_POWER2
<< slot
;
2317 newhtsize
= primes
[slot
];
2319 /* Recommended size is same as the current size. Done */
2320 if (nscdb
->htsize
== newhtsize
) {
2321 (void) mutex_unlock(&nscdb
->db_mutex
);
2325 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2326 (me
, "%s: resizing hash table from %d to %d\n",
2327 nscdb
->name
, nscdb
->htsize
, newhtsize
);
2330 * Dump old hashes because it would be time
2331 * consuming to rehash them.
2333 (void) free(nscdb
->htable
);
2334 nscdb
->htable
= calloc(newhtsize
, sizeof (*(nscdb
->htable
)));
2335 if (nscdb
->htable
== NULL
) {
2336 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2337 (me
, "%s: memory allocation failure\n",
2339 /* -1 to try later */
2342 nscdb
->htsize
= newhtsize
;
2344 (void) mutex_unlock(&nscdb
->db_mutex
);
2347 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2348 (me
, "%s: reaped %lu entries\n", ctx
->dbname
, count
);
2351 * if cache is almost full then reduce it to a safe level by
2352 * evicting LRU entries
2355 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2356 maxentries
= ctx
->cfg
.maxentries
;
2357 (void) rw_unlock(&ctx
->cfg_rwlp
);
2359 /* No limit on number of entries. Done */
2360 if (maxentries
== 0)
2363 (void) mutex_lock(&ctx
->stats_mutex
);
2364 nentries
= ctx
->stats
.entries
;
2365 (void) mutex_unlock(&ctx
->stats_mutex
);
2367 /* what is the percentage of cache used ? */
2368 value
= (nentries
* 100) / maxentries
;
2369 if (value
< _NSC_EVICTION_START_LEVEL
)
2373 * cache needs to be reduced to a safe level
2375 value
-= _NSC_EVICTION_SAFE_LEVEL
;
2376 for (i
= 0, count
= 0; i
< ctx
->db_count
; i
++) {
2378 * Reduce each subcache by 'value' percent
2380 nscdb
= ctx
->nsc_db
[i
];
2381 (void) mutex_lock(&nscdb
->db_mutex
);
2382 nodes_togo
= (value
* avl_numnodes(&nscdb
->tree
)) / 100;
2384 /* Start from LRU entry i.e queue head */
2385 next_node
= nscdb
->qhead
;
2386 while (nodes_togo
> 0 && next_node
!= NULL
) {
2388 next_node
= next_node
->qprev
;
2389 if (node
->stats
.status
== ST_DISCARD
||
2390 !(node
->stats
.status
& ST_PENDING
)) {
2391 /* Leave nodes with pending updates alone */
2392 delete_entry(nscdb
, ctx
, node
);
2397 (void) mutex_unlock(&nscdb
->db_mutex
);
2400 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2401 (me
, "%s: evicted %lu LRU entries\n", ctx
->dbname
, count
);
2404 return (total_sleep
);