summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormohans <mohans@FreeBSD.org>2006-05-05 18:04:53 +0000
committermohans <mohans@FreeBSD.org>2006-05-05 18:04:53 +0000
commita10657a423ef23e07c0abf4a8b479a5cdf11332f (patch)
treed243e6fbdf58ff37371a57e8e549aa7d2912d4e9
parentb6dcc22adf0f38ed047b4ac5c3c9277ea671f446 (diff)
downloadFreeBSD-src-a10657a423ef23e07c0abf4a8b479a5cdf11332f.zip
FreeBSD-src-a10657a423ef23e07c0abf4a8b479a5cdf11332f.tar.gz
Fix for a NFS/TCP client bug which would cause the NFS/TCP stream to get
out of sync under heavy loads, forcing frequent reconnets, causing EBADRPC errors etc.
-rw-r--r--sys/nfsclient/nfs_socket.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/sys/nfsclient/nfs_socket.c b/sys/nfsclient/nfs_socket.c
index a26facd..61607bf 100644
--- a/sys/nfsclient/nfs_socket.c
+++ b/sys/nfsclient/nfs_socket.c
@@ -767,6 +767,20 @@ nfstcp_readable(struct socket *so, int bytes)
#define nfstcp_marker_readable(so) nfstcp_readable(so, sizeof(u_int32_t))
+static int
+nfs_copy_len(struct mbuf *mp, char *buf, int len)
+{
+ while (len > 0 && mp != NULL) {
+ int copylen = min(len, mp->m_len);
+
+ bcopy(mp->m_data, buf, copylen);
+ buf += copylen;
+ len -= copylen;
+ mp = mp->m_next;
+ }
+ return (len);
+}
+
static void
nfs_clnt_tcp_soupcall(struct socket *so, void *arg, int waitflag)
{
@@ -792,6 +806,8 @@ nfs_clnt_tcp_soupcall(struct socket *so, void *arg, int waitflag)
auio.uio_rw = UIO_READ;
for ( ; ; ) {
if (nmp->nm_nfstcpstate.flags & NFS_TCP_EXPECT_RPCMARKER) {
+ int resid;
+
if (!nfstcp_marker_readable(so)) {
/* Marker is not readable */
return;
@@ -820,6 +836,21 @@ nfs_clnt_tcp_soupcall(struct socket *so, void *arg, int waitflag)
}
if (mp == NULL)
panic("nfs_clnt_tcp_soupcall: Got empty mbuf chain from sorecv\n");
+ /*
+ * Sigh. We can't do the obvious thing here (which would
+ * be to have soreceive copy the length from mbufs for us).
+ * Calling uiomove() from the context of a socket callback
+ * (even for kernel-kernel copies) leads to LORs (since
+ * we hold network locks at this point).
+ */
+ if ((resid = nfs_copy_len(mp, (char *)&len,
+ sizeof(u_int32_t)))) {
+ log(LOG_ERR, "%s (%d) from nfs server %s\n",
+ "Bad RPC HDR length",
+ (int)(sizeof(u_int32_t) - resid),
+ nmp->nm_mountp->mnt_stat.f_mntfromname);
+ goto mark_reconnect;
+ }
bcopy(mtod(mp, u_int32_t *), &len, sizeof(len));
len = ntohl(len) & ~0x80000000;
m_freem(mp);
OpenPOWER on IntegriCloud