nfs_clsubs.c revision 203119
1202375Srdivacky/*-
2202375Srdivacky * Copyright (c) 1989, 1993
3202375Srdivacky *	The Regents of the University of California.  All rights reserved.
4202375Srdivacky *
5202375Srdivacky * This code is derived from software contributed to Berkeley by
6202375Srdivacky * Rick Macklem at The University of Guelph.
7202375Srdivacky *
8202375Srdivacky * Redistribution and use in source and binary forms, with or without
9202375Srdivacky * modification, are permitted provided that the following conditions
10202375Srdivacky * are met:
11202375Srdivacky * 1. Redistributions of source code must retain the above copyright
12202375Srdivacky *    notice, this list of conditions and the following disclaimer.
13202375Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
14202375Srdivacky *    notice, this list of conditions and the following disclaimer in the
15226633Sdim *    documentation and/or other materials provided with the distribution.
16218893Sdim * 4. Neither the name of the University nor the names of its contributors
17249423Sdim *    may be used to endorse or promote products derived from this software
18202375Srdivacky *    without specific prior written permission.
19202375Srdivacky *
20202375Srdivacky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21202375Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22202375Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23202375Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24202375Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25202375Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26202375Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27202375Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28202375Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29202375Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30202375Srdivacky * SUCH DAMAGE.
31202375Srdivacky *
32202375Srdivacky *	from nfs_subs.c  8.8 (Berkeley) 5/22/95
33202375Srdivacky */
34202375Srdivacky
35202375Srdivacky#include <sys/cdefs.h>
36202375Srdivacky__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clsubs.c 203119 2010-01-28 16:17:24Z rmacklem $");
37202375Srdivacky
38202375Srdivacky/*
39218893Sdim * These functions support the macros and help fiddle mbuf chains for
40218893Sdim * the nfs op functions. They do things like create the rpc header and
41218893Sdim * copy data between mbuf chains and uio lists.
42218893Sdim */
43218893Sdim
44218893Sdim#include <sys/param.h>
45218893Sdim#include <sys/systm.h>
46218893Sdim#include <sys/kernel.h>
47218893Sdim#include <sys/bio.h>
48218893Sdim#include <sys/buf.h>
49218893Sdim#include <sys/proc.h>
50218893Sdim#include <sys/mount.h>
51218893Sdim#include <sys/vnode.h>
52249423Sdim#include <sys/namei.h>
53202375Srdivacky#include <sys/mbuf.h>
54202375Srdivacky#include <sys/socket.h>
55202375Srdivacky#include <sys/stat.h>
56212904Sdim#include <sys/malloc.h>
57212904Sdim#include <sys/sysent.h>
58212904Sdim#include <sys/syscall.h>
59212904Sdim#include <sys/sysproto.h>
60212904Sdim
61212904Sdim#include <vm/vm.h>
62212904Sdim#include <vm/vm_object.h>
63212904Sdim#include <vm/vm_extern.h>
64212904Sdim#include <vm/uma.h>
65212904Sdim
66212904Sdim#include <fs/nfs/nfsport.h>
67212904Sdim#include <fs/nfsclient/nfsnode.h>
68212904Sdim#include <fs/nfsclient/nfsmount.h>
69212904Sdim#include <fs/nfsclient/nfs.h>
70212904Sdim#include <fs/nfsclient/nfs_lock.h>
71212904Sdim
72212904Sdim#include <netinet/in.h>
73249423Sdim
74212904Sdim/*
75212904Sdim * Note that stdarg.h and the ANSI style va_start macro is used for both
76249423Sdim * ANSI and traditional C compilers.
77212904Sdim */
78212904Sdim#include <machine/stdarg.h>
79212904Sdim
80212904Sdimextern struct mtx ncl_iod_mutex;
81218893Sdimextern enum nfsiod_state ncl_iodwant[NFS_MAXRAHEAD];
82212904Sdimextern struct nfsmount *ncl_iodmount[NFS_MAXRAHEAD];
83212904Sdimextern int ncl_numasync;
84212904Sdimextern unsigned int ncl_iodmax;
85212904Sdimextern struct nfsstats newnfsstats;
86212904Sdim
87212904Sdimint
88212904Sdimncl_uninit(struct vfsconf *vfsp)
89212904Sdim{
90212904Sdim	/*
91212904Sdim	 * XXX: Unloading of nfscl module is unsupported.
92212904Sdim	 */
93212904Sdim#if 0
94212904Sdim	int i;
95212904Sdim
96212904Sdim	/*
97212904Sdim	 * Tell all nfsiod processes to exit. Clear ncl_iodmax, and wakeup
98249423Sdim	 * any sleeping nfsiods so they check ncl_iodmax and exit.
99212904Sdim	 */
100212904Sdim	mtx_lock(&ncl_iod_mutex);
101249423Sdim	ncl_iodmax = 0;
102212904Sdim	for (i = 0; i < ncl_numasync; i++)
103212904Sdim		if (ncl_iodwant[i] == NFSIOD_AVAILABLE)
104212904Sdim			wakeup(&ncl_iodwant[i]);
105249423Sdim	/* The last nfsiod to exit will wake us up when ncl_numasync hits 0 */
106212904Sdim	while (ncl_numasync)
107212904Sdim		msleep(&ncl_numasync, &ncl_iod_mutex, PWAIT, "ioddie", 0);
108212904Sdim	mtx_unlock(&ncl_iod_mutex);
109212904Sdim	ncl_nhuninit();
110212904Sdim	return (0);
111212904Sdim#else
112212904Sdim	return (EOPNOTSUPP);
113212904Sdim#endif
114249423Sdim}
115212904Sdim
116212904Sdimvoid
117212904Sdimncl_dircookie_lock(struct nfsnode *np)
118212904Sdim{
119212904Sdim	mtx_lock(&np->n_mtx);
120212904Sdim	while (np->n_flag & NDIRCOOKIELK)
121212904Sdim		(void) msleep(&np->n_flag, &np->n_mtx, PZERO, "nfsdirlk", 0);
122249423Sdim	np->n_flag |= NDIRCOOKIELK;
123212904Sdim	mtx_unlock(&np->n_mtx);
124212904Sdim}
125249423Sdim
126212904Sdimvoid
127212904Sdimncl_dircookie_unlock(struct nfsnode *np)
128212904Sdim{
129212904Sdim	mtx_lock(&np->n_mtx);
130212904Sdim	np->n_flag &= ~NDIRCOOKIELK;
131218893Sdim	wakeup(&np->n_flag);
132212904Sdim	mtx_unlock(&np->n_mtx);
133218893Sdim}
134212904Sdim
135212904Sdimint
136249423Sdimncl_upgrade_vnlock(struct vnode *vp)
137212904Sdim{
138212904Sdim	int old_lock;
139212904Sdim
140212904Sdim	ASSERT_VOP_LOCKED(vp, "ncl_upgrade_vnlock");
141212904Sdim	old_lock = VOP_ISLOCKED(vp);
142212904Sdim	if (old_lock != LK_EXCLUSIVE) {
143249423Sdim		KASSERT(old_lock == LK_SHARED,
144212904Sdim		    ("ncl_upgrade_vnlock: wrong old_lock %d", old_lock));
145212904Sdim		/* Upgrade to exclusive lock, this might block */
146249423Sdim		vn_lock(vp, LK_UPGRADE | LK_RETRY);
147212904Sdim  	}
148212904Sdim	return (old_lock);
149249423Sdim}
150212904Sdim
151212904Sdimvoid
152212904Sdimncl_downgrade_vnlock(struct vnode *vp, int old_lock)
153212904Sdim{
154239462Sdim	if (old_lock != LK_EXCLUSIVE) {
155212904Sdim		KASSERT(old_lock == LK_SHARED, ("wrong old_lock %d", old_lock));
156212904Sdim		/* Downgrade from exclusive lock. */
157218893Sdim		vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
158212904Sdim  	}
159212904Sdim}
160249423Sdim
161212904Sdimvoid
162212904Sdimncl_printf(const char *fmt, ...)
163212904Sdim{
164212904Sdim	va_list ap;
165212904Sdim
166212904Sdim	mtx_lock(&Giant);
167212904Sdim	va_start(ap, fmt);
168212904Sdim	printf(fmt, ap);
169212904Sdim	va_end(ap);
170212904Sdim	mtx_unlock(&Giant);
171212904Sdim}
172212904Sdim
173212904Sdim#ifdef NFS_ACDEBUG
174212904Sdim#include <sys/sysctl.h>
175212904SdimSYSCTL_DECL(_vfs_newnfs);
176212904Sdimstatic int nfs_acdebug;
177212904SdimSYSCTL_INT(_vfs_newnfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
178249423Sdim#endif
179212904Sdim
180212904Sdim/*
181212904Sdim * Check the time stamp
182212904Sdim * If the cache is valid, copy contents to *vap and return 0
183212904Sdim * otherwise return an error
184212904Sdim */
185212904Sdimint
186212904Sdimncl_getattrcache(struct vnode *vp, struct vattr *vaper)
187212904Sdim{
188212904Sdim	struct nfsnode *np;
189212904Sdim	struct vattr *vap;
190212904Sdim	struct nfsmount *nmp;
191212904Sdim	int timeo;
192212904Sdim
193243830Sdim	np = VTONFS(vp);
194234353Sdim	vap = &np->n_vattr.na_vattr;
195212904Sdim	nmp = VFSTONFS(vp->v_mount);
196212904Sdim#ifdef NFS_ACDEBUG
197249423Sdim	mtx_lock(&Giant);	/* ncl_printf() */
198212904Sdim#endif
199212904Sdim	mtx_lock(&np->n_mtx);
200212904Sdim	/* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
201212904Sdim	timeo = (time_second - np->n_mtime.tv_sec) / 10;
202234353Sdim
203212904Sdim#ifdef NFS_ACDEBUG
204212904Sdim	if (nfs_acdebug>1)
205212904Sdim		ncl_printf("nfs_getattrcache: initial timeo = %d\n", timeo);
206212904Sdim#endif
207212904Sdim
208212904Sdim	if (vap->va_type == VDIR) {
209212904Sdim		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
210249423Sdim			timeo = nmp->nm_acdirmin;
211212904Sdim		else if (timeo > nmp->nm_acdirmax)
212226633Sdim			timeo = nmp->nm_acdirmax;
213226633Sdim	} else {
214212904Sdim		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
215212904Sdim			timeo = nmp->nm_acregmin;
216226633Sdim		else if (timeo > nmp->nm_acregmax)
217226633Sdim			timeo = nmp->nm_acregmax;
218212904Sdim	}
219212904Sdim
220212904Sdim#ifdef NFS_ACDEBUG
221212904Sdim	if (nfs_acdebug > 2)
222212904Sdim		ncl_printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
223212904Sdim			   nmp->nm_acregmin, nmp->nm_acregmax,
224212904Sdim			   nmp->nm_acdirmin, nmp->nm_acdirmax);
225226633Sdim
226226633Sdim	if (nfs_acdebug)
227226633Sdim		ncl_printf("nfs_getattrcache: age = %d; final timeo = %d\n",
228212904Sdim			   (time_second - np->n_attrstamp), timeo);
229212904Sdim#endif
230249423Sdim
231212904Sdim	if ((time_second - np->n_attrstamp) >= timeo) {
232212904Sdim		newnfsstats.attrcache_misses++;
233212904Sdim		mtx_unlock(&np->n_mtx);
234212904Sdim		return( ENOENT);
235226633Sdim	}
236226633Sdim	newnfsstats.attrcache_hits++;
237212904Sdim	if (vap->va_size != np->n_size) {
238226633Sdim		if (vap->va_type == VREG) {
239226633Sdim			if (np->n_flag & NMODIFIED) {
240212904Sdim				if (vap->va_size < np->n_size)
241212904Sdim					vap->va_size = np->n_size;
242212904Sdim				else
243249423Sdim					np->n_size = vap->va_size;
244212904Sdim			} else {
245212904Sdim				np->n_size = vap->va_size;
246212904Sdim			}
247226633Sdim			vnode_pager_setsize(vp, np->n_size);
248226633Sdim		} else {
249226633Sdim			np->n_size = vap->va_size;
250226633Sdim		}
251226633Sdim	}
252212904Sdim	bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
253212904Sdim	if (np->n_flag & NCHG) {
254226633Sdim		if (np->n_flag & NACC)
255226633Sdim			vaper->va_atime = np->n_atim;
256212904Sdim		if (np->n_flag & NUPD)
257226633Sdim			vaper->va_mtime = np->n_mtim;
258249423Sdim	}
259212904Sdim	mtx_unlock(&np->n_mtx);
260212904Sdim#ifdef NFS_ACDEBUG
261212904Sdim	mtx_unlock(&Giant);	/* ncl_printf() */
262212904Sdim#endif
263212904Sdim	return (0);
264226633Sdim}
265249423Sdim
266226633Sdimstatic nfsuint64 nfs_nullcookie = { { 0, 0 } };
267226633Sdim/*
268212904Sdim * This function finds the directory cookie that corresponds to the
269212904Sdim * logical byte offset given.
270249423Sdim */
271212904Sdimnfsuint64 *
272212904Sdimncl_getcookie(struct nfsnode *np, off_t off, int add)
273212904Sdim{
274212904Sdim	struct nfsdmap *dp, *dp2;
275212904Sdim	int pos;
276226633Sdim	nfsuint64 *retval = NULL;
277212904Sdim
278212904Sdim	pos = (uoff_t)off / NFS_DIRBLKSIZ;
279212904Sdim	if (pos == 0 || off < 0) {
280212904Sdim#ifdef DIAGNOSTIC
281212904Sdim		if (add)
282212904Sdim			panic("nfs getcookie add at <= 0");
283249423Sdim#endif
284212904Sdim		return (&nfs_nullcookie);
285212904Sdim	}
286212904Sdim	pos--;
287226633Sdim	dp = LIST_FIRST(&np->n_cookies);
288226633Sdim	if (!dp) {
289226633Sdim		if (add) {
290226633Sdim			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
291212904Sdim				M_NFSDIROFF, M_WAITOK);
292249423Sdim			dp->ndm_eocookie = 0;
293212904Sdim			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
294212904Sdim		} else
295212904Sdim			goto out;
296212904Sdim	}
297212904Sdim	while (pos >= NFSNUMCOOKIES) {
298212904Sdim		pos -= NFSNUMCOOKIES;
299212904Sdim		if (LIST_NEXT(dp, ndm_list)) {
300212904Sdim			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
301212904Sdim			    pos >= dp->ndm_eocookie)
302212904Sdim				goto out;
303212904Sdim			dp = LIST_NEXT(dp, ndm_list);
304212904Sdim		} else if (add) {
305212904Sdim			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
306212904Sdim				M_NFSDIROFF, M_WAITOK);
307249423Sdim			dp2->ndm_eocookie = 0;
308212904Sdim			LIST_INSERT_AFTER(dp, dp2, ndm_list);
309212904Sdim			dp = dp2;
310212904Sdim		} else
311212904Sdim			goto out;
312202375Srdivacky	}
313202375Srdivacky	if (pos >= dp->ndm_eocookie) {
314202375Srdivacky		if (add)
315249423Sdim			dp->ndm_eocookie = pos + 1;
316249423Sdim		else
317212904Sdim			goto out;
318212904Sdim	}
319212904Sdim	retval = &dp->ndm_cookies[pos];
320212904Sdimout:
321212904Sdim	return (retval);
322212904Sdim}
323249423Sdim
324249423Sdim/*
325212904Sdim * Invalidate cached directory information, except for the actual directory
326212904Sdim * blocks (which are invalidated separately).
327249423Sdim * Done mainly to avoid the use of stale offset cookies.
328249423Sdim */
329249423Sdimvoid
330202375Srdivackyncl_invaldir(struct vnode *vp)
331202375Srdivacky{
332249423Sdim	struct nfsnode *np = VTONFS(vp);
333202375Srdivacky
334202375Srdivacky#ifdef DIAGNOSTIC
335202375Srdivacky	if (vp->v_type != VDIR)
336202375Srdivacky		panic("nfs: invaldir not dir");
337202375Srdivacky#endif
338202375Srdivacky	ncl_dircookie_lock(np);
339203954Srdivacky	np->n_direofoffset = 0;
340203954Srdivacky	np->n_cookieverf.nfsuquad[0] = 0;
341203954Srdivacky	np->n_cookieverf.nfsuquad[1] = 0;
342202375Srdivacky	if (LIST_FIRST(&np->n_cookies))
343249423Sdim		LIST_FIRST(&np->n_cookies)->ndm_eocookie = 0;
344202375Srdivacky	ncl_dircookie_unlock(np);
345202375Srdivacky}
346202375Srdivacky
347202375Srdivacky/*
348202375Srdivacky * The write verifier has changed (probably due to a server reboot), so all
349202375Srdivacky * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
350249423Sdim * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
351202375Srdivacky * and B_CLUSTEROK flags.  Once done the new write verifier can be set for the
352202375Srdivacky * mount point.
353202375Srdivacky *
354202375Srdivacky * B_CLUSTEROK must be cleared along with B_NEEDCOMMIT because stage 1 data
355202375Srdivacky * writes are not clusterable.
356202375Srdivacky */
357202375Srdivackyvoid
358249423Sdimncl_clearcommit(struct mount *mp)
359202375Srdivacky{
360202375Srdivacky	struct vnode *vp, *nvp;
361202375Srdivacky	struct buf *bp, *nbp;
362202375Srdivacky	struct bufobj *bo;
363202375Srdivacky
364202375Srdivacky	MNT_ILOCK(mp);
365202375Srdivacky	MNT_VNODE_FOREACH(vp, mp, nvp) {
366202375Srdivacky		bo = &vp->v_bufobj;
367249423Sdim		VI_LOCK(vp);
368202375Srdivacky		if (vp->v_iflag & VI_DOOMED) {
369202375Srdivacky			VI_UNLOCK(vp);
370202375Srdivacky			continue;
371202375Srdivacky		}
372202375Srdivacky		vholdl(vp);
373202375Srdivacky		VI_UNLOCK(vp);
374202375Srdivacky		MNT_IUNLOCK(mp);
375202375Srdivacky		BO_LOCK(bo);
376202375Srdivacky		TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
377202375Srdivacky			if (!BUF_ISLOCKED(bp) &&
378202375Srdivacky			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
379202375Srdivacky				== (B_DELWRI | B_NEEDCOMMIT))
380202375Srdivacky				bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
381249423Sdim		}
382202375Srdivacky		BO_UNLOCK(bo);
383202375Srdivacky		vdrop(vp);
384202375Srdivacky		MNT_ILOCK(mp);
385202375Srdivacky	}
386202375Srdivacky	MNT_IUNLOCK(mp);
387202375Srdivacky}
388202375Srdivacky
389202375Srdivacky/*
390202375Srdivacky * Called once to initialize data structures...
391202375Srdivacky */
392202375Srdivackyint
393202375Srdivackyncl_init(struct vfsconf *vfsp)
394202375Srdivacky{
395202375Srdivacky	int i;
396202375Srdivacky
397202375Srdivacky	/* Ensure async daemons disabled */
398202375Srdivacky	for (i = 0; i < NFS_MAXRAHEAD; i++) {
399202375Srdivacky		ncl_iodwant[i] = NFSIOD_NOT_AVAILABLE;
400202375Srdivacky		ncl_iodmount[i] = NULL;
401202375Srdivacky	}
402249423Sdim	ncl_nhinit();			/* Init the nfsnode table */
403202375Srdivacky
404202375Srdivacky	return (0);
405202375Srdivacky}
406202375Srdivacky
407202375Srdivacky