* 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.9 (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 "../netinet/in.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
];
* 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
, xid
, proc
, repp
)
register struct nfsrvcache
*rp
;
register union rhead
*rh
;
rh
= &rhead
[NFSRCHASH(xid
)];
for (rp
= rh
->rh_chain
[0]; rp
!= (struct nfsrvcache
*)rh
; rp
= rp
->rc_forw
) {
if (xid
== rp
->rc_xid
&& proc
== rp
->rc_proc
&&
nfs_netaddr_match(nam
, &rp
->rc_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_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, xid
, rp
->rc_status
, repp
, &mb
,
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);
if (rp
->rc_flag
& RC_REPMBUF
)
rp
->rc_state
= RC_INPROG
;
bcopy((caddr_t
)nam
, (caddr_t
)&rp
->rc_nam
, sizeof (struct mbuf
));
* Update a request cache entry after the rpc has been done
nfsrv_updatecache(nam
, xid
, proc
, repvalid
, repstat
, repmbuf
)
register struct nfsrvcache
*rp
;
register union rhead
*rh
;
rh
= &rhead
[NFSRCHASH(xid
)];
for (rp
= rh
->rh_chain
[0]; rp
!= (struct nfsrvcache
*)rh
; rp
= rp
->rc_forw
) {
if (xid
== rp
->rc_xid
&& proc
== rp
->rc_proc
&&
nfs_netaddr_match(nam
, &rp
->rc_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
[proc
]) {
if (repliesstatus
[proc
]) {
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
;