upgraded to the latest NetBSD version
[unix-history] / usr / src / sys / nfs / nfs_srvcache.c
CommitLineData
95f0eacd 1/*
99315dca
KB
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
95f0eacd
KM
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
dbf0c423 8 * %sccs.include.redist.c%
95f0eacd 9 *
4acac3d6 10 * @(#)nfs_srvcache.c 8.3 (Berkeley) %G%
b54e05cc
KM
11 */
12
13/*
14 * Reference: Chet Juszczak, "Improving the Performance and Correctness
2c5b44a2
KM
15 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
16 * pages 53-63. San Diego, February 1989.
95f0eacd 17 */
d615f2fe
KM
18#include <sys/param.h>
19#include <sys/vnode.h>
20#include <sys/mount.h>
21#include <sys/kernel.h>
22#include <sys/systm.h>
23#include <sys/proc.h>
24#include <sys/mbuf.h>
96964be4 25#include <sys/malloc.h>
d615f2fe
KM
26#include <sys/socket.h>
27#include <sys/socketvar.h>
5548a02f 28
d615f2fe 29#include <netinet/in.h>
2c5b44a2 30#ifdef ISO
d615f2fe 31#include <netiso/iso.h>
2c5b44a2 32#endif
d615f2fe
KM
33#include <nfs/nfsm_subs.h>
34#include <nfs/rpcv2.h>
4acac3d6 35#include <nfs/nfsproto.h>
d615f2fe
KM
36#include <nfs/nfs.h>
37#include <nfs/nfsrvcache.h>
38#include <nfs/nqnfs.h>
95f0eacd 39
4acac3d6
KM
40extern struct nfsstats nfsstats;
41extern int nfsv2_procid[NFS_NPROCS];
96964be4 42long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
95f0eacd 43
0bb2c2fc
KM
44#define NFSRCHASH(xid) \
45 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
46LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
47TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
48u_long nfsrvhash;
95f0eacd
KM
49
50#define TRUE 1
51#define FALSE 0
52
2c5b44a2
KM
53#define NETFAMILY(rp) \
54 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
55
95f0eacd
KM
56/*
57 * Static array that defines which nfs rpc's are nonidempotent
58 */
cdf8c1c6 59int nonidempotent[NFS_NPROCS] = {
95f0eacd
KM
60 FALSE,
61 FALSE,
62 TRUE,
63 FALSE,
64 FALSE,
65 FALSE,
66 FALSE,
95f0eacd
KM
67 TRUE,
68 TRUE,
69 TRUE,
70 TRUE,
71 TRUE,
72 TRUE,
73 TRUE,
74 TRUE,
4acac3d6
KM
75 TRUE,
76 FALSE,
77 FALSE,
78 FALSE,
95f0eacd
KM
79 FALSE,
80 FALSE,
2c5b44a2
KM
81 FALSE,
82 FALSE,
83 FALSE,
84 FALSE,
b330a7ec 85 FALSE,
95f0eacd
KM
86};
87
88/* True iff the rpc reply is an nfs status ONLY! */
4acac3d6 89static int nfsv2_repstat[NFS_NPROCS] = {
95f0eacd
KM
90 FALSE,
91 FALSE,
92 FALSE,
93 FALSE,
94 FALSE,
95 FALSE,
96 FALSE,
97 FALSE,
98 FALSE,
99 FALSE,
100 TRUE,
101 TRUE,
102 TRUE,
103 TRUE,
104 FALSE,
105 TRUE,
106 FALSE,
107 FALSE,
108};
109
110/*
111 * Initialize the server request cache list
112 */
4acac3d6 113void
95f0eacd
KM
114nfsrv_initcache()
115{
95f0eacd 116
0bb2c2fc
KM
117 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
118 TAILQ_INIT(&nfsrvlruhead);
95f0eacd
KM
119}
120
121/*
122 * Look for the request in the cache
123 * If found then
124 * return action and optionally reply
125 * else
126 * insert it in the cache
127 *
128 * The rules are as follows:
129 * - if in progress, return DROP request
130 * - if completed within DELAY of the current time, return DROP it
131 * - if completed a longer time ago return REPLY if the reply was cached or
132 * return DOIT
133 * Update/add new request at end of lru list
134 */
4acac3d6
KM
135int
136nfsrv_getcache(nd, slp, repp)
137 register struct nfsrv_descript *nd;
138 struct nfssvc_sock *slp;
95f0eacd
KM
139 struct mbuf **repp;
140{
0bb2c2fc 141 register struct nfsrvcache *rp;
95f0eacd 142 struct mbuf *mb;
2c5b44a2 143 struct sockaddr_in *saddr;
95f0eacd
KM
144 caddr_t bpos;
145 int ret;
146
4acac3d6
KM
147 /*
148 * Don't cache recent requests for reliable transport protocols.
149 * (Maybe we should for the case of a reconnect, but..)
150 */
151 if (!nd->nd_nam2)
2c5b44a2 152 return (RC_DOIT);
95f0eacd 153loop:
0bb2c2fc
KM
154 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
155 rp = rp->rc_hash.le_next) {
2c5b44a2 156 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
4acac3d6 157 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
95f0eacd
KM
158 if ((rp->rc_flag & RC_LOCKED) != 0) {
159 rp->rc_flag |= RC_WANTED;
170bfd05 160 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
95f0eacd
KM
161 goto loop;
162 }
163 rp->rc_flag |= RC_LOCKED;
96964be4 164 /* If not at end of LRU chain, move it there */
0bb2c2fc
KM
165 if (rp->rc_lru.tqe_next) {
166 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
167 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
2c5b44a2 168 }
95f0eacd
KM
169 if (rp->rc_state == RC_UNUSED)
170 panic("nfsrv cache");
202c1e6b 171 if (rp->rc_state == RC_INPROG) {
95f0eacd
KM
172 nfsstats.srvcache_inproghits++;
173 ret = RC_DROPIT;
174 } else if (rp->rc_flag & RC_REPSTATUS) {
202c1e6b 175 nfsstats.srvcache_nonidemdonehits++;
4acac3d6 176 nfs_rephead(0, nd, slp, rp->rc_status,
2c5b44a2 177 0, (u_quad_t *)0, repp, &mb, &bpos);
95f0eacd
KM
178 ret = RC_REPLY;
179 } else if (rp->rc_flag & RC_REPMBUF) {
202c1e6b 180 nfsstats.srvcache_nonidemdonehits++;
f0f1cbaa 181 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
95f0eacd 182 M_WAIT);
95f0eacd
KM
183 ret = RC_REPLY;
184 } else {
202c1e6b 185 nfsstats.srvcache_idemdonehits++;
95f0eacd
KM
186 rp->rc_state = RC_INPROG;
187 ret = RC_DOIT;
188 }
189 rp->rc_flag &= ~RC_LOCKED;
190 if (rp->rc_flag & RC_WANTED) {
191 rp->rc_flag &= ~RC_WANTED;
192 wakeup((caddr_t)rp);
193 }
194 return (ret);
195 }
196 }
197 nfsstats.srvcache_misses++;
96964be4
KM
198 if (numnfsrvcache < desirednfsrvcache) {
199 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
200 M_NFSD, M_WAITOK);
201 bzero((char *)rp, sizeof *rp);
202 numnfsrvcache++;
203 rp->rc_flag = RC_LOCKED;
204 } else {
0bb2c2fc 205 rp = nfsrvlruhead.tqh_first;
96964be4
KM
206 while ((rp->rc_flag & RC_LOCKED) != 0) {
207 rp->rc_flag |= RC_WANTED;
208 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
0bb2c2fc 209 rp = nfsrvlruhead.tqh_first;
96964be4
KM
210 }
211 rp->rc_flag |= RC_LOCKED;
0bb2c2fc
KM
212 LIST_REMOVE(rp, rc_hash);
213 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
96964be4
KM
214 if (rp->rc_flag & RC_REPMBUF)
215 m_freem(rp->rc_reply);
216 if (rp->rc_flag & RC_NAM)
217 MFREE(rp->rc_nam, mb);
218 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
2c5b44a2 219 }
0bb2c2fc 220 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
95f0eacd 221 rp->rc_state = RC_INPROG;
2c5b44a2 222 rp->rc_xid = nd->nd_retxid;
4acac3d6 223 saddr = mtod(nd->nd_nam, struct sockaddr_in *);
2c5b44a2
KM
224 switch (saddr->sin_family) {
225 case AF_INET:
226 rp->rc_flag |= RC_INETADDR;
227 rp->rc_inetaddr = saddr->sin_addr.s_addr;
228 break;
229 case AF_ISO:
230 default:
231 rp->rc_flag |= RC_NAM;
4acac3d6 232 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
2c5b44a2
KM
233 break;
234 };
235 rp->rc_proc = nd->nd_procnum;
0bb2c2fc 236 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
2c5b44a2
KM
237 rp->rc_flag &= ~RC_LOCKED;
238 if (rp->rc_flag & RC_WANTED) {
239 rp->rc_flag &= ~RC_WANTED;
240 wakeup((caddr_t)rp);
241 }
95f0eacd
KM
242 return (RC_DOIT);
243}
244
245/*
246 * Update a request cache entry after the rpc has been done
247 */
2c5b44a2 248void
4acac3d6
KM
249nfsrv_updatecache(nd, repvalid, repmbuf)
250 register struct nfsrv_descript *nd;
f170c5c2 251 int repvalid;
95f0eacd
KM
252 struct mbuf *repmbuf;
253{
254 register struct nfsrvcache *rp;
95f0eacd 255
4acac3d6 256 if (!nd->nd_nam2)
2c5b44a2 257 return;
95f0eacd 258loop:
0bb2c2fc
KM
259 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
260 rp = rp->rc_hash.le_next) {
2c5b44a2 261 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
4acac3d6 262 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
95f0eacd
KM
263 if ((rp->rc_flag & RC_LOCKED) != 0) {
264 rp->rc_flag |= RC_WANTED;
170bfd05 265 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
95f0eacd
KM
266 goto loop;
267 }
268 rp->rc_flag |= RC_LOCKED;
269 rp->rc_state = RC_DONE;
f170c5c2
KM
270 /*
271 * If we have a valid reply update status and save
f0f1cbaa 272 * the reply for non-idempotent rpc's.
f170c5c2 273 */
202c1e6b 274 if (repvalid && nonidempotent[nd->nd_procnum]) {
4acac3d6
KM
275 if ((nd->nd_flag & ND_NFSV3) == 0 &&
276 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
202c1e6b
KM
277 rp->rc_status = nd->nd_repstat;
278 rp->rc_flag |= RC_REPSTATUS;
279 } else {
280 rp->rc_reply = m_copym(repmbuf,
281 0, M_COPYALL, M_WAIT);
282 rp->rc_flag |= RC_REPMBUF;
95f0eacd
KM
283 }
284 }
285 rp->rc_flag &= ~RC_LOCKED;
286 if (rp->rc_flag & RC_WANTED) {
287 rp->rc_flag &= ~RC_WANTED;
288 wakeup((caddr_t)rp);
289 }
290 return;
291 }
292 }
293}
2c5b44a2
KM
294
295/*
296 * Clean out the cache. Called when the last nfsd terminates.
297 */
298void
299nfsrv_cleancache()
300{
96964be4 301 register struct nfsrvcache *rp, *nextrp;
2c5b44a2 302
0bb2c2fc
KM
303 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
304 nextrp = rp->rc_lru.tqe_next;
305 LIST_REMOVE(rp, rc_hash);
306 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
96964be4 307 free(rp, M_NFSD);
2c5b44a2 308 }
96964be4 309 numnfsrvcache = 0;
2c5b44a2 310}