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

Cache object: e6b231bb1e43a8a9e9e057bc062fc6c2


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