aboutsummaryrefslogtreecommitdiff
path: root/sys/nfs/nfs_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/nfs/nfs_common.c')
-rw-r--r--sys/nfs/nfs_common.c67
1 files changed, 67 insertions, 0 deletions
diff --git a/sys/nfs/nfs_common.c b/sys/nfs/nfs_common.c
index 6f426455171b..abf575f9232b 100644
--- a/sys/nfs/nfs_common.c
+++ b/sys/nfs/nfs_common.c
@@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/sysent.h>
#include <sys/syscall.h>
+#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
@@ -78,6 +79,16 @@ nfstype nfsv3_type[9] = {
static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos,
int how);
+SYSCTL_DECL(_vfs_nfs);
+
+static int nfs_realign_test;
+SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RD, &nfs_realign_test,
+ 0, "Number of realign tests done");
+
+static int nfs_realign_count;
+SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RD, &nfs_realign_count,
+ 0, "Number of mbuf realignments done");
+
u_quad_t
nfs_curusec(void)
{
@@ -337,3 +348,59 @@ nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
return (t1);
return (0);
}
+
+/*
+ * Check for badly aligned mbuf data and realign by copying the unaligned
+ * portion of the data into a new mbuf chain and freeing the portions of the
+ * old chain that were replaced.
+ *
+ * We cannot simply realign the data within the existing mbuf chain because
+ * the underlying buffers may contain other rpc commands and we cannot afford
+ * to overwrite them.
+ *
+ * We would prefer to avoid this situation entirely. The situation does not
+ * occur with NFS/UDP and is supposed to only occassionally occur with TCP.
+ * Use vfs.nfs.realign_count and realign_test to check this.
+ */
+int
+nfs_realign(struct mbuf **pm, int how)
+{
+ struct mbuf *m, *n;
+ int off;
+
+ ++nfs_realign_test;
+ while ((m = *pm) != NULL) {
+ if (!nfsm_aligned(m->m_len, u_int32_t) ||
+ !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) {
+ /*
+ * NB: we can't depend on m_pkthdr.len to help us
+ * decide what to do here. May not be worth doing
+ * the m_length calculation as m_copyback will
+ * expand the mbuf chain below as needed.
+ */
+ if (m_length(m, NULL) >= MINCLSIZE) {
+ /* NB: m_copyback handles space > MCLBYTES */
+ n = m_getcl(how, MT_DATA, 0);
+ } else
+ n = m_get(how, MT_DATA);
+ if (n == NULL)
+ return (ENOMEM);
+ /*
+ * Align the remainder of the mbuf chain.
+ */
+ n->m_len = 0;
+ off = 0;
+ while (m != NULL) {
+ m_copyback(n, off, m->m_len, mtod(m, caddr_t));
+ off += m->m_len;
+ m = m->m_next;
+ }
+ m_freem(*pm);
+ *pm = n;
+ ++nfs_realign_count;
+ break;
+ }
+ pm = &m->m_next;
+ }
+ return (0);
+}