[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]

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

Version: -  FREEBSD  -  FREEBSD7  -  FREEBSD70  -  FREEBSD6  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  OPENSOLARIS  -  minix-3-1-1  -  TRUSTEDBSD-SEBSD  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
SearchContext: -  none  -  excerpts  -  bigexcerpts 

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

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]


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