nfs_common.c revision 203732
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sys/nfs/nfs_common.c 203732 2010-02-09 23:45:14Z marius $");
37
38/*
39 * These functions support the macros and help fiddle mbuf chains for
40 * the nfs op functions.  They do things like create the rpc header and
41 * copy data between mbuf chains and uio lists.
42 */
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/kernel.h>
47#include <sys/bio.h>
48#include <sys/buf.h>
49#include <sys/proc.h>
50#include <sys/mount.h>
51#include <sys/vnode.h>
52#include <sys/namei.h>
53#include <sys/mbuf.h>
54#include <sys/socket.h>
55#include <sys/stat.h>
56#include <sys/malloc.h>
57#include <sys/sysent.h>
58#include <sys/syscall.h>
59#include <sys/sysctl.h>
60
61#include <vm/vm.h>
62#include <vm/vm_object.h>
63#include <vm/vm_extern.h>
64
65#include <nfs/nfsproto.h>
66#include <nfsserver/nfs.h>
67#include <nfs/xdr_subs.h>
68#include <nfs/nfs_common.h>
69
70#include <netinet/in.h>
71
72enum vtype nv3tov_type[8]= {
73	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
74};
75nfstype nfsv3_type[9] = {
76	NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON
77};
78
79static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos,
80    int how);
81
82SYSCTL_DECL(_vfs_nfs);
83
84static int nfs_realign_test;
85SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RD, &nfs_realign_test,
86    0, "Number of realign tests done");
87
88static int nfs_realign_count;
89SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RD, &nfs_realign_count,
90    0, "Number of mbuf realignments done");
91
92u_quad_t
93nfs_curusec(void)
94{
95	struct timeval tv;
96
97	getmicrotime(&tv);
98	return ((u_quad_t)tv.tv_sec * 1000000 + (u_quad_t)tv.tv_usec);
99}
100
101/*
102 * copies mbuf chain to the uio scatter/gather list
103 */
104int
105nfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
106{
107	char *mbufcp, *uiocp;
108	int xfer, left, len;
109	struct mbuf *mp;
110	long uiosiz, rem;
111	int error = 0;
112
113	mp = *mrep;
114	mbufcp = *dpos;
115	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
116	rem = nfsm_rndup(siz)-siz;
117	while (siz > 0) {
118		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
119			return (EFBIG);
120		left = uiop->uio_iov->iov_len;
121		uiocp = uiop->uio_iov->iov_base;
122		if (left > siz)
123			left = siz;
124		uiosiz = left;
125		while (left > 0) {
126			while (len == 0) {
127				mp = mp->m_next;
128				if (mp == NULL)
129					return (EBADRPC);
130				mbufcp = mtod(mp, caddr_t);
131				len = mp->m_len;
132			}
133			xfer = (left > len) ? len : left;
134#ifdef notdef
135			/* Not Yet.. */
136			if (uiop->uio_iov->iov_op != NULL)
137				(*(uiop->uio_iov->iov_op))
138				(mbufcp, uiocp, xfer);
139			else
140#endif
141			if (uiop->uio_segflg == UIO_SYSSPACE)
142				bcopy(mbufcp, uiocp, xfer);
143			else
144				copyout(mbufcp, uiocp, xfer);
145			left -= xfer;
146			len -= xfer;
147			mbufcp += xfer;
148			uiocp += xfer;
149			uiop->uio_offset += xfer;
150			uiop->uio_resid -= xfer;
151		}
152		if (uiop->uio_iov->iov_len <= siz) {
153			uiop->uio_iovcnt--;
154			uiop->uio_iov++;
155		} else {
156			uiop->uio_iov->iov_base =
157			    (char *)uiop->uio_iov->iov_base + uiosiz;
158			uiop->uio_iov->iov_len -= uiosiz;
159		}
160		siz -= uiosiz;
161	}
162	*dpos = mbufcp;
163	*mrep = mp;
164	if (rem > 0) {
165		if (len < rem)
166			error = nfs_adv(mrep, dpos, rem, len);
167		else
168			*dpos += rem;
169	}
170	return (error);
171}
172
173/*
174 * Help break down an mbuf chain by setting the first siz bytes contiguous
175 * pointed to by returned val.
176 * This is used by the macros nfsm_dissect for tough
177 * cases. (The macros use the vars. dpos and dpos2)
178 */
179void *
180nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how)
181{
182	struct mbuf *mp, *mp2;
183	int siz2, xfer;
184	caddr_t ptr, npos = NULL;
185	void *ret;
186
187	mp = *mdp;
188	while (left == 0) {
189		*mdp = mp = mp->m_next;
190		if (mp == NULL)
191			return (NULL);
192		left = mp->m_len;
193		*dposp = mtod(mp, caddr_t);
194	}
195	if (left >= siz) {
196		ret = *dposp;
197		*dposp += siz;
198	} else if (mp->m_next == NULL) {
199		return (NULL);
200	} else if (siz > MHLEN) {
201		panic("nfs S too big");
202	} else {
203		MGET(mp2, how, MT_DATA);
204		if (mp2 == NULL)
205			return (NULL);
206		mp2->m_len = siz;
207		mp2->m_next = mp->m_next;
208		mp->m_next = mp2;
209		mp->m_len -= left;
210		mp = mp2;
211		ptr = mtod(mp, caddr_t);
212		ret = ptr;
213		bcopy(*dposp, ptr, left);		/* Copy what was left */
214		siz2 = siz-left;
215		ptr += left;
216		mp2 = mp->m_next;
217		npos = mtod(mp2, caddr_t);
218		/* Loop around copying up the siz2 bytes */
219		while (siz2 > 0) {
220			if (mp2 == NULL)
221				return (NULL);
222			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
223			if (xfer > 0) {
224				bcopy(mtod(mp2, caddr_t), ptr, xfer);
225				mp2->m_data += xfer;
226				mp2->m_len -= xfer;
227				ptr += xfer;
228				siz2 -= xfer;
229			}
230			if (siz2 > 0) {
231				mp2 = mp2->m_next;
232				if (mp2 != NULL)
233					npos = mtod(mp2, caddr_t);
234			}
235		}
236		*mdp = mp2;
237		*dposp = mtod(mp2, caddr_t);
238		if (!nfsm_aligned(*dposp, u_int32_t)) {
239			bcopy(*dposp, npos, mp2->m_len);
240			mp2->m_data = npos;
241			*dposp = npos;
242		}
243	}
244	return (ret);
245}
246
247/*
248 * Advance the position in the mbuf chain.
249 */
250int
251nfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
252{
253	struct mbuf *m;
254	int s;
255
256	m = *mdp;
257	s = left;
258	while (s < offs) {
259		offs -= s;
260		m = m->m_next;
261		if (m == NULL)
262			return (EBADRPC);
263		s = m->m_len;
264	}
265	*mdp = m;
266	*dposp = mtod(m, caddr_t)+offs;
267	return (0);
268}
269
270void *
271nfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos)
272{
273	struct mbuf *mb2;
274	void *ret;
275
276	if (s > M_TRAILINGSPACE(*mb)) {
277		MGET(mb2, M_WAIT, MT_DATA);
278		if (s > MLEN)
279			panic("build > MLEN");
280		(*mb)->m_next = mb2;
281		*mb = mb2;
282		(*mb)->m_len = 0;
283		*bpos = mtod(*mb, caddr_t);
284	}
285	ret = *bpos;
286	(*mb)->m_len += s;
287	*bpos += s;
288	return (ret);
289}
290
291void *
292nfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos)
293{
294
295	return (nfsm_dissect_xx_sub(s, md, dpos, M_WAIT));
296}
297
298void *
299nfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos)
300{
301
302	return (nfsm_dissect_xx_sub(s, md, dpos, M_DONTWAIT));
303}
304
305static void *
306nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how)
307{
308	int t1;
309	char *cp2;
310	void *ret;
311
312	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
313	if (t1 >= s) {
314		ret = *dpos;
315		*dpos += s;
316		return (ret);
317	}
318	cp2 = nfsm_disct(md, dpos, s, t1, how);
319	return (cp2);
320}
321
322int
323nfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos)
324{
325	u_int32_t *tl;
326
327	tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos);
328	if (tl == NULL)
329		return (EBADRPC);
330	*s = fxdr_unsigned(int32_t, *tl);
331	if (*s > m)
332		return (EBADRPC);
333	return (0);
334}
335
336int
337nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
338{
339	int t1;
340
341	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
342	if (t1 >= s) {
343		*dpos += s;
344		return (0);
345	}
346	t1 = nfs_adv(md, dpos, s, t1);
347	if (t1)
348		return (t1);
349	return (0);
350}
351
352/*
353 * Check for badly aligned mbuf data and realign by copying the unaligned
354 * portion of the data into a new mbuf chain and freeing the portions of the
355 * old chain that were replaced.
356 *
357 * We cannot simply realign the data within the existing mbuf chain because
358 * the underlying buffers may contain other rpc commands and we cannot afford
359 * to overwrite them.
360 *
361 * We would prefer to avoid this situation entirely.  The situation does not
362 * occur with NFS/UDP and is supposed to only occassionally occur with TCP.
363 * Use vfs.nfs.realign_count and realign_test to check this.
364 */
365int
366nfs_realign(struct mbuf **pm, int how)
367{
368	struct mbuf *m, *n;
369	int off;
370
371	++nfs_realign_test;
372	while ((m = *pm) != NULL) {
373		if (!nfsm_aligned(m->m_len, u_int32_t) ||
374		    !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) {
375			/*
376			 * NB: we can't depend on m_pkthdr.len to help us
377			 * decide what to do here.  May not be worth doing
378			 * the m_length calculation as m_copyback will
379			 * expand the mbuf chain below as needed.
380			 */
381			if (m_length(m, NULL) >= MINCLSIZE) {
382				/* NB: m_copyback handles space > MCLBYTES */
383				n = m_getcl(how, MT_DATA, 0);
384			} else
385				n = m_get(how, MT_DATA);
386			if (n == NULL)
387				return (ENOMEM);
388			/*
389			 * Align the remainder of the mbuf chain.
390			 */
391			n->m_len = 0;
392			off = 0;
393			while (m != NULL) {
394				m_copyback(n, off, m->m_len, mtod(m, caddr_t));
395				off += m->m_len;
396				m = m->m_next;
397			}
398			m_freem(*pm);
399			*pm = n;
400			++nfs_realign_count;
401			break;
402		}
403		pm = &m->m_next;
404	}
405	return (0);
406}
407