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