* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* %sccs.include.redist.c%
* @(#)nfs_srvcache.c 7.15 (Berkeley) %G%
* Reference: Chet Juszczak, "Improving the Performance and Correctness
* of an NFS Server", in Proc. Winter 1989 USENIX Conference,
* pages 53-63. San Diego, February 1989.
#include <sys/socketvar.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nfsrvcache.h>
#if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
#define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1))
#define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
struct nfsrvcache
*rh_chain
[2];
static struct nfsrvcache nfsrvcachehead
;
static struct nfsrvcache nfsrvcache
[NFSRVCACHESIZ
];
(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
* Static array that defines which nfs rpc's are nonidempotent
int nonidempotent
[NFS_NPROCS
] = {
/* True iff the rpc reply is an nfs status ONLY! */
static int repliesstatus
[NFS_NPROCS
] = {
* Initialize the server request cache list
register struct nfsrvcache
*rp
= nfsrvcache
;
register struct nfsrvcache
*hp
= &nfsrvcachehead
;
register union rhead
*rh
= rhead
;
for (i
= NFSRCHSZ
; --i
>= 0; rh
++) {
hp
->rc_next
= hp
->rc_prev
= hp
;
for (i
= NFSRVCACHESIZ
; i
-- > 0; ) {
rp
->rc_state
= RC_UNUSED
;
rp
->rc_next
= hp
->rc_next
;
hp
->rc_next
->rc_prev
= rp
;
* Look for the request in the cache
* return action and optionally reply
* The rules are as follows:
* - if in progress, return DROP request
* - if completed within DELAY of the current time, return DROP it
* - if completed a longer time ago return REPLY if the reply was cached or
* Update/add new request at end of lru list
nfsrv_getcache(nam
, nd
, repp
)
register struct nfsd
*nd
;
register struct nfsrvcache
*rp
;
register union rhead
*rh
;
struct sockaddr_in
*saddr
;
if (nd
->nd_nqlflag
!= NQL_NOVAL
)
rh
= &rhead
[NFSRCHASH(nd
->nd_retxid
)];
for (rp
= rh
->rh_chain
[0]; rp
!= (struct nfsrvcache
*)rh
; rp
= rp
->rc_forw
) {
if (nd
->nd_retxid
== rp
->rc_xid
&& nd
->nd_procnum
== rp
->rc_proc
&&
netaddr_match(NETFAMILY(rp
), &rp
->rc_haddr
, (union nethostaddr
*)0, nam
)) {
if ((rp
->rc_flag
& RC_LOCKED
) != 0) {
rp
->rc_flag
|= RC_WANTED
;
(void) tsleep((caddr_t
)rp
, PZERO
-1, "nfsrc", 0);
rp
->rc_flag
|= RC_LOCKED
;
if (rp
->rc_prev
!= &nfsrvcachehead
) {
rp
->rc_next
->rc_prev
= rp
->rc_prev
;
rp
->rc_prev
->rc_next
= rp
->rc_next
;
rp
->rc_next
= nfsrvcachehead
.rc_next
;
nfsrvcachehead
.rc_next
= rp
;
rp
->rc_prev
= &nfsrvcachehead
;
rp
->rc_next
->rc_prev
= rp
;
if (rp
->rc_state
== RC_UNUSED
)
if (rp
->rc_state
== RC_INPROG
||
(time
.tv_sec
- rp
->rc_timestamp
) < RC_DELAY
) {
nfsstats
.srvcache_inproghits
++;
} else if (rp
->rc_flag
& RC_REPSTATUS
) {
nfsstats
.srvcache_idemdonehits
++;
nfs_rephead(0, nd
, rp
->rc_status
,
0, (u_quad_t
*)0, repp
, &mb
, &bpos
);
rp
->rc_timestamp
= time
.tv_sec
;
} else if (rp
->rc_flag
& RC_REPMBUF
) {
nfsstats
.srvcache_idemdonehits
++;
*repp
= m_copym(rp
->rc_reply
, 0, M_COPYALL
,
rp
->rc_timestamp
= time
.tv_sec
;
nfsstats
.srvcache_nonidemdonehits
++;
rp
->rc_state
= RC_INPROG
;
rp
->rc_flag
&= ~RC_LOCKED
;
if (rp
->rc_flag
& RC_WANTED
) {
rp
->rc_flag
&= ~RC_WANTED
;
nfsstats
.srvcache_misses
++;
rp
= nfsrvcachehead
.rc_prev
;
while ((rp
->rc_flag
& RC_LOCKED
) != 0) {
rp
->rc_flag
|= RC_WANTED
;
(void) tsleep((caddr_t
)rp
, PZERO
-1, "nfsrc", 0);
rp
= nfsrvcachehead
.rc_prev
;
rp
->rc_flag
|= RC_LOCKED
;
if (rp
->rc_prev
!= &nfsrvcachehead
) {
rp
->rc_next
->rc_prev
= rp
->rc_prev
;
rp
->rc_prev
->rc_next
= rp
->rc_next
;
rp
->rc_next
= nfsrvcachehead
.rc_next
;
nfsrvcachehead
.rc_next
= rp
;
rp
->rc_prev
= &nfsrvcachehead
;
rp
->rc_next
->rc_prev
= rp
;
if (rp
->rc_flag
& RC_REPMBUF
)
if (rp
->rc_flag
& RC_NAM
)
rp
->rc_flag
&= (RC_LOCKED
| RC_WANTED
);
rp
->rc_state
= RC_INPROG
;
rp
->rc_xid
= nd
->nd_retxid
;
saddr
= mtod(nam
, struct sockaddr_in
*);
switch (saddr
->sin_family
) {
rp
->rc_flag
|= RC_INETADDR
;
rp
->rc_inetaddr
= saddr
->sin_addr
.s_addr
;
rp
->rc_nam
= m_copym(nam
, 0, M_COPYALL
, M_WAIT
);
rp
->rc_proc
= nd
->nd_procnum
;
rp
->rc_flag
&= ~RC_LOCKED
;
if (rp
->rc_flag
& RC_WANTED
) {
rp
->rc_flag
&= ~RC_WANTED
;
* Update a request cache entry after the rpc has been done
nfsrv_updatecache(nam
, nd
, repvalid
, repmbuf
)
register struct nfsd
*nd
;
register struct nfsrvcache
*rp
;
register union rhead
*rh
;
if (nd
->nd_nqlflag
!= NQL_NOVAL
)
rh
= &rhead
[NFSRCHASH(nd
->nd_retxid
)];
for (rp
= rh
->rh_chain
[0]; rp
!= (struct nfsrvcache
*)rh
; rp
= rp
->rc_forw
) {
if (nd
->nd_retxid
== rp
->rc_xid
&& nd
->nd_procnum
== rp
->rc_proc
&&
netaddr_match(NETFAMILY(rp
), &rp
->rc_haddr
, (union nethostaddr
*)0, nam
)) {
if ((rp
->rc_flag
& RC_LOCKED
) != 0) {
rp
->rc_flag
|= RC_WANTED
;
(void) tsleep((caddr_t
)rp
, PZERO
-1, "nfsrc", 0);
rp
->rc_flag
|= RC_LOCKED
;
* If we have a valid reply update status and save
* the reply for non-idempotent rpc's.
* Otherwise invalidate entry by setting the timestamp
rp
->rc_timestamp
= time
.tv_sec
;
if (nonidempotent
[nd
->nd_procnum
]) {
if (repliesstatus
[nd
->nd_procnum
]) {
rp
->rc_status
= nd
->nd_repstat
;
rp
->rc_flag
|= RC_REPSTATUS
;
rp
->rc_reply
= m_copym(repmbuf
,
rp
->rc_flag
|= RC_REPMBUF
;
rp
->rc_flag
&= ~RC_LOCKED
;
if (rp
->rc_flag
& RC_WANTED
) {
rp
->rc_flag
&= ~RC_WANTED
;
* Clean out the cache. Called when the last nfsd terminates.
register struct nfsrvcache
*rp
= nfsrvcache
;
register struct nfsrvcache
*hp
= &nfsrvcachehead
;
register union rhead
*rh
= rhead
;
for (i
= NFSRCHSZ
; --i
>= 0; rh
++) {
hp
->rc_next
= hp
->rc_prev
= hp
;
for (i
= NFSRVCACHESIZ
; i
-- > 0; ) {
if (rp
->rc_flag
& RC_REPMBUF
)
if (rp
->rc_flag
& RC_NAM
)
rp
->rc_state
= RC_UNUSED
;
rp
->rc_next
= hp
->rc_next
;
hp
->rc_next
->rc_prev
= rp
;