#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: coda_namecache.c,v 1.30 2024/05/17 23:57:46 thorpej Exp $");
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/select.h>
#include <sys/kauth.h>
#include <coda/coda.h>
#include <coda/cnode.h>
#include <coda/coda_namecache.h>
#include <coda/coda_subr.h>
int coda_nc_use = 1;
int coda_nc_size = CODA_NC_CACHESIZE;
int coda_nc_hashsize = CODA_NC_HASHSIZE;
struct coda_cache *coda_nc_heap;
struct coda_hash *coda_nc_hash;
struct coda_lru coda_nc_lru;
struct coda_nc_statistics coda_nc_stat;
int coda_nc_debug = 0;
static struct coda_cache *
coda_nc_find(struct cnode *dcp, const char *name, int namelen,
kauth_cred_t cred, int hash);
static void
coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat);
#define TOTAL_CACHE_SIZE (sizeof(struct coda_cache) * coda_nc_size)
#define TOTAL_HASH_SIZE (sizeof(struct coda_hash) * coda_nc_hashsize)
int coda_nc_initialized = 0;
void
coda_nc_init(void)
{
int i;
memset(&coda_nc_stat, 0, (sizeof(struct coda_nc_statistics)));
#ifdef CODA_VERBOSE
printf("CODA NAME CACHE: CACHE %d, HASH TBL %d\n", CODA_NC_CACHESIZE, CODA_NC_HASHSIZE);
#endif
CODA_ALLOC(coda_nc_heap, struct coda_cache *, TOTAL_CACHE_SIZE);
CODA_ALLOC(coda_nc_hash, struct coda_hash *, TOTAL_HASH_SIZE);
memset(coda_nc_heap, 0, TOTAL_CACHE_SIZE);
memset(coda_nc_hash, 0, TOTAL_HASH_SIZE);
TAILQ_INIT(&coda_nc_lru.head);
for (i=0; i < coda_nc_size; i++) {
TAILQ_INSERT_HEAD(&coda_nc_lru.head, &coda_nc_heap[i], lru);
}
for (i=0; i < coda_nc_hashsize; i++) {
LIST_INIT(&coda_nc_hash[i].head);
}
coda_nc_initialized++;
}
static struct coda_cache *
coda_nc_find(struct cnode *dcp, const char *name, int namelen,
kauth_cred_t cred, int hash)
{
struct coda_cache *cncp;
int count = 1;
CODA_NC_DEBUG(CODA_NC_FIND,
myprintf(("coda_nc_find(dcp %p, name %s, len %d, cred %p, hash %d\n",
dcp, name, namelen, cred, hash));)
LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash)
{
if ((CODA_NAMEMATCH(cncp, name, namelen, dcp)) &&
((cred == 0) || (cncp->cred == cred)))
{
coda_nc_stat.Search_len += count;
return(cncp);
}
#ifdef DEBUG
else if (CODA_NAMEMATCH(cncp, name, namelen, dcp)) {
printf("coda_nc_find: name %s, new cred = %p, cred = %p\n",
name, cred, cncp->cred);
printf("nref %d, nuid %d, ngid %d // oref %d, ocred %d, ogid %d\n",
kauth_cred_getrefcnt(cred),
kauth_cred_geteuid(cred),
kauth_cred_getegid(cred),
kauth_cred_getrefcnt(cncp->cred),
kauth_cred_geteuid(cncp->cred),
kauth_cred_getegid(cncp->cred));
coda_print_cred(cred);
coda_print_cred(cncp->cred);
}
#endif
count++;
}
return((struct coda_cache *)0);
}
void
coda_nc_enter(struct cnode *dcp, const char *name, int namelen,
kauth_cred_t cred, struct cnode *cp)
{
struct coda_cache *cncp;
int hash;
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_ENTER,
myprintf(("Enter: dcp %p cp %p name %s cred %p \n",
dcp, cp, name, cred)); )
if (namelen > CODA_NC_NAMELEN) {
CODA_NC_DEBUG(CODA_NC_ENTER,
myprintf(("long name enter %s\n",name));)
coda_nc_stat.long_name_enters++;
return;
}
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, cred, hash);
if (cncp != (struct coda_cache *) 0) {
coda_nc_stat.dbl_enters++;
return;
}
coda_nc_stat.enters++;
cncp = TAILQ_FIRST(&coda_nc_lru.head);
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
if (CODA_NC_VALID(cncp)) {
coda_nc_hash[CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp)].length--;
coda_nc_stat.lru_rm++;
LIST_REMOVE(cncp, hash);
vrele(CTOV(cncp->dcp));
vrele(CTOV(cncp->cp));
kauth_cred_free(cncp->cred);
}
vref(CTOV(cp));
vref(CTOV(dcp));
kauth_cred_hold(cred);
cncp->dcp = dcp;
cncp->cp = cp;
cncp->namelen = namelen;
cncp->cred = cred;
memcpy(cncp->name, name, (unsigned)namelen);
TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru);
LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash);
coda_nc_hash[hash].length++;
CODA_NC_DEBUG(CODA_NC_PRINTCODA_NC, print_coda_nc(); )
}
struct cnode *
coda_nc_lookup(struct cnode *dcp, const char *name, int namelen,
kauth_cred_t cred)
{
int hash;
struct coda_cache *cncp;
if (coda_nc_use == 0)
return((struct cnode *) 0);
if (namelen > CODA_NC_NAMELEN) {
CODA_NC_DEBUG(CODA_NC_LOOKUP,
myprintf(("long name lookup %s\n",name));)
coda_nc_stat.long_name_lookups++;
return((struct cnode *) 0);
}
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, cred, hash);
if (cncp == (struct coda_cache *) 0) {
coda_nc_stat.misses++;
return((struct cnode *) 0);
}
coda_nc_stat.hits++;
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
TAILQ_INSERT_TAIL(&coda_nc_lru.head, cncp, lru);
LIST_REMOVE(cncp, hash);
LIST_INSERT_HEAD(&coda_nc_hash[hash].head, cncp, hash);
CODA_NC_DEBUG(CODA_NC_LOOKUP,
printf("lookup: dcp %p, name %s, cred %p = cp %p\n",
dcp, name, cred, cncp->cp); )
return(cncp->cp);
}
static void
coda_nc_remove(struct coda_cache *cncp, enum dc_status dcstat)
{
CODA_NC_DEBUG(CODA_NC_REMOVE,
myprintf(("coda_nc_remove %s from parent %s\n",
cncp->name, coda_f2s(&cncp->dcp->c_fid))); )
LIST_REMOVE(cncp, hash);
memset(&cncp->hash, 0, sizeof(cncp->hash));
if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->dcp)) == 1)) {
cncp->dcp->c_flags |= C_PURGING;
}
vrele(CTOV(cncp->dcp));
if ((dcstat == IS_DOWNCALL) && (vrefcnt(CTOV(cncp->cp)) == 1)) {
cncp->cp->c_flags |= C_PURGING;
}
vrele(CTOV(cncp->cp));
kauth_cred_free(cncp->cred);
memset(DATA_PART(cncp), 0, DATA_SIZE);
TAILQ_REMOVE(&coda_nc_lru.head, cncp, lru);
TAILQ_INSERT_HEAD(&coda_nc_lru.head, cncp, lru);
}
void
coda_nc_zapParentfid(CodaFid *fid, enum dc_status dcstat)
{
struct coda_cache *cncp, *ncncp;
int i;
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_ZAPPFID,
myprintf(("ZapParent: fid %s\n", coda_f2s(fid))); )
coda_nc_stat.zapPfids++;
for (i = 0; i < coda_nc_hashsize; i++) {
ncncp = LIST_FIRST(&coda_nc_hash[i].head);
while ((cncp = ncncp) != NULL) {
ncncp = LIST_NEXT(cncp, hash);
if (coda_fid_eq(&(cncp->dcp->c_fid), fid)) {
coda_nc_hash[i].length--;
coda_nc_remove(cncp, dcstat);
}
}
}
}
void
coda_nc_zapfid(CodaFid *fid, enum dc_status dcstat)
{
struct coda_cache *cncp, *ncncp;
int i;
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_ZAPFID,
myprintf(("Zapfid: fid %s\n", coda_f2s(fid))); )
coda_nc_stat.zapFids++;
for (i = 0; i < coda_nc_hashsize; i++) {
ncncp = LIST_FIRST(&coda_nc_hash[i].head);
while ((cncp = ncncp) != NULL) {
ncncp = LIST_NEXT(cncp, hash);
if (coda_fid_eq(&cncp->cp->c_fid, fid)) {
coda_nc_hash[i].length--;
coda_nc_remove(cncp, dcstat);
}
}
}
}
void
coda_nc_zapvnode(CodaFid *fid, kauth_cred_t cred,
enum dc_status dcstat)
{
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_ZAPVNODE,
myprintf(("Zapvnode: fid %s cred %p\n",
coda_f2s(fid), cred)); )
}
void
coda_nc_zapfile(struct cnode *dcp, const char *name, int namelen)
{
struct coda_cache *cncp;
int hash;
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_ZAPFILE,
myprintf(("Zapfile: dcp %p name %s \n",
dcp, name)); )
if (namelen > CODA_NC_NAMELEN) {
coda_nc_stat.long_remove++;
return;
}
coda_nc_stat.zapFile++;
hash = CODA_NC_HASH(name, namelen, dcp);
cncp = coda_nc_find(dcp, name, namelen, 0, hash);
while (cncp) {
coda_nc_hash[hash].length--;
coda_nc_remove(cncp, NOT_DOWNCALL);
cncp = coda_nc_find(dcp, name, namelen, 0, hash);
}
}
void
coda_nc_purge_user(uid_t uid, enum dc_status dcstat)
{
struct coda_cache *cncp, *ncncp;
int hash;
if (coda_nc_use == 0)
return;
CODA_NC_DEBUG(CODA_NC_PURGEUSER,
myprintf(("ZapDude: uid %x\n", uid)); )
coda_nc_stat.zapUsers++;
ncncp = TAILQ_FIRST(&coda_nc_lru.head);
while ((cncp = ncncp) != NULL) {
ncncp = TAILQ_NEXT(cncp, lru);
if ((CODA_NC_VALID(cncp)) &&
(kauth_cred_geteuid(cncp->cred) == uid)) {
hash = CODA_NC_HASH(cncp->name, cncp->namelen, cncp->dcp);
coda_nc_hash[hash].length--;
coda_nc_remove(cncp, dcstat);
}
}
}
void
coda_nc_flush(enum dc_status dcstat)
{
struct coda_cache *cncp;
int i;
if (coda_nc_use == 0)
return;
coda_nc_stat.Flushes++;
TAILQ_FOREACH(cncp, &coda_nc_lru.head, lru) {
if (CODA_NC_VALID(cncp)) {
LIST_REMOVE(cncp, hash);
memset(&cncp->hash, 0, sizeof(cncp->hash));
if ((dcstat == IS_DOWNCALL)
&& (vrefcnt(CTOV(cncp->dcp)) == 1))
{
cncp->dcp->c_flags |= C_PURGING;
}
vrele(CTOV(cncp->dcp));
if (CTOV(cncp->cp)->v_iflag & VI_TEXT) {
if (coda_vmflush(cncp->cp))
CODADEBUG(CODA_FLUSH,
myprintf(("coda_nc_flush: %s busy\n",
coda_f2s(&cncp->cp->c_fid))); )
}
if ((dcstat == IS_DOWNCALL)
&& (vrefcnt(CTOV(cncp->cp)) == 1))
{
cncp->cp->c_flags |= C_PURGING;
}
vrele(CTOV(cncp->cp));
kauth_cred_free(cncp->cred);
memset(DATA_PART(cncp), 0, DATA_SIZE);
}
}
for (i = 0; i < coda_nc_hashsize; i++)
coda_nc_hash[i].length = 0;
}
void
print_coda_nc(void)
{
int hash;
struct coda_cache *cncp;
for (hash = 0; hash < coda_nc_hashsize; hash++) {
myprintf(("\nhash %d\n",hash));
LIST_FOREACH(cncp, &coda_nc_hash[hash].head, hash) {
myprintf(("cp %p dcp %p cred %p name %s\n",
cncp->cp, cncp->dcp,
cncp->cred, cncp->name));
}
}
}
void
coda_nc_gather_stats(void)
{
int i, xmax = 0, sum = 0, temp, zeros = 0, ave, n;
for (i = 0; i < coda_nc_hashsize; i++) {
if (coda_nc_hash[i].length) {
sum += coda_nc_hash[i].length;
} else {
zeros++;
}
if (coda_nc_hash[i].length > xmax)
xmax = coda_nc_hash[i].length;
}
coda_nc_stat.Sum_bucket_len = sum;
coda_nc_stat.Num_zero_len = zeros;
coda_nc_stat.Max_bucket_len = xmax;
if ((n = coda_nc_hashsize - zeros) > 0)
ave = sum / n;
else
ave = 0;
sum = 0;
for (i = 0; i < coda_nc_hashsize; i++) {
if (coda_nc_hash[i].length) {
temp = coda_nc_hash[i].length - ave;
sum += temp * temp;
}
}
coda_nc_stat.Sum2_bucket_len = sum;
}
int
coda_nc_resize(int hashsize, int heapsize, enum dc_status dcstat)
{
if ((hashsize % 2) || (heapsize % 2)) {
return(EINVAL);
}
coda_nc_use = 0;
coda_nc_flush(dcstat);
CODA_FREE(coda_nc_heap,TOTAL_CACHE_SIZE);
CODA_FREE(coda_nc_hash,TOTAL_HASH_SIZE);
coda_nc_hashsize = hashsize;
coda_nc_size = heapsize;
coda_nc_init();
coda_nc_use = 1;
return(0);
}
char coda_nc_name_buf[CODA_MAXNAMLEN+1];
void
coda_nc_name(struct cnode *cp)
{
struct coda_cache *cncp;
int i;
if (coda_nc_use == 0)
return;
for (i = 0; i < coda_nc_hashsize; i++) {
LIST_FOREACH(cncp, &coda_nc_hash[i].head, hash) {
if (cncp->cp == cp) {
memcpy(coda_nc_name_buf, cncp->name, cncp->namelen);
coda_nc_name_buf[cncp->namelen] = 0;
printf(" is %s (%p,%p)@%p",
coda_nc_name_buf, cncp->cp, cncp->dcp, cncp);
}
}
}
}