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 #include <nfs/nfs.h> 28 #include <nfs/nfs4.h> 29 #include <nfs/rnode4.h> 30 #include <nfs/nfs4_clnt.h> 31 #include <sys/bitmap.h> 32 33 /* 34 * Access cache 35 */ 36 static acache4_hash_t *acache4; 37 static long nacache; /* used strictly to size the number of hash queues */ 38 39 static int acache4size; 40 static int acache4mask; 41 static struct kmem_cache *acache4_cache; 42 static int acache4_hashlen = 4; 43 44 /* 45 * This probably needs to be larger than or equal to 46 * log2(sizeof (struct rnode)) due to the way that rnodes are 47 * allocated. 48 */ 49 #define ACACHE4_SHIFT_BITS 9 50 51 static int 52 acache4hash(rnode4_t *rp, cred_t *cred) 53 { 54 return ((((intptr_t)rp >> ACACHE4_SHIFT_BITS) + crgetuid(cred)) & 55 acache4mask); 56 } 57 58 #ifdef DEBUG 59 static long nfs4_access_cache_hits = 0; 60 static long nfs4_access_cache_misses = 0; 61 #endif 62 63 nfs4_access_type_t 64 nfs4_access_check(rnode4_t *rp, uint32_t acc, cred_t *cr) 65 { 66 acache4_t *ap; 67 acache4_hash_t *hp; 68 nfs4_access_type_t all; 69 vnode_t *vp; 70 71 vp = RTOV4(rp); 72 if (!ATTRCACHE4_VALID(vp) || nfs4_waitfor_purge_complete(vp)) 73 return (NFS4_ACCESS_UNKNOWN); 74 75 if (rp->r_acache != NULL) { 76 hp = &acache4[acache4hash(rp, cr)]; 77 rw_enter(&hp->lock, RW_READER); 78 ap = hp->next; 79 while (ap != (acache4_t *)hp) { 80 if (crcmp(ap->cred, cr) == 0 && ap->rnode == rp) { 81 if ((ap->known & acc) == acc) { 82 #ifdef DEBUG 83 nfs4_access_cache_hits++; 84 #endif 85 if ((ap->allowed & acc) == acc) 86 all = NFS4_ACCESS_ALLOWED; 87 else 88 all = NFS4_ACCESS_DENIED; 89 } else { 90 #ifdef DEBUG 91 nfs4_access_cache_misses++; 92 #endif 93 all = NFS4_ACCESS_UNKNOWN; 94 } 95 rw_exit(&hp->lock); 96 return (all); 97 } 98 ap = ap->next; 99 } 100 rw_exit(&hp->lock); 101 } 102 103 #ifdef DEBUG 104 nfs4_access_cache_misses++; 105 #endif 106 return (NFS4_ACCESS_UNKNOWN); 107 } 108 109 void 110 nfs4_access_cache(rnode4_t *rp, uint32_t acc, uint32_t resacc, cred_t *cr) 111 { 112 acache4_t *ap; 113 acache4_t *nap; 114 acache4_hash_t *hp; 115 116 hp = &acache4[acache4hash(rp, cr)]; 117 118 /* 119 * Allocate now assuming that mostly an allocation will be 120 * required. This allows the allocation to happen without 121 * holding the hash bucket locked. 122 */ 123 nap = kmem_cache_alloc(acache4_cache, KM_NOSLEEP); 124 if (nap != NULL) { 125 nap->known = acc; 126 nap->allowed = resacc; 127 nap->rnode = rp; 128 crhold(cr); 129 nap->cred = cr; 130 nap->hashq = hp; 131 } 132 133 rw_enter(&hp->lock, RW_WRITER); 134 135 if (rp->r_acache != NULL) { 136 ap = hp->next; 137 while (ap != (acache4_t *)hp) { 138 if (crcmp(ap->cred, cr) == 0 && ap->rnode == rp) { 139 ap->known |= acc; 140 ap->allowed &= ~acc; 141 ap->allowed |= resacc; 142 rw_exit(&hp->lock); 143 if (nap != NULL) { 144 crfree(nap->cred); 145 kmem_cache_free(acache4_cache, nap); 146 } 147 return; 148 } 149 ap = ap->next; 150 } 151 } 152 153 if (nap != NULL) { 154 #ifdef DEBUG 155 clstat4_debug.access.value.ui64++; 156 #endif 157 nap->next = hp->next; 158 hp->next = nap; 159 nap->next->prev = nap; 160 nap->prev = (acache4_t *)hp; 161 162 mutex_enter(&rp->r_statelock); 163 nap->list = rp->r_acache; 164 rp->r_acache = nap; 165 mutex_exit(&rp->r_statelock); 166 } 167 168 rw_exit(&hp->lock); 169 } 170 171 int 172 nfs4_access_purge_rp(rnode4_t *rp) 173 { 174 acache4_t *ap, *tmpap, *rplist; 175 176 /* 177 * If there aren't any cached entries, then there is nothing 178 * to free. 179 */ 180 if (rp->r_acache == NULL) 181 return (0); 182 183 mutex_enter(&rp->r_statelock); 184 rplist = rp->r_acache; 185 rp->r_acache = NULL; 186 mutex_exit(&rp->r_statelock); 187 188 /* 189 * Loop through each entry in the list pointed to in the 190 * rnode. Remove each of these entries from the hash 191 * queue that it is on and remove it from the list in 192 * the rnode. 193 */ 194 for (ap = rplist; ap != NULL; ap = tmpap) { 195 rw_enter(&ap->hashq->lock, RW_WRITER); 196 ap->prev->next = ap->next; 197 ap->next->prev = ap->prev; 198 rw_exit(&ap->hashq->lock); 199 200 tmpap = ap->list; 201 crfree(ap->cred); 202 kmem_cache_free(acache4_cache, ap); 203 #ifdef DEBUG 204 clstat4_debug.access.value.ui64--; 205 #endif 206 } 207 208 return (1); 209 } 210 211 int 212 nfs4_acache_init(void) 213 { 214 extern int rtable4size; 215 int i; 216 217 /* 218 * Initial guess is one access cache entry per rnode unless 219 * nacache is set to a non-zero value and then it is used to 220 * indicate a guess at the number of access cache entries. 221 */ 222 if (nacache > 0) 223 acache4size = 1 << highbit(nacache / acache4_hashlen); 224 else 225 acache4size = rtable4size; 226 acache4mask = acache4size - 1; 227 acache4 = kmem_alloc(acache4size * sizeof (*acache4), KM_SLEEP); 228 for (i = 0; i < acache4size; i++) { 229 acache4[i].next = (acache4_t *)&acache4[i]; 230 acache4[i].prev = (acache4_t *)&acache4[i]; 231 rw_init(&acache4[i].lock, NULL, RW_DEFAULT, NULL); 232 } 233 acache4_cache = kmem_cache_create("nfs4_access_cache", 234 sizeof (acache4_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 235 236 return (0); 237 } 238 239 int 240 nfs4_acache_fini(void) 241 { 242 int i; 243 244 /* 245 * Deallocated the access cache 246 */ 247 kmem_cache_destroy(acache4_cache); 248 249 for (i = 0; i < acache4size; i++) 250 rw_destroy(&acache4[i].lock); 251 kmem_free(acache4, acache4size * sizeof (*acache4)); 252 253 return (0); 254 } 255