* 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.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* From: @(#)nfs_srvcache.c 7.11 (Berkeley) 4/16/91
* 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 struct nfsrvcache
*nfsrvcache
;
* 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
;
register struct nfsrvcache
*hp
= &nfsrvcachehead
;
register union rhead
*rh
= rhead
;
MALLOC(nfsrvcache
, struct nfsrvcache
*,
NFSRVCACHESIZ
* sizeof *nfsrvcache
, M_CACHE
, M_WAITOK
);
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
;
if(!nfsrvcache
) nfsrv_initcache();
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
;
if(!nfsrvcache
) nfsrv_initcache();
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
;