1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <nfs/nfs.h> 30 #include <nfs/nfs4.h> 31 #include <nfs/rnode4.h> 32 #include <nfs/nfs4_clnt.h> 33 #include <sys/bitmap.h> 34 35 /* 36 * Access cache 37 */ 38 static acache4_hash_t *acache4; 39 static long nacache; /* used strictly to size the number of hash queues */ 40 41 static int acache4size; 42 static int acache4mask; 43 static struct kmem_cache *acache4_cache; 44 static int acache4_hashlen = 4; 45 46 /* 47 * This probably needs to be larger than or equal to 48 * log2(sizeof (struct rnode)) due to the way that rnodes are 49 * allocated. 50 */ 51 #define ACACHE4_SHIFT_BITS 9 52 53 static int 54 acache4hash(rnode4_t *rp, cred_t *cred) 55 { 56 return ((((intptr_t)rp >> ACACHE4_SHIFT_BITS) + crgetuid(cred)) & 57 acache4mask); 58 } 59 60 #ifdef DEBUG 61 static long nfs4_access_cache_hits = 0; 62 static long nfs4_access_cache_misses = 0; 63 #endif 64 65 nfs4_access_type_t 66 nfs4_access_check(rnode4_t *rp, uint32_t acc, cred_t *cr) 67 { 68 acache4_t *ap; 69 acache4_hash_t *hp; 70 nfs4_access_type_t all; 71 vnode_t *vp; 72 73 vp = RTOV4(rp); 74 if (!ATTRCACHE4_VALID(vp) || nfs4_waitfor_purge_complete(vp)) 75 return (NFS4_ACCESS_UNKNOWN); 76 77 if (rp->r_acache != NULL) { 78 hp = &acache4[acache4hash(rp, cr)]; 79 rw_enter(&hp->lock, RW_READER); 80 ap = hp->next; 81 while (ap != (acache4_t *)hp) { 82 if (crcmp(ap->cred, cr) == 0 && ap->rnode == rp) { 83 if ((ap->known & acc) == acc) { 84 #ifdef DEBUG 85 nfs4_access_cache_hits++; 86 #endif 87 if ((ap->allowed & acc) == acc) 88 all = NFS4_ACCESS_ALLOWED; 89 else 90 all = NFS4_ACCESS_DENIED; 91 } else { 92 #ifdef DEBUG 93 nfs4_access_cache_misses++; 94 #endif 95 all = NFS4_ACCESS_UNKNOWN; 96 } 97 rw_exit(&hp->lock); 98 return (all); 99 } 100 ap = ap->next; 101 } 102 rw_exit(&hp->lock); 103 } 104 105 #ifdef DEBUG 106 nfs4_access_cache_misses++; 107 #endif 108 return (NFS4_ACCESS_UNKNOWN); 109 } 110 111 void 112 nfs4_access_cache(rnode4_t *rp, uint32_t acc, uint32_t resacc, cred_t *cr) 113 { 114 acache4_t *ap; 115 acache4_t *nap; 116 acache4_hash_t *hp; 117 118 hp = &acache4[acache4hash(rp, cr)]; 119 120 /* 121 * Allocate now assuming that mostly an allocation will be 122 * required. This allows the allocation to happen without 123 * holding the hash bucket locked. 124 */ 125 nap = kmem_cache_alloc(acache4_cache, KM_NOSLEEP); 126 if (nap != NULL) { 127 nap->known = acc; 128 nap->allowed = resacc; 129 nap->rnode = rp; 130 crhold(cr); 131 nap->cred = cr; 132 nap->hashq = hp; 133 } 134 135 rw_enter(&hp->lock, RW_WRITER); 136 137 if (rp->r_acache != NULL) { 138 ap = hp->next; 139 while (ap != (acache4_t *)hp) { 140 if (crcmp(ap->cred, cr) == 0 && ap->rnode == rp) { 141 ap->known |= acc; 142 ap->allowed &= ~acc; 143 ap->allowed |= resacc; 144 rw_exit(&hp->lock); 145 if (nap != NULL) { 146 crfree(nap->cred); 147 kmem_cache_free(acache4_cache, nap); 148 } 149 return; 150 } 151 ap = ap->next; 152 } 153 } 154 155 if (nap != NULL) { 156 #ifdef DEBUG 157 clstat4_debug.access.value.ui64++; 158 #endif 159 nap->next = hp->next; 160 hp->next = nap; 161 nap->next->prev = nap; 162 nap->prev = (acache4_t *)hp; 163 164 mutex_enter(&rp->r_statelock); 165 nap->list = rp->r_acache; 166 rp->r_acache = nap; 167 mutex_exit(&rp->r_statelock); 168 } 169 170 rw_exit(&hp->lock); 171 } 172 173 int 174 nfs4_access_purge_rp(rnode4_t *rp) 175 { 176 acache4_t *ap, *tmpap, *rplist; 177 178 /* 179 * If there aren't any cached entries, then there is nothing 180 * to free. 181 */ 182 if (rp->r_acache == NULL) 183 return (0); 184 185 mutex_enter(&rp->r_statelock); 186 rplist = rp->r_acache; 187 rp->r_acache = NULL; 188 mutex_exit(&rp->r_statelock); 189 190 /* 191 * Loop through each entry in the list pointed to in the 192 * rnode. Remove each of these entries from the hash 193 * queue that it is on and remove it from the list in 194 * the rnode. 195 */ 196 for (ap = rplist; ap != NULL; ap = tmpap) { 197 rw_enter(&ap->hashq->lock, RW_WRITER); 198 ap->prev->next = ap->next; 199 ap->next->prev = ap->prev; 200 rw_exit(&ap->hashq->lock); 201 202 tmpap = ap->list; 203 crfree(ap->cred); 204 kmem_cache_free(acache4_cache, ap); 205 #ifdef DEBUG 206 clstat4_debug.access.value.ui64--; 207 #endif 208 } 209 210 return (1); 211 } 212 213 int 214 nfs4_acache_init(void) 215 { 216 extern int rtable4size; 217 int i; 218 219 /* 220 * Initial guess is one access cache entry per rnode unless 221 * nacache is set to a non-zero value and then it is used to 222 * indicate a guess at the number of access cache entries. 223 */ 224 if (nacache > 0) 225 acache4size = 1 << highbit(nacache / acache4_hashlen); 226 else 227 acache4size = rtable4size; 228 acache4mask = acache4size - 1; 229 acache4 = kmem_alloc(acache4size * sizeof (*acache4), KM_SLEEP); 230 for (i = 0; i < acache4size; i++) { 231 acache4[i].next = (acache4_t *)&acache4[i]; 232 acache4[i].prev = (acache4_t *)&acache4[i]; 233 rw_init(&acache4[i].lock, NULL, RW_DEFAULT, NULL); 234 } 235 acache4_cache = kmem_cache_create("nfs4_access_cache", 236 sizeof (acache4_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 237 238 return (0); 239 } 240 241 int 242 nfs4_acache_fini(void) 243 { 244 int i; 245 246 /* 247 * Deallocated the access cache 248 */ 249 kmem_cache_destroy(acache4_cache); 250 251 for (i = 0; i < acache4size; i++) 252 rw_destroy(&acache4[i].lock); 253 kmem_free(acache4, acache4size * sizeof (*acache4)); 254 255 return (0); 256 } 257