summaryrefslogtreecommitdiffstats
path: root/sys/fs/nfsserver/nfs_nfsdkrpc.c
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2014-01-03 15:09:59 +0000
committermav <mav@FreeBSD.org>2014-01-03 15:09:59 +0000
commitd75fe782e9f66e5daa9ee5ad39eecc2be925db47 (patch)
tree41fae690ab667a8b53a31423d51d2ff36ca697a6 /sys/fs/nfsserver/nfs_nfsdkrpc.c
parent4d725b0decffd3025cc73d299156cafb82885d99 (diff)
downloadFreeBSD-src-d75fe782e9f66e5daa9ee5ad39eecc2be925db47.zip
FreeBSD-src-d75fe782e9f66e5daa9ee5ad39eecc2be925db47.tar.gz
Rework NFS Duplicate Request Cache cleanup logic.
- Introduce additional hash to group requests by hash of sockref. This allows to process TCP acknowledgements without looping though all the cache, and as result allows to do it every time. - Indroduce additional callbacks to notify application layer about sockets disconnection. Without this last few requests processed just before socket disconnection never processed their ACKs and stuck in cache for many hours. - Implement transport-specific method for tracking reply acknowledgements. New implementation does not cross multiple stack layers to get the data and does not have race conditions that previously made some requests stuck in cache. This could be done more efficiently at sockbuf layer, but that would broke some KBIs, while I don't know other consumers for it aside NFS. - Instead of traversing all DRC twice per request, run cleaning only once per request, and except in some conditions traverse only single hash slot at a time. Together this limits NFS DRC growth only to situations of real connectivity problems. If network is working well, and so all replies are acknowledged, cache remains almost empty even after hours of heavy load. Without this change on the same test cache was growing to many thousand requests even with perfectly working local network. As another result this reduces CPU time spent on the DRC handling during SPEC NFS benchmark from about 10% to 0.5%. Sponsored by: iXsystems, Inc.
Diffstat (limited to 'sys/fs/nfsserver/nfs_nfsdkrpc.c')
-rw-r--r--sys/fs/nfsserver/nfs_nfsdkrpc.c39
1 files changed, 28 insertions, 11 deletions
diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c
index 9185b135..96cb833 100644
--- a/sys/fs/nfsserver/nfs_nfsdkrpc.c
+++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c
@@ -97,8 +97,8 @@ static int nfs_maxvers = NFS_VER4;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_nfsvers, CTLFLAG_RW,
&nfs_maxvers, 0, "The highest version of NFS handled by the server");
-static int nfs_proc(struct nfsrv_descript *, u_int32_t, struct socket *,
- u_int64_t, struct nfsrvcache **);
+static int nfs_proc(struct nfsrv_descript *, u_int32_t, SVCXPRT *xprt,
+ struct nfsrvcache **);
extern u_long sb_max_adj;
extern int newnfs_numnfsd;
@@ -251,8 +251,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
}
}
- cacherep = nfs_proc(&nd, rqst->rq_xid, xprt->xp_socket,
- xprt->xp_sockref, &rp);
+ cacherep = nfs_proc(&nd, rqst->rq_xid, xprt, &rp);
NFSLOCKV4ROOTMUTEX();
nfsv4_relref(&nfsd_suspend_lock);
NFSUNLOCKV4ROOTMUTEX();
@@ -287,8 +286,10 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
} else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq)) {
svcerr_systemerr(rqst);
}
- if (rp != NULL)
- nfsrvd_sentcache(rp, xprt->xp_socket, 0);
+ if (rp != NULL) {
+ if (rqst->rq_reply_seq != 0 || SVC_ACK(xprt, NULL))
+ nfsrvd_sentcache(rp, rqst->rq_reply_seq);
+ }
svc_freereq(rqst);
out:
@@ -300,11 +301,12 @@ out:
* Return the appropriate cache response.
*/
static int
-nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so,
- u_int64_t sockref, struct nfsrvcache **rpp)
+nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt,
+ struct nfsrvcache **rpp)
{
struct thread *td = curthread;
int cacherep = RC_DOIT, isdgram;
+ uint32_t ack;
*rpp = NULL;
if (nd->nd_nam2 == NULL) {
@@ -336,8 +338,11 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so,
nd->nd_flag |= ND_SAMETCPCONN;
nd->nd_retxid = xid;
nd->nd_tcpconntime = NFSD_MONOSEC;
- nd->nd_sockref = sockref;
- cacherep = nfsrvd_getcache(nd, so);
+ nd->nd_sockref = xprt->xp_sockref;
+ cacherep = nfsrvd_getcache(nd);
+ ack = 0;
+ SVC_ACK(xprt, &ack);
+ nfsrc_trimcache(xprt->xp_sockref, ack, 0);
}
/*
@@ -352,13 +357,23 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so,
cacherep = RC_DROPIT;
else
cacherep = RC_REPLY;
- *rpp = nfsrvd_updatecache(nd, so);
+ *rpp = nfsrvd_updatecache(nd);
}
NFSEXITCODE2(0, nd);
return (cacherep);
}
+static void
+nfssvc_loss(SVCXPRT *xprt)
+{
+ uint32_t ack;
+
+ ack = 0;
+ SVC_ACK(xprt, &ack);
+ nfsrc_trimcache(xprt->xp_sockref, ack, 1);
+}
+
/*
* Adds a socket to the list for servicing by nfsds.
*/
@@ -399,6 +414,8 @@ nfsrvd_addsock(struct file *fp)
if (nfs_maxvers >= NFS_VER4)
svc_reg(xprt, NFS_PROG, NFS_VER4, nfssvc_program,
NULL);
+ if (so->so_type == SOCK_STREAM)
+ svc_loss_reg(xprt, nfssvc_loss);
SVC_RELEASE(xprt);
}
OpenPOWER on IntegriCloud