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