nfs_clsubs.c revision 191783
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem *	from nfs_subs.c  8.8 (Berkeley) 5/22/95
33191783Srmacklem */
34191783Srmacklem
35191783Srmacklem#include <sys/cdefs.h>
36191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clsubs.c 191783 2009-05-04 15:23:58Z rmacklem $");
37191783Srmacklem
38191783Srmacklem/*
39191783Srmacklem * These functions support the macros and help fiddle mbuf chains for
40191783Srmacklem * the nfs op functions. They do things like create the rpc header and
41191783Srmacklem * copy data between mbuf chains and uio lists.
42191783Srmacklem */
43191783Srmacklem
44191783Srmacklem#include <sys/param.h>
45191783Srmacklem#include <sys/systm.h>
46191783Srmacklem#include <sys/kernel.h>
47191783Srmacklem#include <sys/bio.h>
48191783Srmacklem#include <sys/buf.h>
49191783Srmacklem#include <sys/proc.h>
50191783Srmacklem#include <sys/mount.h>
51191783Srmacklem#include <sys/vnode.h>
52191783Srmacklem#include <sys/namei.h>
53191783Srmacklem#include <sys/mbuf.h>
54191783Srmacklem#include <sys/socket.h>
55191783Srmacklem#include <sys/stat.h>
56191783Srmacklem#include <sys/malloc.h>
57191783Srmacklem#include <sys/sysent.h>
58191783Srmacklem#include <sys/syscall.h>
59191783Srmacklem#include <sys/sysproto.h>
60191783Srmacklem
61191783Srmacklem#include <vm/vm.h>
62191783Srmacklem#include <vm/vm_object.h>
63191783Srmacklem#include <vm/vm_extern.h>
64191783Srmacklem#include <vm/uma.h>
65191783Srmacklem
66191783Srmacklem#include <fs/nfs/nfsport.h>
67191783Srmacklem#include <fs/nfsclient/nfsnode.h>
68191783Srmacklem#include <fs/nfsclient/nfsmount.h>
69191783Srmacklem#include <fs/nfsclient/nfs.h>
70191783Srmacklem#include <fs/nfsclient/nfs_lock.h>
71191783Srmacklem
72191783Srmacklem#include <netinet/in.h>
73191783Srmacklem
74191783Srmacklem/*
75191783Srmacklem * Note that stdarg.h and the ANSI style va_start macro is used for both
76191783Srmacklem * ANSI and traditional C compilers.
77191783Srmacklem */
78191783Srmacklem#include <machine/stdarg.h>
79191783Srmacklem
80191783Srmacklemextern struct mtx ncl_iod_mutex;
81191783Srmacklemextern struct proc *ncl_iodwant[NFS_MAXRAHEAD];
82191783Srmacklemextern struct nfsmount *ncl_iodmount[NFS_MAXRAHEAD];
83191783Srmacklemextern int ncl_numasync;
84191783Srmacklemextern unsigned int ncl_iodmax;
85191783Srmacklemextern struct nfsstats newnfsstats;
86191783Srmacklem
87191783Srmacklemint
88191783Srmacklemncl_uninit(struct vfsconf *vfsp)
89191783Srmacklem{
90191783Srmacklem	int i;
91191783Srmacklem
92191783Srmacklem	/*
93191783Srmacklem	 * Tell all nfsiod processes to exit. Clear ncl_iodmax, and wakeup
94191783Srmacklem	 * any sleeping nfsiods so they check ncl_iodmax and exit.
95191783Srmacklem	 */
96191783Srmacklem	mtx_lock(&ncl_iod_mutex);
97191783Srmacklem	ncl_iodmax = 0;
98191783Srmacklem	for (i = 0; i < ncl_numasync; i++)
99191783Srmacklem		if (ncl_iodwant[i])
100191783Srmacklem			wakeup(&ncl_iodwant[i]);
101191783Srmacklem	/* The last nfsiod to exit will wake us up when ncl_numasync hits 0 */
102191783Srmacklem	while (ncl_numasync)
103191783Srmacklem		msleep(&ncl_numasync, &ncl_iod_mutex, PWAIT, "ioddie", 0);
104191783Srmacklem	mtx_unlock(&ncl_iod_mutex);
105191783Srmacklem	ncl_nhuninit();
106191783Srmacklem	return (0);
107191783Srmacklem}
108191783Srmacklem
109191783Srmacklemvoid
110191783Srmacklemncl_dircookie_lock(struct nfsnode *np)
111191783Srmacklem{
112191783Srmacklem	mtx_lock(&np->n_mtx);
113191783Srmacklem	while (np->n_flag & NDIRCOOKIELK)
114191783Srmacklem		(void) msleep(&np->n_flag, &np->n_mtx, PZERO, "nfsdirlk", 0);
115191783Srmacklem	np->n_flag |= NDIRCOOKIELK;
116191783Srmacklem	mtx_unlock(&np->n_mtx);
117191783Srmacklem}
118191783Srmacklem
119191783Srmacklemvoid
120191783Srmacklemncl_dircookie_unlock(struct nfsnode *np)
121191783Srmacklem{
122191783Srmacklem	mtx_lock(&np->n_mtx);
123191783Srmacklem	np->n_flag &= ~NDIRCOOKIELK;
124191783Srmacklem	wakeup(&np->n_flag);
125191783Srmacklem	mtx_unlock(&np->n_mtx);
126191783Srmacklem}
127191783Srmacklem
128191783Srmacklemint
129191783Srmacklemncl_upgrade_vnlock(struct vnode *vp)
130191783Srmacklem{
131191783Srmacklem	int old_lock;
132191783Srmacklem
133191783Srmacklem 	if ((old_lock = VOP_ISLOCKED(vp)) != LK_EXCLUSIVE) {
134191783Srmacklem 		if (old_lock == LK_SHARED) {
135191783Srmacklem 			/* Upgrade to exclusive lock, this might block */
136191783Srmacklem 			vn_lock(vp, LK_UPGRADE | LK_RETRY);
137191783Srmacklem 		} else {
138191783Srmacklem 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
139191783Srmacklem 		}
140191783Srmacklem  	}
141191783Srmacklem	return old_lock;
142191783Srmacklem}
143191783Srmacklem
144191783Srmacklemvoid
145191783Srmacklemncl_downgrade_vnlock(struct vnode *vp, int old_lock)
146191783Srmacklem{
147191783Srmacklem	if (old_lock != LK_EXCLUSIVE) {
148191783Srmacklem 		if (old_lock == LK_SHARED) {
149191783Srmacklem 			/* Downgrade from exclusive lock, this might block */
150191783Srmacklem 			vn_lock(vp, LK_DOWNGRADE);
151191783Srmacklem 		} else {
152191783Srmacklem 			VOP_UNLOCK(vp, 0);
153191783Srmacklem 		}
154191783Srmacklem  	}
155191783Srmacklem}
156191783Srmacklem
157191783Srmacklemvoid
158191783Srmacklemncl_printf(const char *fmt, ...)
159191783Srmacklem{
160191783Srmacklem	va_list ap;
161191783Srmacklem
162191783Srmacklem	mtx_lock(&Giant);
163191783Srmacklem	va_start(ap, fmt);
164191783Srmacklem	printf(fmt, ap);
165191783Srmacklem	va_end(ap);
166191783Srmacklem	mtx_unlock(&Giant);
167191783Srmacklem}
168191783Srmacklem
169191783Srmacklem#ifdef NFS_ACDEBUG
170191783Srmacklem#include <sys/sysctl.h>
171191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
172191783Srmacklemstatic int nfs_acdebug;
173191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
174191783Srmacklem#endif
175191783Srmacklem
176191783Srmacklem/*
177191783Srmacklem * Check the time stamp
178191783Srmacklem * If the cache is valid, copy contents to *vap and return 0
179191783Srmacklem * otherwise return an error
180191783Srmacklem */
181191783Srmacklemint
182191783Srmacklemncl_getattrcache(struct vnode *vp, struct vattr *vaper)
183191783Srmacklem{
184191783Srmacklem	struct nfsnode *np;
185191783Srmacklem	struct vattr *vap;
186191783Srmacklem	struct nfsmount *nmp;
187191783Srmacklem	int timeo;
188191783Srmacklem
189191783Srmacklem	np = VTONFS(vp);
190191783Srmacklem	vap = &np->n_vattr.na_vattr;
191191783Srmacklem	nmp = VFSTONFS(vp->v_mount);
192191783Srmacklem#ifdef NFS_ACDEBUG
193191783Srmacklem	mtx_lock(&Giant);	/* ncl_printf() */
194191783Srmacklem#endif
195191783Srmacklem	mtx_lock(&np->n_mtx);
196191783Srmacklem	/* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
197191783Srmacklem	timeo = (time_second - np->n_mtime.tv_sec) / 10;
198191783Srmacklem
199191783Srmacklem#ifdef NFS_ACDEBUG
200191783Srmacklem	if (nfs_acdebug>1)
201191783Srmacklem		ncl_printf("nfs_getattrcache: initial timeo = %d\n", timeo);
202191783Srmacklem#endif
203191783Srmacklem
204191783Srmacklem	if (vap->va_type == VDIR) {
205191783Srmacklem		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
206191783Srmacklem			timeo = nmp->nm_acdirmin;
207191783Srmacklem		else if (timeo > nmp->nm_acdirmax)
208191783Srmacklem			timeo = nmp->nm_acdirmax;
209191783Srmacklem	} else {
210191783Srmacklem		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
211191783Srmacklem			timeo = nmp->nm_acregmin;
212191783Srmacklem		else if (timeo > nmp->nm_acregmax)
213191783Srmacklem			timeo = nmp->nm_acregmax;
214191783Srmacklem	}
215191783Srmacklem
216191783Srmacklem#ifdef NFS_ACDEBUG
217191783Srmacklem	if (nfs_acdebug > 2)
218191783Srmacklem		ncl_printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
219191783Srmacklem			   nmp->nm_acregmin, nmp->nm_acregmax,
220191783Srmacklem			   nmp->nm_acdirmin, nmp->nm_acdirmax);
221191783Srmacklem
222191783Srmacklem	if (nfs_acdebug)
223191783Srmacklem		ncl_printf("nfs_getattrcache: age = %d; final timeo = %d\n",
224191783Srmacklem			   (time_second - np->n_attrstamp), timeo);
225191783Srmacklem#endif
226191783Srmacklem
227191783Srmacklem	if ((time_second - np->n_attrstamp) >= timeo) {
228191783Srmacklem		newnfsstats.attrcache_misses++;
229191783Srmacklem		mtx_unlock(&np->n_mtx);
230191783Srmacklem		return( ENOENT);
231191783Srmacklem	}
232191783Srmacklem	newnfsstats.attrcache_hits++;
233191783Srmacklem	if (vap->va_size != np->n_size) {
234191783Srmacklem		if (vap->va_type == VREG) {
235191783Srmacklem			if (np->n_flag & NMODIFIED) {
236191783Srmacklem				if (vap->va_size < np->n_size)
237191783Srmacklem					vap->va_size = np->n_size;
238191783Srmacklem				else
239191783Srmacklem					np->n_size = vap->va_size;
240191783Srmacklem			} else {
241191783Srmacklem				np->n_size = vap->va_size;
242191783Srmacklem			}
243191783Srmacklem			vnode_pager_setsize(vp, np->n_size);
244191783Srmacklem		} else {
245191783Srmacklem			np->n_size = vap->va_size;
246191783Srmacklem		}
247191783Srmacklem	}
248191783Srmacklem	bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
249191783Srmacklem	if (np->n_flag & NCHG) {
250191783Srmacklem		if (np->n_flag & NACC)
251191783Srmacklem			vaper->va_atime = np->n_atim;
252191783Srmacklem		if (np->n_flag & NUPD)
253191783Srmacklem			vaper->va_mtime = np->n_mtim;
254191783Srmacklem	}
255191783Srmacklem	mtx_unlock(&np->n_mtx);
256191783Srmacklem#ifdef NFS_ACDEBUG
257191783Srmacklem	mtx_unlock(&Giant);	/* ncl_printf() */
258191783Srmacklem#endif
259191783Srmacklem	return (0);
260191783Srmacklem}
261191783Srmacklem
262191783Srmacklemstatic nfsuint64 nfs_nullcookie = { { 0, 0 } };
263191783Srmacklem/*
264191783Srmacklem * This function finds the directory cookie that corresponds to the
265191783Srmacklem * logical byte offset given.
266191783Srmacklem */
267191783Srmacklemnfsuint64 *
268191783Srmacklemncl_getcookie(struct nfsnode *np, off_t off, int add)
269191783Srmacklem{
270191783Srmacklem	struct nfsdmap *dp, *dp2;
271191783Srmacklem	int pos;
272191783Srmacklem	nfsuint64 *retval = NULL;
273191783Srmacklem
274191783Srmacklem	pos = (uoff_t)off / NFS_DIRBLKSIZ;
275191783Srmacklem	if (pos == 0 || off < 0) {
276191783Srmacklem#ifdef DIAGNOSTIC
277191783Srmacklem		if (add)
278191783Srmacklem			panic("nfs getcookie add at <= 0");
279191783Srmacklem#endif
280191783Srmacklem		return (&nfs_nullcookie);
281191783Srmacklem	}
282191783Srmacklem	pos--;
283191783Srmacklem	dp = LIST_FIRST(&np->n_cookies);
284191783Srmacklem	if (!dp) {
285191783Srmacklem		if (add) {
286191783Srmacklem			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
287191783Srmacklem				M_NFSDIROFF, M_WAITOK);
288191783Srmacklem			dp->ndm_eocookie = 0;
289191783Srmacklem			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
290191783Srmacklem		} else
291191783Srmacklem			goto out;
292191783Srmacklem	}
293191783Srmacklem	while (pos >= NFSNUMCOOKIES) {
294191783Srmacklem		pos -= NFSNUMCOOKIES;
295191783Srmacklem		if (LIST_NEXT(dp, ndm_list)) {
296191783Srmacklem			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
297191783Srmacklem			    pos >= dp->ndm_eocookie)
298191783Srmacklem				goto out;
299191783Srmacklem			dp = LIST_NEXT(dp, ndm_list);
300191783Srmacklem		} else if (add) {
301191783Srmacklem			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
302191783Srmacklem				M_NFSDIROFF, M_WAITOK);
303191783Srmacklem			dp2->ndm_eocookie = 0;
304191783Srmacklem			LIST_INSERT_AFTER(dp, dp2, ndm_list);
305191783Srmacklem			dp = dp2;
306191783Srmacklem		} else
307191783Srmacklem			goto out;
308191783Srmacklem	}
309191783Srmacklem	if (pos >= dp->ndm_eocookie) {
310191783Srmacklem		if (add)
311191783Srmacklem			dp->ndm_eocookie = pos + 1;
312191783Srmacklem		else
313191783Srmacklem			goto out;
314191783Srmacklem	}
315191783Srmacklem	retval = &dp->ndm_cookies[pos];
316191783Srmacklemout:
317191783Srmacklem	return (retval);
318191783Srmacklem}
319191783Srmacklem
320191783Srmacklem/*
321191783Srmacklem * Invalidate cached directory information, except for the actual directory
322191783Srmacklem * blocks (which are invalidated separately).
323191783Srmacklem * Done mainly to avoid the use of stale offset cookies.
324191783Srmacklem */
325191783Srmacklemvoid
326191783Srmacklemncl_invaldir(struct vnode *vp)
327191783Srmacklem{
328191783Srmacklem	struct nfsnode *np = VTONFS(vp);
329191783Srmacklem
330191783Srmacklem#ifdef DIAGNOSTIC
331191783Srmacklem	if (vp->v_type != VDIR)
332191783Srmacklem		panic("nfs: invaldir not dir");
333191783Srmacklem#endif
334191783Srmacklem	ncl_dircookie_lock(np);
335191783Srmacklem	np->n_direofoffset = 0;
336191783Srmacklem	np->n_cookieverf.nfsuquad[0] = 0;
337191783Srmacklem	np->n_cookieverf.nfsuquad[1] = 0;
338191783Srmacklem	if (LIST_FIRST(&np->n_cookies))
339191783Srmacklem		LIST_FIRST(&np->n_cookies)->ndm_eocookie = 0;
340191783Srmacklem	ncl_dircookie_unlock(np);
341191783Srmacklem}
342191783Srmacklem
343191783Srmacklem/*
344191783Srmacklem * The write verifier has changed (probably due to a server reboot), so all
345191783Srmacklem * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
346191783Srmacklem * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
347191783Srmacklem * and B_CLUSTEROK flags.  Once done the new write verifier can be set for the
348191783Srmacklem * mount point.
349191783Srmacklem *
350191783Srmacklem * B_CLUSTEROK must be cleared along with B_NEEDCOMMIT because stage 1 data
351191783Srmacklem * writes are not clusterable.
352191783Srmacklem */
353191783Srmacklemvoid
354191783Srmacklemncl_clearcommit(struct mount *mp)
355191783Srmacklem{
356191783Srmacklem	struct vnode *vp, *nvp;
357191783Srmacklem	struct buf *bp, *nbp;
358191783Srmacklem	struct bufobj *bo;
359191783Srmacklem
360191783Srmacklem	MNT_ILOCK(mp);
361191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, nvp) {
362191783Srmacklem		bo = &vp->v_bufobj;
363191783Srmacklem		VI_LOCK(vp);
364191783Srmacklem		if (vp->v_iflag & VI_DOOMED) {
365191783Srmacklem			VI_UNLOCK(vp);
366191783Srmacklem			continue;
367191783Srmacklem		}
368191783Srmacklem		vholdl(vp);
369191783Srmacklem		VI_UNLOCK(vp);
370191783Srmacklem		MNT_IUNLOCK(mp);
371191783Srmacklem		BO_LOCK(bo);
372191783Srmacklem		TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
373191783Srmacklem			if (!BUF_ISLOCKED(bp) &&
374191783Srmacklem			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
375191783Srmacklem				== (B_DELWRI | B_NEEDCOMMIT))
376191783Srmacklem				bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
377191783Srmacklem		}
378191783Srmacklem		BO_UNLOCK(bo);
379191783Srmacklem		vdrop(vp);
380191783Srmacklem		MNT_ILOCK(mp);
381191783Srmacklem	}
382191783Srmacklem	MNT_IUNLOCK(mp);
383191783Srmacklem}
384191783Srmacklem
385191783Srmacklem/*
386191783Srmacklem * Called once to initialize data structures...
387191783Srmacklem */
388191783Srmacklemint
389191783Srmacklemncl_init(struct vfsconf *vfsp)
390191783Srmacklem{
391191783Srmacklem	int i;
392191783Srmacklem
393191783Srmacklem	/* Ensure async daemons disabled */
394191783Srmacklem	for (i = 0; i < NFS_MAXRAHEAD; i++) {
395191783Srmacklem		ncl_iodwant[i] = NULL;
396191783Srmacklem		ncl_iodmount[i] = NULL;
397191783Srmacklem	}
398191783Srmacklem	ncl_nhinit();			/* Init the nfsnode table */
399191783Srmacklem
400191783Srmacklem	return (0);
401191783Srmacklem}
402191783Srmacklem
403