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/nfs/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 /*      $OpenBSD: nfs_srvcache.c,v 1.29 2019/12/05 10:41:57 mpi Exp $   */
    2 /*      $NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl Exp $   */
    3 
    4 /*
    5  * Copyright (c) 1989, 1993
    6  *      The Regents of the University of California.  All rights reserved.
    7  *
    8  * This code is derived from software contributed to Berkeley by
    9  * Rick Macklem at The University of Guelph.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. Neither the name of the University nor the names of its contributors
   20  *    may be used to endorse or promote products derived from this software
   21  *    without specific prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  *
   35  *      @(#)nfs_srvcache.c      8.3 (Berkeley) 3/30/95
   36  */
   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/mount.h>
   45 #include <sys/kernel.h>
   46 #include <sys/systm.h>
   47 #include <sys/mbuf.h>
   48 #include <sys/malloc.h>
   49 #include <sys/socket.h>
   50 #include <sys/queue.h>
   51 
   52 #include <crypto/siphash.h>
   53 
   54 #include <netinet/in.h>
   55 #include <nfs/nfsproto.h>
   56 #include <nfs/nfs.h>
   57 #include <nfs/nfsrvcache.h>
   58 #include <nfs/nfs_var.h>
   59 
   60 extern struct nfsstats nfsstats;
   61 extern int nfsv2_procid[NFS_NPROCS];
   62 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
   63 
   64 struct nfsrvcache       *nfsrv_lookupcache(struct nfsrv_descript *);
   65 void                     nfsrv_cleanentry(struct nfsrvcache *);
   66 
   67 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
   68 SIPHASH_KEY nfsrvhashkey;
   69 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
   70 u_long nfsrvhash;
   71 #define NFSRCHASH(xid) \
   72     (&nfsrvhashtbl[SipHash24(&nfsrvhashkey, &(xid), sizeof(xid)) & nfsrvhash])
   73 
   74 #define NETFAMILY(rp)   \
   75         (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_UNSPEC)
   76 
   77 /* Array that defines which nfs rpc's are nonidempotent */
   78 int nonidempotent[NFS_NPROCS] = {
   79         0, 0, 1, 0, 0, 0, 0, 1,
   80         1, 1, 1, 1, 1, 1, 1, 1,
   81         0, 0, 0, 0, 0, 0, 0
   82 };
   83 
   84 /* True iff the rpc reply is an nfs status ONLY! */
   85 int nfsv2_repstat[NFS_NPROCS] = {
   86         0, 0, 0, 0, 0, 0, 0, 0,
   87         0, 0, 1, 1, 1, 1, 0, 1,
   88         0, 0
   89 };
   90 
   91 void
   92 nfsrv_cleanentry(struct nfsrvcache *rp)
   93 {
   94         if ((rp->rc_flag & RC_REPMBUF) != 0)
   95                 m_freem(rp->rc_reply);
   96 
   97         if ((rp->rc_flag & RC_NAM) != 0)
   98                 m_free(rp->rc_nam);
   99 
  100         rp->rc_flag &= ~(RC_REPSTATUS|RC_REPMBUF);
  101 }
  102 
  103 /* Initialize the server request cache list */
  104 void
  105 nfsrv_initcache(void)
  106 {
  107 
  108         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash);
  109         arc4random_buf(&nfsrvhashkey, sizeof(nfsrvhashkey));
  110         TAILQ_INIT(&nfsrvlruhead);
  111 }
  112 
  113 /*
  114  * Look for the request in the cache
  115  * If found then
  116  *    return action and optionally reply
  117  * else
  118  *    insert it in the cache
  119  *
  120  * The rules are as follows:
  121  * - if in progress, return DROP request
  122  * - if completed within DELAY of the current time, return DROP it
  123  * - if completed a longer time ago return REPLY if the reply was cached or
  124  *   return DOIT
  125  * Update/add new request at end of lru list
  126  */
  127 int
  128 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
  129     struct mbuf **repp)
  130 {
  131         struct nfsrvhash *hash;
  132         struct nfsrvcache *rp;
  133         struct mbuf *mb;
  134         struct sockaddr_in *saddr;
  135         int ret;
  136 
  137         /*
  138          * Don't cache recent requests for reliable transport protocols.
  139          * (Maybe we should for the case of a reconnect, but..)
  140          */
  141         if (!nd->nd_nam2)
  142                 return (RC_DOIT);
  143 
  144         rp = nfsrv_lookupcache(nd);
  145         if (rp) {
  146                 /* If not at end of LRU chain, move it there */
  147                 if (TAILQ_NEXT(rp, rc_lru)) {
  148                         TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  149                         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  150                 }
  151                 if (rp->rc_state == RC_UNUSED)
  152                         panic("nfsrv cache");
  153                 if (rp->rc_state == RC_INPROG) {
  154                         nfsstats.srvcache_inproghits++;
  155                         ret = RC_DROPIT;
  156                 } else if (rp->rc_flag & RC_REPSTATUS) {
  157                         nfsstats.srvcache_nonidemdonehits++;
  158                         nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb);
  159                         ret = RC_REPLY;
  160                 } else if (rp->rc_flag & RC_REPMBUF) {
  161                         nfsstats.srvcache_nonidemdonehits++;
  162                         *repp = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAIT);
  163                         ret = RC_REPLY;
  164                 } else {
  165                         nfsstats.srvcache_idemdonehits++;
  166                         rp->rc_state = RC_INPROG;
  167                         ret = RC_DOIT;
  168                 }
  169                 rp->rc_flag &= ~RC_LOCKED;
  170                 if (rp->rc_flag & RC_WANTED) {
  171                         rp->rc_flag &= ~RC_WANTED;
  172                         wakeup(rp);
  173                 }
  174                 return (ret);
  175         }
  176 
  177         nfsstats.srvcache_misses++;
  178         if (numnfsrvcache < desirednfsrvcache) {
  179                 rp = malloc(sizeof(*rp), M_NFSD, M_WAITOK|M_ZERO);
  180                 numnfsrvcache++;
  181                 rp->rc_flag = RC_LOCKED;
  182         } else {
  183                 rp = TAILQ_FIRST(&nfsrvlruhead);
  184                 while ((rp->rc_flag & RC_LOCKED) != 0) {
  185                         rp->rc_flag |= RC_WANTED;
  186                         tsleep_nsec(rp, PZERO-1, "nfsrc", INFSLP);
  187                         rp = TAILQ_FIRST(&nfsrvlruhead);
  188                 }
  189                 rp->rc_flag |= RC_LOCKED;
  190                 LIST_REMOVE(rp, rc_hash);
  191                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  192                 nfsrv_cleanentry(rp);
  193                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
  194         }
  195         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
  196         rp->rc_state = RC_INPROG;
  197         rp->rc_xid = nd->nd_retxid;
  198         saddr = mtod(nd->nd_nam, struct sockaddr_in *);
  199         switch (saddr->sin_family) {
  200         case AF_INET:
  201                 rp->rc_flag |= RC_INETADDR;
  202                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
  203                 break;
  204         default:
  205                 rp->rc_flag |= RC_NAM;
  206                 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
  207                 break;
  208         };
  209         rp->rc_proc = nd->nd_procnum;
  210         hash = NFSRCHASH(nd->nd_retxid);
  211         LIST_INSERT_HEAD(hash, rp, rc_hash);
  212         rp->rc_flag &= ~RC_LOCKED;
  213         if (rp->rc_flag & RC_WANTED) {
  214                 rp->rc_flag &= ~RC_WANTED;
  215                 wakeup(rp);
  216         }
  217         return (RC_DOIT);
  218 }
  219 
  220 /* Update a request cache entry after the rpc has been done */
  221 void
  222 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid,
  223     struct mbuf *repmbuf)
  224 {
  225         struct nfsrvcache *rp;
  226 
  227         if (!nd->nd_nam2)
  228                 return;
  229 
  230         rp = nfsrv_lookupcache(nd);
  231         if (rp) {
  232                 nfsrv_cleanentry(rp);
  233                 rp->rc_state = RC_DONE;
  234                 /*
  235                  * If we have a valid reply update status and save
  236                  * the reply for non-idempotent rpc's.
  237                  */
  238                 if (repvalid && nonidempotent[nd->nd_procnum]) {
  239                         if ((nd->nd_flag & ND_NFSV3) == 0 &&
  240                           nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
  241                                 rp->rc_status = nd->nd_repstat;
  242                                 rp->rc_flag |= RC_REPSTATUS;
  243                         } else {
  244                                 rp->rc_reply = m_copym(repmbuf, 0, M_COPYALL,
  245                                     M_WAIT);
  246                                 rp->rc_flag |= RC_REPMBUF;
  247                         }
  248                 }
  249                 rp->rc_flag &= ~RC_LOCKED;
  250                 if (rp->rc_flag & RC_WANTED) {
  251                         rp->rc_flag &= ~RC_WANTED;
  252                         wakeup(rp);
  253                 }
  254                 return;
  255         }
  256 }
  257 
  258 /* Clean out the cache. Called when the last nfsd terminates. */
  259 void
  260 nfsrv_cleancache(void)
  261 {
  262         struct nfsrvcache *rp, *nextrp;
  263 
  264         for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) {
  265                 nextrp = TAILQ_NEXT(rp, rc_lru);
  266                 LIST_REMOVE(rp, rc_hash);
  267                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
  268                 nfsrv_cleanentry(rp);
  269                 free(rp, M_NFSD, sizeof(*rp));
  270         }
  271         numnfsrvcache = 0;
  272 }
  273 
  274 struct nfsrvcache *
  275 nfsrv_lookupcache(struct nfsrv_descript *nd)
  276 {
  277         struct nfsrvhash        *hash;
  278         struct nfsrvcache       *rp;
  279 
  280         hash = NFSRCHASH(nd->nd_retxid);
  281 loop:
  282         LIST_FOREACH(rp, hash, rc_hash) {
  283                 if (nd->nd_retxid == rp->rc_xid &&
  284                     nd->nd_procnum == rp->rc_proc &&
  285                     netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
  286                         if ((rp->rc_flag & RC_LOCKED)) {
  287                                 rp->rc_flag |= RC_WANTED;
  288                                 tsleep_nsec(rp, PZERO - 1, "nfsrc", INFSLP);
  289                                 goto loop;
  290                         }
  291                         rp->rc_flag |= RC_LOCKED;
  292                         return (rp);
  293                 }
  294         }
  295 
  296         return (NULL);
  297 }

Cache object: e3e11b433af2c27a34fb5e05465a7454


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