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.
25 * Copyright 2018 Joyent, Inc.
29 * Cache routines for nscd
41 #include <sys/types.h>
45 #include <nss_common.h>
53 #include "nscd_door.h"
55 #include "nscd_config.h"
56 #include "nscd_frontend.h"
57 #include "nscd_switch.h"
61 #define SERVERERROR -2
65 static nsc_db_t
*nsc_get_db(nsc_ctx_t
*, int);
66 static nscd_rc_t
lookup_cache(nsc_lookup_args_t
*, nscd_cfg_cache_t
*,
67 nss_XbyY_args_t
*, char *, nsc_entry_t
**);
68 static uint_t
reap_cache(nsc_ctx_t
*, uint_t
, uint_t
);
69 static void delete_entry(nsc_db_t
*, nsc_ctx_t
*, nsc_entry_t
*);
70 static void print_stats(nscd_cfg_stat_cache_t
*);
71 static void print_cfg(nscd_cfg_cache_t
*);
72 static int lookup_int(nsc_lookup_args_t
*, int);
75 static void print_entry(nsc_db_t
*, time_t, nsc_entry_t
*);
76 static void avl_dump(nsc_db_t
*, time_t);
77 static void hash_dump(nsc_db_t
*, time_t);
78 #endif /* NSCD_DEBUG */
79 static nsc_entry_t
*hash_find(nsc_db_t
*, nsc_entry_t
*, uint_t
*, nscd_bool_t
);
81 static void queue_adjust(nsc_db_t
*, nsc_entry_t
*);
82 static void queue_remove(nsc_db_t
*, nsc_entry_t
*);
84 static void queue_dump(nsc_db_t
*, time_t);
85 #endif /* NSCD_DEBUG */
87 static int launch_update(nsc_lookup_args_t
*);
88 static void do_update(nsc_lookup_args_t
*);
89 static void getxy_keepalive(nsc_ctx_t
*, nsc_db_t
*, int, int);
91 static void ctx_info(nsc_ctx_t
*);
92 static void ctx_info_nolock(nsc_ctx_t
*);
93 static void ctx_invalidate(nsc_ctx_t
*);
95 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
96 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
97 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
99 static int nsc_db_cis_key_compar(const void *, const void *);
100 static int nsc_db_ces_key_compar(const void *, const void *);
101 static int nsc_db_int_key_compar(const void *, const void *);
103 static uint_t
nsc_db_cis_key_gethash(nss_XbyY_key_t
*, int);
104 static uint_t
nsc_db_ces_key_gethash(nss_XbyY_key_t
*, int);
105 static uint_t
nsc_db_int_key_gethash(nss_XbyY_key_t
*, int);
107 static umem_cache_t
*nsc_entry_cache
;
109 static nsc_ctx_t
*init_cache_ctx(int);
110 static void reaper(nsc_ctx_t
*);
111 static void revalidate(nsc_ctx_t
*);
114 dup_packed_buffer(void *src
, void *dst
) {
115 nsc_lookup_args_t
*s
= (nsc_lookup_args_t
*)src
;
116 nsc_entry_t
*d
= (nsc_entry_t
*)dst
;
117 nss_pheader_t
*sphdr
= (nss_pheader_t
*)s
->buffer
;
118 nss_pheader_t
*dphdr
= (nss_pheader_t
*)d
->buffer
;
119 int slen
, new_pbufsiz
= 0;
121 if (NSCD_GET_STATUS(sphdr
) != NSS_SUCCESS
) {
123 /* no result, copy header only (status, errno, etc) */
124 slen
= sphdr
->data_off
;
127 * lookup result returned, data to copy is the packed
128 * header plus result (add 1 for the terminating NULL
131 slen
= sphdr
->data_off
+ sphdr
->data_len
+ 1;
134 /* allocate cache packed buffer */
135 if (dphdr
!= NULL
&& d
->bufsize
<= slen
&& d
->bufsize
!= 0) {
136 /* old buffer too small, free it */
144 dphdr
= calloc(1, slen
+ 1);
148 d
->bufsize
= slen
+ 1;
149 new_pbufsiz
= slen
+ 1;
152 (void) memcpy(dphdr
, sphdr
, slen
);
153 if (new_pbufsiz
!= 0)
154 dphdr
->pbufsiz
= new_pbufsiz
;
156 return (NSS_SUCCESS
);
159 char *cache_name
[CACHE_CTX_COUNT
] = {
171 NSS_DBNAM_BOOTPARAMS
,
181 typedef void (*cache_init_ctx_t
)(nsc_ctx_t
*);
182 static cache_init_ctx_t cache_init_ctx
[CACHE_CTX_COUNT
] = {
204 nsc_ctx_t
*cache_ctx_p
[CACHE_CTX_COUNT
] = { 0 };
205 static nscd_cfg_stat_cache_t null_stats
= { 0 };
206 static nscd_cfg_global_cache_t global_cfg
;
209 * Given database name 'dbname' find cache index
212 get_cache_idx(char *dbname
) {
216 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
217 nsc_name
= cache_name
[i
];
218 if (strcmp(nsc_name
, dbname
) == 0)
225 * Given database name 'dbname' retrieve cache context,
226 * if not created yet, allocate and initialize it.
229 get_cache_ctx(char *dbname
, nsc_ctx_t
**ctx
) {
234 i
= get_cache_idx(dbname
);
236 return (NSCD_INVALID_ARGUMENT
);
237 if ((*ctx
= cache_ctx_p
[i
]) == NULL
) {
238 *ctx
= init_cache_ctx(i
);
240 return (NSCD_NO_MEMORY
);
243 return (NSCD_SUCCESS
);
247 * Generate a log string to identify backend operation in debug logs
250 nsc_db_str_key_getlogstr(char *name
, char *whoami
, size_t len
,
251 nss_XbyY_args_t
*argp
) {
252 (void) snprintf(whoami
, len
, "%s [key=%s]", name
, argp
->key
.name
);
257 nsc_db_int_key_getlogstr(char *name
, char *whoami
, size_t len
,
258 nss_XbyY_args_t
*argp
) {
259 (void) snprintf(whoami
, len
, "%s [key=%d]", name
, argp
->key
.number
);
264 nsc_db_any_key_getlogstr(char *name
, char *whoami
, size_t len
,
265 nss_XbyY_args_t
*argp
) {
266 (void) snprintf(whoami
, len
, "%s", name
);
271 * Returns cache based on dbop
274 nsc_get_db(nsc_ctx_t
*ctx
, int dbop
) {
277 for (i
= 0; i
< ctx
->db_count
; i
++) {
278 if (ctx
->nsc_db
[i
] && dbop
== ctx
->nsc_db
[i
]->dbop
)
279 return (ctx
->nsc_db
[i
]);
286 * integer compare routine for _NSC_DB_INT_KEY
289 nsc_db_int_key_compar(const void *n1
, const void *n2
) {
290 nsc_entry_t
*e1
, *e2
;
292 e1
= (nsc_entry_t
*)n1
;
293 e2
= (nsc_entry_t
*)n2
;
294 return (_NSC_INT_KEY_CMP(e1
->key
.number
, e2
->key
.number
));
299 * case sensitive name compare routine for _NSC_DB_CES_KEY
302 nsc_db_ces_key_compar(const void *n1
, const void *n2
) {
303 nsc_entry_t
*e1
, *e2
;
306 e1
= (nsc_entry_t
*)n1
;
307 e2
= (nsc_entry_t
*)n2
;
308 l1
= strlen(e1
->key
.name
);
309 l2
= strlen(e2
->key
.name
);
310 res
= strncmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
311 return (_NSC_INT_KEY_CMP(res
, 0));
316 * case insensitive name compare routine _NSC_DB_CIS_KEY
319 nsc_db_cis_key_compar(const void *n1
, const void *n2
) {
320 nsc_entry_t
*e1
, *e2
;
323 e1
= (nsc_entry_t
*)n1
;
324 e2
= (nsc_entry_t
*)n2
;
325 l1
= strlen(e1
->key
.name
);
326 l2
= strlen(e2
->key
.name
);
327 res
= strncasecmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
328 return (_NSC_INT_KEY_CMP(res
, 0));
332 * macro used to generate elf hashes for strings
334 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
338 hval = (hval << 4) + func(*str++); \
339 if ((g = (hval & 0xf0000000)) != 0) \
350 cis_gethash(const char *key
, int htsize
) {
354 _NSC_ELF_STR_GETHASH(tolower
, key
, htsize
, hval
);
363 ces_gethash(const char *key
, int htsize
) {
367 _NSC_ELF_STR_GETHASH(, key
, htsize
, hval
);
373 * one-at-a-time hash function
376 db_gethash(const void *key
, int len
, int htsize
) {
378 const char *str
= key
;
383 for (hval
= 0, i
= 0; i
< len
; i
++) {
385 hval
+= (hval
<< 10);
389 hval
^= (hval
>> 11);
390 hval
+= (hval
<< 15);
391 return (hval
% htsize
);
396 * case insensitive name gethash routine _NSC_DB_CIS_KEY
399 nsc_db_cis_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
400 return (cis_gethash(key
->name
, htsize
));
405 * case sensitive name gethash routine _NSC_DB_CES_KEY
408 nsc_db_ces_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
409 return (ces_gethash(key
->name
, htsize
));
414 * integer gethash routine _NSC_DB_INT_KEY
417 nsc_db_int_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
418 return (db_gethash(&key
->number
, sizeof (key
->number
), htsize
));
423 * Find entry in the hash table
424 * if cmp == nscd_true)
425 * return entry only if the keys match
427 * return entry in the hash location without checking the keys
431 hash_find(nsc_db_t
*nscdb
, nsc_entry_t
*entry
, uint_t
*hash
,
434 nsc_entry_t
*hashentry
;
437 *hash
= nscdb
->gethash(&entry
->key
, nscdb
->htsize
);
441 hashentry
= nscdb
->htable
[*hash
];
442 if (cmp
== nscd_false
|| hashentry
== NULL
)
445 if (nscdb
->compar(entry
, hashentry
) == 0)
452 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
453 if (nscdb->htable) { \
454 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
455 nscdb->htable[hash] = NULL; \
459 #define HASH_INSERT(nscdb, entry, hash, cmp) \
460 if (nscdb->htable) { \
461 (void) hash_find(nscdb, entry, &hash, cmp); \
462 nscdb->htable[hash] = entry; \
468 print_entry(nsc_db_t
*nscdb
, time_t now
, nsc_entry_t
*entry
) {
469 nss_XbyY_args_t args
;
472 switch (entry
->stats
.status
) {
474 (void) fprintf(stdout
, gettext("\t status: new entry\n"));
476 case ST_UPDATE_PENDING
:
477 (void) fprintf(stdout
, gettext("\t status: update pending\n"));
479 case ST_LOOKUP_PENDING
:
480 (void) fprintf(stdout
, gettext("\t status: lookup pending\n"));
483 (void) fprintf(stdout
, gettext("\t status: discarded entry\n"));
486 if (entry
->stats
.timestamp
< now
)
487 (void) fprintf(stdout
,
488 gettext("\t status: expired (%d seconds ago)\n"),
489 now
- entry
->stats
.timestamp
);
491 (void) fprintf(stdout
,
492 gettext("\t status: valid (expiry in %d seconds)\n"),
493 entry
->stats
.timestamp
- now
);
496 (void) fprintf(stdout
, gettext("\t hits: %u\n"), entry
->stats
.hits
);
497 args
.key
= entry
->key
;
498 (void) nscdb
->getlogstr(nscdb
->name
, whoami
, sizeof (whoami
), &args
);
499 (void) fprintf(stdout
, "\t %s\n", whoami
);
501 #endif /* NSCD_DEBUG */
504 print_stats(nscd_cfg_stat_cache_t
*statsp
) {
506 (void) fprintf(stdout
, gettext("\n\t STATISTICS:\n"));
507 (void) fprintf(stdout
, gettext("\t positive hits: %lu\n"),
509 (void) fprintf(stdout
, gettext("\t negative hits: %lu\n"),
511 (void) fprintf(stdout
, gettext("\t positive misses: %lu\n"),
513 (void) fprintf(stdout
, gettext("\t negative misses: %lu\n"),
515 (void) fprintf(stdout
, gettext("\t total entries: %lu\n"),
517 (void) fprintf(stdout
, gettext("\t queries queued: %lu\n"),
519 (void) fprintf(stdout
, gettext("\t queries dropped: %lu\n"),
521 (void) fprintf(stdout
, gettext("\t cache invalidations: %lu\n"),
522 statsp
->invalidate_count
);
524 _NSC_GET_HITRATE(statsp
);
525 (void) fprintf(stdout
, gettext("\t cache hit rate: %10.1f\n"),
531 print_cfg(nscd_cfg_cache_t
*cfgp
) {
532 (void) fprintf(stdout
, gettext("\n\t CONFIG:\n"));
533 (void) fprintf(stdout
, gettext("\t enabled: %s\n"),
534 yes_no(cfgp
->enable
));
535 (void) fprintf(stdout
, gettext("\t per user cache: %s\n"),
536 yes_no(cfgp
->per_user
));
537 (void) fprintf(stdout
, gettext("\t avoid name service: %s\n"),
538 yes_no(cfgp
->avoid_ns
));
539 (void) fprintf(stdout
, gettext("\t check file: %s\n"),
540 yes_no(cfgp
->check_files
));
541 (void) fprintf(stdout
, gettext("\t check file interval: %d\n"),
542 cfgp
->check_interval
);
543 (void) fprintf(stdout
, gettext("\t positive ttl: %d\n"),
545 (void) fprintf(stdout
, gettext("\t negative ttl: %d\n"),
547 (void) fprintf(stdout
, gettext("\t keep hot count: %d\n"),
549 (void) fprintf(stdout
, gettext("\t hint size: %d\n"),
551 (void) fprintf(stdout
, gettext("\t max entries: %lu%s"),
553 cfgp
->maxentries
?"\n":" (unlimited)\n");
559 hash_dump(nsc_db_t
*nscdb
, time_t now
) {
563 (void) fprintf(stdout
, gettext("\n\nHASH TABLE:\n"));
564 for (i
= 0; i
< nscdb
->htsize
; i
++) {
565 if ((entry
= nscdb
->htable
[i
]) != NULL
) {
566 (void) fprintf(stdout
, "hash[%d]:\n", i
);
567 print_entry(nscdb
, now
, entry
);
571 #endif /* NSCD_DEBUG */
576 avl_dump(nsc_db_t
*nscdb
, time_t now
) {
580 (void) fprintf(stdout
, gettext("\n\nAVL TREE:\n"));
581 for (entry
= avl_first(&nscdb
->tree
), i
= 0; entry
!= NULL
;
582 entry
= avl_walk(&nscdb
->tree
, entry
, AVL_AFTER
)) {
583 (void) fprintf(stdout
, "avl node[%d]:\n", i
++);
584 print_entry(nscdb
, now
, entry
);
587 #endif /* NSCD_DEBUG */
592 queue_dump(nsc_db_t
*nscdb
, time_t now
) {
596 (void) fprintf(stdout
,
597 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
598 nscdb
->name
, avl_numnodes(&nscdb
->tree
));
600 (void) fprintf(stdout
,
601 gettext("Starting with the most recently accessed:\n"));
603 for (entry
= nscdb
->qtail
, i
= 0; entry
; entry
= entry
->qnext
) {
604 (void) fprintf(stdout
, "entry[%d]:\n", i
++);
605 print_entry(nscdb
, now
, entry
);
608 #endif /* NSCD_DEBUG */
611 queue_remove(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
613 if (nscdb
->qtail
== entry
)
614 nscdb
->qtail
= entry
->qnext
;
616 entry
->qprev
->qnext
= entry
->qnext
;
618 if (nscdb
->qhead
== entry
)
619 nscdb
->qhead
= entry
->qprev
;
621 entry
->qnext
->qprev
= entry
->qprev
;
623 if (nscdb
->reap_node
== entry
)
624 nscdb
->reap_node
= entry
->qnext
;
625 entry
->qnext
= entry
->qprev
= NULL
;
630 queue_adjust(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
633 assert(nscdb
->qtail
|| entry
->qnext
== NULL
&&
634 entry
->qprev
== NULL
);
636 assert(nscdb
->qtail
&& nscdb
->qhead
||
637 nscdb
->qtail
== NULL
&& nscdb
->qhead
== NULL
);
639 assert(entry
->qprev
|| entry
->qnext
== NULL
||
640 nscdb
->qtail
== entry
);
641 #endif /* NSCD_DEBUG */
643 /* already in the desired position */
644 if (nscdb
->qtail
== entry
)
648 if (nscdb
->qtail
== NULL
) {
649 nscdb
->qhead
= nscdb
->qtail
= entry
;
653 /* new entry (prev == NULL AND tail != entry) */
654 if (entry
->qprev
== NULL
) {
655 nscdb
->qtail
->qprev
= entry
;
656 entry
->qnext
= nscdb
->qtail
;
657 nscdb
->qtail
= entry
;
662 if (nscdb
->reap_node
== entry
)
663 nscdb
->reap_node
= entry
->qnext
;
664 if (nscdb
->qhead
== entry
)
665 nscdb
->qhead
= entry
->qprev
;
667 entry
->qnext
->qprev
= entry
->qprev
;
668 entry
->qprev
->qnext
= entry
->qnext
;
670 entry
->qnext
= nscdb
->qtail
;
671 nscdb
->qtail
->qprev
= entry
;
672 nscdb
->qtail
= entry
;
680 init_cache(int debug_level
) {
683 cflags
= (debug_level
> 0)?0:UMC_NODEBUG
;
684 nsc_entry_cache
= umem_cache_create("nsc_entry_cache",
685 sizeof (nsc_entry_t
), 0, NULL
, NULL
, NULL
,
687 if (nsc_entry_cache
== NULL
)
688 return (NSCD_NO_MEMORY
);
689 return (NSCD_SUCCESS
);
697 make_cache(enum db_type dbtype
, int dbop
, char *name
,
698 int (*compar
) (const void *, const void *),
699 void (*getlogstr
)(char *, char *, size_t, nss_XbyY_args_t
*),
700 uint_t (*gethash
)(nss_XbyY_key_t
*, int),
701 enum hash_type httype
, int htsize
)
704 char *me
= "make_cache";
706 nscdb
= (nsc_db_t
*)malloc(sizeof (*nscdb
));
708 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
709 (me
, "%s: memory allocation failure\n", name
);
712 (void) memset(nscdb
, 0, sizeof (*nscdb
));
716 nscdb
->db_type
= dbtype
;
718 /* Assign compare routine */
719 if (compar
== NULL
) {
720 if (_NSC_DB_CES_KEY(nscdb
))
721 nscdb
->compar
= nsc_db_ces_key_compar
;
722 else if (_NSC_DB_CIS_KEY(nscdb
))
723 nscdb
->compar
= nsc_db_cis_key_compar
;
724 else if (_NSC_DB_INT_KEY(nscdb
))
725 nscdb
->compar
= nsc_db_int_key_compar
;
729 nscdb
->compar
= compar
;
732 /* The cache is an AVL tree */
733 avl_create(&nscdb
->tree
, nscdb
->compar
, sizeof (nsc_entry_t
),
734 offsetof(nsc_entry_t
, avl_link
));
736 /* Assign log routine */
737 if (getlogstr
== NULL
) {
738 if (_NSC_DB_STR_KEY(nscdb
))
739 nscdb
->getlogstr
= nsc_db_str_key_getlogstr
;
740 else if (_NSC_DB_INT_KEY(nscdb
))
741 nscdb
->getlogstr
= nsc_db_int_key_getlogstr
;
743 nscdb
->getlogstr
= nsc_db_any_key_getlogstr
;
745 nscdb
->getlogstr
= getlogstr
;
748 /* The AVL tree based cache uses a hash table for quick access */
750 /* Determine hash table size based on type */
751 nscdb
->hash_type
= httype
;
755 htsize
= _NSC_INIT_HTSIZE_POWER2
;
760 htsize
= _NSC_INIT_HTSIZE_PRIME
;
763 nscdb
->htsize
= htsize
;
765 /* Create the hash table */
766 nscdb
->htable
= calloc(htsize
, sizeof (*(nscdb
->htable
)));
767 if (nscdb
->htable
== NULL
) {
768 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
769 (me
, "%s: memory allocation failure\n", name
);
773 /* Assign gethash routine */
774 if (gethash
== NULL
) {
775 if (_NSC_DB_CES_KEY(nscdb
))
776 nscdb
->gethash
= nsc_db_ces_key_gethash
;
777 else if (_NSC_DB_CIS_KEY(nscdb
))
778 nscdb
->gethash
= nsc_db_cis_key_gethash
;
779 else if (_NSC_DB_INT_KEY(nscdb
))
780 nscdb
->gethash
= nsc_db_int_key_gethash
;
784 nscdb
->gethash
= gethash
;
788 (void) mutex_init(&nscdb
->db_mutex
, USYNC_THREAD
, NULL
);
805 _nscd_cfg_cache_verify(
807 struct nscd_cfg_param_desc
*pdesc
,
808 nscd_cfg_id_t
*nswdb
,
809 nscd_cfg_flag_t dflag
,
810 nscd_cfg_error_t
**errorp
,
814 return (NSCD_SUCCESS
);
822 _nscd_cfg_cache_notify(
824 struct nscd_cfg_param_desc
*pdesc
,
825 nscd_cfg_id_t
*nswdb
,
826 nscd_cfg_flag_t dflag
,
827 nscd_cfg_error_t
**errorp
,
835 if (_nscd_cfg_flag_is_set(dflag
, NSCD_CFG_DFLAG_GROUP
)) {
836 if (_nscd_cfg_flag_is_set(pdesc
->pflag
,
837 NSCD_CFG_PFLAG_GLOBAL
)) {
839 global_cfg
= *(nscd_cfg_global_cache_t
*)data
;
840 } else if (_nscd_cfg_flag_is_set(dflag
,
841 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
842 /* non-global config for all dbs */
843 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
844 ctx
= cache_ctx_p
[i
];
846 return (NSCD_CTX_NOT_FOUND
);
847 (void) rw_wrlock(&ctx
->cfg_rwlp
);
848 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
849 ctx
->cfg_mtime
= time(NULL
);
850 (void) rw_unlock(&ctx
->cfg_rwlp
);
853 /* non-global config for a specific db */
855 /* ignore non-caching databases */
856 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
857 return (NSCD_SUCCESS
);
858 (void) rw_wrlock(&ctx
->cfg_rwlp
);
859 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
860 ctx
->cfg_mtime
= time(NULL
);
861 (void) rw_unlock(&ctx
->cfg_rwlp
);
863 return (NSCD_SUCCESS
);
866 /* individual data */
867 if (_nscd_cfg_flag_is_set(pdesc
->pflag
, NSCD_CFG_PFLAG_GLOBAL
)) {
869 dp
= (char *)&global_cfg
+ pdesc
->p_offset
;
870 (void) memcpy(dp
, data
, pdesc
->p_size
);
871 } else if (_nscd_cfg_flag_is_set(dflag
,
872 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
873 /* non-global config for all dbs */
874 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
875 ctx
= cache_ctx_p
[i
];
877 return (NSCD_CTX_NOT_FOUND
);
878 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
879 (void) rw_wrlock(&ctx
->cfg_rwlp
);
880 (void) memcpy(dp
, data
, pdesc
->p_size
);
881 ctx
->cfg_mtime
= time(NULL
);
882 (void) rw_unlock(&ctx
->cfg_rwlp
);
885 /* non-global config for a specific db */
887 /* ignore non-caching databases */
888 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
889 return (NSCD_SUCCESS
);
890 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
891 (void) rw_wrlock(&ctx
->cfg_rwlp
);
892 (void) memcpy(dp
, data
, pdesc
->p_size
);
893 ctx
->cfg_mtime
= time(NULL
);
894 (void) rw_unlock(&ctx
->cfg_rwlp
);
896 return (NSCD_SUCCESS
);
905 _nscd_cfg_cache_get_stat(
907 struct nscd_cfg_stat_desc
*sdesc
,
908 nscd_cfg_id_t
*nswdb
,
909 nscd_cfg_flag_t
*dflag
,
910 void (**free_stat
)(void *stat
),
911 nscd_cfg_error_t
**errorp
)
913 nscd_cfg_stat_cache_t
*statsp
, stats
;
918 statsp
= calloc(1, sizeof (*statsp
));
920 return (NSCD_NO_MEMORY
);
922 if (_nscd_cfg_flag_is_set(sdesc
->sflag
, NSCD_CFG_SFLAG_GLOBAL
)) {
923 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
924 if (cache_ctx_p
[i
] == NULL
)
927 (void) mutex_lock(&cache_ctx_p
[i
]->stats_mutex
);
928 stats
= cache_ctx_p
[i
]->stats
;
930 &cache_ctx_p
[i
]->stats_mutex
);
932 statsp
->pos_hits
+= stats
.pos_hits
;
933 statsp
->neg_hits
+= stats
.neg_hits
;
934 statsp
->pos_misses
+= stats
.pos_misses
;
935 statsp
->neg_misses
+= stats
.neg_misses
;
936 statsp
->entries
+= stats
.entries
;
937 statsp
->drop_count
+= stats
.drop_count
;
938 statsp
->wait_count
+= stats
.wait_count
;
939 statsp
->invalidate_count
+=
940 stats
.invalidate_count
;
943 if ((rc
= get_cache_ctx(nswdb
->name
, &ctx
)) != NSCD_SUCCESS
) {
947 (void) mutex_lock(&ctx
->stats_mutex
);
948 *statsp
= ctx
->stats
;
949 (void) mutex_unlock(&ctx
->stats_mutex
);
952 _NSC_GET_HITRATE(statsp
);
954 return (NSCD_SUCCESS
);
958 * This function should only be called when nscd is
962 nsc_info(nsc_ctx_t
*ctx
, char *dbname
, nscd_cfg_cache_t cfg
[],
963 nscd_cfg_stat_cache_t stats
[])
966 char *me
= "nsc_info";
977 rc
= get_cache_ctx(dbname
, &ctx1
);
978 if (rc
== NSCD_INVALID_ARGUMENT
) {
979 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
980 (me
, "%s: no cache context found\n", dbname
);
982 } else if (rc
== NSCD_NO_MEMORY
) {
983 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
984 (me
, "%s: unable to create cache context - no memory\n",
992 if (cfg
== NULL
|| stats
== NULL
)
995 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
997 ctx2
.dbname
= cache_name
[i
];
999 ctx2
.stats
= stats
[i
];
1000 ctx_info_nolock(&ctx2
);
1005 ctx_info_nolock(nsc_ctx_t
*ctx
) {
1006 nscd_cfg_cache_t cfg
;
1007 nscd_cfg_stat_cache_t stats
;
1010 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1011 (void) print_cfg(&cfg
);
1013 if (cfg
.enable
== nscd_false
)
1017 (void) print_stats(&stats
);
1021 ctx_info(nsc_ctx_t
*ctx
) {
1022 nscd_cfg_cache_t cfg
;
1023 nscd_cfg_stat_cache_t stats
;
1025 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1027 (void) rw_unlock(&ctx
->cfg_rwlp
);
1028 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1029 (void) print_cfg(&cfg
);
1031 if (cfg
.enable
== nscd_false
)
1034 (void) mutex_lock(&ctx
->stats_mutex
);
1036 (void) mutex_unlock(&ctx
->stats_mutex
);
1037 (void) print_stats(&stats
);
1042 * This function should only be called when nscd is
1046 nsc_dump(char *dbname
, int dbop
)
1050 nscd_bool_t enabled
;
1052 char *me
= "nsc_dump";
1055 if ((i
= get_cache_idx(dbname
)) == -1) {
1056 (void) fprintf(stdout
, gettext("invalid cache name\n"));
1058 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1059 (me
, "%s: invalid cache name\n", dbname
);
1060 return (NSCD_CACHE_INVALID_CACHE_NAME
);
1063 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1064 (void) fprintf(stdout
, gettext("no cache context\n"));
1066 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1067 (me
, "%s: no cache context\n", dbname
);
1068 return (NSCD_CACHE_NO_CACHE_CTX
);
1072 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1073 enabled
= ctx
->cfg
.enable
;
1074 (void) rw_unlock(&ctx
->cfg_rwlp
);
1076 if (enabled
== nscd_false
)
1077 return (NSCD_CACHE_DISABLED
);
1079 nscdb
= nsc_get_db(ctx
, dbop
);
1080 if (nscdb
== NULL
) {
1081 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1082 (me
, "%s:%d: no cache found\n", dbname
, dbop
);
1083 return (NSCD_CACHE_NO_CACHE_FOUND
);
1086 (void) mutex_lock(&nscdb
->db_mutex
);
1087 (void) queue_dump(nscdb
, now
);
1088 (void) hash_dump(nscdb
, now
);
1089 (void) avl_dump(nscdb
, now
);
1090 (void) mutex_unlock(&nscdb
->db_mutex
);
1091 return (NSCD_SUCCESS
);
1093 #endif /* NSCD_DEBUG */
1096 * These macros are for exclusive use of nsc_lookup
1098 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1099 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1103 nsc_lookup_no_cache(nsc_lookup_args_t
*largs
, const char *str
)
1105 char *me
= "nsc_lookup_no_cache";
1106 nss_status_t status
;
1108 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1109 (me
, "%s: name service lookup (bypassing cache)\n", str
);
1110 nss_psearch(largs
->buffer
, largs
->bufsize
);
1111 status
= NSCD_GET_STATUS(largs
->buffer
);
1112 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1113 (me
, "%s: name service lookup status = %d\n", str
, status
);
1114 if (status
== NSS_SUCCESS
) {
1116 } else if (status
== NSS_NOTFOUND
) {
1119 return (SERVERERROR
);
1124 * This function starts the revalidation and reaper threads
1128 start_threads(nsc_ctx_t
*ctx
)
1131 char *me
= "start_threads";
1134 * kick off the revalidate thread (if necessary)
1136 if (ctx
->revalidate_on
!= nscd_true
) {
1137 if (thr_create(NULL
, NULL
, (void *(*)(void *))revalidate
,
1138 ctx
, 0, NULL
) != 0) {
1140 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1141 (me
, "thr_create (revalidate thread for %s): %s\n",
1142 ctx
->dbname
, strerror(errnum
));
1145 ctx
->revalidate_on
= nscd_true
;
1149 * kick off the reaper thread (if necessary)
1151 if (ctx
->reaper_on
!= nscd_true
) {
1152 if (thr_create(NULL
, NULL
, (void *(*)(void *))reaper
,
1153 ctx
, 0, NULL
) != 0) {
1155 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1156 (me
, "thr_create (reaper thread for %s): %s\n",
1157 ctx
->dbname
, strerror(errnum
));
1160 ctx
->reaper_on
= nscd_true
;
1165 * Examine the packed buffer, see if the front-end parameters
1166 * indicate that the caller specified nsswitch config should be
1167 * used for the lookup. Return 1 if yes, otherwise 0.
1170 nsw_config_in_phdr(void *buf
)
1172 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buf
;
1175 char *me
= "nsw_config_in_phdr";
1177 off
= pbuf
->dbd_off
;
1180 pdbd
= (nss_dbd_t
*)((void *)((char *)pbuf
+ off
));
1181 if (pdbd
->o_default_config
== 0)
1184 if ((enum nss_dbp_flags
)pdbd
->flags
& NSS_USE_DEFAULT_CONFIG
) {
1185 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1186 (me
, "use caller specified nsswitch config\n");
1193 copy_result(void *rbuf
, void *cbuf
)
1195 nss_pheader_t
*rphdr
= (nss_pheader_t
*)rbuf
;
1196 nss_pheader_t
*cphdr
= (nss_pheader_t
*)cbuf
;
1197 char *me
= "copy_result";
1199 /* return NSS_ERROR if not enough room to copy result */
1200 if (cphdr
->data_len
+ 1 > rphdr
->data_len
) {
1201 NSCD_SET_STATUS(rphdr
, NSS_ERROR
, ERANGE
);
1206 if (cphdr
->data_len
== 0)
1207 return (NSS_SUCCESS
);
1209 dst
= (char *)rphdr
+ rphdr
->data_off
;
1210 (void) memcpy(dst
, (char *)cphdr
+ cphdr
->data_off
,
1212 rphdr
->data_len
= cphdr
->data_len
;
1213 /* some frontend code expects a terminating NULL char */
1214 *(dst
+ rphdr
->data_len
) = '\0';
1216 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1217 (me
, "cache data (len = %lld): >>%s<<\n",
1218 cphdr
->data_len
, (char *)cphdr
+ cphdr
->data_off
);
1220 return (NSS_SUCCESS
);
1225 get_dns_ttl(void *pbuf
, char *dbname
)
1227 nss_pheader_t
*phdr
= (nss_pheader_t
*)pbuf
;
1229 char *me
= "get_dns_ttl";
1231 /* if returned, dns ttl is stored in the extended data area */
1232 if (phdr
->ext_off
== 0)
1235 if (strcmp(dbname
, NSS_DBNAM_HOSTS
) != 0 &&
1236 strcmp(dbname
, NSS_DBNAM_IPNODES
) != 0)
1239 ttl
= *(nssuint_t
*)((void *)((char *)pbuf
+ phdr
->ext_off
));
1241 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1242 (me
, "dns ttl is %d seconds\n", ttl
);
1248 check_config(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
1249 char *whoami
, int flag
)
1253 char *me
= "check_config";
1256 nscdb
= largs
->nscdb
;
1258 /* see if the cached config needs update */
1259 if (nscdb
->cfg_mtime
!= ctx
->cfg_mtime
) {
1260 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1261 nscdb
->cfg
= ctx
->cfg
;
1262 nscdb
->cfg_mtime
= ctx
->cfg_mtime
;
1263 (void) rw_unlock(&ctx
->cfg_rwlp
);
1264 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1265 (me
, "config for context %s, database %s updated\n",
1266 ctx
->dbname
, nscdb
->name
);
1270 if (cfgp
->enable
== nscd_false
) {
1271 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1272 (me
, "%s: cache disabled\n", ctx
->dbname
);
1274 if (UPDATEBIT
& flag
)
1277 return (nsc_lookup_no_cache(largs
, whoami
));
1281 * if caller requests lookup using its
1282 * own nsswitch config, bypass cache
1284 if (nsw_config_in_phdr(largs
->buffer
))
1285 return (nsc_lookup_no_cache(largs
, whoami
));
1287 /* no need of cache if we are dealing with 0 ttls */
1288 if (cfgp
->pos_ttl
<= 0 && cfgp
->neg_ttl
<= 0) {
1289 if (flag
& UPDATEBIT
)
1291 else if (cfgp
->avoid_ns
== nscd_true
)
1292 return (SERVERERROR
);
1293 return (nsc_lookup_no_cache(largs
, whoami
));
1300 * Invalidate cache if database file has been modified.
1301 * See check_files config param for details.
1304 check_db_file(nsc_ctx_t
*ctx
, nscd_cfg_cache_t cfg
,
1305 char *whoami
, time_t now
)
1308 nscd_bool_t file_modified
= nscd_false
;
1309 char *me
= "check_db_file";
1311 if (cfg
.check_interval
!= 0 &&
1312 (now
- ctx
->file_chktime
) < cfg
.check_interval
)
1315 ctx
->file_chktime
= now
;
1316 if (stat(ctx
->file_name
, &buf
) == 0) {
1317 if (ctx
->file_mtime
== 0) {
1318 (void) mutex_lock(&ctx
->file_mutex
);
1319 if (ctx
->file_mtime
== 0) {
1320 ctx
->file_mtime
= buf
.st_mtime
;
1321 ctx
->file_size
= buf
.st_size
;
1322 ctx
->file_ino
= buf
.st_ino
;
1324 (void) mutex_unlock(&ctx
->file_mutex
);
1325 } else if (ctx
->file_mtime
< buf
.st_mtime
||
1326 ctx
->file_size
!= buf
.st_size
||
1327 ctx
->file_ino
!= buf
.st_ino
) {
1328 (void) mutex_lock(&ctx
->file_mutex
);
1329 if (ctx
->file_mtime
< buf
.st_mtime
||
1330 ctx
->file_size
!= buf
.st_size
||
1331 ctx
->file_ino
!= buf
.st_ino
) {
1332 file_modified
= nscd_true
;
1333 ctx
->file_mtime
= buf
.st_mtime
;
1334 ctx
->file_size
= buf
.st_size
;
1335 ctx
->file_ino
= buf
.st_ino
;
1337 (void) mutex_unlock(&ctx
->file_mutex
);
1341 if (file_modified
== nscd_true
) {
1342 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1343 (me
, "%s: file %s has been modified - invalidating cache\n",
1344 whoami
, ctx
->file_name
);
1345 ctx_invalidate(ctx
);
1350 lookup_int(nsc_lookup_args_t
*largs
, int flag
)
1354 nscd_cfg_cache_t cfg
;
1355 nsc_entry_t
*this_entry
;
1356 nsc_entry_stat_t
*this_stats
;
1357 nsc_action_t next_action
;
1358 nss_status_t status
;
1364 nss_XbyY_args_t args
;
1366 time_t now
= time(NULL
); /* current time */
1367 char *me
= "lookup_int";
1369 /* extract dbop, dbname, key and cred */
1370 status
= nss_packed_getkey(largs
->buffer
, largs
->bufsize
, &dbname
,
1372 if (status
!= NSS_SUCCESS
) {
1373 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1374 (me
, "nss_packed_getkey failure (%d)\n", status
);
1375 return (SERVERERROR
);
1378 /* get the cache context */
1379 if (largs
->ctx
== NULL
) {
1380 if (get_cache_ctx(dbname
, &largs
->ctx
) != NSCD_SUCCESS
) {
1381 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1382 (me
, "%s: no cache context found\n", dbname
);
1384 if (UPDATEBIT
& flag
)
1387 return (nsc_lookup_no_cache(largs
, dbname
));
1392 if (largs
->nscdb
== NULL
) {
1393 if ((largs
->nscdb
= nsc_get_db(ctx
, dbop
)) == NULL
) {
1394 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1395 (me
, "%s:%d: no cache found\n",
1398 if (UPDATEBIT
& flag
)
1401 return (nsc_lookup_no_cache(largs
, dbname
));
1405 nscdb
= largs
->nscdb
;
1407 _NSCD_LOG_IF(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ALL
) {
1408 (void) nscdb
->getlogstr(nscdb
->name
, whoami
,
1409 sizeof (whoami
), &args
);
1412 if (UPDATEBIT
& flag
) {
1413 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1414 (me
, "%s: refresh start\n", whoami
);
1416 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1417 (me
, "%s: lookup start\n", whoami
);
1420 cfg_rc
= check_config(largs
, &cfg
, whoami
, flag
);
1421 if (cfg_rc
!= CONTINUE
)
1425 * Invalidate cache if file has been modified.
1427 if (cfg
.check_files
== nscd_true
)
1428 check_db_file(ctx
, cfg
, whoami
, now
);
1430 (void) mutex_lock(&nscdb
->db_mutex
);
1432 /* Lookup the cache table */
1434 delete = nscd_false
;
1435 rc
= lookup_cache(largs
, &cfg
, &args
, whoami
, &this_entry
);
1436 if (rc
!= NSCD_SUCCESS
) {
1437 (void) mutex_unlock(&nscdb
->db_mutex
);
1439 /* Either no entry and avoid name service */
1440 if (rc
== NSCD_DB_ENTRY_NOT_FOUND
||
1441 rc
== NSCD_INVALID_ARGUMENT
)
1444 /* OR memory error */
1445 return (SERVERERROR
);
1448 /* get the stats from the entry */
1449 this_stats
= &this_entry
->stats
;
1452 * What should we do next ?
1454 switch (this_stats
->status
) {
1457 next_action
= _NSC_NSLOOKUP
;
1459 case ST_UPDATE_PENDING
:
1460 if (flag
& UPDATEBIT
) {
1461 (void) mutex_unlock(&nscdb
->db_mutex
);
1463 } else if (this_stats
->timestamp
< now
)
1464 next_action
= _NSC_WAIT
;
1466 next_action
= _NSC_USECACHED
;
1468 case ST_LOOKUP_PENDING
:
1469 if (flag
& UPDATEBIT
) {
1470 (void) mutex_unlock(&nscdb
->db_mutex
);
1473 next_action
= _NSC_WAIT
;
1476 if (cfg
.avoid_ns
== nscd_true
) {
1477 (void) mutex_unlock(&nscdb
->db_mutex
);
1480 /* otherwise reuse the entry */
1481 (void) memset(this_stats
, 0, sizeof (*this_stats
));
1482 next_action
= _NSC_NSLOOKUP
;
1485 if (cfg
.avoid_ns
== nscd_true
)
1486 next_action
= _NSC_USECACHED
;
1487 else if ((flag
& UPDATEBIT
) ||
1488 (this_stats
->timestamp
< now
)) {
1489 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1490 (me
, "%s: cached entry needs to be updated\n",
1492 next_action
= _NSC_NSLOOKUP
;
1494 next_action
= _NSC_USECACHED
;
1498 if (next_action
== _NSC_WAIT
) {
1499 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1500 (me
, "%s: need to wait\n", whoami
);
1502 /* do we have clearance ? */
1503 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1505 (void) mutex_lock(&ctx
->stats_mutex
);
1506 ctx
->stats
.drop_count
++;
1507 (void) mutex_unlock(&ctx
->stats_mutex
);
1508 _NSCD_LOG(NSCD_LOG_CACHE
,
1509 NSCD_LOG_LEVEL_DEBUG_6
)
1510 (me
, "%s: throttling load\n", whoami
);
1511 (void) mutex_unlock(&nscdb
->db_mutex
);
1512 NSC_LOOKUP_LOG(WARNING
,
1513 "%s: no clearance to wait\n");
1517 (void) nscd_wait(ctx
, nscdb
, this_entry
);
1518 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1526 if (!(UPDATEBIT
& flag
))
1527 this_stats
->hits
++; /* update hit count */
1529 if (next_action
== _NSC_NSLOOKUP
) {
1531 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1532 (me
, "%s: name service lookup required\n", whoami
);
1534 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1535 if (delete == nscd_true
)
1536 delete_entry(nscdb
, ctx
, this_entry
);
1538 this_stats
->status
= ST_DISCARD
;
1539 (void) mutex_lock(&ctx
->stats_mutex
);
1540 ctx
->stats
.drop_count
++;
1541 (void) mutex_unlock(&ctx
->stats_mutex
);
1542 (void) mutex_unlock(&nscdb
->db_mutex
);
1543 NSC_LOOKUP_LOG(WARNING
,
1544 "%s: no clearance for lookup\n");
1548 /* block any threads accessing this entry */
1549 this_stats
->status
= (flag
& UPDATEBIT
) ?
1550 ST_UPDATE_PENDING
: ST_LOOKUP_PENDING
;
1552 /* release lock and do name service lookup */
1553 (void) mutex_unlock(&nscdb
->db_mutex
);
1554 nss_psearch(largs
->buffer
, largs
->bufsize
);
1555 status
= NSCD_GET_STATUS(largs
->buffer
);
1556 (void) mutex_lock(&nscdb
->db_mutex
);
1557 this_stats
->status
= 0;
1558 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1560 /* signal waiting threads */
1561 (void) nscd_signal(ctx
, nscdb
, this_entry
);
1563 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1564 (me
, "%s: name service lookup status = %d\n",
1567 if (status
== NSS_SUCCESS
) {
1571 * data found in name service
1574 status
= dup_packed_buffer(largs
, this_entry
);
1575 if (status
!= NSS_SUCCESS
) {
1576 delete_entry(nscdb
, ctx
, this_entry
);
1577 (void) mutex_unlock(&nscdb
->db_mutex
);
1578 NSC_LOOKUP_LOG(ERROR
,
1579 "%s: failed to update cache\n");
1580 return (SERVERERROR
);
1584 * store unpacked key in cache
1586 status
= nss_packed_getkey(this_entry
->buffer
,
1587 this_entry
->bufsize
,
1588 &dbname
, &dbop
, &args
);
1589 if (status
!= NSS_SUCCESS
) {
1590 delete_entry(nscdb
, ctx
, this_entry
);
1591 (void) mutex_unlock(&nscdb
->db_mutex
);
1592 NSC_LOOKUP_LOG(ERROR
,
1593 "%s: failed to extract key\n");
1594 return (SERVERERROR
);
1596 this_entry
->key
= args
.key
; /* struct copy */
1598 /* update +ve miss count */
1599 if (!(UPDATEBIT
& flag
)) {
1600 (void) mutex_lock(&ctx
->stats_mutex
);
1601 ctx
->stats
.pos_misses
++;
1602 (void) mutex_unlock(&ctx
->stats_mutex
);
1605 /* update +ve ttl */
1606 ttl
= get_dns_ttl(largs
->buffer
, dbname
);
1607 /* honor the dns ttl less than postive ttl */
1608 if (ttl
< 0 || ttl
> cfg
.pos_ttl
)
1610 this_stats
->timestamp
= time(NULL
) + ttl
;
1613 * start the revalidation and reaper threads
1614 * if not already started
1618 (void) mutex_unlock(&nscdb
->db_mutex
);
1619 NSC_LOOKUP_LOG(DEBUG
,
1620 "%s: cache updated with positive entry\n");
1622 } else if (status
== NSS_NOTFOUND
) {
1624 * data not found in name service
1627 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1628 (me
, "%s: name service lookup failed\n", whoami
);
1630 if (NSCD_GET_ERRNO(largs
->buffer
) == ERANGE
) {
1631 delete_entry(nscdb
, ctx
, this_entry
);
1632 (void) mutex_unlock(&nscdb
->db_mutex
);
1633 NSC_LOOKUP_LOG(DEBUG
,
1634 "%s: ERANGE, cache not updated "
1635 "with negative entry\n");
1639 status
= dup_packed_buffer(largs
, this_entry
);
1640 if (status
!= NSS_SUCCESS
) {
1641 delete_entry(nscdb
, ctx
, this_entry
);
1642 (void) mutex_unlock(&nscdb
->db_mutex
);
1643 NSC_LOOKUP_LOG(ERROR
,
1644 "%s: failed to update cache\n");
1645 return (SERVERERROR
);
1648 /* store unpacked key in cache */
1649 status
= nss_packed_getkey(this_entry
->buffer
,
1650 this_entry
->bufsize
,
1651 &dbname
, &dbop
, &args
);
1652 if (status
!= NSS_SUCCESS
) {
1653 delete_entry(nscdb
, ctx
, this_entry
);
1654 (void) mutex_unlock(&nscdb
->db_mutex
);
1655 NSC_LOOKUP_LOG(ERROR
,
1656 "%s: failed to extract key\n");
1657 return (SERVERERROR
);
1659 this_entry
->key
= args
.key
; /* struct copy */
1661 /* update -ve ttl */
1662 this_stats
->timestamp
= time(NULL
) + cfg
.neg_ttl
;
1664 /* update -ve miss count */
1665 if (!(UPDATEBIT
& flag
)) {
1666 (void) mutex_lock(&ctx
->stats_mutex
);
1667 ctx
->stats
.neg_misses
++;
1668 (void) mutex_unlock(&ctx
->stats_mutex
);
1672 * start the revalidation and reaper threads
1673 * if not already started
1677 (void) mutex_unlock(&nscdb
->db_mutex
);
1678 NSC_LOOKUP_LOG(DEBUG
,
1679 "%s: cache updated with negative entry\n");
1683 * name service lookup failed
1685 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1686 (me
, "%s: name service lookup failed\n", whoami
);
1688 errnum
= NSCD_GET_ERRNO(largs
->buffer
);
1689 if (delete == nscd_true
)
1690 delete_entry(nscdb
, ctx
, this_entry
);
1692 this_stats
->status
= ST_DISCARD
;
1694 (void) mutex_unlock(&nscdb
->db_mutex
);
1695 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1696 (me
, "%s: name service lookup failed "
1697 "(status=%d, errno=%d)\n",
1698 whoami
, status
, errnum
);
1700 return (SERVERERROR
);
1702 } else if (next_action
== _NSC_USECACHED
) {
1704 * found entry in cache
1706 if (UPDATEBIT
& flag
) {
1707 (void) mutex_unlock(&nscdb
->db_mutex
);
1708 NSC_LOOKUP_LOG(DEBUG
, "%s: no need to update\n");
1712 if (NSCD_GET_STATUS((nss_pheader_t
*)this_entry
->buffer
) ==
1715 (void) mutex_lock(&ctx
->stats_mutex
);
1716 ctx
->stats
.pos_hits
++;
1717 (void) mutex_unlock(&ctx
->stats_mutex
);
1719 /* update response buffer */
1720 if (copy_result(largs
->buffer
,
1721 this_entry
->buffer
) != NSS_SUCCESS
) {
1722 (void) mutex_unlock(&nscdb
->db_mutex
);
1723 NSC_LOOKUP_LOG(ERROR
,
1724 "%s: response buffer insufficient\n");
1725 return (SERVERERROR
);
1728 (void) mutex_unlock(&nscdb
->db_mutex
);
1729 NSC_LOOKUP_LOG(DEBUG
,
1730 "%s: positive entry in cache\n");
1734 (void) mutex_lock(&ctx
->stats_mutex
);
1735 ctx
->stats
.neg_hits
++;
1736 (void) mutex_unlock(&ctx
->stats_mutex
);
1738 NSCD_SET_STATUS((nss_pheader_t
*)largs
->buffer
,
1739 NSCD_GET_STATUS(this_entry
->buffer
),
1740 NSCD_GET_ERRNO(this_entry
->buffer
));
1741 NSCD_SET_HERRNO((nss_pheader_t
*)largs
->buffer
,
1742 NSCD_GET_HERRNO(this_entry
->buffer
));
1744 (void) mutex_unlock(&nscdb
->db_mutex
);
1745 NSC_LOOKUP_LOG(DEBUG
,
1746 "%s: negative entry in cache\n");
1751 (void) mutex_unlock(&nscdb
->db_mutex
);
1752 NSC_LOOKUP_LOG(ERROR
, "%s: cache backend failure\n");
1753 return (SERVERERROR
);
1757 * NSCD cache backend lookup function
1761 nsc_lookup(nsc_lookup_args_t
*largs
, int flag
) {
1763 nss_pheader_t
*phdr
= (nss_pheader_t
*)largs
->buffer
;
1766 rc
= lookup_int(largs
, 0);
1768 if (NSCD_GET_STATUS(phdr
) == NSS_TRYLOCAL
)
1774 NSCD_SET_STATUS(phdr
, NSS_SUCCESS
, 0);
1778 NSCD_SET_STATUS(phdr
, NSS_NOTFOUND
, -1);
1783 * status and errno should have been set in the phdr,
1784 * if not, set status to NSS_ERROR
1786 if (NSCD_STATUS_IS_OK(phdr
)) {
1787 NSCD_SET_STATUS(phdr
, NSS_ERROR
, 0);
1792 NSCD_SET_STATUS(phdr
, NSS_TRYLOCAL
, -1);
1799 init_cache_ctx(int i
) {
1802 ctx
= calloc(1, sizeof (nsc_ctx_t
));
1806 /* init locks and semaphores */
1807 (void) mutex_init(&ctx
->file_mutex
, USYNC_THREAD
, NULL
);
1808 (void) rwlock_init(&ctx
->cfg_rwlp
, USYNC_THREAD
, NULL
);
1809 (void) mutex_init(&ctx
->stats_mutex
, USYNC_THREAD
, NULL
);
1810 (void) _nscd_init_cache_sema(&ctx
->throttle_sema
, cache_name
[i
]);
1811 cache_init_ctx
[i
](ctx
);
1812 cache_ctx_p
[i
] = ctx
;
1819 revalidate(nsc_ctx_t
*ctx
)
1821 (void) thr_setname(thr_self(), "revalidate");
1824 int i
, slp
, interval
, count
;
1826 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1827 slp
= ctx
->cfg
.pos_ttl
;
1828 count
= ctx
->cfg
.keephot
;
1829 (void) rw_unlock(&ctx
->cfg_rwlp
);
1834 interval
= (slp
/2)/count
;
1837 (void) sleep(slp
*2/3);
1838 for (i
= 0; i
< ctx
->db_count
; i
++) {
1839 getxy_keepalive(ctx
, ctx
->nsc_db
[i
],
1850 getxy_keepalive(nsc_ctx_t
*ctx
, nsc_db_t
*nscdb
, int keep
, int interval
)
1852 nsc_keephot_t
*table
;
1853 nsc_entry_t
*entry
, *ptr
;
1855 nsc_lookup_args_t
*largs
;
1856 nss_pheader_t
*phdr
;
1858 char *me
= "getxy_keepalive";
1860 /* we won't be here if keep == 0 so need to check that */
1862 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1863 (me
, "%s: keep alive\n", nscdb
->name
);
1865 if ((table
= maken(keep
)) == NULL
) {
1866 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1867 (me
, "memory allocation failure\n");
1871 (void) mutex_lock(&nscdb
->db_mutex
);
1872 entry
= nscdb
->qtail
;
1873 while (entry
!= NULL
) {
1874 /* leave pending calls alone */
1875 if (!(entry
->stats
.status
& ST_PENDING
)) {
1877 (void) insertn(table
, entry
->stats
.hits
, entry
);
1879 entry
= entry
->qnext
;
1881 for (i
= 1; i
<= keep
; i
++) {
1882 if (table
[i
].ptr
== NULL
)
1884 ptr
= (nsc_entry_t
*)table
[i
].ptr
;
1885 phdr
= (nss_pheader_t
*)ptr
->buffer
;
1886 if (NSCD_GET_STATUS(phdr
) == NSS_SUCCESS
)
1888 * for positive cache, in addition to the packed
1889 * header size, allocate twice the size of the
1890 * existing result (in case the result grows
1891 * larger) plus 2K (for the file/compat backend to
1892 * process a possible large entry in the /etc files)
1894 bufsiz
= phdr
->data_off
+ 2 * phdr
->data_len
+ 2048;
1897 * for negative cache, allocate 8K buffer to
1898 * hold result in case the next lookup may
1899 * return something (in addition to the
1900 * packed header size)
1902 bufsiz
= phdr
->data_off
+ 8096;
1903 table
[i
].ptr
= malloc(bufsiz
);
1904 if (table
[i
].ptr
== NULL
) {
1905 (void) mutex_unlock(&nscdb
->db_mutex
);
1906 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1907 (me
, "memory allocation failure\n");
1910 (void) memcpy(table
[i
].ptr
, ptr
->buffer
, ptr
->bufsize
);
1911 ((nss_pheader_t
*)table
[i
].ptr
)->pbufsiz
= bufsiz
;
1912 table
[i
].num
= bufsiz
;
1914 (void) mutex_unlock(&nscdb
->db_mutex
);
1916 /* launch update thread for each keep hot entry */
1917 for (i
= keep
; i
> 0; i
--) {
1918 if (table
[i
].ptr
== NULL
)
1919 continue; /* unused slot in table */
1920 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1921 (me
, "%s: launching update\n", nscdb
->name
);
1922 largs
= (nsc_lookup_args_t
*)malloc(sizeof (*largs
));
1923 if (largs
== NULL
) {
1924 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1925 (me
, "memory allocation failure\n");
1928 largs
->buffer
= table
[i
].ptr
;
1929 largs
->bufsize
= table
[i
].num
;
1931 largs
->nscdb
= nscdb
;
1932 if (launch_update(largs
) < 0)
1934 (void) sleep(interval
);
1938 * The update thread will handle freeing of buffer and largs.
1939 * Free the table here.
1946 launch_update(nsc_lookup_args_t
*in
)
1948 char *me
= "launch_update";
1951 errnum
= thr_create(NULL
, NULL
, (void *(*)(void*))do_update
,
1952 in
, 0|THR_DETACHED
, NULL
);
1954 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1955 (me
, "%s: thread creation failure (%d)\n",
1956 in
->nscdb
->name
, errnum
);
1964 do_update(nsc_lookup_args_t
*in
) {
1965 nss_pheader_t
*phdr
= (nss_pheader_t
*)in
->buffer
;
1967 (void) thr_setname(thr_self(), "do_update");
1969 /* update the length of the data buffer */
1970 phdr
->data_len
= phdr
->pbufsiz
- phdr
->data_off
;
1972 (void) lookup_int(in
, UPDATEBIT
);
1983 nsc_invalidate(nsc_ctx_t
*ctx
, char *dbname
, nsc_ctx_t
**ctxs
)
1986 char *me
= "nsc_invalidate";
1989 ctx_invalidate(ctx
);
1994 if ((i
= get_cache_idx(dbname
)) == -1) {
1995 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1996 (me
, "%s: invalid cache name\n", dbname
);
1999 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
2000 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
2001 (me
, "%s: no cache context found\n",
2005 ctx_invalidate(ctx
);
2012 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
2013 if (ctxs
[i
] != NULL
)
2014 ctx_invalidate(ctxs
[i
]);
2020 * Invalidate cache by context
2023 ctx_invalidate(nsc_ctx_t
*ctx
)
2027 char *me
= "ctx_invalidate";
2029 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2030 (me
, "%s: invalidate cache\n", ctx
->dbname
);
2032 for (i
= 0; i
< ctx
->db_count
; i
++) {
2033 if (ctx
->nsc_db
[i
] == NULL
)
2035 (void) mutex_lock(&ctx
->nsc_db
[i
]->db_mutex
);
2036 entry
= ctx
->nsc_db
[i
]->qtail
;
2037 while (entry
!= NULL
) {
2038 /* leave pending calls alone */
2039 if (!(entry
->stats
.status
& ST_PENDING
))
2040 entry
->stats
.status
= ST_DISCARD
;
2041 entry
= entry
->qnext
;
2043 (void) mutex_unlock(&ctx
->nsc_db
[i
]->db_mutex
);
2046 (void) mutex_lock(&ctx
->stats_mutex
);
2047 ctx
->stats
.invalidate_count
++;
2048 (void) mutex_unlock(&ctx
->stats_mutex
);
2056 * nscdb->db_mutex lock must be held before calling this function
2059 delete_entry(nsc_db_t
*nscdb
, nsc_ctx_t
*ctx
, nsc_entry_t
*entry
) {
2062 avl_remove(&nscdb
->tree
, entry
);
2063 HASH_REMOVE(nscdb
, entry
, hash
, nscd_false
);
2064 queue_remove(nscdb
, entry
);
2065 if (entry
->buffer
!= NULL
) {
2066 free(entry
->buffer
);
2067 entry
->buffer
= NULL
;
2069 umem_cache_free(nsc_entry_cache
, entry
);
2070 (void) mutex_lock(&ctx
->stats_mutex
);
2071 ctx
->stats
.entries
--;
2072 (void) mutex_unlock(&ctx
->stats_mutex
);
2077 lookup_cache(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
2078 nss_XbyY_args_t
*argp
, char *whoami
, nsc_entry_t
**entry
)
2085 nsc_entry_t find_entry
, *node
;
2086 char *me
= "lookup_cache";
2089 nscdb
= largs
->nscdb
;
2091 /* set the search key */
2092 find_entry
.key
= argp
->key
; /* struct copy (not deep) */
2094 /* lookup the hash table ==> O(1) */
2095 if (nscdb
->htable
) {
2096 *entry
= hash_find(nscdb
, &find_entry
, &hash
, nscd_true
);
2097 if (*entry
!= NULL
) {
2098 (void) queue_adjust(nscdb
, *entry
);
2099 return (NSCD_SUCCESS
);
2103 /* if not found, lookup the AVL tree ==> O(log n) */
2104 *entry
= (nsc_entry_t
*)avl_find(&nscdb
->tree
, &find_entry
, &pos
);
2105 if (*entry
!= NULL
) {
2106 (void) queue_adjust(nscdb
, *entry
);
2107 /* move it to the hash table */
2108 if (nscdb
->htable
) {
2109 if (nscdb
->htable
[hash
] == NULL
||
2110 (*entry
)->stats
.hits
>=
2111 nscdb
->htable
[hash
]->stats
.hits
) {
2112 nscdb
->htable
[hash
] = *entry
;
2115 return (NSCD_SUCCESS
);
2118 /* entry not found in the cache */
2119 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2120 (me
, "%s: cache miss\n", whoami
);
2122 if (cfgp
->avoid_ns
== nscd_true
) {
2123 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2124 (me
, "%s: avoid name service\n", whoami
);
2125 return (NSCD_DB_ENTRY_NOT_FOUND
);
2128 /* allocate memory for new entry (stub) */
2129 *entry
= (nsc_entry_t
*)umem_cache_alloc(nsc_entry_cache
,
2131 if (*entry
== NULL
) {
2132 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2133 (me
, "%s: memory allocation failure\n", whoami
);
2134 return (NSCD_NO_MEMORY
);
2136 (void) memset(*entry
, 0, sizeof (**entry
));
2139 * Note that the actual data for the key is stored within
2140 * the largs->buffer (input buffer to nsc_lookup).
2141 * find_entry.key only contains pointers to this data.
2143 * If largs->buffer will be re-allocated by nss_psearch
2144 * then (*entry)->key will have dangling pointers.
2145 * In such case, the following assignment needs to be
2146 * replaced by code that duplicates the key.
2148 (*entry
)->key
= find_entry
.key
;
2151 * Add the entry to the cache.
2153 avl_insert(&nscdb
->tree
, *entry
, pos
); /* O(log n) */
2154 (void) queue_adjust(nscdb
, *entry
); /* constant */
2155 if (nscdb
->htable
) /* constant */
2156 nscdb
->htable
[hash
] = *entry
;
2157 (*entry
)->stats
.status
= ST_NEW_ENTRY
;
2159 (void) mutex_lock(&ctx
->stats_mutex
);
2160 nentries
= ++(ctx
->stats
.entries
);
2161 (void) mutex_unlock(&ctx
->stats_mutex
);
2163 /* Have we exceeded max entries ? */
2164 if (cfgp
->maxentries
> 0 && nentries
> cfgp
->maxentries
) {
2165 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2166 (me
, "%s: maximum entries exceeded -- "
2167 "deleting least recently used entry\n",
2170 node
= nscdb
->qhead
;
2171 while (node
!= NULL
&& node
!= *entry
) {
2172 if (node
->stats
.status
== ST_DISCARD
||
2173 !(node
->stats
.status
& ST_PENDING
)) {
2174 delete_entry(nscdb
, ctx
, node
);
2181 * It's okay if we were not able to find one to delete.
2182 * The reaper (when invoked) will return the cache to a
2187 return (NSCD_SUCCESS
);
2191 reaper(nsc_ctx_t
*ctx
)
2193 uint_t ttl
, extra_sleep
, total_sleep
, intervals
;
2194 uint_t nodes_per_interval
, seconds_per_interval
;
2195 ulong_t nsc_entries
;
2196 char *me
= "reaper";
2198 (void) thr_setname(thr_self(), me
);
2201 (void) mutex_lock(&ctx
->stats_mutex
);
2202 nsc_entries
= ctx
->stats
.entries
;
2203 (void) mutex_unlock(&ctx
->stats_mutex
);
2205 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2206 ttl
= ctx
->cfg
.pos_ttl
;
2207 (void) rw_unlock(&ctx
->cfg_rwlp
);
2209 if (nsc_entries
== 0) {
2210 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2211 (me
, "%s: nothing to reap\n", ctx
->dbname
);
2213 /* sleep for atleast 60 seconds */
2216 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2217 (me
, "%s: sleep %d\n", ctx
->dbname
, ttl
);
2222 if (ttl
< 32) ttl
= 32;
2223 if (ttl
> (1<<28)) ttl
= 1<<28;
2226 * minimum nodes_per_interval = 256 or 1<<8
2227 * maximum nodes_per_interval = nsc_entries
2228 * minimum seconds_per_interval = 32 or 1<<5
2229 * maximum_seconds_per_interval = ttl
2231 if (nsc_entries
<= ttl
) {
2232 intervals
= (nsc_entries
>> 8) + 1;
2233 seconds_per_interval
= ttl
/ intervals
;
2234 nodes_per_interval
= 256;
2236 intervals
= (ttl
>> 5) + 1;
2237 seconds_per_interval
= 32;
2238 nodes_per_interval
= nsc_entries
/ intervals
;
2239 if (nodes_per_interval
< 256)
2240 nodes_per_interval
= 256;
2243 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2244 (me
, "%s: total entries = %d, "
2245 "seconds per interval = %d, "
2246 "nodes per interval = %d\n",
2247 ctx
->dbname
, nsc_entries
, seconds_per_interval
,
2248 nodes_per_interval
);
2249 total_sleep
= reap_cache(ctx
, nodes_per_interval
,
2250 seconds_per_interval
);
2251 extra_sleep
= 1 + ttl
- total_sleep
;
2252 if (extra_sleep
> 0)
2253 (void) sleep(extra_sleep
);
2259 reap_cache(nsc_ctx_t
*ctx
, uint_t nodes_per_interval
,
2260 uint_t seconds_per_interval
)
2262 uint_t nodes_togo
, total_sleep
;
2264 nsc_entry_t
*node
, *next_node
;
2266 uint_t primes
[] = {_NSC_HTSIZE_PRIMES
};
2267 ulong_t count
, nentries
, maxentries
;
2268 int i
, slot
, value
, newhtsize
;
2269 char *me
= "reap_cache";
2273 nodes_togo
= nodes_per_interval
;
2276 for (i
= 0; i
< ctx
->db_count
; i
++) {
2277 nscdb
= ctx
->nsc_db
[i
];
2278 (void) mutex_lock(&nscdb
->db_mutex
);
2279 nscdb
->reap_node
= nscdb
->qtail
;
2280 while (nscdb
->reap_node
!= NULL
) {
2281 if (nodes_togo
== 0) {
2282 (void) mutex_unlock(&nscdb
->db_mutex
);
2283 (void) sleep(seconds_per_interval
);
2284 total_sleep
+= seconds_per_interval
;
2285 nodes_togo
= nodes_per_interval
;
2287 (void) mutex_lock(&nscdb
->db_mutex
);
2289 /* delete ST_DISCARD and expired nodes */
2290 if ((node
= nscdb
->reap_node
) == NULL
)
2292 if (node
->stats
.status
== ST_DISCARD
||
2293 (!(node
->stats
.status
& ST_PENDING
) &&
2294 node
->stats
.timestamp
< now
)) {
2296 * Delete entry if its discard flag is
2297 * set OR if it has expired. Entries
2298 * with pending updates are not
2300 * nscdb->reap_node will be adjusted
2303 delete_entry(nscdb
, ctx
, node
);
2306 nscdb
->reap_node
= node
->qnext
;
2311 if (nscdb
->htsize
== 0) {
2312 (void) mutex_unlock(&nscdb
->db_mutex
);
2317 * Dynamic adjustment of hash table size.
2319 * Hash table size is roughly 1/8th of the
2320 * total entries. However the size is changed
2321 * only when the number of entries double or
2324 nentries
= avl_numnodes(&nscdb
->tree
);
2325 for (slot
= 0, value
= _NSC_INIT_HTSIZE_SLOT_VALUE
;
2326 slot
< _NSC_HTSIZE_NUM_SLOTS
&& nentries
> value
;
2327 value
= (value
<< 1) + 1, slot
++)
2329 if (nscdb
->hash_type
== nsc_ht_power2
)
2330 newhtsize
= _NSC_INIT_HTSIZE_POWER2
<< slot
;
2332 newhtsize
= primes
[slot
];
2334 /* Recommended size is same as the current size. Done */
2335 if (nscdb
->htsize
== newhtsize
) {
2336 (void) mutex_unlock(&nscdb
->db_mutex
);
2340 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2341 (me
, "%s: resizing hash table from %d to %d\n",
2342 nscdb
->name
, nscdb
->htsize
, newhtsize
);
2345 * Dump old hashes because it would be time
2346 * consuming to rehash them.
2348 (void) free(nscdb
->htable
);
2349 nscdb
->htable
= calloc(newhtsize
, sizeof (*(nscdb
->htable
)));
2350 if (nscdb
->htable
== NULL
) {
2351 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2352 (me
, "%s: memory allocation failure\n",
2354 /* -1 to try later */
2357 nscdb
->htsize
= newhtsize
;
2359 (void) mutex_unlock(&nscdb
->db_mutex
);
2362 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2363 (me
, "%s: reaped %lu entries\n", ctx
->dbname
, count
);
2366 * if cache is almost full then reduce it to a safe level by
2367 * evicting LRU entries
2370 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2371 maxentries
= ctx
->cfg
.maxentries
;
2372 (void) rw_unlock(&ctx
->cfg_rwlp
);
2374 /* No limit on number of entries. Done */
2375 if (maxentries
== 0)
2378 (void) mutex_lock(&ctx
->stats_mutex
);
2379 nentries
= ctx
->stats
.entries
;
2380 (void) mutex_unlock(&ctx
->stats_mutex
);
2382 /* what is the percentage of cache used ? */
2383 value
= (nentries
* 100) / maxentries
;
2384 if (value
< _NSC_EVICTION_START_LEVEL
)
2388 * cache needs to be reduced to a safe level
2390 value
-= _NSC_EVICTION_SAFE_LEVEL
;
2391 for (i
= 0, count
= 0; i
< ctx
->db_count
; i
++) {
2393 * Reduce each subcache by 'value' percent
2395 nscdb
= ctx
->nsc_db
[i
];
2396 (void) mutex_lock(&nscdb
->db_mutex
);
2397 nodes_togo
= (value
* avl_numnodes(&nscdb
->tree
)) / 100;
2399 /* Start from LRU entry i.e queue head */
2400 next_node
= nscdb
->qhead
;
2401 while (nodes_togo
> 0 && next_node
!= NULL
) {
2403 next_node
= next_node
->qprev
;
2404 if (node
->stats
.status
== ST_DISCARD
||
2405 !(node
->stats
.status
& ST_PENDING
)) {
2406 /* Leave nodes with pending updates alone */
2407 delete_entry(nscdb
, ctx
, node
);
2412 (void) mutex_unlock(&nscdb
->db_mutex
);
2415 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2416 (me
, "%s: evicted %lu LRU entries\n", ctx
->dbname
, count
);
2419 return (total_sleep
);