Home | History | Annotate | Download | only in nfs
      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