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 }