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: releng/6.2/sys/nfsserver/nfs_srvcache.c 161221 2006-08-11 19:13:25Z jhb $");
   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 
   53 #include <netinet/in.h>
   54 #include <nfs/rpcv2.h>
   55 #include <nfs/nfsproto.h>
   56 #include <nfsserver/nfs.h>
   57 #include <nfsserver/nfsrvcache.h>
   58 
   59 static long numnfsrvcache;
   60 static long desirednfsrvcache = NFSRVCACHESIZ;
   61 
   62 #define NFSRCHASH(xid) \
   63         (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
   64 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
   65 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
   66 static u_long nfsrvhash;
   67 
   68 #define TRUE    1
   69 #define FALSE   0
   70 
   71 #define NETFAMILY(rp) \
   72                 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
   73 
   74 /*
   75  * Static array that defines which nfs rpc's are nonidempotent
   76  */
   77 static const int nonidempotent[NFS_NPROCS] = {
   78         FALSE,
   79         FALSE,
   80         TRUE,
   81         FALSE,
   82         FALSE,
   83         FALSE,
   84         FALSE,
   85         TRUE,
   86         TRUE,
   87         TRUE,
   88         TRUE,
   89         TRUE,
   90         TRUE,
   91         TRUE,
   92         TRUE,
   93         TRUE,
   94         FALSE,
   95         FALSE,
   96         FALSE,
   97         FALSE,
   98         FALSE,
   99         FALSE,
  100         FALSE,
  101 };
  102 
  103 /* True iff the rpc reply is an nfs status ONLY! */
  104 static const int nfsv2_repstat[NFS_NPROCS] = {
  105         FALSE,
  106         FALSE,
  107         FALSE,
  108         FALSE,
  109         FALSE,
  110         FALSE,
  111         FALSE,
  112         FALSE,
  113         FALSE,
  114         FALSE,
  115         TRUE,
  116         TRUE,
  117         TRUE,
  118         TRUE,
  119         FALSE,
  120         TRUE,
  121         FALSE,
  122         FALSE,
  123 };
  124 
  125 /*
  126  * Initialize the server request cache list
  127  */
  128 void
  129 nfsrv_initcache(void)
  130 {
  131 
  132         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
  133         TAILQ_INIT(&nfsrvlruhead);
  134 }
  135 
  136 /*
  137  * Teardown the server request cache list
  138  */
  139 void
  140 nfsrv_destroycache(void)
  141 {
  142 
  143         KASSERT(TAILQ_EMPTY(&nfsrvlruhead), ("%s: pending requests", __func__));
  144         hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
  145 }
  146 
  147 /*
  148  * Look for the request in the cache
  149  * If found then
  150  *    return action and optionally reply
  151  * else
  152  *    insert it in the cache
  153  *
  154  * The rules are as follows:
  155  * - if in progress, return DROP request
  156  * - if completed within DELAY of the current time, return DROP it
  157  * - if completed a longer time ago return REPLY if the reply was cached or
  158  *   return DOIT
  159  * Update/add new request at end of lru list
  160  */
  161 int
  162 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
  163 {
  164         struct nfsrvcache *rp;
  165         struct mbuf *mb;
  166         struct sockaddr_in *saddr;
  167         caddr_t bpos;
  168         int ret;
  169 
  170         NFSD_LOCK_ASSERT();
  171 
  172         /*
  173          * Don't cache recent requests for reliable transport protocols.
  174          * (Maybe we should for the case of a reconnect, but..)
  175          */
  176         if (!nd->nd_nam2)
  177                 return (RC_DOIT);
  178 loop:
  179         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
  180             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
  181                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
  182                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
  183                         if ((rp->rc_flag & RC_LOCKED) != 0) {
  184                                 rp->rc_flag |= RC_WANTED;
  185                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
  186                                     "nfsrc", 0);
  187                                 goto loop;
  188                         }
  189                         rp->rc_flag |= RC_LOCKED;
  190                         /* If not at end of LRU chain, move it there */
  191                         if (TAILQ_NEXT(rp, rc_lru)) {
  192                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  193                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  194                         }
  195                         if (rp->rc_state == RC_UNUSED)
  196                                 panic("nfsrv cache");
  197                         if (rp->rc_state == RC_INPROG) {
  198                                 nfsrvstats.srvcache_inproghits++;
  199                                 ret = RC_DROPIT;
  200                         } else if (rp->rc_flag & RC_REPSTATUS) {
  201                                 nfsrvstats.srvcache_nonidemdonehits++;
  202                                 *repp = nfs_rephead(0, nd, rp->rc_status,
  203                                     &mb, &bpos);
  204                                 ret = RC_REPLY;
  205                         } else if (rp->rc_flag & RC_REPMBUF) {
  206                                 nfsrvstats.srvcache_nonidemdonehits++;
  207                                 NFSD_UNLOCK();
  208                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
  209                                                 M_TRYWAIT);
  210                                 NFSD_LOCK();
  211                                 ret = RC_REPLY;
  212                         } else {
  213                                 nfsrvstats.srvcache_idemdonehits++;
  214                                 rp->rc_state = RC_INPROG;
  215                                 ret = RC_DOIT;
  216                         }
  217                         rp->rc_flag &= ~RC_LOCKED;
  218                         if (rp->rc_flag & RC_WANTED) {
  219                                 rp->rc_flag &= ~RC_WANTED;
  220                                 wakeup(rp);
  221                         }
  222                         return (ret);
  223                 }
  224         }
  225         nfsrvstats.srvcache_misses++;
  226         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
  227         if (numnfsrvcache < desirednfsrvcache) {
  228                 NFSD_UNLOCK();
  229                 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
  230                     M_NFSD, M_WAITOK | M_ZERO);
  231                 NFSD_LOCK();
  232                 numnfsrvcache++;
  233                 rp->rc_flag = RC_LOCKED;
  234         } else {
  235                 rp = TAILQ_FIRST(&nfsrvlruhead);
  236                 while ((rp->rc_flag & RC_LOCKED) != 0) {
  237                         rp->rc_flag |= RC_WANTED;
  238                         (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
  239                         rp = TAILQ_FIRST(&nfsrvlruhead);
  240                 }
  241                 rp->rc_flag |= RC_LOCKED;
  242                 LIST_REMOVE(rp, rc_hash);
  243                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  244                 if (rp->rc_flag & RC_REPMBUF)
  245                         m_freem(rp->rc_reply);
  246                 if (rp->rc_flag & RC_NAM)
  247                         FREE(rp->rc_nam, M_SONAME);
  248                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
  249         }
  250         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  251         rp->rc_state = RC_INPROG;
  252         rp->rc_xid = nd->nd_retxid;
  253         saddr = (struct sockaddr_in *)nd->nd_nam;
  254         switch (saddr->sin_family) {
  255         case AF_INET:
  256                 rp->rc_flag |= RC_INETADDR;
  257                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
  258                 break;
  259 /*      case AF_INET6:  */
  260 /*      case AF_ISO:    */
  261         default:
  262                 /*
  263                  * XXXRW: Seems like we should only set RC_NAM if we
  264                  * actually manage to set rc_nam to something non-NULL.
  265                  */
  266                 rp->rc_flag |= RC_NAM;
  267                 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
  268                 break;
  269         };
  270         rp->rc_proc = nd->nd_procnum;
  271         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
  272         rp->rc_flag &= ~RC_LOCKED;
  273         if (rp->rc_flag & RC_WANTED) {
  274                 rp->rc_flag &= ~RC_WANTED;
  275                 wakeup(rp);
  276         }
  277         return (RC_DOIT);
  278 }
  279 
  280 /*
  281  * Update a request cache entry after the rpc has been done
  282  */
  283 void
  284 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
  285 {
  286         struct nfsrvcache *rp;
  287 
  288         NFSD_LOCK_ASSERT();
  289 
  290         if (!nd->nd_nam2)
  291                 return;
  292 loop:
  293         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
  294             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
  295                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
  296                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
  297                         if ((rp->rc_flag & RC_LOCKED) != 0) {
  298                                 rp->rc_flag |= RC_WANTED;
  299                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
  300                                     "nfsrc", 0);
  301                                 goto loop;
  302                         }
  303                         rp->rc_flag |= RC_LOCKED;
  304                         if (rp->rc_state == RC_DONE) {
  305                                 /*
  306                                  * This can occur if the cache is too small.
  307                                  * Retransmits of the same request aren't
  308                                  * dropped so we may see the operation
  309                                  * complete more then once.
  310                                  */
  311                                 if (rp->rc_flag & RC_REPMBUF) {
  312                                         m_freem(rp->rc_reply);
  313                                         rp->rc_flag &= ~RC_REPMBUF;
  314                                 }
  315                         }
  316                         rp->rc_state = RC_DONE;
  317                         /*
  318                          * If we have a valid reply update status and save
  319                          * the reply for non-idempotent rpc's.
  320                          */
  321                         if (repvalid && nonidempotent[nd->nd_procnum]) {
  322                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
  323                                     nfsv2_repstat[
  324                                     nfsrvv2_procid[nd->nd_procnum]]) {
  325                                         rp->rc_status = nd->nd_repstat;
  326                                         rp->rc_flag |= RC_REPSTATUS;
  327                                 } else {
  328                                         NFSD_UNLOCK();
  329                                         rp->rc_reply = m_copym(repmbuf,
  330                                                 0, M_COPYALL, M_TRYWAIT);
  331                                         NFSD_LOCK();
  332                                         rp->rc_flag |= RC_REPMBUF;
  333                                 }
  334                         }
  335                         rp->rc_flag &= ~RC_LOCKED;
  336                         if (rp->rc_flag & RC_WANTED) {
  337                                 rp->rc_flag &= ~RC_WANTED;
  338                                 wakeup(rp);
  339                         }
  340                         return;
  341                 }
  342         }
  343         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
  344 }
  345 
  346 /*
  347  * Clean out the cache. Called when the last nfsd terminates.
  348  */
  349 void
  350 nfsrv_cleancache(void)
  351 {
  352         struct nfsrvcache *rp, *nextrp;
  353 
  354         NFSD_LOCK_ASSERT();
  355 
  356         for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
  357                 nextrp = TAILQ_NEXT(rp, rc_lru);
  358                 LIST_REMOVE(rp, rc_hash);
  359                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  360                 if (rp->rc_flag & RC_REPMBUF)
  361                         m_freem(rp->rc_reply);
  362                 if (rp->rc_flag & RC_NAM)
  363                         free(rp->rc_nam, M_SONAME);
  364                 free(rp, M_NFSD);
  365         }
  366         numnfsrvcache = 0;
  367 }

Cache object: 9b018b94ea5241d3adb62967501a6224


[ 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.