Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/nfsserver/nfs_nfsdcache.c
Show First 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | |||||
* nfsrc_floodlevel is set to the allowable upper limit for saved replies | * nfsrc_floodlevel is set to the allowable upper limit for saved replies | ||||
* for TCP. For V3, a reply won't be saved when the flood level is | * for TCP. For V3, a reply won't be saved when the flood level is | ||||
* hit. For V4, the non-idempotent Op will return NFSERR_RESOURCE in | * hit. For V4, the non-idempotent Op will return NFSERR_RESOURCE in | ||||
* that case. This level should be set high enough that this almost | * that case. This level should be set high enough that this almost | ||||
* never happens. | * never happens. | ||||
*/ | */ | ||||
#include <fs/nfs/nfsport.h> | #include <fs/nfs/nfsport.h> | ||||
extern struct nfsstatsv1 nfsstatsv1; | |||||
extern struct mtx nfsrc_udpmtx; | extern struct mtx nfsrc_udpmtx; | ||||
extern struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE]; | |||||
extern struct nfsrchash_bucket nfsrcahash_table[NFSRVCACHE_HASHSIZE]; | |||||
int nfsrc_floodlevel = NFSRVCACHE_FLOODLEVEL, nfsrc_tcpsavedreplies = 0; | |||||
NFSDSTATSDECLARE(nfsstatsv1); | |||||
NFSD_VNET_DECLARE(struct nfsrvhashhead *, nfsrvudphashtbl); | |||||
NFSD_VNET_DECLARE(struct nfsrchash_bucket *, nfsrchash_table); | |||||
NFSD_VNET_DECLARE(struct nfsrchash_bucket *, nfsrcahash_table); | |||||
NFSD_VNET_DEFINE(int, nfsrc_floodlevel) = NFSRVCACHE_FLOODLEVEL; | |||||
NFSD_VNET_DEFINE(int, nfsrc_tcpsavedreplies) = 0; | |||||
SYSCTL_DECL(_vfs_nfsd); | SYSCTL_DECL(_vfs_nfsd); | ||||
static u_int nfsrc_tcphighwater = 0; | static u_int nfsrc_tcphighwater = 0; | ||||
static int | static int | ||||
sysctl_tcphighwater(SYSCTL_HANDLER_ARGS) | sysctl_tcphighwater(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error, newhighwater; | int error, newhighwater; | ||||
newhighwater = nfsrc_tcphighwater; | newhighwater = nfsrc_tcphighwater; | ||||
error = sysctl_handle_int(oidp, &newhighwater, 0, req); | error = sysctl_handle_int(oidp, &newhighwater, 0, req); | ||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||
return (error); | return (error); | ||||
if (newhighwater < 0) | if (newhighwater < 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (newhighwater >= nfsrc_floodlevel) | if (newhighwater >= NFSD_VNET(nfsrc_floodlevel)) | ||||
nfsrc_floodlevel = newhighwater + newhighwater / 5; | NFSD_VNET(nfsrc_floodlevel) = newhighwater + newhighwater / 5; | ||||
nfsrc_tcphighwater = newhighwater; | nfsrc_tcphighwater = newhighwater; | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSCTL_PROC(_vfs_nfsd, OID_AUTO, tcphighwater, | SYSCTL_PROC(_vfs_nfsd, OID_AUTO, tcphighwater, | ||||
CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW, 0, sizeof(nfsrc_tcphighwater), | CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW, 0, sizeof(nfsrc_tcphighwater), | ||||
sysctl_tcphighwater, "IU", "High water mark for TCP cache entries"); | sysctl_tcphighwater, "IU", "High water mark for TCP cache entries"); | ||||
static u_int nfsrc_udphighwater = NFSRVCACHE_UDPHIGHWATER; | static u_int nfsrc_udphighwater = NFSRVCACHE_UDPHIGHWATER; | ||||
SYSCTL_UINT(_vfs_nfsd, OID_AUTO, udphighwater, CTLFLAG_RW, | SYSCTL_UINT(_vfs_nfsd, OID_AUTO, udphighwater, CTLFLAG_RW, | ||||
&nfsrc_udphighwater, 0, | &nfsrc_udphighwater, 0, | ||||
"High water mark for UDP cache entries"); | "High water mark for UDP cache entries"); | ||||
static u_int nfsrc_tcptimeout = NFSRVCACHE_TCPTIMEOUT; | static u_int nfsrc_tcptimeout = NFSRVCACHE_TCPTIMEOUT; | ||||
SYSCTL_UINT(_vfs_nfsd, OID_AUTO, tcpcachetimeo, CTLFLAG_RW, | SYSCTL_UINT(_vfs_nfsd, OID_AUTO, tcpcachetimeo, CTLFLAG_RW, | ||||
&nfsrc_tcptimeout, 0, | &nfsrc_tcptimeout, 0, | ||||
"Timeout for TCP entries in the DRC"); | "Timeout for TCP entries in the DRC"); | ||||
static u_int nfsrc_tcpnonidempotent = 1; | static u_int nfsrc_tcpnonidempotent = 1; | ||||
SYSCTL_UINT(_vfs_nfsd, OID_AUTO, cachetcp, CTLFLAG_RW, | SYSCTL_UINT(_vfs_nfsd, OID_AUTO, cachetcp, CTLFLAG_RW, | ||||
&nfsrc_tcpnonidempotent, 0, | &nfsrc_tcpnonidempotent, 0, | ||||
"Enable the DRC for NFS over TCP"); | "Enable the DRC for NFS over TCP"); | ||||
static int nfsrc_udpcachesize = 0; | NFSD_VNET_DEFINE_STATIC(int, nfsrc_udpcachesize) = 0; | ||||
static TAILQ_HEAD(, nfsrvcache) nfsrvudplru; | NFSD_VNET_DEFINE_STATIC(TAILQ_HEAD(, nfsrvcache), nfsrvudplru); | ||||
static struct nfsrvhashhead nfsrvudphashtbl[NFSRVCACHE_HASHSIZE]; | |||||
/* | /* | ||||
* and the reverse mapping from generic to Version 2 procedure numbers | * and the reverse mapping from generic to Version 2 procedure numbers | ||||
*/ | */ | ||||
static int newnfsv2_procid[NFS_V3NPROCS] = { | static int newnfsv2_procid[NFS_V3NPROCS] = { | ||||
NFSV2PROC_NULL, | NFSV2PROC_NULL, | ||||
NFSV2PROC_GETATTR, | NFSV2PROC_GETATTR, | ||||
NFSV2PROC_SETATTR, | NFSV2PROC_SETATTR, | ||||
Show All 15 Lines | static int newnfsv2_procid[NFS_V3NPROCS] = { | ||||
NFSV2PROC_STATFS, | NFSV2PROC_STATFS, | ||||
NFSV2PROC_NOOP, | NFSV2PROC_NOOP, | ||||
NFSV2PROC_NOOP, | NFSV2PROC_NOOP, | ||||
NFSV2PROC_NOOP, | NFSV2PROC_NOOP, | ||||
}; | }; | ||||
#define nfsrc_hash(xid) (((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE) | #define nfsrc_hash(xid) (((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE) | ||||
#define NFSRCUDPHASH(xid) \ | #define NFSRCUDPHASH(xid) \ | ||||
(&nfsrvudphashtbl[nfsrc_hash(xid)]) | (&NFSD_VNET(nfsrvudphashtbl)[nfsrc_hash(xid)]) | ||||
#define NFSRCHASH(xid) \ | #define NFSRCHASH(xid) \ | ||||
(&nfsrchash_table[nfsrc_hash(xid)].tbl) | (&NFSD_VNET(nfsrchash_table)[nfsrc_hash(xid)].tbl) | ||||
#define NFSRCAHASH(xid) (&nfsrcahash_table[nfsrc_hash(xid)]) | #define NFSRCAHASH(xid) (&NFSD_VNET(nfsrcahash_table)[nfsrc_hash(xid)]) | ||||
#define TRUE 1 | #define TRUE 1 | ||||
#define FALSE 0 | #define FALSE 0 | ||||
#define NFSRVCACHE_CHECKLEN 100 | #define NFSRVCACHE_CHECKLEN 100 | ||||
/* True iff the rpc reply is an nfs status ONLY! */ | /* True iff the rpc reply is an nfs status ONLY! */ | ||||
static int nfsv2_repstat[NFS_V3NPROCS] = { | static int nfsv2_repstat[NFS_V3NPROCS] = { | ||||
FALSE, | FALSE, | ||||
FALSE, | FALSE, | ||||
Show All 39 Lines | |||||
* Return the correct mutex for this cache entry. | * Return the correct mutex for this cache entry. | ||||
*/ | */ | ||||
static __inline struct mtx * | static __inline struct mtx * | ||||
nfsrc_cachemutex(struct nfsrvcache *rp) | nfsrc_cachemutex(struct nfsrvcache *rp) | ||||
{ | { | ||||
if ((rp->rc_flag & RC_UDP) != 0) | if ((rp->rc_flag & RC_UDP) != 0) | ||||
return (&nfsrc_udpmtx); | return (&nfsrc_udpmtx); | ||||
return (&nfsrchash_table[nfsrc_hash(rp->rc_xid)].mtx); | return (&NFSD_VNET(nfsrchash_table)[nfsrc_hash(rp->rc_xid)].mtx); | ||||
} | } | ||||
/* | /* | ||||
* Initialize the server request cache list | * Initialize the server request cache list | ||||
*/ | */ | ||||
void | void | ||||
nfsrvd_initcache(void) | nfsrvd_initcache(void) | ||||
{ | { | ||||
int i; | int i; | ||||
static int inited = 0; | |||||
if (inited) | NFSD_VNET(nfsrvudphashtbl) = malloc(sizeof(struct nfsrvhashhead) * | ||||
return; | NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO); | ||||
inited = 1; | NFSD_VNET(nfsrchash_table) = malloc(sizeof(struct nfsrchash_bucket) * | ||||
NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO); | |||||
NFSD_VNET(nfsrcahash_table) = malloc(sizeof(struct nfsrchash_bucket) * | |||||
NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO); | |||||
for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | ||||
LIST_INIT(&nfsrvudphashtbl[i]); | mtx_init(&NFSD_VNET(nfsrchash_table)[i].mtx, "nfsrtc", NULL, | ||||
LIST_INIT(&nfsrchash_table[i].tbl); | MTX_DEF); | ||||
LIST_INIT(&nfsrcahash_table[i].tbl); | mtx_init(&NFSD_VNET(nfsrcahash_table)[i].mtx, "nfsrtca", NULL, | ||||
MTX_DEF); | |||||
} | } | ||||
TAILQ_INIT(&nfsrvudplru); | for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | ||||
nfsrc_tcpsavedreplies = 0; | LIST_INIT(&NFSD_VNET(nfsrvudphashtbl)[i]); | ||||
nfsrc_udpcachesize = 0; | LIST_INIT(&NFSD_VNET(nfsrchash_table)[i].tbl); | ||||
nfsstatsv1.srvcache_tcppeak = 0; | LIST_INIT(&NFSD_VNET(nfsrcahash_table)[i].tbl); | ||||
nfsstatsv1.srvcache_size = 0; | |||||
} | } | ||||
TAILQ_INIT(&NFSD_VNET(nfsrvudplru)); | |||||
NFSD_VNET(nfsrc_tcpsavedreplies) = 0; | |||||
NFSD_VNET(nfsrc_udpcachesize) = 0; | |||||
} | |||||
/* | /* | ||||
* Get a cache entry for this request. Basically just malloc a new one | * Get a cache entry for this request. Basically just malloc a new one | ||||
* and then call nfsrc_getudp() or nfsrc_gettcp() to do the rest. | * and then call nfsrc_getudp() or nfsrc_gettcp() to do the rest. | ||||
*/ | */ | ||||
int | int | ||||
nfsrvd_getcache(struct nfsrv_descript *nd) | nfsrvd_getcache(struct nfsrv_descript *nd) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | nfsaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { | ||||
rp->rc_flag |= RC_WANTED; | rp->rc_flag |= RC_WANTED; | ||||
(void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, | (void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, | ||||
"nfsrc", 10 * hz); | "nfsrc", 10 * hz); | ||||
goto loop; | goto loop; | ||||
} | } | ||||
if (rp->rc_flag == 0) | if (rp->rc_flag == 0) | ||||
panic("nfs udp cache0"); | panic("nfs udp cache0"); | ||||
rp->rc_flag |= RC_LOCKED; | rp->rc_flag |= RC_LOCKED; | ||||
TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); | TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru); | ||||
TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru); | TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), rp, rc_lru); | ||||
if (rp->rc_flag & RC_INPROG) { | if (rp->rc_flag & RC_INPROG) { | ||||
nfsstatsv1.srvcache_inproghits++; | NFSDSTATS()->srvcache_inproghits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
ret = RC_DROPIT; | ret = RC_DROPIT; | ||||
} else if (rp->rc_flag & RC_REPSTATUS) { | } else if (rp->rc_flag & RC_REPSTATUS) { | ||||
/* | /* | ||||
* V2 only. | * V2 only. | ||||
*/ | */ | ||||
nfsstatsv1.srvcache_nonidemdonehits++; | NFSDSTATS()->srvcache_nonidemdonehits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
nfsrvd_rephead(nd); | nfsrvd_rephead(nd); | ||||
*(nd->nd_errp) = rp->rc_status; | *(nd->nd_errp) = rp->rc_status; | ||||
ret = RC_REPLY; | ret = RC_REPLY; | ||||
rp->rc_timestamp = NFSD_MONOSEC + | rp->rc_timestamp = NFSD_MONOSEC + | ||||
NFSRVCACHE_UDPTIMEOUT; | NFSRVCACHE_UDPTIMEOUT; | ||||
} else if (rp->rc_flag & RC_REPMBUF) { | } else if (rp->rc_flag & RC_REPMBUF) { | ||||
nfsstatsv1.srvcache_nonidemdonehits++; | NFSDSTATS()->srvcache_nonidemdonehits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
nd->nd_mreq = m_copym(rp->rc_reply, 0, | nd->nd_mreq = m_copym(rp->rc_reply, 0, | ||||
M_COPYALL, M_WAITOK); | M_COPYALL, M_WAITOK); | ||||
ret = RC_REPLY; | ret = RC_REPLY; | ||||
rp->rc_timestamp = NFSD_MONOSEC + | rp->rc_timestamp = NFSD_MONOSEC + | ||||
NFSRVCACHE_UDPTIMEOUT; | NFSRVCACHE_UDPTIMEOUT; | ||||
} else { | } else { | ||||
panic("nfs udp cache1"); | panic("nfs udp cache1"); | ||||
} | } | ||||
nfsrc_unlock(rp); | nfsrc_unlock(rp); | ||||
free(newrp, M_NFSRVCACHE); | free(newrp, M_NFSRVCACHE); | ||||
goto out; | goto out; | ||||
} | } | ||||
} | } | ||||
nfsstatsv1.srvcache_misses++; | NFSDSTATS()->srvcache_misses++; | ||||
atomic_add_int(&nfsstatsv1.srvcache_size, 1); | atomic_add_int(&NFSDSTATS()->srvcache_size, 1); | ||||
nfsrc_udpcachesize++; | NFSD_VNET(nfsrc_udpcachesize)++; | ||||
newrp->rc_flag |= RC_INPROG; | newrp->rc_flag |= RC_INPROG; | ||||
saddr = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *); | saddr = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *); | ||||
if (saddr->sin_family == AF_INET) | if (saddr->sin_family == AF_INET) | ||||
newrp->rc_inet = saddr->sin_addr.s_addr; | newrp->rc_inet = saddr->sin_addr.s_addr; | ||||
else if (saddr->sin_family == AF_INET6) { | else if (saddr->sin_family == AF_INET6) { | ||||
saddr6 = (struct sockaddr_in6 *)saddr; | saddr6 = (struct sockaddr_in6 *)saddr; | ||||
NFSBCOPY((caddr_t)&saddr6->sin6_addr, (caddr_t)&newrp->rc_inet6, | NFSBCOPY((caddr_t)&saddr6->sin6_addr, (caddr_t)&newrp->rc_inet6, | ||||
sizeof (struct in6_addr)); | sizeof (struct in6_addr)); | ||||
newrp->rc_flag |= RC_INETIPV6; | newrp->rc_flag |= RC_INETIPV6; | ||||
} | } | ||||
LIST_INSERT_HEAD(hp, newrp, rc_hash); | LIST_INSERT_HEAD(hp, newrp, rc_hash); | ||||
TAILQ_INSERT_TAIL(&nfsrvudplru, newrp, rc_lru); | TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), newrp, rc_lru); | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
nd->nd_rp = newrp; | nd->nd_rp = newrp; | ||||
ret = RC_DOIT; | ret = RC_DOIT; | ||||
out: | out: | ||||
NFSEXITCODE2(0, nd); | NFSEXITCODE2(0, nd); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
Show All 15 Lines | nfsrvd_updatecache(struct nfsrv_descript *nd) | ||||
nd->nd_rp = NULL; | nd->nd_rp = NULL; | ||||
mutex = nfsrc_cachemutex(rp); | mutex = nfsrc_cachemutex(rp); | ||||
mtx_lock(mutex); | mtx_lock(mutex); | ||||
nfsrc_lock(rp); | nfsrc_lock(rp); | ||||
if (!(rp->rc_flag & RC_INPROG)) | if (!(rp->rc_flag & RC_INPROG)) | ||||
panic("nfsrvd_updatecache not inprog"); | panic("nfsrvd_updatecache not inprog"); | ||||
rp->rc_flag &= ~RC_INPROG; | rp->rc_flag &= ~RC_INPROG; | ||||
if (rp->rc_flag & RC_UDP) { | if (rp->rc_flag & RC_UDP) { | ||||
TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); | TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru); | ||||
TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru); | TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), rp, rc_lru); | ||||
} | } | ||||
/* | /* | ||||
* Reply from cache is a special case returned by nfsrv_checkseqid(). | * Reply from cache is a special case returned by nfsrv_checkseqid(). | ||||
*/ | */ | ||||
if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) { | if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) { | ||||
nfsstatsv1.srvcache_nonidemdonehits++; | NFSDSTATS()->srvcache_nonidemdonehits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
nd->nd_repstat = 0; | nd->nd_repstat = 0; | ||||
if (nd->nd_mreq) | if (nd->nd_mreq) | ||||
m_freem(nd->nd_mreq); | m_freem(nd->nd_mreq); | ||||
if (!(rp->rc_flag & RC_REPMBUF)) | if (!(rp->rc_flag & RC_REPMBUF)) | ||||
panic("reply from cache"); | panic("reply from cache"); | ||||
nd->nd_mreq = m_copym(rp->rc_reply, 0, | nd->nd_mreq = m_copym(rp->rc_reply, 0, | ||||
M_COPYALL, M_WAITOK); | M_COPYALL, M_WAITOK); | ||||
rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | ||||
nfsrc_unlock(rp); | nfsrc_unlock(rp); | ||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* If rc_refcnt > 0, save it | * If rc_refcnt > 0, save it | ||||
* For UDP, save it if ND_SAVEREPLY is set | * For UDP, save it if ND_SAVEREPLY is set | ||||
* For TCP, save it if ND_SAVEREPLY and nfsrc_tcpnonidempotent is set | * For TCP, save it if ND_SAVEREPLY and nfsrc_tcpnonidempotent is set | ||||
*/ | */ | ||||
if (nd->nd_repstat != NFSERR_DONTREPLY && | if (nd->nd_repstat != NFSERR_DONTREPLY && | ||||
(rp->rc_refcnt > 0 || | (rp->rc_refcnt > 0 || | ||||
((nd->nd_flag & ND_SAVEREPLY) && (rp->rc_flag & RC_UDP)) || | ((nd->nd_flag & ND_SAVEREPLY) && (rp->rc_flag & RC_UDP)) || | ||||
((nd->nd_flag & ND_SAVEREPLY) && !(rp->rc_flag & RC_UDP) && | ((nd->nd_flag & ND_SAVEREPLY) && !(rp->rc_flag & RC_UDP) && | ||||
nfsrc_tcpsavedreplies <= nfsrc_floodlevel && | NFSD_VNET(nfsrc_tcpsavedreplies) <= NFSD_VNET(nfsrc_floodlevel) && | ||||
nfsrc_tcpnonidempotent))) { | nfsrc_tcpnonidempotent))) { | ||||
if (rp->rc_refcnt > 0) { | if (rp->rc_refcnt > 0) { | ||||
if (!(rp->rc_flag & RC_NFSV4)) | if (!(rp->rc_flag & RC_NFSV4)) | ||||
panic("update_cache refcnt"); | panic("update_cache refcnt"); | ||||
rp->rc_flag |= RC_REFCNT; | rp->rc_flag |= RC_REFCNT; | ||||
} | } | ||||
if ((nd->nd_flag & ND_NFSV2) && | if ((nd->nd_flag & ND_NFSV2) && | ||||
nfsv2_repstat[newnfsv2_procid[nd->nd_procnum]]) { | nfsv2_repstat[newnfsv2_procid[nd->nd_procnum]]) { | ||||
rp->rc_status = nd->nd_repstat; | rp->rc_status = nd->nd_repstat; | ||||
rp->rc_flag |= RC_REPSTATUS; | rp->rc_flag |= RC_REPSTATUS; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
} else { | } else { | ||||
if (!(rp->rc_flag & RC_UDP)) { | if (!(rp->rc_flag & RC_UDP)) { | ||||
atomic_add_int(&nfsrc_tcpsavedreplies, 1); | atomic_add_int(&NFSD_VNET(nfsrc_tcpsavedreplies), | ||||
if (nfsrc_tcpsavedreplies > | 1); | ||||
nfsstatsv1.srvcache_tcppeak) | if (NFSD_VNET(nfsrc_tcpsavedreplies) > | ||||
nfsstatsv1.srvcache_tcppeak = | NFSDSTATS()->srvcache_tcppeak) | ||||
nfsrc_tcpsavedreplies; | NFSDSTATS()->srvcache_tcppeak = | ||||
NFSD_VNET(nfsrc_tcpsavedreplies); | |||||
} | } | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
m = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); | m = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); | ||||
mtx_lock(mutex); | mtx_lock(mutex); | ||||
rp->rc_reply = m; | rp->rc_reply = m; | ||||
rp->rc_flag |= RC_REPMBUF; | rp->rc_flag |= RC_REPMBUF; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | if ((rp->rc_flag & RC_LOCKED) != 0) { | ||||
(void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, | (void)mtx_sleep(rp, mutex, (PZERO - 1) | PDROP, | ||||
"nfsrc", 10 * hz); | "nfsrc", 10 * hz); | ||||
goto tryagain; | goto tryagain; | ||||
} | } | ||||
if (rp->rc_flag == 0) | if (rp->rc_flag == 0) | ||||
panic("nfs tcp cache0"); | panic("nfs tcp cache0"); | ||||
rp->rc_flag |= RC_LOCKED; | rp->rc_flag |= RC_LOCKED; | ||||
if (rp->rc_flag & RC_INPROG) { | if (rp->rc_flag & RC_INPROG) { | ||||
nfsstatsv1.srvcache_inproghits++; | NFSDSTATS()->srvcache_inproghits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
if (newrp->rc_sockref == rp->rc_sockref) | if (newrp->rc_sockref == rp->rc_sockref) | ||||
nfsrc_marksametcpconn(rp->rc_sockref); | nfsrc_marksametcpconn(rp->rc_sockref); | ||||
ret = RC_DROPIT; | ret = RC_DROPIT; | ||||
} else if (rp->rc_flag & RC_REPSTATUS) { | } else if (rp->rc_flag & RC_REPSTATUS) { | ||||
/* | /* | ||||
* V2 only. | * V2 only. | ||||
*/ | */ | ||||
nfsstatsv1.srvcache_nonidemdonehits++; | NFSDSTATS()->srvcache_nonidemdonehits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
if (newrp->rc_sockref == rp->rc_sockref) | if (newrp->rc_sockref == rp->rc_sockref) | ||||
nfsrc_marksametcpconn(rp->rc_sockref); | nfsrc_marksametcpconn(rp->rc_sockref); | ||||
ret = RC_REPLY; | ret = RC_REPLY; | ||||
nfsrvd_rephead(nd); | nfsrvd_rephead(nd); | ||||
*(nd->nd_errp) = rp->rc_status; | *(nd->nd_errp) = rp->rc_status; | ||||
rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | ||||
} else if (rp->rc_flag & RC_REPMBUF) { | } else if (rp->rc_flag & RC_REPMBUF) { | ||||
nfsstatsv1.srvcache_nonidemdonehits++; | NFSDSTATS()->srvcache_nonidemdonehits++; | ||||
mtx_unlock(mutex); | mtx_unlock(mutex); | ||||
if (newrp->rc_sockref == rp->rc_sockref) | if (newrp->rc_sockref == rp->rc_sockref) | ||||
nfsrc_marksametcpconn(rp->rc_sockref); | nfsrc_marksametcpconn(rp->rc_sockref); | ||||
ret = RC_REPLY; | ret = RC_REPLY; | ||||
nd->nd_mreq = m_copym(rp->rc_reply, 0, | nd->nd_mreq = m_copym(rp->rc_reply, 0, | ||||
M_COPYALL, M_WAITOK); | M_COPYALL, M_WAITOK); | ||||
rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; | ||||
} else { | } else { | ||||
panic("nfs tcp cache1"); | panic("nfs tcp cache1"); | ||||
} | } | ||||
nfsrc_unlock(rp); | nfsrc_unlock(rp); | ||||
free(newrp, M_NFSRVCACHE); | free(newrp, M_NFSRVCACHE); | ||||
goto out; | goto out; | ||||
} | } | ||||
nfsstatsv1.srvcache_misses++; | NFSDSTATS()->srvcache_misses++; | ||||
atomic_add_int(&nfsstatsv1.srvcache_size, 1); | atomic_add_int(&NFSDSTATS()->srvcache_size, 1); | ||||
/* | /* | ||||
* For TCP, multiple entries for a key are allowed, so don't | * For TCP, multiple entries for a key are allowed, so don't | ||||
* chain it into the hash table until done. | * chain it into the hash table until done. | ||||
*/ | */ | ||||
newrp->rc_cachetime = NFSD_MONOSEC; | newrp->rc_cachetime = NFSD_MONOSEC; | ||||
newrp->rc_flag |= RC_INPROG; | newrp->rc_flag |= RC_INPROG; | ||||
LIST_INSERT_HEAD(hp, newrp, rc_hash); | LIST_INSERT_HEAD(hp, newrp, rc_hash); | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
nfsrc_freecache(struct nfsrvcache *rp) | nfsrc_freecache(struct nfsrvcache *rp) | ||||
{ | { | ||||
struct nfsrchash_bucket *hbp; | struct nfsrchash_bucket *hbp; | ||||
LIST_REMOVE(rp, rc_hash); | LIST_REMOVE(rp, rc_hash); | ||||
if (rp->rc_flag & RC_UDP) { | if (rp->rc_flag & RC_UDP) { | ||||
TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); | TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru); | ||||
nfsrc_udpcachesize--; | NFSD_VNET(nfsrc_udpcachesize)--; | ||||
} else if (rp->rc_acked != RC_NO_SEQ) { | } else if (rp->rc_acked != RC_NO_SEQ) { | ||||
hbp = NFSRCAHASH(rp->rc_sockref); | hbp = NFSRCAHASH(rp->rc_sockref); | ||||
mtx_lock(&hbp->mtx); | mtx_lock(&hbp->mtx); | ||||
if (rp->rc_acked == RC_NO_ACK) | if (rp->rc_acked == RC_NO_ACK) | ||||
LIST_REMOVE(rp, rc_ahash); | LIST_REMOVE(rp, rc_ahash); | ||||
mtx_unlock(&hbp->mtx); | mtx_unlock(&hbp->mtx); | ||||
} | } | ||||
nfsrc_wanted(rp); | nfsrc_wanted(rp); | ||||
if (rp->rc_flag & RC_REPMBUF) { | if (rp->rc_flag & RC_REPMBUF) { | ||||
m_freem(rp->rc_reply); | m_freem(rp->rc_reply); | ||||
if (!(rp->rc_flag & RC_UDP)) | if (!(rp->rc_flag & RC_UDP)) | ||||
atomic_add_int(&nfsrc_tcpsavedreplies, -1); | atomic_add_int(&NFSD_VNET(nfsrc_tcpsavedreplies), -1); | ||||
} | } | ||||
free(rp, M_NFSRVCACHE); | free(rp, M_NFSRVCACHE); | ||||
atomic_add_int(&nfsstatsv1.srvcache_size, -1); | atomic_add_int(&NFSDSTATS()->srvcache_size, -1); | ||||
} | } | ||||
/* | /* | ||||
* Clean out the cache. Called when nfsserver module is unloaded. | * Clean out the cache. Called when nfsserver module is unloaded. | ||||
*/ | */ | ||||
void | void | ||||
nfsrvd_cleancache(void) | nfsrvd_cleancache(void) | ||||
{ | { | ||||
struct nfsrvcache *rp, *nextrp; | struct nfsrvcache *rp, *nextrp; | ||||
int i; | int i; | ||||
for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | ||||
mtx_lock(&nfsrchash_table[i].mtx); | mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, nextrp) | LIST_FOREACH_SAFE(rp, &NFSD_VNET(nfsrchash_table)[i].tbl, | ||||
rc_hash, nextrp) | |||||
nfsrc_freecache(rp); | nfsrc_freecache(rp); | ||||
mtx_unlock(&nfsrchash_table[i].mtx); | mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
} | } | ||||
mtx_lock(&nfsrc_udpmtx); | mtx_lock(&nfsrc_udpmtx); | ||||
for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | ||||
LIST_FOREACH_SAFE(rp, &nfsrvudphashtbl[i], rc_hash, nextrp) { | LIST_FOREACH_SAFE(rp, &NFSD_VNET(nfsrvudphashtbl)[i], rc_hash, | ||||
nextrp) { | |||||
nfsrc_freecache(rp); | nfsrc_freecache(rp); | ||||
} | } | ||||
} | } | ||||
nfsstatsv1.srvcache_size = 0; | NFSDSTATS()->srvcache_size = 0; | ||||
mtx_unlock(&nfsrc_udpmtx); | mtx_unlock(&nfsrc_udpmtx); | ||||
nfsrc_tcpsavedreplies = 0; | NFSD_VNET(nfsrc_tcpsavedreplies) = 0; | ||||
} | } | ||||
#define HISTSIZE 16 | #define HISTSIZE 16 | ||||
/* | /* | ||||
* The basic rule is to get rid of entries that are expired. | * The basic rule is to get rid of entries that are expired. | ||||
*/ | */ | ||||
void | void | ||||
nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final) | nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final) | ||||
Show All 20 Lines | LIST_FOREACH_SAFE(rp, &hbp->tbl, rc_ahash, nextrp) { | ||||
} | } | ||||
} | } | ||||
mtx_unlock(&hbp->mtx); | mtx_unlock(&hbp->mtx); | ||||
} | } | ||||
if (atomic_cmpset_acq_int(&onethread, 0, 1) == 0) | if (atomic_cmpset_acq_int(&onethread, 0, 1) == 0) | ||||
return; | return; | ||||
if (NFSD_MONOSEC != udp_lasttrim || | if (NFSD_MONOSEC != udp_lasttrim || | ||||
nfsrc_udpcachesize >= (nfsrc_udphighwater + | NFSD_VNET(nfsrc_udpcachesize) >= (nfsrc_udphighwater + | ||||
nfsrc_udphighwater / 2)) { | nfsrc_udphighwater / 2)) { | ||||
mtx_lock(&nfsrc_udpmtx); | mtx_lock(&nfsrc_udpmtx); | ||||
udp_lasttrim = NFSD_MONOSEC; | udp_lasttrim = NFSD_MONOSEC; | ||||
TAILQ_FOREACH_SAFE(rp, &nfsrvudplru, rc_lru, nextrp) { | TAILQ_FOREACH_SAFE(rp, &NFSD_VNET(nfsrvudplru), rc_lru, | ||||
nextrp) { | |||||
if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED)) | if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED)) | ||||
&& rp->rc_refcnt == 0 | && rp->rc_refcnt == 0 | ||||
&& ((rp->rc_flag & RC_REFCNT) || | && ((rp->rc_flag & RC_REFCNT) || | ||||
udp_lasttrim > rp->rc_timestamp || | udp_lasttrim > rp->rc_timestamp || | ||||
nfsrc_udpcachesize > nfsrc_udphighwater)) | NFSD_VNET(nfsrc_udpcachesize) > | ||||
nfsrc_udphighwater)) | |||||
nfsrc_freecache(rp); | nfsrc_freecache(rp); | ||||
} | } | ||||
mtx_unlock(&nfsrc_udpmtx); | mtx_unlock(&nfsrc_udpmtx); | ||||
} | } | ||||
if (NFSD_MONOSEC != tcp_lasttrim || | if (NFSD_MONOSEC != tcp_lasttrim || | ||||
nfsrc_tcpsavedreplies >= nfsrc_tcphighwater) { | NFSD_VNET(nfsrc_tcpsavedreplies) >= nfsrc_tcphighwater) { | ||||
force = nfsrc_tcphighwater / 4; | force = nfsrc_tcphighwater / 4; | ||||
if (force > 0 && | if (force > 0 && | ||||
nfsrc_tcpsavedreplies + force >= nfsrc_tcphighwater) { | NFSD_VNET(nfsrc_tcpsavedreplies) + force >= | ||||
nfsrc_tcphighwater) { | |||||
for (i = 0; i < HISTSIZE; i++) | for (i = 0; i < HISTSIZE; i++) | ||||
time_histo[i] = 0; | time_histo[i] = 0; | ||||
i = 0; | i = 0; | ||||
lastslot = NFSRVCACHE_HASHSIZE - 1; | lastslot = NFSRVCACHE_HASHSIZE - 1; | ||||
} else { | } else { | ||||
force = 0; | force = 0; | ||||
if (NFSD_MONOSEC != tcp_lasttrim) { | if (NFSD_MONOSEC != tcp_lasttrim) { | ||||
i = 0; | i = 0; | ||||
lastslot = NFSRVCACHE_HASHSIZE - 1; | lastslot = NFSRVCACHE_HASHSIZE - 1; | ||||
} else { | } else { | ||||
lastslot = i = oneslot; | lastslot = i = oneslot; | ||||
if (++oneslot >= NFSRVCACHE_HASHSIZE) | if (++oneslot >= NFSRVCACHE_HASHSIZE) | ||||
oneslot = 0; | oneslot = 0; | ||||
} | } | ||||
} | } | ||||
tto = nfsrc_tcptimeout; | tto = nfsrc_tcptimeout; | ||||
tcp_lasttrim = NFSD_MONOSEC; | tcp_lasttrim = NFSD_MONOSEC; | ||||
for (; i <= lastslot; i++) { | for (; i <= lastslot; i++) { | ||||
mtx_lock(&nfsrchash_table[i].mtx); | mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, | LIST_FOREACH_SAFE(rp, | ||||
&NFSD_VNET(nfsrchash_table)[i].tbl, rc_hash, | |||||
nextrp) { | nextrp) { | ||||
if (!(rp->rc_flag & | if (!(rp->rc_flag & | ||||
(RC_INPROG|RC_LOCKED|RC_WANTED)) | (RC_INPROG|RC_LOCKED|RC_WANTED)) | ||||
&& rp->rc_refcnt == 0) { | && rp->rc_refcnt == 0) { | ||||
if ((rp->rc_flag & RC_REFCNT) || | if ((rp->rc_flag & RC_REFCNT) || | ||||
tcp_lasttrim > rp->rc_timestamp || | tcp_lasttrim > rp->rc_timestamp || | ||||
rp->rc_acked == RC_ACK) { | rp->rc_acked == RC_ACK) { | ||||
nfsrc_freecache(rp); | nfsrc_freecache(rp); | ||||
Show All 13 Lines | for (; i <= lastslot; i++) { | ||||
j = HISTSIZE - 1; | j = HISTSIZE - 1; | ||||
else if (j < 0) | else if (j < 0) | ||||
j = 0; | j = 0; | ||||
else | else | ||||
j = j * HISTSIZE / tto; | j = j * HISTSIZE / tto; | ||||
time_histo[j]++; | time_histo[j]++; | ||||
} | } | ||||
} | } | ||||
mtx_unlock(&nfsrchash_table[i].mtx); | mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
} | } | ||||
if (force) { | if (force) { | ||||
/* | /* | ||||
* Trim some more with a smaller timeout of as little | * Trim some more with a smaller timeout of as little | ||||
* as 20% of nfsrc_tcptimeout to try and get below | * as 20% of nfsrc_tcptimeout to try and get below | ||||
* 80% of the nfsrc_tcphighwater. | * 80% of the nfsrc_tcphighwater. | ||||
*/ | */ | ||||
k = 0; | k = 0; | ||||
for (i = 0; i < (HISTSIZE - 2); i++) { | for (i = 0; i < (HISTSIZE - 2); i++) { | ||||
k += time_histo[i]; | k += time_histo[i]; | ||||
if (k > force) | if (k > force) | ||||
break; | break; | ||||
} | } | ||||
k = tto * (i + 1) / HISTSIZE; | k = tto * (i + 1) / HISTSIZE; | ||||
if (k < 1) | if (k < 1) | ||||
k = 1; | k = 1; | ||||
thisstamp = tcp_lasttrim + k; | thisstamp = tcp_lasttrim + k; | ||||
for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { | ||||
mtx_lock(&nfsrchash_table[i].mtx); | mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, | LIST_FOREACH_SAFE(rp, | ||||
&NFSD_VNET(nfsrchash_table)[i].tbl, | |||||
rc_hash, nextrp) { | rc_hash, nextrp) { | ||||
if (!(rp->rc_flag & | if (!(rp->rc_flag & | ||||
(RC_INPROG|RC_LOCKED|RC_WANTED)) | (RC_INPROG|RC_LOCKED|RC_WANTED)) | ||||
&& rp->rc_refcnt == 0 | && rp->rc_refcnt == 0 | ||||
&& ((rp->rc_flag & RC_REFCNT) || | && ((rp->rc_flag & RC_REFCNT) || | ||||
thisstamp > rp->rc_timestamp || | thisstamp > rp->rc_timestamp || | ||||
rp->rc_acked == RC_ACK)) | rp->rc_acked == RC_ACK)) | ||||
nfsrc_freecache(rp); | nfsrc_freecache(rp); | ||||
} | } | ||||
mtx_unlock(&nfsrchash_table[i].mtx); | mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
atomic_store_rel_int(&onethread, 0); | atomic_store_rel_int(&onethread, 0); | ||||
} | } | ||||
/* | /* | ||||
* Add a seqid# reference to the cache entry. | * Add a seqid# reference to the cache entry. | ||||
▲ Show 20 Lines • Show All 63 Lines • Show Last 20 Lines |