diff options
Diffstat (limited to 'sys/nfs/nfs_common.c')
-rw-r--r-- | sys/nfs/nfs_common.c | 67 |
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); +} |