The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/nfsserver/nfs_srvcache.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1989, 1993
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * This code is derived from software contributed to Berkeley by
    6  * Rick Macklem at The University of Guelph.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 4. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)nfs_srvcache.c      8.3 (Berkeley) 3/30/95
   33  */
   34 
   35 #include <sys/cdefs.h>
   36 __FBSDID("$FreeBSD$");
   37 
   38 /*
   39  * Reference: Chet Juszczak, "Improving the Performance and Correctness
   40  *              of an NFS Server", in Proc. Winter 1989 USENIX Conference,
   41  *              pages 53-63. San Diego, February 1989.
   42  */
   43 #include <sys/param.h>
   44 #include <sys/malloc.h>
   45 #include <sys/mount.h>
   46 #include <sys/systm.h>
   47 #include <sys/lock.h>
   48 #include <sys/mbuf.h>
   49 #include <sys/mutex.h>
   50 #include <sys/socket.h>
   51 #include <sys/socketvar.h>      /* for sodupsockaddr */
   52 #include <sys/eventhandler.h>
   53 
   54 #include <netinet/in.h>
   55 #include <nfs/rpcv2.h>
   56 #include <nfs/nfsproto.h>
   57 #include <nfsserver/nfs.h>
   58 #include <nfsserver/nfsrvcache.h>
   59 
   60 static long numnfsrvcache;
   61 static long desirednfsrvcache;
   62 
   63 #define NFSRCHASH(xid) \
   64         (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
   65 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
   66 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
   67 static u_long nfsrvhash;
   68 
   69 #define TRUE    1
   70 #define FALSE   0
   71 
   72 #define NETFAMILY(rp) \
   73                 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
   74 
   75 /*
   76  * Static array that defines which nfs rpc's are nonidempotent
   77  */
   78 static const int nonidempotent[NFS_NPROCS] = {
   79         FALSE,
   80         FALSE,
   81         TRUE,
   82         FALSE,
   83         FALSE,
   84         FALSE,
   85         FALSE,
   86         TRUE,
   87         TRUE,
   88         TRUE,
   89         TRUE,
   90         TRUE,
   91         TRUE,
   92         TRUE,
   93         TRUE,
   94         TRUE,
   95         FALSE,
   96         FALSE,
   97         FALSE,
   98         FALSE,
   99         FALSE,
  100         FALSE,
  101         FALSE,
  102 };
  103 
  104 /* True iff the rpc reply is an nfs status ONLY! */
  105 static const int nfsv2_repstat[NFS_NPROCS] = {
  106         FALSE,
  107         FALSE,
  108         FALSE,
  109         FALSE,
  110         FALSE,
  111         FALSE,
  112         FALSE,
  113         FALSE,
  114         FALSE,
  115         FALSE,
  116         TRUE,
  117         TRUE,
  118         TRUE,
  119         TRUE,
  120         FALSE,
  121         TRUE,
  122         FALSE,
  123         FALSE,
  124 };
  125 
  126 /* 
  127  * Size the NFS server's duplicate request cache at 1/2 the nmbclsters, floating 
  128  * within a (64, 2048) range. This is to prevent all mbuf clusters being tied up 
  129  * in the NFS dupreq cache for small values of nmbclusters. 
  130  */
  131 static void
  132 nfsrvcache_size_change(void *tag)
  133 {
  134         desirednfsrvcache = nmbclusters /2;
  135         if (desirednfsrvcache > NFSRVCACHE_MAX_SIZE)
  136                 desirednfsrvcache = NFSRVCACHE_MAX_SIZE;
  137         if (desirednfsrvcache < NFSRVCACHE_MIN_SIZE)
  138                 desirednfsrvcache = NFSRVCACHE_MIN_SIZE;        
  139 }
  140 
  141 /*
  142  * Initialize the server request cache list
  143  */
  144 void
  145 nfsrv_initcache(void)
  146 {
  147         nfsrvcache_size_change(NULL);
  148         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
  149         TAILQ_INIT(&nfsrvlruhead);
  150         EVENTHANDLER_REGISTER(nmbclusters_change, nfsrvcache_size_change, NULL,
  151                               EVENTHANDLER_PRI_FIRST);
  152 }
  153 
  154 /*
  155  * Teardown the server request cache list
  156  */
  157 void
  158 nfsrv_destroycache(void)
  159 {
  160 
  161         KASSERT(TAILQ_EMPTY(&nfsrvlruhead), ("%s: pending requests", __func__));
  162         hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
  163 }
  164 
  165 /*
  166  * Look for the request in the cache
  167  * If found then
  168  *    return action and optionally reply
  169  * else
  170  *    insert it in the cache
  171  *
  172  * The rules are as follows:
  173  * - if in progress, return DROP request
  174  * - if completed within DELAY of the current time, return DROP it
  175  * - if completed a longer time ago return REPLY if the reply was cached or
  176  *   return DOIT
  177  * Update/add new request at end of lru list
  178  */
  179 int
  180 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
  181 {
  182         struct nfsrvcache *rp;
  183         struct mbuf *mb;
  184         struct sockaddr_in *saddr;
  185         caddr_t bpos;
  186         int ret;
  187 
  188         NFSD_LOCK_ASSERT();
  189 
  190         /*
  191          * Don't cache recent requests for reliable transport protocols.
  192          * (Maybe we should for the case of a reconnect, but..)
  193          */
  194         if (!nd->nd_nam2)
  195                 return (RC_DOIT);
  196 loop:
  197         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
  198             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
  199                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
  200                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
  201                         if ((rp->rc_flag & RC_LOCKED) != 0) {
  202                                 rp->rc_flag |= RC_WANTED;
  203                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
  204                                     "nfsrc", 0);
  205                                 goto loop;
  206                         }
  207                         rp->rc_flag |= RC_LOCKED;
  208                         /* If not at end of LRU chain, move it there */
  209                         if (TAILQ_NEXT(rp, rc_lru)) {
  210                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  211                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  212                         }
  213                         if (rp->rc_state == RC_UNUSED)
  214                                 panic("nfsrv cache");
  215                         if (rp->rc_state == RC_INPROG) {
  216                                 nfsrvstats.srvcache_inproghits++;
  217                                 ret = RC_DROPIT;
  218                         } else if (rp->rc_flag & RC_REPSTATUS) {
  219                                 nfsrvstats.srvcache_nonidemdonehits++;
  220                                 *repp = nfs_rephead(0, nd, rp->rc_status,
  221                                     &mb, &bpos);
  222                                 ret = RC_REPLY;
  223                         } else if (rp->rc_flag & RC_REPMBUF) {
  224                                 nfsrvstats.srvcache_nonidemdonehits++;
  225                                 NFSD_UNLOCK();
  226                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
  227                                                 M_TRYWAIT);
  228                                 NFSD_LOCK();
  229                                 ret = RC_REPLY;
  230                         } else {
  231                                 nfsrvstats.srvcache_idemdonehits++;
  232                                 rp->rc_state = RC_INPROG;
  233                                 ret = RC_DOIT;
  234                         }
  235                         rp->rc_flag &= ~RC_LOCKED;
  236                         if (rp->rc_flag & RC_WANTED) {
  237                                 rp->rc_flag &= ~RC_WANTED;
  238                                 wakeup(rp);
  239                         }
  240                         return (ret);
  241                 }
  242         }
  243         nfsrvstats.srvcache_misses++;
  244         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
  245         if (numnfsrvcache < desirednfsrvcache) {
  246                 NFSD_UNLOCK();
  247                 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
  248                     M_NFSD, M_WAITOK | M_ZERO);
  249                 NFSD_LOCK();
  250                 numnfsrvcache++;
  251                 rp->rc_flag = RC_LOCKED;
  252         } else {
  253                 rp = TAILQ_FIRST(&nfsrvlruhead);
  254                 while ((rp->rc_flag & RC_LOCKED) != 0) {
  255                         rp->rc_flag |= RC_WANTED;
  256                         (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
  257                         rp = TAILQ_FIRST(&nfsrvlruhead);
  258                 }
  259                 rp->rc_flag |= RC_LOCKED;
  260                 LIST_REMOVE(rp, rc_hash);
  261                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  262                 if (rp->rc_flag & RC_REPMBUF)
  263                         m_freem(rp->rc_reply);
  264                 if (rp->rc_flag & RC_NAM)
  265                         FREE(rp->rc_nam, M_SONAME);
  266                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
  267         }
  268         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  269         rp->rc_state = RC_INPROG;
  270         rp->rc_xid = nd->nd_retxid;
  271         saddr = (struct sockaddr_in *)nd->nd_nam;
  272         switch (saddr->sin_family) {
  273         case AF_INET:
  274                 rp->rc_flag |= RC_INETADDR;
  275                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
  276                 break;
  277 /*      case AF_INET6:  */
  278 /*      case AF_ISO:    */
  279         default:
  280                 /*
  281                  * XXXRW: Seems like we should only set RC_NAM if we
  282                  * actually manage to set rc_nam to something non-NULL.
  283                  */
  284                 rp->rc_flag |= RC_NAM;
  285                 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
  286                 break;
  287         };
  288         rp->rc_proc = nd->nd_procnum;
  289         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
  290         rp->rc_flag &= ~RC_LOCKED;
  291         if (rp->rc_flag & RC_WANTED) {
  292                 rp->rc_flag &= ~RC_WANTED;
  293                 wakeup(rp);
  294         }
  295         return (RC_DOIT);
  296 }
  297 
  298 /*
  299  * Update a request cache entry after the rpc has been done
  300  */
  301 void
  302 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
  303 {
  304         struct nfsrvcache *rp;
  305 
  306         NFSD_LOCK_ASSERT();
  307 
  308         if (!nd->nd_nam2)
  309                 return;
  310 loop:
  311         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
  312             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
  313                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
  314                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
  315                         if ((rp->rc_flag & RC_LOCKED) != 0) {
  316                                 rp->rc_flag |= RC_WANTED;
  317                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
  318                                     "nfsrc", 0);
  319                                 goto loop;
  320                         }
  321                         rp->rc_flag |= RC_LOCKED;
  322                         if (rp->rc_state == RC_DONE) {
  323                                 /*
  324                                  * This can occur if the cache is too small.
  325                                  * Retransmits of the same request aren't
  326                                  * dropped so we may see the operation
  327                                  * complete more then once.
  328                                  */
  329                                 if (rp->rc_flag & RC_REPMBUF) {
  330                                         m_freem(rp->rc_reply);
  331                                         rp->rc_flag &= ~RC_REPMBUF;
  332                                 }
  333                         }
  334                         rp->rc_state = RC_DONE;
  335                         /*
  336                          * If we have a valid reply update status and save
  337                          * the reply for non-idempotent rpc's.
  338                          */
  339                         if (repvalid && nonidempotent[nd->nd_procnum]) {
  340                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
  341                                     nfsv2_repstat[
  342                                     nfsrvv2_procid[nd->nd_procnum]]) {
  343                                         rp->rc_status = nd->nd_repstat;
  344                                         rp->rc_flag |= RC_REPSTATUS;
  345                                 } else {
  346                                         NFSD_UNLOCK();
  347                                         rp->rc_reply = m_copym(repmbuf,
  348                                                 0, M_COPYALL, M_TRYWAIT);
  349                                         NFSD_LOCK();
  350                                         rp->rc_flag |= RC_REPMBUF;
  351                                 }
  352                         }
  353                         rp->rc_flag &= ~RC_LOCKED;
  354                         if (rp->rc_flag & RC_WANTED) {
  355                                 rp->rc_flag &= ~RC_WANTED;
  356                                 wakeup(rp);
  357                         }
  358                         return;
  359                 }
  360         }
  361         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
  362 }
  363 
  364 /*
  365  * Clean out the cache. Called when the last nfsd terminates.
  366  */
  367 void
  368 nfsrv_cleancache(void)
  369 {
  370         struct nfsrvcache *rp, *nextrp;
  371 
  372         NFSD_LOCK_ASSERT();
  373 
  374         for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
  375                 nextrp = TAILQ_NEXT(rp, rc_lru);
  376                 LIST_REMOVE(rp, rc_hash);
  377                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  378                 if (rp->rc_flag & RC_REPMBUF)
  379                         m_freem(rp->rc_reply);
  380                 if (rp->rc_flag & RC_NAM)
  381                         free(rp->rc_nam, M_SONAME);
  382                 free(rp, M_NFSD);
  383         }
  384         numnfsrvcache = 0;
  385 }

Cache object: efc9950ddc9472b2ceb1bcc2c5e2d930


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.