* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* %sccs.include.redist.c%
* @(#)nfs_srvcache.c 8.1 (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>
long numnfsrvcache
, desirednfsrvcache
= NFSRVCACHESIZ
;
#define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & rheadhash)
static struct nfsrvcache
*nfsrvlruhead
, **nfsrvlrutail
= &nfsrvlruhead
;
static struct nfsrvcache
**rheadhtbl
;
(((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
rheadhtbl
= hashinit(desirednfsrvcache
, M_NFSD
, &rheadhash
);
* 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
, *rq
, **rpp
;
struct sockaddr_in
*saddr
;
if (nd
->nd_nqlflag
!= NQL_NOVAL
)
rpp
= &rheadhtbl
[NFSRCHASH(nd
->nd_retxid
)];
for (rp
= *rpp
; rp
; rp
= rp
->rc_forw
) {
if (nd
->nd_retxid
== rp
->rc_xid
&& nd
->nd_procnum
== rp
->rc_proc
&&
netaddr_match(NETFAMILY(rp
), &rp
->rc_haddr
, 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 not at end of LRU chain, move it there */
/* remove from LRU chain */
*rp
->rc_prev
= rp
->rc_next
;
rp
->rc_next
->rc_prev
= rp
->rc_prev
;
/* and replace at end of it */
rp
->rc_prev
= nfsrvlrutail
;
nfsrvlrutail
= &rp
->rc_next
;
if (rp
->rc_state
== RC_UNUSED
)
if (rp
->rc_state
== RC_INPROG
) {
nfsstats
.srvcache_inproghits
++;
} else if (rp
->rc_flag
& RC_REPSTATUS
) {
nfsstats
.srvcache_nonidemdonehits
++;
nfs_rephead(0, nd
, rp
->rc_status
,
0, (u_quad_t
*)0, repp
, &mb
, &bpos
);
} else if (rp
->rc_flag
& RC_REPMBUF
) {
nfsstats
.srvcache_nonidemdonehits
++;
*repp
= m_copym(rp
->rc_reply
, 0, M_COPYALL
,
nfsstats
.srvcache_idemdonehits
++;
rp
->rc_state
= RC_INPROG
;
rp
->rc_flag
&= ~RC_LOCKED
;
if (rp
->rc_flag
& RC_WANTED
) {
rp
->rc_flag
&= ~RC_WANTED
;
nfsstats
.srvcache_misses
++;
if (numnfsrvcache
< desirednfsrvcache
) {
rp
= (struct nfsrvcache
*)malloc((u_long
)sizeof *rp
,
bzero((char *)rp
, sizeof *rp
);
while ((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
;
/* remove from hash chain */
rq
->rc_back
= rp
->rc_back
;
/* remove from LRU chain */
*rp
->rc_prev
= rp
->rc_next
;
rp
->rc_next
->rc_prev
= rp
->rc_prev
;
if (rp
->rc_flag
& RC_REPMBUF
)
if (rp
->rc_flag
& RC_NAM
)
rp
->rc_flag
&= (RC_LOCKED
| RC_WANTED
);
/* place at end of LRU list */
rp
->rc_prev
= nfsrvlrutail
;
nfsrvlrutail
= &rp
->rc_next
;
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
;
/* insert into hash chain */
rq
->rc_back
= &rp
->rc_forw
;
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
;
if (nd
->nd_nqlflag
!= NQL_NOVAL
)
for (rp
= rheadhtbl
[NFSRCHASH(nd
->nd_retxid
)]; rp
; rp
= rp
->rc_forw
) {
if (nd
->nd_retxid
== rp
->rc_xid
&& nd
->nd_procnum
== rp
->rc_proc
&&
netaddr_match(NETFAMILY(rp
), &rp
->rc_haddr
, 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.
if (repvalid
&& 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
, *nextrp
;
for (rp
= nfsrvlruhead
; rp
; rp
= nextrp
) {
bzero((char *)rheadhtbl
, (rheadhash
+ 1) * sizeof(void *));
nfsrvlrutail
= &nfsrvlruhead
;