netinet{,6}: Assert in{,6}_inithead() are only used for system routing tables.
[dragonfly.git] / sys / vfs / nfs / nfs_srvcache.c
CommitLineData
984263bc
MD
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.
dc71b7ab 16 * 3. Neither the name of the University nor the names of its contributors
984263bc
MD
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 * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $
34 */
35
36/*
37 * Reference: Chet Juszczak, "Improving the Performance and Correctness
38 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
39 * pages 53-63. San Diego, February 1989.
40 */
41#include <sys/param.h>
42#include <sys/malloc.h>
43#include <sys/mount.h>
44#include <sys/systm.h>
45#include <sys/mbuf.h>
46#include <sys/socket.h>
47#include <sys/socketvar.h> /* for dup_sockaddr */
48
49#include <netinet/in.h>
1f2de5d4
MD
50#include "rpcv2.h"
51#include "nfsproto.h"
52#include "nfs.h"
53#include "nfsrvcache.h"
984263bc
MD
54
55#ifndef NFS_NOSERVER
984263bc 56static long numnfsrvcache;
a084b5c3 57static long desirednfsrvcache;
984263bc
MD
58
59#define NFSRCHASH(xid) \
60 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
61static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
62static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
63static u_long nfsrvhash;
64
65#define TRUE 1
66#define FALSE 0
67
a3c18566 68struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token);
5e6f1ca5 69
984263bc
MD
70/*
71 * Static array that defines which nfs rpc's are nonidempotent
72 */
73static int nonidempotent[NFS_NPROCS] = {
74 FALSE,
75 FALSE,
76 TRUE,
77 FALSE,
78 FALSE,
79 FALSE,
80 FALSE,
81 TRUE,
82 TRUE,
83 TRUE,
84 TRUE,
85 TRUE,
86 TRUE,
87 TRUE,
88 TRUE,
89 TRUE,
90 FALSE,
91 FALSE,
92 FALSE,
93 FALSE,
94 FALSE,
95 FALSE,
96 FALSE,
97 FALSE,
98 FALSE,
99 FALSE,
100};
101
102/* True iff the rpc reply is an nfs status ONLY! */
103static int nfsv2_repstat[NFS_NPROCS] = {
104 FALSE,
105 FALSE,
106 FALSE,
107 FALSE,
108 FALSE,
109 FALSE,
110 FALSE,
111 FALSE,
112 FALSE,
113 FALSE,
114 TRUE,
115 TRUE,
116 TRUE,
117 TRUE,
118 FALSE,
119 TRUE,
120 FALSE,
121 FALSE,
122};
123
a084b5c3
VS
124/*
125 * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
126 * within a (64, 2048) range. This is to prevent all mbuf clusters being
127 * tied up in the NFS dupreq cache for small values of nmbclusters.
128 */
129static void
130nfsrvcache_size_change(void)
131{
132 desirednfsrvcache = nmbclusters / 2;
133 desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
134 desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
135}
136
984263bc
MD
137/*
138 * Initialize the server request cache list
139 */
140void
e851b29e 141nfsrv_initcache(void)
984263bc 142{
a084b5c3 143 nfsrvcache_size_change();
984263bc
MD
144 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
145 TAILQ_INIT(&nfsrvlruhead);
146}
147
148/*
149 * Look for the request in the cache
150 * If found then
151 * return action and optionally reply
152 * else
153 * insert it in the cache
154 *
155 * The rules are as follows:
156 * - if in progress, return DROP request
157 * - if completed within DELAY of the current time, return DROP it
158 * - if completed a longer time ago return REPLY if the reply was cached or
159 * return DOIT
160 * Update/add new request at end of lru list
161 */
162int
e851b29e
CP
163nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
164 struct mbuf **repp)
984263bc 165{
40393ded 166 struct nfsrvcache *rp;
984263bc
MD
167 struct mbuf *mb;
168 struct sockaddr_in *saddr;
169 caddr_t bpos;
170 int ret;
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);
5e6f1ca5
MD
178
179 lwkt_gettoken(&srvcache_token);
984263bc 180loop:
4090d6ff 181 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
984263bc
MD
182 rp = rp->rc_hash.le_next) {
183 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
6ec8f7f2 184 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
984263bc
MD
185 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
186 if ((rp->rc_flag & RC_LOCKED) != 0) {
187 rp->rc_flag |= RC_WANTED;
96f9dac1 188 tsleep((caddr_t)rp, 0, "nfsrc", 0);
984263bc
MD
189 goto loop;
190 }
191 rp->rc_flag |= RC_LOCKED;
192 /* If not at end of LRU chain, move it there */
ecd80f47 193 if (TAILQ_NEXT(rp, rc_lru) != NULL) {
984263bc
MD
194 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
195 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
196 }
197 if (rp->rc_state == RC_UNUSED)
198 panic("nfsrv cache");
199 if (rp->rc_state == RC_INPROG) {
200 nfsstats.srvcache_inproghits++;
201 ret = RC_DROPIT;
202 } else if (rp->rc_flag & RC_REPSTATUS) {
203 nfsstats.srvcache_nonidemdonehits++;
204 nfs_rephead(0, nd, slp, rp->rc_status,
e07fef60 205 repp, &mb, &bpos);
984263bc
MD
206 ret = RC_REPLY;
207 } else if (rp->rc_flag & RC_REPMBUF) {
208 nfsstats.srvcache_nonidemdonehits++;
209 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
b5523eac 210 M_WAITOK);
984263bc
MD
211 ret = RC_REPLY;
212 } else {
213 nfsstats.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((caddr_t)rp);
221 }
5e6f1ca5 222 lwkt_reltoken(&srvcache_token);
984263bc
MD
223 return (ret);
224 }
225 }
5e6f1ca5 226
984263bc
MD
227 nfsstats.srvcache_misses++;
228 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
229 if (numnfsrvcache < desirednfsrvcache) {
96f9dac1 230 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
984263bc
MD
231 numnfsrvcache++;
232 rp->rc_flag = RC_LOCKED;
233 } else {
ecd80f47 234 rp = TAILQ_FIRST(&nfsrvlruhead);
984263bc
MD
235 while ((rp->rc_flag & RC_LOCKED) != 0) {
236 rp->rc_flag |= RC_WANTED;
96f9dac1 237 tsleep((caddr_t)rp, 0, "nfsrc", 0);
ecd80f47 238 rp = TAILQ_FIRST(&nfsrvlruhead);
984263bc
MD
239 }
240 rp->rc_flag |= RC_LOCKED;
241 LIST_REMOVE(rp, rc_hash);
242 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
96f9dac1 243 if (rp->rc_flag & RC_REPMBUF) {
984263bc 244 m_freem(rp->rc_reply);
96f9dac1
MD
245 rp->rc_reply = NULL;
246 rp->rc_flag &= ~RC_REPMBUF;
247 }
248 if (rp->rc_flag & RC_NAM) {
884717e1 249 kfree(rp->rc_nam, M_SONAME);
96f9dac1
MD
250 rp->rc_nam = NULL;
251 rp->rc_flag &= ~RC_NAM;
252 }
984263bc
MD
253 }
254 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
5e6f1ca5 255
984263bc
MD
256 rp->rc_state = RC_INPROG;
257 rp->rc_xid = nd->nd_retxid;
258 saddr = (struct sockaddr_in *)nd->nd_nam;
259 switch (saddr->sin_family) {
260 case AF_INET:
261 rp->rc_flag |= RC_INETADDR;
262 rp->rc_inetaddr = saddr->sin_addr.s_addr;
263 break;
984263bc
MD
264 default:
265 rp->rc_flag |= RC_NAM;
cfa2ba21 266 rp->rc_nam = dup_sockaddr(nd->nd_nam);
984263bc 267 break;
0fdb7d01 268 }
984263bc
MD
269 rp->rc_proc = nd->nd_procnum;
270 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
271 rp->rc_flag &= ~RC_LOCKED;
272 if (rp->rc_flag & RC_WANTED) {
273 rp->rc_flag &= ~RC_WANTED;
274 wakeup((caddr_t)rp);
275 }
5e6f1ca5
MD
276 lwkt_reltoken(&srvcache_token);
277
984263bc
MD
278 return (RC_DOIT);
279}
280
281/*
282 * Update a request cache entry after the rpc has been done
283 */
284void
e851b29e 285nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
984263bc 286{
40393ded 287 struct nfsrvcache *rp;
984263bc
MD
288
289 if (!nd->nd_nam2)
290 return;
5e6f1ca5
MD
291
292 lwkt_gettoken(&srvcache_token);
984263bc 293loop:
4090d6ff 294 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
984263bc
MD
295 rp = rp->rc_hash.le_next) {
296 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
6ec8f7f2 297 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
984263bc
MD
298 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
299 if ((rp->rc_flag & RC_LOCKED) != 0) {
300 rp->rc_flag |= RC_WANTED;
96f9dac1 301 tsleep((caddr_t)rp, 0, "nfsrc", 0);
984263bc
MD
302 goto loop;
303 }
304 rp->rc_flag |= RC_LOCKED;
305 if (rp->rc_state == RC_DONE) {
306 /*
307 * This can occur if the cache is too small.
308 * Retransmits of the same request aren't
309 * dropped so we may see the operation
310 * complete more then once.
311 */
312 if (rp->rc_flag & RC_REPMBUF) {
313 m_freem(rp->rc_reply);
96f9dac1 314 rp->rc_reply = NULL;
984263bc
MD
315 rp->rc_flag &= ~RC_REPMBUF;
316 }
317 }
318 rp->rc_state = RC_DONE;
96f9dac1 319
984263bc
MD
320 /*
321 * If we have a valid reply update status and save
322 * the reply for non-idempotent rpc's.
323 */
324 if (repvalid && nonidempotent[nd->nd_procnum]) {
325 if ((nd->nd_flag & ND_NFSV3) == 0 &&
326 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
327 rp->rc_status = nd->nd_repstat;
328 rp->rc_flag |= RC_REPSTATUS;
329 } else {
96f9dac1
MD
330 if (rp->rc_flag & RC_REPMBUF) {
331 m_freem(rp->rc_reply);
332 rp->rc_reply = NULL;
333 rp->rc_flag &= ~RC_REPMBUF;
334 }
335 rp->rc_reply = m_copym(repmbuf, 0,
b5523eac 336 M_COPYALL, M_WAITOK);
984263bc
MD
337 rp->rc_flag |= RC_REPMBUF;
338 }
339 }
340 rp->rc_flag &= ~RC_LOCKED;
341 if (rp->rc_flag & RC_WANTED) {
342 rp->rc_flag &= ~RC_WANTED;
343 wakeup((caddr_t)rp);
344 }
5e6f1ca5 345 break;
984263bc
MD
346 }
347 }
5e6f1ca5 348 lwkt_reltoken(&srvcache_token);
984263bc
MD
349 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
350}
351
352/*
353 * Clean out the cache. Called when the last nfsd terminates.
354 */
355void
e851b29e 356nfsrv_cleancache(void)
984263bc 357{
96f9dac1 358 struct nfsrvcache *rp;
984263bc 359
5e6f1ca5 360 lwkt_gettoken(&srvcache_token);
96f9dac1
MD
361 while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
362 if (rp->rc_flag & RC_LOCKED) {
363 rp->rc_flag |= RC_WANTED;
364 tsleep((caddr_t)rp, 0, "nfsrc", 0);
365 continue;
366 }
984263bc
MD
367 LIST_REMOVE(rp, rc_hash);
368 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
96f9dac1 369 if (rp->rc_flag & RC_REPMBUF) {
984263bc 370 m_freem(rp->rc_reply);
96f9dac1
MD
371 rp->rc_reply = NULL;
372 rp->rc_flag &= ~RC_REPMBUF;
373 }
374 if (rp->rc_flag & RC_NAM) {
efda3bd0 375 kfree(rp->rc_nam, M_SONAME);
96f9dac1
MD
376 rp->rc_nam = NULL;
377 rp->rc_flag &= ~RC_NAM;
378 }
efda3bd0 379 kfree(rp, M_NFSD);
984263bc
MD
380 }
381 numnfsrvcache = 0;
5e6f1ca5 382 lwkt_reltoken(&srvcache_token);
984263bc
MD
383}
384
385#endif /* NFS_NOSERVER */