4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
28 * Cache routines for nscd
40 #include <sys/types.h>
44 #include <nss_common.h>
52 #include "nscd_door.h"
54 #include "nscd_config.h"
55 #include "nscd_frontend.h"
56 #include "nscd_switch.h"
60 #define SERVERERROR -2
64 static nsc_db_t
*nsc_get_db(nsc_ctx_t
*, int);
65 static nscd_rc_t
lookup_cache(nsc_lookup_args_t
*, nscd_cfg_cache_t
*,
66 nss_XbyY_args_t
*, char *, nsc_entry_t
**);
67 static uint_t
reap_cache(nsc_ctx_t
*, uint_t
, uint_t
);
68 static void delete_entry(nsc_db_t
*, nsc_ctx_t
*, nsc_entry_t
*);
69 static void print_stats(nscd_cfg_stat_cache_t
*);
70 static void print_cfg(nscd_cfg_cache_t
*);
71 static int lookup_int(nsc_lookup_args_t
*, int);
74 static void print_entry(nsc_db_t
*, time_t, nsc_entry_t
*);
75 static void avl_dump(nsc_db_t
*, time_t);
76 static void hash_dump(nsc_db_t
*, time_t);
77 #endif /* NSCD_DEBUG */
78 static nsc_entry_t
*hash_find(nsc_db_t
*, nsc_entry_t
*, uint_t
*, nscd_bool_t
);
80 static void queue_adjust(nsc_db_t
*, nsc_entry_t
*);
81 static void queue_remove(nsc_db_t
*, nsc_entry_t
*);
83 static void queue_dump(nsc_db_t
*, time_t);
84 #endif /* NSCD_DEBUG */
86 static int launch_update(nsc_lookup_args_t
*);
87 static void do_update(nsc_lookup_args_t
*);
88 static void getxy_keepalive(nsc_ctx_t
*, nsc_db_t
*, int, int);
90 static void ctx_info(nsc_ctx_t
*);
91 static void ctx_info_nolock(nsc_ctx_t
*);
92 static void ctx_invalidate(nsc_ctx_t
*);
94 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
95 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
96 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t
*);
98 static int nsc_db_cis_key_compar(const void *, const void *);
99 static int nsc_db_ces_key_compar(const void *, const void *);
100 static int nsc_db_int_key_compar(const void *, const void *);
102 static uint_t
nsc_db_cis_key_gethash(nss_XbyY_key_t
*, int);
103 static uint_t
nsc_db_ces_key_gethash(nss_XbyY_key_t
*, int);
104 static uint_t
nsc_db_int_key_gethash(nss_XbyY_key_t
*, int);
106 static umem_cache_t
*nsc_entry_cache
;
108 static nsc_ctx_t
*init_cache_ctx(int);
109 static void reaper(nsc_ctx_t
*);
110 static void revalidate(nsc_ctx_t
*);
113 dup_packed_buffer(void *src
, void *dst
) {
114 nsc_lookup_args_t
*s
= (nsc_lookup_args_t
*)src
;
115 nsc_entry_t
*d
= (nsc_entry_t
*)dst
;
116 nss_pheader_t
*sphdr
= (nss_pheader_t
*)s
->buffer
;
117 nss_pheader_t
*dphdr
= (nss_pheader_t
*)d
->buffer
;
118 int slen
, new_pbufsiz
= 0;
120 if (NSCD_GET_STATUS(sphdr
) != NSS_SUCCESS
) {
122 /* no result, copy header only (status, errno, etc) */
123 slen
= sphdr
->data_off
;
126 * lookup result returned, data to copy is the packed
127 * header plus result (add 1 for the terminating NULL
130 slen
= sphdr
->data_off
+ sphdr
->data_len
+ 1;
133 /* allocate cache packed buffer */
134 if (dphdr
!= NULL
&& d
->bufsize
<= slen
&& d
->bufsize
!= 0) {
135 /* old buffer too small, free it */
143 dphdr
= calloc(1, slen
+ 1);
147 d
->bufsize
= slen
+ 1;
148 new_pbufsiz
= slen
+ 1;
151 (void) memcpy(dphdr
, sphdr
, slen
);
152 if (new_pbufsiz
!= 0)
153 dphdr
->pbufsiz
= new_pbufsiz
;
155 return (NSS_SUCCESS
);
158 char *cache_name
[CACHE_CTX_COUNT
] = {
170 NSS_DBNAM_BOOTPARAMS
,
178 typedef void (*cache_init_ctx_t
)(nsc_ctx_t
*);
179 static cache_init_ctx_t cache_init_ctx
[CACHE_CTX_COUNT
] = {
199 nsc_ctx_t
*cache_ctx_p
[CACHE_CTX_COUNT
] = { 0 };
200 static nscd_cfg_stat_cache_t null_stats
= { 0 };
201 static nscd_cfg_global_cache_t global_cfg
;
204 * Given database name 'dbname' find cache index
207 get_cache_idx(char *dbname
) {
211 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
212 nsc_name
= cache_name
[i
];
213 if (strcmp(nsc_name
, dbname
) == 0)
220 * Given database name 'dbname' retrieve cache context,
221 * if not created yet, allocate and initialize it.
224 get_cache_ctx(char *dbname
, nsc_ctx_t
**ctx
) {
229 i
= get_cache_idx(dbname
);
231 return (NSCD_INVALID_ARGUMENT
);
232 if ((*ctx
= cache_ctx_p
[i
]) == NULL
) {
233 *ctx
= init_cache_ctx(i
);
235 return (NSCD_NO_MEMORY
);
238 return (NSCD_SUCCESS
);
242 * Generate a log string to identify backend operation in debug logs
245 nsc_db_str_key_getlogstr(char *name
, char *whoami
, size_t len
,
246 nss_XbyY_args_t
*argp
) {
247 (void) snprintf(whoami
, len
, "%s [key=%s]", name
, argp
->key
.name
);
252 nsc_db_int_key_getlogstr(char *name
, char *whoami
, size_t len
,
253 nss_XbyY_args_t
*argp
) {
254 (void) snprintf(whoami
, len
, "%s [key=%d]", name
, argp
->key
.number
);
259 nsc_db_any_key_getlogstr(char *name
, char *whoami
, size_t len
,
260 nss_XbyY_args_t
*argp
) {
261 (void) snprintf(whoami
, len
, "%s", name
);
266 * Returns cache based on dbop
269 nsc_get_db(nsc_ctx_t
*ctx
, int dbop
) {
272 for (i
= 0; i
< ctx
->db_count
; i
++) {
273 if (ctx
->nsc_db
[i
] && dbop
== ctx
->nsc_db
[i
]->dbop
)
274 return (ctx
->nsc_db
[i
]);
281 * integer compare routine for _NSC_DB_INT_KEY
284 nsc_db_int_key_compar(const void *n1
, const void *n2
) {
285 nsc_entry_t
*e1
, *e2
;
287 e1
= (nsc_entry_t
*)n1
;
288 e2
= (nsc_entry_t
*)n2
;
289 return (_NSC_INT_KEY_CMP(e1
->key
.number
, e2
->key
.number
));
294 * case sensitive name compare routine for _NSC_DB_CES_KEY
297 nsc_db_ces_key_compar(const void *n1
, const void *n2
) {
298 nsc_entry_t
*e1
, *e2
;
301 e1
= (nsc_entry_t
*)n1
;
302 e2
= (nsc_entry_t
*)n2
;
303 l1
= strlen(e1
->key
.name
);
304 l2
= strlen(e2
->key
.name
);
305 res
= strncmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
306 return (_NSC_INT_KEY_CMP(res
, 0));
311 * case insensitive name compare routine _NSC_DB_CIS_KEY
314 nsc_db_cis_key_compar(const void *n1
, const void *n2
) {
315 nsc_entry_t
*e1
, *e2
;
318 e1
= (nsc_entry_t
*)n1
;
319 e2
= (nsc_entry_t
*)n2
;
320 l1
= strlen(e1
->key
.name
);
321 l2
= strlen(e2
->key
.name
);
322 res
= strncasecmp(e1
->key
.name
, e2
->key
.name
, (l1
> l2
)?l1
:l2
);
323 return (_NSC_INT_KEY_CMP(res
, 0));
327 * macro used to generate elf hashes for strings
329 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
333 hval = (hval << 4) + func(*str++); \
334 if ((g = (hval & 0xf0000000)) != 0) \
345 cis_gethash(const char *key
, int htsize
) {
349 _NSC_ELF_STR_GETHASH(tolower
, key
, htsize
, hval
);
358 ces_gethash(const char *key
, int htsize
) {
362 _NSC_ELF_STR_GETHASH(, key
, htsize
, hval
);
368 * one-at-a-time hash function
371 db_gethash(const void *key
, int len
, int htsize
) {
373 const char *str
= key
;
378 for (hval
= 0, i
= 0; i
< len
; i
++) {
380 hval
+= (hval
<< 10);
384 hval
^= (hval
>> 11);
385 hval
+= (hval
<< 15);
386 return (hval
% htsize
);
391 * case insensitive name gethash routine _NSC_DB_CIS_KEY
394 nsc_db_cis_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
395 return (cis_gethash(key
->name
, htsize
));
400 * case sensitive name gethash routine _NSC_DB_CES_KEY
403 nsc_db_ces_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
404 return (ces_gethash(key
->name
, htsize
));
409 * integer gethash routine _NSC_DB_INT_KEY
412 nsc_db_int_key_gethash(nss_XbyY_key_t
*key
, int htsize
) {
413 return (db_gethash(&key
->number
, sizeof (key
->number
), htsize
));
418 * Find entry in the hash table
419 * if cmp == nscd_true)
420 * return entry only if the keys match
422 * return entry in the hash location without checking the keys
426 hash_find(nsc_db_t
*nscdb
, nsc_entry_t
*entry
, uint_t
*hash
,
429 nsc_entry_t
*hashentry
;
432 *hash
= nscdb
->gethash(&entry
->key
, nscdb
->htsize
);
436 hashentry
= nscdb
->htable
[*hash
];
437 if (cmp
== nscd_false
|| hashentry
== NULL
)
440 if (nscdb
->compar(entry
, hashentry
) == 0)
447 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
448 if (nscdb->htable) { \
449 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
450 nscdb->htable[hash] = NULL; \
454 #define HASH_INSERT(nscdb, entry, hash, cmp) \
455 if (nscdb->htable) { \
456 (void) hash_find(nscdb, entry, &hash, cmp); \
457 nscdb->htable[hash] = entry; \
463 print_entry(nsc_db_t
*nscdb
, time_t now
, nsc_entry_t
*entry
) {
464 nss_XbyY_args_t args
;
467 switch (entry
->stats
.status
) {
469 (void) fprintf(stdout
, gettext("\t status: new entry\n"));
471 case ST_UPDATE_PENDING
:
472 (void) fprintf(stdout
, gettext("\t status: update pending\n"));
474 case ST_LOOKUP_PENDING
:
475 (void) fprintf(stdout
, gettext("\t status: lookup pending\n"));
478 (void) fprintf(stdout
, gettext("\t status: discarded entry\n"));
481 if (entry
->stats
.timestamp
< now
)
482 (void) fprintf(stdout
,
483 gettext("\t status: expired (%d seconds ago)\n"),
484 now
- entry
->stats
.timestamp
);
486 (void) fprintf(stdout
,
487 gettext("\t status: valid (expiry in %d seconds)\n"),
488 entry
->stats
.timestamp
- now
);
491 (void) fprintf(stdout
, gettext("\t hits: %u\n"), entry
->stats
.hits
);
492 args
.key
= entry
->key
;
493 (void) nscdb
->getlogstr(nscdb
->name
, whoami
, sizeof (whoami
), &args
);
494 (void) fprintf(stdout
, "\t %s\n", whoami
);
496 #endif /* NSCD_DEBUG */
499 print_stats(nscd_cfg_stat_cache_t
*statsp
) {
501 (void) fprintf(stdout
, gettext("\n\t STATISTICS:\n"));
502 (void) fprintf(stdout
, gettext("\t positive hits: %lu\n"),
504 (void) fprintf(stdout
, gettext("\t negative hits: %lu\n"),
506 (void) fprintf(stdout
, gettext("\t positive misses: %lu\n"),
508 (void) fprintf(stdout
, gettext("\t negative misses: %lu\n"),
510 (void) fprintf(stdout
, gettext("\t total entries: %lu\n"),
512 (void) fprintf(stdout
, gettext("\t queries queued: %lu\n"),
514 (void) fprintf(stdout
, gettext("\t queries dropped: %lu\n"),
516 (void) fprintf(stdout
, gettext("\t cache invalidations: %lu\n"),
517 statsp
->invalidate_count
);
519 _NSC_GET_HITRATE(statsp
);
520 (void) fprintf(stdout
, gettext("\t cache hit rate: %10.1f\n"),
526 print_cfg(nscd_cfg_cache_t
*cfgp
) {
527 (void) fprintf(stdout
, gettext("\n\t CONFIG:\n"));
528 (void) fprintf(stdout
, gettext("\t enabled: %s\n"),
529 yes_no(cfgp
->enable
));
530 (void) fprintf(stdout
, gettext("\t per user cache: %s\n"),
531 yes_no(cfgp
->per_user
));
532 (void) fprintf(stdout
, gettext("\t avoid name service: %s\n"),
533 yes_no(cfgp
->avoid_ns
));
534 (void) fprintf(stdout
, gettext("\t check file: %s\n"),
535 yes_no(cfgp
->check_files
));
536 (void) fprintf(stdout
, gettext("\t check file interval: %d\n"),
537 cfgp
->check_interval
);
538 (void) fprintf(stdout
, gettext("\t positive ttl: %d\n"),
540 (void) fprintf(stdout
, gettext("\t negative ttl: %d\n"),
542 (void) fprintf(stdout
, gettext("\t keep hot count: %d\n"),
544 (void) fprintf(stdout
, gettext("\t hint size: %d\n"),
546 (void) fprintf(stdout
, gettext("\t max entries: %lu%s"),
548 cfgp
->maxentries
?"\n":" (unlimited)\n");
554 hash_dump(nsc_db_t
*nscdb
, time_t now
) {
558 (void) fprintf(stdout
, gettext("\n\nHASH TABLE:\n"));
559 for (i
= 0; i
< nscdb
->htsize
; i
++) {
560 if ((entry
= nscdb
->htable
[i
]) != NULL
) {
561 (void) fprintf(stdout
, "hash[%d]:\n", i
);
562 print_entry(nscdb
, now
, entry
);
566 #endif /* NSCD_DEBUG */
571 avl_dump(nsc_db_t
*nscdb
, time_t now
) {
575 (void) fprintf(stdout
, gettext("\n\nAVL TREE:\n"));
576 for (entry
= avl_first(&nscdb
->tree
), i
= 0; entry
!= NULL
;
577 entry
= avl_walk(&nscdb
->tree
, entry
, AVL_AFTER
)) {
578 (void) fprintf(stdout
, "avl node[%d]:\n", i
++);
579 print_entry(nscdb
, now
, entry
);
582 #endif /* NSCD_DEBUG */
587 queue_dump(nsc_db_t
*nscdb
, time_t now
) {
591 (void) fprintf(stdout
,
592 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
593 nscdb
->name
, avl_numnodes(&nscdb
->tree
));
595 (void) fprintf(stdout
,
596 gettext("Starting with the most recently accessed:\n"));
598 for (entry
= nscdb
->qtail
, i
= 0; entry
; entry
= entry
->qnext
) {
599 (void) fprintf(stdout
, "entry[%d]:\n", i
++);
600 print_entry(nscdb
, now
, entry
);
603 #endif /* NSCD_DEBUG */
606 queue_remove(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
608 if (nscdb
->qtail
== entry
)
609 nscdb
->qtail
= entry
->qnext
;
611 entry
->qprev
->qnext
= entry
->qnext
;
613 if (nscdb
->qhead
== entry
)
614 nscdb
->qhead
= entry
->qprev
;
616 entry
->qnext
->qprev
= entry
->qprev
;
618 if (nscdb
->reap_node
== entry
)
619 nscdb
->reap_node
= entry
->qnext
;
620 entry
->qnext
= entry
->qprev
= NULL
;
625 queue_adjust(nsc_db_t
*nscdb
, nsc_entry_t
*entry
) {
628 assert(nscdb
->qtail
|| entry
->qnext
== NULL
&&
629 entry
->qprev
== NULL
);
631 assert(nscdb
->qtail
&& nscdb
->qhead
||
632 nscdb
->qtail
== NULL
&& nscdb
->qhead
== NULL
);
634 assert(entry
->qprev
|| entry
->qnext
== NULL
||
635 nscdb
->qtail
== entry
);
636 #endif /* NSCD_DEBUG */
638 /* already in the desired position */
639 if (nscdb
->qtail
== entry
)
643 if (nscdb
->qtail
== NULL
) {
644 nscdb
->qhead
= nscdb
->qtail
= entry
;
648 /* new entry (prev == NULL AND tail != entry) */
649 if (entry
->qprev
== NULL
) {
650 nscdb
->qtail
->qprev
= entry
;
651 entry
->qnext
= nscdb
->qtail
;
652 nscdb
->qtail
= entry
;
657 if (nscdb
->reap_node
== entry
)
658 nscdb
->reap_node
= entry
->qnext
;
659 if (nscdb
->qhead
== entry
)
660 nscdb
->qhead
= entry
->qprev
;
662 entry
->qnext
->qprev
= entry
->qprev
;
663 entry
->qprev
->qnext
= entry
->qnext
;
665 entry
->qnext
= nscdb
->qtail
;
666 nscdb
->qtail
->qprev
= entry
;
667 nscdb
->qtail
= entry
;
675 init_cache(int debug_level
) {
678 cflags
= (debug_level
> 0)?0:UMC_NODEBUG
;
679 nsc_entry_cache
= umem_cache_create("nsc_entry_cache",
680 sizeof (nsc_entry_t
), 0, NULL
, NULL
, NULL
,
682 if (nsc_entry_cache
== NULL
)
683 return (NSCD_NO_MEMORY
);
684 return (NSCD_SUCCESS
);
692 make_cache(enum db_type dbtype
, int dbop
, char *name
,
693 int (*compar
) (const void *, const void *),
694 void (*getlogstr
)(char *, char *, size_t, nss_XbyY_args_t
*),
695 uint_t (*gethash
)(nss_XbyY_key_t
*, int),
696 enum hash_type httype
, int htsize
)
699 char *me
= "make_cache";
701 nscdb
= (nsc_db_t
*)malloc(sizeof (*nscdb
));
703 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
704 (me
, "%s: memory allocation failure\n", name
);
707 (void) memset(nscdb
, 0, sizeof (*nscdb
));
711 nscdb
->db_type
= dbtype
;
713 /* Assign compare routine */
714 if (compar
== NULL
) {
715 if (_NSC_DB_CES_KEY(nscdb
))
716 nscdb
->compar
= nsc_db_ces_key_compar
;
717 else if (_NSC_DB_CIS_KEY(nscdb
))
718 nscdb
->compar
= nsc_db_cis_key_compar
;
719 else if (_NSC_DB_INT_KEY(nscdb
))
720 nscdb
->compar
= nsc_db_int_key_compar
;
724 nscdb
->compar
= compar
;
727 /* The cache is an AVL tree */
728 avl_create(&nscdb
->tree
, nscdb
->compar
, sizeof (nsc_entry_t
),
729 offsetof(nsc_entry_t
, avl_link
));
731 /* Assign log routine */
732 if (getlogstr
== NULL
) {
733 if (_NSC_DB_STR_KEY(nscdb
))
734 nscdb
->getlogstr
= nsc_db_str_key_getlogstr
;
735 else if (_NSC_DB_INT_KEY(nscdb
))
736 nscdb
->getlogstr
= nsc_db_int_key_getlogstr
;
738 nscdb
->getlogstr
= nsc_db_any_key_getlogstr
;
740 nscdb
->getlogstr
= getlogstr
;
743 /* The AVL tree based cache uses a hash table for quick access */
745 /* Determine hash table size based on type */
746 nscdb
->hash_type
= httype
;
750 htsize
= _NSC_INIT_HTSIZE_POWER2
;
755 htsize
= _NSC_INIT_HTSIZE_PRIME
;
758 nscdb
->htsize
= htsize
;
760 /* Create the hash table */
761 nscdb
->htable
= calloc(htsize
, sizeof (*(nscdb
->htable
)));
762 if (nscdb
->htable
== NULL
) {
763 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
764 (me
, "%s: memory allocation failure\n", name
);
768 /* Assign gethash routine */
769 if (gethash
== NULL
) {
770 if (_NSC_DB_CES_KEY(nscdb
))
771 nscdb
->gethash
= nsc_db_ces_key_gethash
;
772 else if (_NSC_DB_CIS_KEY(nscdb
))
773 nscdb
->gethash
= nsc_db_cis_key_gethash
;
774 else if (_NSC_DB_INT_KEY(nscdb
))
775 nscdb
->gethash
= nsc_db_int_key_gethash
;
779 nscdb
->gethash
= gethash
;
783 (void) mutex_init(&nscdb
->db_mutex
, USYNC_THREAD
, NULL
);
798 _nscd_cfg_cache_verify(
800 struct nscd_cfg_param_desc
*pdesc
,
801 nscd_cfg_id_t
*nswdb
,
802 nscd_cfg_flag_t dflag
,
803 nscd_cfg_error_t
**errorp
,
807 return (NSCD_SUCCESS
);
815 _nscd_cfg_cache_notify(
817 struct nscd_cfg_param_desc
*pdesc
,
818 nscd_cfg_id_t
*nswdb
,
819 nscd_cfg_flag_t dflag
,
820 nscd_cfg_error_t
**errorp
,
828 if (_nscd_cfg_flag_is_set(dflag
, NSCD_CFG_DFLAG_GROUP
)) {
829 if (_nscd_cfg_flag_is_set(pdesc
->pflag
,
830 NSCD_CFG_PFLAG_GLOBAL
)) {
832 global_cfg
= *(nscd_cfg_global_cache_t
*)data
;
833 } else if (_nscd_cfg_flag_is_set(dflag
,
834 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
835 /* non-global config for all dbs */
836 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
837 ctx
= cache_ctx_p
[i
];
839 return (NSCD_CTX_NOT_FOUND
);
840 (void) rw_wrlock(&ctx
->cfg_rwlp
);
841 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
842 ctx
->cfg_mtime
= time(NULL
);
843 (void) rw_unlock(&ctx
->cfg_rwlp
);
846 /* non-global config for a specific db */
848 /* ignore non-caching databases */
849 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
850 return (NSCD_SUCCESS
);
851 (void) rw_wrlock(&ctx
->cfg_rwlp
);
852 ctx
->cfg
= *(nscd_cfg_cache_t
*)data
;
853 ctx
->cfg_mtime
= time(NULL
);
854 (void) rw_unlock(&ctx
->cfg_rwlp
);
856 return (NSCD_SUCCESS
);
859 /* individual data */
860 if (_nscd_cfg_flag_is_set(pdesc
->pflag
, NSCD_CFG_PFLAG_GLOBAL
)) {
862 dp
= (char *)&global_cfg
+ pdesc
->p_offset
;
863 (void) memcpy(dp
, data
, pdesc
->p_size
);
864 } else if (_nscd_cfg_flag_is_set(dflag
,
865 NSCD_CFG_DFLAG_SET_ALL_DB
)) {
866 /* non-global config for all dbs */
867 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
868 ctx
= cache_ctx_p
[i
];
870 return (NSCD_CTX_NOT_FOUND
);
871 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
872 (void) rw_wrlock(&ctx
->cfg_rwlp
);
873 (void) memcpy(dp
, data
, pdesc
->p_size
);
874 ctx
->cfg_mtime
= time(NULL
);
875 (void) rw_unlock(&ctx
->cfg_rwlp
);
878 /* non-global config for a specific db */
880 /* ignore non-caching databases */
881 if (get_cache_ctx(nswdb
->name
, &ctx
) != NSCD_SUCCESS
)
882 return (NSCD_SUCCESS
);
883 dp
= (char *)&ctx
->cfg
+ pdesc
->p_offset
;
884 (void) rw_wrlock(&ctx
->cfg_rwlp
);
885 (void) memcpy(dp
, data
, pdesc
->p_size
);
886 ctx
->cfg_mtime
= time(NULL
);
887 (void) rw_unlock(&ctx
->cfg_rwlp
);
889 return (NSCD_SUCCESS
);
898 _nscd_cfg_cache_get_stat(
900 struct nscd_cfg_stat_desc
*sdesc
,
901 nscd_cfg_id_t
*nswdb
,
902 nscd_cfg_flag_t
*dflag
,
903 void (**free_stat
)(void *stat
),
904 nscd_cfg_error_t
**errorp
)
906 nscd_cfg_stat_cache_t
*statsp
, stats
;
911 statsp
= calloc(1, sizeof (*statsp
));
913 return (NSCD_NO_MEMORY
);
915 if (_nscd_cfg_flag_is_set(sdesc
->sflag
, NSCD_CFG_SFLAG_GLOBAL
)) {
916 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
917 if (cache_ctx_p
[i
] == NULL
)
920 (void) mutex_lock(&cache_ctx_p
[i
]->stats_mutex
);
921 stats
= cache_ctx_p
[i
]->stats
;
923 &cache_ctx_p
[i
]->stats_mutex
);
925 statsp
->pos_hits
+= stats
.pos_hits
;
926 statsp
->neg_hits
+= stats
.neg_hits
;
927 statsp
->pos_misses
+= stats
.pos_misses
;
928 statsp
->neg_misses
+= stats
.neg_misses
;
929 statsp
->entries
+= stats
.entries
;
930 statsp
->drop_count
+= stats
.drop_count
;
931 statsp
->wait_count
+= stats
.wait_count
;
932 statsp
->invalidate_count
+=
933 stats
.invalidate_count
;
936 if ((rc
= get_cache_ctx(nswdb
->name
, &ctx
)) != NSCD_SUCCESS
) {
940 (void) mutex_lock(&ctx
->stats_mutex
);
941 *statsp
= ctx
->stats
;
942 (void) mutex_unlock(&ctx
->stats_mutex
);
945 _NSC_GET_HITRATE(statsp
);
947 return (NSCD_SUCCESS
);
951 * This function should only be called when nscd is
955 nsc_info(nsc_ctx_t
*ctx
, char *dbname
, nscd_cfg_cache_t cfg
[],
956 nscd_cfg_stat_cache_t stats
[])
959 char *me
= "nsc_info";
970 rc
= get_cache_ctx(dbname
, &ctx1
);
971 if (rc
== NSCD_INVALID_ARGUMENT
) {
972 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
973 (me
, "%s: no cache context found\n", dbname
);
975 } else if (rc
== NSCD_NO_MEMORY
) {
976 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
977 (me
, "%s: unable to create cache context - no memory\n",
985 if (cfg
== NULL
|| stats
== NULL
)
988 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
990 ctx2
.dbname
= cache_name
[i
];
992 ctx2
.stats
= stats
[i
];
993 ctx_info_nolock(&ctx2
);
998 ctx_info_nolock(nsc_ctx_t
*ctx
) {
999 nscd_cfg_cache_t cfg
;
1000 nscd_cfg_stat_cache_t stats
;
1003 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1004 (void) print_cfg(&cfg
);
1006 if (cfg
.enable
== nscd_false
)
1010 (void) print_stats(&stats
);
1014 ctx_info(nsc_ctx_t
*ctx
) {
1015 nscd_cfg_cache_t cfg
;
1016 nscd_cfg_stat_cache_t stats
;
1018 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1020 (void) rw_unlock(&ctx
->cfg_rwlp
);
1021 (void) fprintf(stdout
, gettext("\n\nCACHE: %s\n"), ctx
->dbname
);
1022 (void) print_cfg(&cfg
);
1024 if (cfg
.enable
== nscd_false
)
1027 (void) mutex_lock(&ctx
->stats_mutex
);
1029 (void) mutex_unlock(&ctx
->stats_mutex
);
1030 (void) print_stats(&stats
);
1035 * This function should only be called when nscd is
1039 nsc_dump(char *dbname
, int dbop
)
1043 nscd_bool_t enabled
;
1045 char *me
= "nsc_dump";
1048 if ((i
= get_cache_idx(dbname
)) == -1) {
1049 (void) fprintf(stdout
, gettext("invalid cache name\n"));
1051 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1052 (me
, "%s: invalid cache name\n", dbname
);
1053 return (NSCD_CACHE_INVALID_CACHE_NAME
);
1056 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1057 (void) fprintf(stdout
, gettext("no cache context\n"));
1059 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1060 (me
, "%s: no cache context\n", dbname
);
1061 return (NSCD_CACHE_NO_CACHE_CTX
);
1065 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1066 enabled
= ctx
->cfg
.enable
;
1067 (void) rw_unlock(&ctx
->cfg_rwlp
);
1069 if (enabled
== nscd_false
)
1070 return (NSCD_CACHE_DISABLED
);
1072 nscdb
= nsc_get_db(ctx
, dbop
);
1073 if (nscdb
== NULL
) {
1074 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1075 (me
, "%s:%d: no cache found\n", dbname
, dbop
);
1076 return (NSCD_CACHE_NO_CACHE_FOUND
);
1079 (void) mutex_lock(&nscdb
->db_mutex
);
1080 (void) queue_dump(nscdb
, now
);
1081 (void) hash_dump(nscdb
, now
);
1082 (void) avl_dump(nscdb
, now
);
1083 (void) mutex_unlock(&nscdb
->db_mutex
);
1084 return (NSCD_SUCCESS
);
1086 #endif /* NSCD_DEBUG */
1089 * These macros are for exclusive use of nsc_lookup
1091 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1092 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1096 nsc_lookup_no_cache(nsc_lookup_args_t
*largs
, const char *str
)
1098 char *me
= "nsc_lookup_no_cache";
1099 nss_status_t status
;
1101 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1102 (me
, "%s: name service lookup (bypassing cache)\n", str
);
1103 nss_psearch(largs
->buffer
, largs
->bufsize
);
1104 status
= NSCD_GET_STATUS(largs
->buffer
);
1105 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1106 (me
, "%s: name service lookup status = %d\n", str
, status
);
1107 if (status
== NSS_SUCCESS
) {
1109 } else if (status
== NSS_NOTFOUND
) {
1112 return (SERVERERROR
);
1117 * This function starts the revalidation and reaper threads
1121 start_threads(nsc_ctx_t
*ctx
)
1124 char *me
= "start_threads";
1127 * kick off the revalidate thread (if necessary)
1129 if (ctx
->revalidate_on
!= nscd_true
) {
1130 if (thr_create(NULL
, 0, (void *(*)(void *))revalidate
,
1131 ctx
, 0, NULL
) != 0) {
1133 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1134 (me
, "thr_create (revalidate thread for %s): %s\n",
1135 ctx
->dbname
, strerror(errnum
));
1138 ctx
->revalidate_on
= nscd_true
;
1142 * kick off the reaper thread (if necessary)
1144 if (ctx
->reaper_on
!= nscd_true
) {
1145 if (thr_create(NULL
, 0, (void *(*)(void *))reaper
,
1146 ctx
, 0, NULL
) != 0) {
1148 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1149 (me
, "thr_create (reaper thread for %s): %s\n",
1150 ctx
->dbname
, strerror(errnum
));
1153 ctx
->reaper_on
= nscd_true
;
1158 * Examine the packed buffer, see if the front-end parameters
1159 * indicate that the caller specified nsswitch config should be
1160 * used for the lookup. Return 1 if yes, otherwise 0.
1163 nsw_config_in_phdr(void *buf
)
1165 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buf
;
1168 char *me
= "nsw_config_in_phdr";
1170 off
= pbuf
->dbd_off
;
1173 pdbd
= (nss_dbd_t
*)((void *)((char *)pbuf
+ off
));
1174 if (pdbd
->o_default_config
== 0)
1177 if ((enum nss_dbp_flags
)pdbd
->flags
& NSS_USE_DEFAULT_CONFIG
) {
1178 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1179 (me
, "use caller specified nsswitch config\n");
1186 copy_result(void *rbuf
, void *cbuf
)
1188 nss_pheader_t
*rphdr
= (nss_pheader_t
*)rbuf
;
1189 nss_pheader_t
*cphdr
= (nss_pheader_t
*)cbuf
;
1190 char *me
= "copy_result";
1192 /* return NSS_ERROR if not enough room to copy result */
1193 if (cphdr
->data_len
+ 1 > rphdr
->data_len
) {
1194 NSCD_SET_STATUS(rphdr
, NSS_ERROR
, ERANGE
);
1199 if (cphdr
->data_len
== 0)
1200 return (NSS_SUCCESS
);
1202 dst
= (char *)rphdr
+ rphdr
->data_off
;
1203 (void) memcpy(dst
, (char *)cphdr
+ cphdr
->data_off
,
1205 rphdr
->data_len
= cphdr
->data_len
;
1206 /* some frontend code expects a terminating NULL char */
1207 *(dst
+ rphdr
->data_len
) = '\0';
1209 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1210 (me
, "cache data (len = %lld): >>%s<<\n",
1211 cphdr
->data_len
, (char *)cphdr
+ cphdr
->data_off
);
1213 return (NSS_SUCCESS
);
1218 get_dns_ttl(void *pbuf
, char *dbname
)
1220 nss_pheader_t
*phdr
= (nss_pheader_t
*)pbuf
;
1222 char *me
= "get_dns_ttl";
1224 /* if returned, dns ttl is stored in the extended data area */
1225 if (phdr
->ext_off
== 0)
1228 if (strcmp(dbname
, NSS_DBNAM_HOSTS
) != 0 &&
1229 strcmp(dbname
, NSS_DBNAM_IPNODES
) != 0)
1232 ttl
= *(nssuint_t
*)((void *)((char *)pbuf
+ phdr
->ext_off
));
1234 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1235 (me
, "dns ttl is %d seconds\n", ttl
);
1241 check_config(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
1242 char *whoami
, int flag
)
1246 char *me
= "check_config";
1249 nscdb
= largs
->nscdb
;
1251 /* see if the cached config needs update */
1252 if (nscdb
->cfg_mtime
!= ctx
->cfg_mtime
) {
1253 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1254 nscdb
->cfg
= ctx
->cfg
;
1255 nscdb
->cfg_mtime
= ctx
->cfg_mtime
;
1256 (void) rw_unlock(&ctx
->cfg_rwlp
);
1257 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1258 (me
, "config for context %s, database %s updated\n",
1259 ctx
->dbname
, nscdb
->name
);
1263 if (cfgp
->enable
== nscd_false
) {
1264 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1265 (me
, "%s: cache disabled\n", ctx
->dbname
);
1267 if (UPDATEBIT
& flag
)
1270 return (nsc_lookup_no_cache(largs
, whoami
));
1274 * if caller requests lookup using its
1275 * own nsswitch config, bypass cache
1277 if (nsw_config_in_phdr(largs
->buffer
))
1278 return (nsc_lookup_no_cache(largs
, whoami
));
1280 /* no need of cache if we are dealing with 0 ttls */
1281 if (cfgp
->pos_ttl
<= 0 && cfgp
->neg_ttl
<= 0) {
1282 if (flag
& UPDATEBIT
)
1284 else if (cfgp
->avoid_ns
== nscd_true
)
1285 return (SERVERERROR
);
1286 return (nsc_lookup_no_cache(largs
, whoami
));
1293 * Invalidate cache if database file has been modified.
1294 * See check_files config param for details.
1297 check_db_file(nsc_ctx_t
*ctx
, nscd_cfg_cache_t cfg
,
1298 char *whoami
, time_t now
)
1301 nscd_bool_t file_modified
= nscd_false
;
1302 char *me
= "check_db_file";
1304 if (cfg
.check_interval
!= 0 &&
1305 (now
- ctx
->file_chktime
) < cfg
.check_interval
)
1308 ctx
->file_chktime
= now
;
1309 if (stat(ctx
->file_name
, &buf
) == 0) {
1310 if (ctx
->file_mtime
== 0) {
1311 (void) mutex_lock(&ctx
->file_mutex
);
1312 if (ctx
->file_mtime
== 0) {
1313 ctx
->file_mtime
= buf
.st_mtime
;
1314 ctx
->file_size
= buf
.st_size
;
1315 ctx
->file_ino
= buf
.st_ino
;
1317 (void) mutex_unlock(&ctx
->file_mutex
);
1318 } else if (ctx
->file_mtime
< buf
.st_mtime
||
1319 ctx
->file_size
!= buf
.st_size
||
1320 ctx
->file_ino
!= buf
.st_ino
) {
1321 (void) mutex_lock(&ctx
->file_mutex
);
1322 if (ctx
->file_mtime
< buf
.st_mtime
||
1323 ctx
->file_size
!= buf
.st_size
||
1324 ctx
->file_ino
!= buf
.st_ino
) {
1325 file_modified
= nscd_true
;
1326 ctx
->file_mtime
= buf
.st_mtime
;
1327 ctx
->file_size
= buf
.st_size
;
1328 ctx
->file_ino
= buf
.st_ino
;
1330 (void) mutex_unlock(&ctx
->file_mutex
);
1334 if (file_modified
== nscd_true
) {
1335 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1336 (me
, "%s: file %s has been modified - invalidating cache\n",
1337 whoami
, ctx
->file_name
);
1338 ctx_invalidate(ctx
);
1343 lookup_int(nsc_lookup_args_t
*largs
, int flag
)
1347 nscd_cfg_cache_t cfg
;
1348 nsc_entry_t
*this_entry
;
1349 nsc_entry_stat_t
*this_stats
;
1350 nsc_action_t next_action
;
1351 nss_status_t status
;
1357 nss_XbyY_args_t args
;
1359 time_t now
= time(NULL
); /* current time */
1360 char *me
= "lookup_int";
1362 /* extract dbop, dbname, key and cred */
1363 status
= nss_packed_getkey(largs
->buffer
, largs
->bufsize
, &dbname
,
1365 if (status
!= NSS_SUCCESS
) {
1366 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1367 (me
, "nss_packed_getkey failure (%d)\n", status
);
1368 return (SERVERERROR
);
1371 /* get the cache context */
1372 if (largs
->ctx
== NULL
) {
1373 if (get_cache_ctx(dbname
, &largs
->ctx
) != NSCD_SUCCESS
) {
1374 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1375 (me
, "%s: no cache context found\n", dbname
);
1377 if (UPDATEBIT
& flag
)
1380 return (nsc_lookup_no_cache(largs
, dbname
));
1385 if (largs
->nscdb
== NULL
) {
1386 if ((largs
->nscdb
= nsc_get_db(ctx
, dbop
)) == NULL
) {
1387 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1388 (me
, "%s:%d: no cache found\n",
1391 if (UPDATEBIT
& flag
)
1394 return (nsc_lookup_no_cache(largs
, dbname
));
1398 nscdb
= largs
->nscdb
;
1400 _NSCD_LOG_IF(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ALL
) {
1401 (void) nscdb
->getlogstr(nscdb
->name
, whoami
,
1402 sizeof (whoami
), &args
);
1405 if (UPDATEBIT
& flag
) {
1406 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1407 (me
, "%s: refresh start\n", whoami
);
1409 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1410 (me
, "%s: lookup start\n", whoami
);
1413 cfg_rc
= check_config(largs
, &cfg
, whoami
, flag
);
1414 if (cfg_rc
!= CONTINUE
)
1418 * Invalidate cache if file has been modified.
1420 if (cfg
.check_files
== nscd_true
)
1421 check_db_file(ctx
, cfg
, whoami
, now
);
1423 (void) mutex_lock(&nscdb
->db_mutex
);
1425 /* Lookup the cache table */
1427 delete = nscd_false
;
1428 rc
= lookup_cache(largs
, &cfg
, &args
, whoami
, &this_entry
);
1429 if (rc
!= NSCD_SUCCESS
) {
1430 (void) mutex_unlock(&nscdb
->db_mutex
);
1432 /* Either no entry and avoid name service */
1433 if (rc
== NSCD_DB_ENTRY_NOT_FOUND
||
1434 rc
== NSCD_INVALID_ARGUMENT
)
1437 /* OR memory error */
1438 return (SERVERERROR
);
1441 /* get the stats from the entry */
1442 this_stats
= &this_entry
->stats
;
1445 * What should we do next ?
1447 switch (this_stats
->status
) {
1450 next_action
= _NSC_NSLOOKUP
;
1452 case ST_UPDATE_PENDING
:
1453 if (flag
& UPDATEBIT
) {
1454 (void) mutex_unlock(&nscdb
->db_mutex
);
1456 } else if (this_stats
->timestamp
< now
)
1457 next_action
= _NSC_WAIT
;
1459 next_action
= _NSC_USECACHED
;
1461 case ST_LOOKUP_PENDING
:
1462 if (flag
& UPDATEBIT
) {
1463 (void) mutex_unlock(&nscdb
->db_mutex
);
1466 next_action
= _NSC_WAIT
;
1469 if (cfg
.avoid_ns
== nscd_true
) {
1470 (void) mutex_unlock(&nscdb
->db_mutex
);
1473 /* otherwise reuse the entry */
1474 (void) memset(this_stats
, 0, sizeof (*this_stats
));
1475 next_action
= _NSC_NSLOOKUP
;
1478 if (cfg
.avoid_ns
== nscd_true
)
1479 next_action
= _NSC_USECACHED
;
1480 else if ((flag
& UPDATEBIT
) ||
1481 (this_stats
->timestamp
< now
)) {
1482 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1483 (me
, "%s: cached entry needs to be updated\n",
1485 next_action
= _NSC_NSLOOKUP
;
1487 next_action
= _NSC_USECACHED
;
1491 if (next_action
== _NSC_WAIT
) {
1492 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1493 (me
, "%s: need to wait\n", whoami
);
1495 /* do we have clearance ? */
1496 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1498 (void) mutex_lock(&ctx
->stats_mutex
);
1499 ctx
->stats
.drop_count
++;
1500 (void) mutex_unlock(&ctx
->stats_mutex
);
1501 _NSCD_LOG(NSCD_LOG_CACHE
,
1502 NSCD_LOG_LEVEL_DEBUG_6
)
1503 (me
, "%s: throttling load\n", whoami
);
1504 (void) mutex_unlock(&nscdb
->db_mutex
);
1505 NSC_LOOKUP_LOG(WARNING
,
1506 "%s: no clearance to wait\n");
1510 (void) nscd_wait(ctx
, nscdb
, this_entry
);
1511 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1519 if (!(UPDATEBIT
& flag
))
1520 this_stats
->hits
++; /* update hit count */
1522 if (next_action
== _NSC_NSLOOKUP
) {
1524 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1525 (me
, "%s: name service lookup required\n", whoami
);
1527 if (_nscd_get_clearance(&ctx
->throttle_sema
) != 0) {
1528 if (delete == nscd_true
)
1529 delete_entry(nscdb
, ctx
, this_entry
);
1531 this_stats
->status
= ST_DISCARD
;
1532 (void) mutex_lock(&ctx
->stats_mutex
);
1533 ctx
->stats
.drop_count
++;
1534 (void) mutex_unlock(&ctx
->stats_mutex
);
1535 (void) mutex_unlock(&nscdb
->db_mutex
);
1536 NSC_LOOKUP_LOG(WARNING
,
1537 "%s: no clearance for lookup\n");
1541 /* block any threads accessing this entry */
1542 this_stats
->status
= (flag
& UPDATEBIT
) ?
1543 ST_UPDATE_PENDING
: ST_LOOKUP_PENDING
;
1545 /* release lock and do name service lookup */
1546 (void) mutex_unlock(&nscdb
->db_mutex
);
1547 nss_psearch(largs
->buffer
, largs
->bufsize
);
1548 status
= NSCD_GET_STATUS(largs
->buffer
);
1549 (void) mutex_lock(&nscdb
->db_mutex
);
1550 this_stats
->status
= 0;
1551 (void) _nscd_release_clearance(&ctx
->throttle_sema
);
1553 /* signal waiting threads */
1554 (void) nscd_signal(ctx
, nscdb
, this_entry
);
1556 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1557 (me
, "%s: name service lookup status = %d\n",
1560 if (status
== NSS_SUCCESS
) {
1564 * data found in name service
1567 status
= dup_packed_buffer(largs
, this_entry
);
1568 if (status
!= NSS_SUCCESS
) {
1569 delete_entry(nscdb
, ctx
, this_entry
);
1570 (void) mutex_unlock(&nscdb
->db_mutex
);
1571 NSC_LOOKUP_LOG(ERROR
,
1572 "%s: failed to update cache\n");
1573 return (SERVERERROR
);
1577 * store unpacked key in cache
1579 status
= nss_packed_getkey(this_entry
->buffer
,
1580 this_entry
->bufsize
,
1581 &dbname
, &dbop
, &args
);
1582 if (status
!= NSS_SUCCESS
) {
1583 delete_entry(nscdb
, ctx
, this_entry
);
1584 (void) mutex_unlock(&nscdb
->db_mutex
);
1585 NSC_LOOKUP_LOG(ERROR
,
1586 "%s: failed to extract key\n");
1587 return (SERVERERROR
);
1589 this_entry
->key
= args
.key
; /* struct copy */
1591 /* update +ve miss count */
1592 if (!(UPDATEBIT
& flag
)) {
1593 (void) mutex_lock(&ctx
->stats_mutex
);
1594 ctx
->stats
.pos_misses
++;
1595 (void) mutex_unlock(&ctx
->stats_mutex
);
1598 /* update +ve ttl */
1599 ttl
= get_dns_ttl(largs
->buffer
, dbname
);
1600 /* honor the dns ttl less than postive ttl */
1601 if (ttl
< 0 || ttl
> cfg
.pos_ttl
)
1603 this_stats
->timestamp
= time(NULL
) + ttl
;
1606 * start the revalidation and reaper threads
1607 * if not already started
1611 (void) mutex_unlock(&nscdb
->db_mutex
);
1612 NSC_LOOKUP_LOG(DEBUG
,
1613 "%s: cache updated with positive entry\n");
1615 } else if (status
== NSS_NOTFOUND
) {
1617 * data not found in name service
1620 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1621 (me
, "%s: name service lookup failed\n", whoami
);
1623 if (NSCD_GET_ERRNO(largs
->buffer
) == ERANGE
) {
1624 delete_entry(nscdb
, ctx
, this_entry
);
1625 (void) mutex_unlock(&nscdb
->db_mutex
);
1626 NSC_LOOKUP_LOG(DEBUG
,
1627 "%s: ERANGE, cache not updated "
1628 "with negative entry\n");
1632 status
= dup_packed_buffer(largs
, this_entry
);
1633 if (status
!= NSS_SUCCESS
) {
1634 delete_entry(nscdb
, ctx
, this_entry
);
1635 (void) mutex_unlock(&nscdb
->db_mutex
);
1636 NSC_LOOKUP_LOG(ERROR
,
1637 "%s: failed to update cache\n");
1638 return (SERVERERROR
);
1641 /* store unpacked key in cache */
1642 status
= nss_packed_getkey(this_entry
->buffer
,
1643 this_entry
->bufsize
,
1644 &dbname
, &dbop
, &args
);
1645 if (status
!= NSS_SUCCESS
) {
1646 delete_entry(nscdb
, ctx
, this_entry
);
1647 (void) mutex_unlock(&nscdb
->db_mutex
);
1648 NSC_LOOKUP_LOG(ERROR
,
1649 "%s: failed to extract key\n");
1650 return (SERVERERROR
);
1652 this_entry
->key
= args
.key
; /* struct copy */
1654 /* update -ve ttl */
1655 this_stats
->timestamp
= time(NULL
) + cfg
.neg_ttl
;
1657 /* update -ve miss count */
1658 if (!(UPDATEBIT
& flag
)) {
1659 (void) mutex_lock(&ctx
->stats_mutex
);
1660 ctx
->stats
.neg_misses
++;
1661 (void) mutex_unlock(&ctx
->stats_mutex
);
1665 * start the revalidation and reaper threads
1666 * if not already started
1670 (void) mutex_unlock(&nscdb
->db_mutex
);
1671 NSC_LOOKUP_LOG(DEBUG
,
1672 "%s: cache updated with negative entry\n");
1676 * name service lookup failed
1678 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG_6
)
1679 (me
, "%s: name service lookup failed\n", whoami
);
1681 errnum
= NSCD_GET_ERRNO(largs
->buffer
);
1682 if (delete == nscd_true
)
1683 delete_entry(nscdb
, ctx
, this_entry
);
1685 this_stats
->status
= ST_DISCARD
;
1687 (void) mutex_unlock(&nscdb
->db_mutex
);
1688 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1689 (me
, "%s: name service lookup failed "
1690 "(status=%d, errno=%d)\n",
1691 whoami
, status
, errnum
);
1693 return (SERVERERROR
);
1695 } else if (next_action
== _NSC_USECACHED
) {
1697 * found entry in cache
1699 if (UPDATEBIT
& flag
) {
1700 (void) mutex_unlock(&nscdb
->db_mutex
);
1701 NSC_LOOKUP_LOG(DEBUG
, "%s: no need to update\n");
1705 if (NSCD_GET_STATUS((nss_pheader_t
*)this_entry
->buffer
) ==
1708 (void) mutex_lock(&ctx
->stats_mutex
);
1709 ctx
->stats
.pos_hits
++;
1710 (void) mutex_unlock(&ctx
->stats_mutex
);
1712 /* update response buffer */
1713 if (copy_result(largs
->buffer
,
1714 this_entry
->buffer
) != NSS_SUCCESS
) {
1715 (void) mutex_unlock(&nscdb
->db_mutex
);
1716 NSC_LOOKUP_LOG(ERROR
,
1717 "%s: response buffer insufficient\n");
1718 return (SERVERERROR
);
1721 (void) mutex_unlock(&nscdb
->db_mutex
);
1722 NSC_LOOKUP_LOG(DEBUG
,
1723 "%s: positive entry in cache\n");
1727 (void) mutex_lock(&ctx
->stats_mutex
);
1728 ctx
->stats
.neg_hits
++;
1729 (void) mutex_unlock(&ctx
->stats_mutex
);
1731 NSCD_SET_STATUS((nss_pheader_t
*)largs
->buffer
,
1732 NSCD_GET_STATUS(this_entry
->buffer
),
1733 NSCD_GET_ERRNO(this_entry
->buffer
));
1734 NSCD_SET_HERRNO((nss_pheader_t
*)largs
->buffer
,
1735 NSCD_GET_HERRNO(this_entry
->buffer
));
1737 (void) mutex_unlock(&nscdb
->db_mutex
);
1738 NSC_LOOKUP_LOG(DEBUG
,
1739 "%s: negative entry in cache\n");
1744 (void) mutex_unlock(&nscdb
->db_mutex
);
1745 NSC_LOOKUP_LOG(ERROR
, "%s: cache backend failure\n");
1746 return (SERVERERROR
);
1750 * NSCD cache backend lookup function
1754 nsc_lookup(nsc_lookup_args_t
*largs
, int flag
) {
1756 nss_pheader_t
*phdr
= (nss_pheader_t
*)largs
->buffer
;
1759 rc
= lookup_int(largs
, 0);
1761 if (NSCD_GET_STATUS(phdr
) == NSS_TRYLOCAL
)
1767 NSCD_SET_STATUS(phdr
, NSS_SUCCESS
, 0);
1771 NSCD_SET_STATUS(phdr
, NSS_NOTFOUND
, -1);
1776 * status and errno should have been set in the phdr,
1777 * if not, set status to NSS_ERROR
1779 if (NSCD_STATUS_IS_OK(phdr
)) {
1780 NSCD_SET_STATUS(phdr
, NSS_ERROR
, 0);
1785 NSCD_SET_STATUS(phdr
, NSS_TRYLOCAL
, -1);
1792 init_cache_ctx(int i
) {
1795 ctx
= calloc(1, sizeof (nsc_ctx_t
));
1799 /* init locks and semaphores */
1800 (void) mutex_init(&ctx
->file_mutex
, USYNC_THREAD
, NULL
);
1801 (void) rwlock_init(&ctx
->cfg_rwlp
, USYNC_THREAD
, NULL
);
1802 (void) mutex_init(&ctx
->stats_mutex
, USYNC_THREAD
, NULL
);
1803 (void) _nscd_init_cache_sema(&ctx
->throttle_sema
, cache_name
[i
]);
1804 cache_init_ctx
[i
](ctx
);
1805 cache_ctx_p
[i
] = ctx
;
1812 revalidate(nsc_ctx_t
*ctx
)
1815 int i
, slp
, interval
, count
;
1817 (void) rw_rdlock(&ctx
->cfg_rwlp
);
1818 slp
= ctx
->cfg
.pos_ttl
;
1819 count
= ctx
->cfg
.keephot
;
1820 (void) rw_unlock(&ctx
->cfg_rwlp
);
1825 interval
= (slp
/2)/count
;
1828 (void) sleep(slp
*2/3);
1829 for (i
= 0; i
< ctx
->db_count
; i
++) {
1830 getxy_keepalive(ctx
, ctx
->nsc_db
[i
],
1841 getxy_keepalive(nsc_ctx_t
*ctx
, nsc_db_t
*nscdb
, int keep
, int interval
)
1843 nsc_keephot_t
*table
;
1844 nsc_entry_t
*entry
, *ptr
;
1846 nsc_lookup_args_t
*largs
;
1847 nss_pheader_t
*phdr
;
1849 char *me
= "getxy_keepalive";
1851 /* we won't be here if keep == 0 so need to check that */
1853 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1854 (me
, "%s: keep alive\n", nscdb
->name
);
1856 if ((table
= maken(keep
)) == NULL
) {
1857 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1858 (me
, "memory allocation failure\n");
1862 (void) mutex_lock(&nscdb
->db_mutex
);
1863 entry
= nscdb
->qtail
;
1864 while (entry
!= NULL
) {
1865 /* leave pending calls alone */
1866 if (!(entry
->stats
.status
& ST_PENDING
)) {
1868 (void) insertn(table
, entry
->stats
.hits
, entry
);
1870 entry
= entry
->qnext
;
1872 for (i
= 1; i
<= keep
; i
++) {
1873 if (table
[i
].ptr
== NULL
)
1875 ptr
= (nsc_entry_t
*)table
[i
].ptr
;
1876 phdr
= (nss_pheader_t
*)ptr
->buffer
;
1877 if (NSCD_GET_STATUS(phdr
) == NSS_SUCCESS
)
1879 * for positive cache, in addition to the packed
1880 * header size, allocate twice the size of the
1881 * existing result (in case the result grows
1882 * larger) plus 2K (for the file/compat backend to
1883 * process a possible large entry in the /etc files)
1885 bufsiz
= phdr
->data_off
+ 2 * phdr
->data_len
+ 2048;
1888 * for negative cache, allocate 8K buffer to
1889 * hold result in case the next lookup may
1890 * return something (in addition to the
1891 * packed header size)
1893 bufsiz
= phdr
->data_off
+ 8096;
1894 table
[i
].ptr
= malloc(bufsiz
);
1895 if (table
[i
].ptr
== NULL
) {
1896 (void) mutex_unlock(&nscdb
->db_mutex
);
1897 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1898 (me
, "memory allocation failure\n");
1901 (void) memcpy(table
[i
].ptr
, ptr
->buffer
, ptr
->bufsize
);
1902 ((nss_pheader_t
*)table
[i
].ptr
)->pbufsiz
= bufsiz
;
1903 table
[i
].num
= bufsiz
;
1905 (void) mutex_unlock(&nscdb
->db_mutex
);
1907 /* launch update thread for each keep hot entry */
1908 for (i
= keep
; i
> 0; i
--) {
1909 if (table
[i
].ptr
== NULL
)
1910 continue; /* unused slot in table */
1911 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
1912 (me
, "%s: launching update\n", nscdb
->name
);
1913 largs
= (nsc_lookup_args_t
*)malloc(sizeof (*largs
));
1914 if (largs
== NULL
) {
1915 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1916 (me
, "memory allocation failure\n");
1919 largs
->buffer
= table
[i
].ptr
;
1920 largs
->bufsize
= table
[i
].num
;
1922 largs
->nscdb
= nscdb
;
1923 if (launch_update(largs
) < 0)
1925 (void) sleep(interval
);
1929 * The update thread will handle freeing of buffer and largs.
1930 * Free the table here.
1937 launch_update(nsc_lookup_args_t
*in
)
1939 char *me
= "launch_update";
1942 errnum
= thr_create(NULL
, 0, (void *(*)(void*))do_update
,
1943 in
, 0|THR_DETACHED
, NULL
);
1945 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
1946 (me
, "%s: thread creation failure (%d)\n",
1947 in
->nscdb
->name
, errnum
);
1955 do_update(nsc_lookup_args_t
*in
) {
1956 nss_pheader_t
*phdr
= (nss_pheader_t
*)in
->buffer
;
1958 /* update the length of the data buffer */
1959 phdr
->data_len
= phdr
->pbufsiz
- phdr
->data_off
;
1961 (void) lookup_int(in
, UPDATEBIT
);
1971 nsc_invalidate(nsc_ctx_t
*ctx
, char *dbname
, nsc_ctx_t
**ctxs
)
1974 char *me
= "nsc_invalidate";
1977 ctx_invalidate(ctx
);
1982 if ((i
= get_cache_idx(dbname
)) == -1) {
1983 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1984 (me
, "%s: invalid cache name\n", dbname
);
1987 if ((ctx
= cache_ctx_p
[i
]) == NULL
) {
1988 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_WARNING
)
1989 (me
, "%s: no cache context found\n",
1993 ctx_invalidate(ctx
);
2000 for (i
= 0; i
< CACHE_CTX_COUNT
; i
++) {
2001 if (ctxs
[i
] != NULL
)
2002 ctx_invalidate(ctxs
[i
]);
2008 * Invalidate cache by context
2011 ctx_invalidate(nsc_ctx_t
*ctx
)
2015 char *me
= "ctx_invalidate";
2017 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2018 (me
, "%s: invalidate cache\n", ctx
->dbname
);
2020 for (i
= 0; i
< ctx
->db_count
; i
++) {
2021 if (ctx
->nsc_db
[i
] == NULL
)
2023 (void) mutex_lock(&ctx
->nsc_db
[i
]->db_mutex
);
2024 entry
= ctx
->nsc_db
[i
]->qtail
;
2025 while (entry
!= NULL
) {
2026 /* leave pending calls alone */
2027 if (!(entry
->stats
.status
& ST_PENDING
))
2028 entry
->stats
.status
= ST_DISCARD
;
2029 entry
= entry
->qnext
;
2031 (void) mutex_unlock(&ctx
->nsc_db
[i
]->db_mutex
);
2034 (void) mutex_lock(&ctx
->stats_mutex
);
2035 ctx
->stats
.invalidate_count
++;
2036 (void) mutex_unlock(&ctx
->stats_mutex
);
2044 * nscdb->db_mutex lock must be held before calling this function
2047 delete_entry(nsc_db_t
*nscdb
, nsc_ctx_t
*ctx
, nsc_entry_t
*entry
) {
2050 avl_remove(&nscdb
->tree
, entry
);
2051 HASH_REMOVE(nscdb
, entry
, hash
, nscd_false
);
2052 queue_remove(nscdb
, entry
);
2053 if (entry
->buffer
!= NULL
) {
2054 free(entry
->buffer
);
2055 entry
->buffer
= NULL
;
2057 umem_cache_free(nsc_entry_cache
, entry
);
2058 (void) mutex_lock(&ctx
->stats_mutex
);
2059 ctx
->stats
.entries
--;
2060 (void) mutex_unlock(&ctx
->stats_mutex
);
2065 lookup_cache(nsc_lookup_args_t
*largs
, nscd_cfg_cache_t
*cfgp
,
2066 nss_XbyY_args_t
*argp
, char *whoami
, nsc_entry_t
**entry
)
2073 nsc_entry_t find_entry
, *node
;
2074 char *me
= "lookup_cache";
2077 nscdb
= largs
->nscdb
;
2079 /* set the search key */
2080 find_entry
.key
= argp
->key
; /* struct copy (not deep) */
2082 /* lookup the hash table ==> O(1) */
2083 if (nscdb
->htable
) {
2084 *entry
= hash_find(nscdb
, &find_entry
, &hash
, nscd_true
);
2085 if (*entry
!= NULL
) {
2086 (void) queue_adjust(nscdb
, *entry
);
2087 return (NSCD_SUCCESS
);
2091 /* if not found, lookup the AVL tree ==> O(log n) */
2092 *entry
= (nsc_entry_t
*)avl_find(&nscdb
->tree
, &find_entry
, &pos
);
2093 if (*entry
!= NULL
) {
2094 (void) queue_adjust(nscdb
, *entry
);
2095 /* move it to the hash table */
2096 if (nscdb
->htable
) {
2097 if (nscdb
->htable
[hash
] == NULL
||
2098 (*entry
)->stats
.hits
>=
2099 nscdb
->htable
[hash
]->stats
.hits
) {
2100 nscdb
->htable
[hash
] = *entry
;
2103 return (NSCD_SUCCESS
);
2106 /* entry not found in the cache */
2107 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2108 (me
, "%s: cache miss\n", whoami
);
2110 if (cfgp
->avoid_ns
== nscd_true
) {
2111 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2112 (me
, "%s: avoid name service\n", whoami
);
2113 return (NSCD_DB_ENTRY_NOT_FOUND
);
2116 /* allocate memory for new entry (stub) */
2117 *entry
= (nsc_entry_t
*)umem_cache_alloc(nsc_entry_cache
,
2119 if (*entry
== NULL
) {
2120 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2121 (me
, "%s: memory allocation failure\n", whoami
);
2122 return (NSCD_NO_MEMORY
);
2124 (void) memset(*entry
, 0, sizeof (**entry
));
2127 * Note that the actual data for the key is stored within
2128 * the largs->buffer (input buffer to nsc_lookup).
2129 * find_entry.key only contains pointers to this data.
2131 * If largs->buffer will be re-allocated by nss_psearch
2132 * then (*entry)->key will have dangling pointers.
2133 * In such case, the following assignment needs to be
2134 * replaced by code that duplicates the key.
2136 (*entry
)->key
= find_entry
.key
;
2139 * Add the entry to the cache.
2141 avl_insert(&nscdb
->tree
, *entry
, pos
); /* O(log n) */
2142 (void) queue_adjust(nscdb
, *entry
); /* constant */
2143 if (nscdb
->htable
) /* constant */
2144 nscdb
->htable
[hash
] = *entry
;
2145 (*entry
)->stats
.status
= ST_NEW_ENTRY
;
2147 (void) mutex_lock(&ctx
->stats_mutex
);
2148 nentries
= ++(ctx
->stats
.entries
);
2149 (void) mutex_unlock(&ctx
->stats_mutex
);
2151 /* Have we exceeded max entries ? */
2152 if (cfgp
->maxentries
> 0 && nentries
> cfgp
->maxentries
) {
2153 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2154 (me
, "%s: maximum entries exceeded -- "
2155 "deleting least recently used entry\n",
2158 node
= nscdb
->qhead
;
2159 while (node
!= NULL
&& node
!= *entry
) {
2160 if (node
->stats
.status
== ST_DISCARD
||
2161 !(node
->stats
.status
& ST_PENDING
)) {
2162 delete_entry(nscdb
, ctx
, node
);
2169 * It's okay if we were not able to find one to delete.
2170 * The reaper (when invoked) will return the cache to a
2175 return (NSCD_SUCCESS
);
2179 reaper(nsc_ctx_t
*ctx
)
2181 uint_t ttl
, extra_sleep
, total_sleep
, intervals
;
2182 uint_t nodes_per_interval
, seconds_per_interval
;
2183 ulong_t nsc_entries
;
2184 char *me
= "reaper";
2187 (void) mutex_lock(&ctx
->stats_mutex
);
2188 nsc_entries
= ctx
->stats
.entries
;
2189 (void) mutex_unlock(&ctx
->stats_mutex
);
2191 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2192 ttl
= ctx
->cfg
.pos_ttl
;
2193 (void) rw_unlock(&ctx
->cfg_rwlp
);
2195 if (nsc_entries
== 0) {
2196 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2197 (me
, "%s: nothing to reap\n", ctx
->dbname
);
2199 /* sleep for atleast 60 seconds */
2202 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2203 (me
, "%s: sleep %d\n", ctx
->dbname
, ttl
);
2208 if (ttl
< 32) ttl
= 32;
2209 if (ttl
> (1<<28)) ttl
= 1<<28;
2212 * minimum nodes_per_interval = 256 or 1<<8
2213 * maximum nodes_per_interval = nsc_entries
2214 * minimum seconds_per_interval = 32 or 1<<5
2215 * maximum_seconds_per_interval = ttl
2217 if (nsc_entries
<= ttl
) {
2218 intervals
= (nsc_entries
>> 8) + 1;
2219 seconds_per_interval
= ttl
/ intervals
;
2220 nodes_per_interval
= 256;
2222 intervals
= (ttl
>> 5) + 1;
2223 seconds_per_interval
= 32;
2224 nodes_per_interval
= nsc_entries
/ intervals
;
2225 if (nodes_per_interval
< 256)
2226 nodes_per_interval
= 256;
2229 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2230 (me
, "%s: total entries = %d, "
2231 "seconds per interval = %d, "
2232 "nodes per interval = %d\n",
2233 ctx
->dbname
, nsc_entries
, seconds_per_interval
,
2234 nodes_per_interval
);
2235 total_sleep
= reap_cache(ctx
, nodes_per_interval
,
2236 seconds_per_interval
);
2237 extra_sleep
= 1 + ttl
- total_sleep
;
2238 if (extra_sleep
> 0)
2239 (void) sleep(extra_sleep
);
2245 reap_cache(nsc_ctx_t
*ctx
, uint_t nodes_per_interval
,
2246 uint_t seconds_per_interval
)
2248 uint_t nodes_togo
, total_sleep
;
2250 nsc_entry_t
*node
, *next_node
;
2252 uint_t primes
[] = {_NSC_HTSIZE_PRIMES
};
2253 ulong_t count
, nentries
, maxentries
;
2254 int i
, slot
, value
, newhtsize
;
2255 char *me
= "reap_cache";
2259 nodes_togo
= nodes_per_interval
;
2262 for (i
= 0; i
< ctx
->db_count
; i
++) {
2263 nscdb
= ctx
->nsc_db
[i
];
2264 (void) mutex_lock(&nscdb
->db_mutex
);
2265 nscdb
->reap_node
= nscdb
->qtail
;
2266 while (nscdb
->reap_node
!= NULL
) {
2267 if (nodes_togo
== 0) {
2268 (void) mutex_unlock(&nscdb
->db_mutex
);
2269 (void) sleep(seconds_per_interval
);
2270 total_sleep
+= seconds_per_interval
;
2271 nodes_togo
= nodes_per_interval
;
2273 (void) mutex_lock(&nscdb
->db_mutex
);
2275 /* delete ST_DISCARD and expired nodes */
2276 if ((node
= nscdb
->reap_node
) == NULL
)
2278 if (node
->stats
.status
== ST_DISCARD
||
2279 (!(node
->stats
.status
& ST_PENDING
) &&
2280 node
->stats
.timestamp
< now
)) {
2282 * Delete entry if its discard flag is
2283 * set OR if it has expired. Entries
2284 * with pending updates are not
2286 * nscdb->reap_node will be adjusted
2289 delete_entry(nscdb
, ctx
, node
);
2292 nscdb
->reap_node
= node
->qnext
;
2297 if (nscdb
->htsize
== 0) {
2298 (void) mutex_unlock(&nscdb
->db_mutex
);
2303 * Dynamic adjustment of hash table size.
2305 * Hash table size is roughly 1/8th of the
2306 * total entries. However the size is changed
2307 * only when the number of entries double or
2310 nentries
= avl_numnodes(&nscdb
->tree
);
2311 for (slot
= 0, value
= _NSC_INIT_HTSIZE_SLOT_VALUE
;
2312 slot
< _NSC_HTSIZE_NUM_SLOTS
&& nentries
> value
;
2313 value
= (value
<< 1) + 1, slot
++)
2315 if (nscdb
->hash_type
== nsc_ht_power2
)
2316 newhtsize
= _NSC_INIT_HTSIZE_POWER2
<< slot
;
2318 newhtsize
= primes
[slot
];
2320 /* Recommended size is same as the current size. Done */
2321 if (nscdb
->htsize
== newhtsize
) {
2322 (void) mutex_unlock(&nscdb
->db_mutex
);
2326 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2327 (me
, "%s: resizing hash table from %d to %d\n",
2328 nscdb
->name
, nscdb
->htsize
, newhtsize
);
2331 * Dump old hashes because it would be time
2332 * consuming to rehash them.
2334 (void) free(nscdb
->htable
);
2335 nscdb
->htable
= calloc(newhtsize
, sizeof (*(nscdb
->htable
)));
2336 if (nscdb
->htable
== NULL
) {
2337 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_ERROR
)
2338 (me
, "%s: memory allocation failure\n",
2340 /* -1 to try later */
2343 nscdb
->htsize
= newhtsize
;
2345 (void) mutex_unlock(&nscdb
->db_mutex
);
2348 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2349 (me
, "%s: reaped %lu entries\n", ctx
->dbname
, count
);
2352 * if cache is almost full then reduce it to a safe level by
2353 * evicting LRU entries
2356 (void) rw_rdlock(&ctx
->cfg_rwlp
);
2357 maxentries
= ctx
->cfg
.maxentries
;
2358 (void) rw_unlock(&ctx
->cfg_rwlp
);
2360 /* No limit on number of entries. Done */
2361 if (maxentries
== 0)
2364 (void) mutex_lock(&ctx
->stats_mutex
);
2365 nentries
= ctx
->stats
.entries
;
2366 (void) mutex_unlock(&ctx
->stats_mutex
);
2368 /* what is the percentage of cache used ? */
2369 value
= (nentries
* 100) / maxentries
;
2370 if (value
< _NSC_EVICTION_START_LEVEL
)
2374 * cache needs to be reduced to a safe level
2376 value
-= _NSC_EVICTION_SAFE_LEVEL
;
2377 for (i
= 0, count
= 0; i
< ctx
->db_count
; i
++) {
2379 * Reduce each subcache by 'value' percent
2381 nscdb
= ctx
->nsc_db
[i
];
2382 (void) mutex_lock(&nscdb
->db_mutex
);
2383 nodes_togo
= (value
* avl_numnodes(&nscdb
->tree
)) / 100;
2385 /* Start from LRU entry i.e queue head */
2386 next_node
= nscdb
->qhead
;
2387 while (nodes_togo
> 0 && next_node
!= NULL
) {
2389 next_node
= next_node
->qprev
;
2390 if (node
->stats
.status
== ST_DISCARD
||
2391 !(node
->stats
.status
& ST_PENDING
)) {
2392 /* Leave nodes with pending updates alone */
2393 delete_entry(nscdb
, ctx
, node
);
2398 (void) mutex_unlock(&nscdb
->db_mutex
);
2401 _NSCD_LOG(NSCD_LOG_CACHE
, NSCD_LOG_LEVEL_DEBUG
)
2402 (me
, "%s: evicted %lu LRU entries\n", ctx
->dbname
, count
);
2405 return (total_sleep
);