add reference
[unix-history] / usr / src / sys / nfs / nfs_srvcache.c
CommitLineData
95f0eacd
KM
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * 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 are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
b54e05cc
KM
20 * @(#)nfs_srvcache.c 7.8 (Berkeley) %G%
21 */
22
23/*
24 * Reference: Chet Juszczak, "Improving the Performance and Correctness
25 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
26 * pages 53-63. San Diego, February 1989.
95f0eacd
KM
27 */
28
29#include "param.h"
30#include "user.h"
31#include "vnode.h"
32#include "mount.h"
33#include "kernel.h"
b4ff7c00 34#include "systm.h"
95f0eacd
KM
35#include "mbuf.h"
36#include "socket.h"
37#include "socketvar.h"
5736e576 38#include "../netinet/in.h"
95f0eacd
KM
39#include "nfsm_subs.h"
40#include "nfsv2.h"
41#include "nfsrvcache.h"
42#include "nfs.h"
43
44#if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
45#define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1))
46#else
47#define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
48#endif
49
50union rhead {
51 union rhead *rh_head[2];
52 struct nfsrvcache *rh_chain[2];
53} rhead[NFSRCHSZ];
54
55static struct nfsrvcache nfsrvcachehead;
56static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
57
58#define TRUE 1
59#define FALSE 0
60
61/*
62 * Static array that defines which nfs rpc's are nonidempotent
63 */
cdf8c1c6 64int nonidempotent[NFS_NPROCS] = {
95f0eacd
KM
65 FALSE,
66 FALSE,
67 TRUE,
68 FALSE,
69 FALSE,
70 FALSE,
71 FALSE,
72 FALSE,
73 TRUE,
74 TRUE,
75 TRUE,
76 TRUE,
77 TRUE,
78 TRUE,
79 TRUE,
80 TRUE,
81 FALSE,
82 FALSE,
83};
84
85/* True iff the rpc reply is an nfs status ONLY! */
86static int repliesstatus[NFS_NPROCS] = {
87 FALSE,
88 FALSE,
89 FALSE,
90 FALSE,
91 FALSE,
92 FALSE,
93 FALSE,
94 FALSE,
95 FALSE,
96 FALSE,
97 TRUE,
98 TRUE,
99 TRUE,
100 TRUE,
101 FALSE,
102 TRUE,
103 FALSE,
104 FALSE,
105};
106
107/*
108 * Initialize the server request cache list
109 */
110nfsrv_initcache()
111{
112 register int i;
113 register struct nfsrvcache *rp = nfsrvcache;
114 register struct nfsrvcache *hp = &nfsrvcachehead;
115 register union rhead *rh = rhead;
116
117 for (i = NFSRCHSZ; --i >= 0; rh++) {
118 rh->rh_head[0] = rh;
119 rh->rh_head[1] = rh;
120 }
121 hp->rc_next = hp->rc_prev = hp;
122 for (i = NFSRVCACHESIZ; i-- > 0; ) {
123 rp->rc_state = RC_UNUSED;
124 rp->rc_flag = 0;
125 rp->rc_forw = rp;
126 rp->rc_back = rp;
127 rp->rc_next = hp->rc_next;
128 hp->rc_next->rc_prev = rp;
129 rp->rc_prev = hp;
130 hp->rc_next = rp;
131 rp++;
132 }
133}
134
135/*
136 * Look for the request in the cache
137 * If found then
138 * return action and optionally reply
139 * else
140 * insert it in the cache
141 *
142 * The rules are as follows:
143 * - if in progress, return DROP request
144 * - if completed within DELAY of the current time, return DROP it
145 * - if completed a longer time ago return REPLY if the reply was cached or
146 * return DOIT
147 * Update/add new request at end of lru list
148 */
149nfsrv_getcache(nam, xid, proc, repp)
150 struct mbuf *nam;
151 u_long xid;
152 int proc;
153 struct mbuf **repp;
154{
155 register struct nfsrvcache *rp;
156 register union rhead *rh;
95f0eacd
KM
157 struct mbuf *mb;
158 caddr_t bpos;
159 int ret;
160
161 rh = &rhead[NFSRCHASH(xid)];
95f0eacd
KM
162loop:
163 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
f0f1cbaa
KM
164 if (xid == rp->rc_xid && proc == rp->rc_proc &&
165 nfs_netaddr_match(nam, &rp->rc_nam)) {
95f0eacd
KM
166 if ((rp->rc_flag & RC_LOCKED) != 0) {
167 rp->rc_flag |= RC_WANTED;
170bfd05 168 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
95f0eacd
KM
169 goto loop;
170 }
171 rp->rc_flag |= RC_LOCKED;
172 put_at_head(rp);
173 if (rp->rc_state == RC_UNUSED)
174 panic("nfsrv cache");
175 if (rp->rc_state == RC_INPROG ||
176 (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
177 nfsstats.srvcache_inproghits++;
178 ret = RC_DROPIT;
179 } else if (rp->rc_flag & RC_REPSTATUS) {
180 nfsstats.srvcache_idemdonehits++;
181 nfs_rephead(0, xid, rp->rc_status, repp, &mb,
182 &bpos);
183 rp->rc_timestamp = time.tv_sec;
184 ret = RC_REPLY;
185 } else if (rp->rc_flag & RC_REPMBUF) {
186 nfsstats.srvcache_idemdonehits++;
f0f1cbaa 187 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
95f0eacd
KM
188 M_WAIT);
189 rp->rc_timestamp = time.tv_sec;
190 ret = RC_REPLY;
191 } else {
192 nfsstats.srvcache_nonidemdonehits++;
193 rp->rc_state = RC_INPROG;
194 ret = RC_DOIT;
195 }
196 rp->rc_flag &= ~RC_LOCKED;
197 if (rp->rc_flag & RC_WANTED) {
198 rp->rc_flag &= ~RC_WANTED;
199 wakeup((caddr_t)rp);
200 }
201 return (ret);
202 }
203 }
204 nfsstats.srvcache_misses++;
205 rp = nfsrvcachehead.rc_prev;
206 while ((rp->rc_flag & RC_LOCKED) != 0) {
207 rp->rc_flag |= RC_WANTED;
170bfd05 208 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
95f0eacd
KM
209 }
210 remque(rp);
211 put_at_head(rp);
212 if (rp->rc_flag & RC_REPMBUF)
213 mb = rp->rc_reply;
214 else
215 mb = (struct mbuf *)0;
216 rp->rc_flag = 0;
217 rp->rc_state = RC_INPROG;
218 rp->rc_xid = xid;
f0f1cbaa 219 bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
95f0eacd
KM
220 rp->rc_proc = proc;
221 insque(rp, rh);
222 if (mb)
223 m_freem(mb);
224 return (RC_DOIT);
225}
226
227/*
228 * Update a request cache entry after the rpc has been done
229 */
f170c5c2 230nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
95f0eacd
KM
231 struct mbuf *nam;
232 u_long xid;
233 int proc;
f170c5c2 234 int repvalid;
95f0eacd
KM
235 int repstat;
236 struct mbuf *repmbuf;
237{
238 register struct nfsrvcache *rp;
239 register union rhead *rh;
95f0eacd
KM
240
241 rh = &rhead[NFSRCHASH(xid)];
95f0eacd
KM
242loop:
243 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
f0f1cbaa
KM
244 if (xid == rp->rc_xid && proc == rp->rc_proc &&
245 nfs_netaddr_match(nam, &rp->rc_nam)) {
95f0eacd
KM
246 if ((rp->rc_flag & RC_LOCKED) != 0) {
247 rp->rc_flag |= RC_WANTED;
170bfd05 248 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
95f0eacd
KM
249 goto loop;
250 }
251 rp->rc_flag |= RC_LOCKED;
252 rp->rc_state = RC_DONE;
f170c5c2
KM
253 /*
254 * If we have a valid reply update status and save
f0f1cbaa
KM
255 * the reply for non-idempotent rpc's.
256 * Otherwise invalidate entry by setting the timestamp
257 * to nil.
f170c5c2
KM
258 */
259 if (repvalid) {
260 rp->rc_timestamp = time.tv_sec;
261 if (nonidempotent[proc]) {
262 if (repliesstatus[proc]) {
263 rp->rc_status = repstat;
264 rp->rc_flag |= RC_REPSTATUS;
265 } else {
f0f1cbaa 266 rp->rc_reply = m_copym(repmbuf,
f170c5c2
KM
267 0, M_COPYALL, M_WAIT);
268 rp->rc_flag |= RC_REPMBUF;
269 }
95f0eacd 270 }
f170c5c2
KM
271 } else {
272 rp->rc_timestamp = 0;
95f0eacd
KM
273 }
274 rp->rc_flag &= ~RC_LOCKED;
275 if (rp->rc_flag & RC_WANTED) {
276 rp->rc_flag &= ~RC_WANTED;
277 wakeup((caddr_t)rp);
278 }
279 return;
280 }
281 }
282}