xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_acache.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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