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
acache4hash(rnode4_t * rp,cred_t * cred)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
nfs4_access_check(rnode4_t * rp,uint32_t acc,cred_t * cr)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
nfs4_access_cache(rnode4_t * rp,uint32_t acc,uint32_t resacc,cred_t * cr)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
nfs4_access_purge_rp(rnode4_t * rp)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
nfs4_acache_init(void)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
nfs4_acache_fini(void)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