/*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License"). You may not use this file except in compliance with the
+ * License. Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
*
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
-#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
extern int nfs_mountroot __P((void));
extern int nfs_ticks;
+extern int nfs_mount_type;
+extern int nfs_resv_mounts;
struct nfsstats nfsstats;
static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
#endif
#endif
+SYSCTL_DECL(_vfs_generic_nfs);
+SYSCTL_NODE(_vfs_generic_nfs, OID_AUTO, client, CTLFLAG_RW, 0,
+ "nfs client hinge");
+/* how long NFS will wait before signalling vfs that it's down. */
+static int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
+SYSCTL_INT(_vfs_generic_nfs_client, NFS_TPRINTF_INITIAL_DELAY,
+ initialdowndelay, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
+/* how long between console messages "nfs server foo not responding" */
+static int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
+SYSCTL_INT(_vfs_generic_nfs_client, NFS_TPRINTF_DELAY,
+ nextdowndelay, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
+
static int nfs_iosize __P((struct nfsmount *nmp));
static int mountnfs __P((struct nfs_args *,struct mount *,
struct mbuf *,char *,char *,struct vnode **));
static int nfs_fhtovp __P((struct mount *mp, struct fid *fhp,
struct mbuf *nam, struct vnode **vpp,
int *exflagsp, struct ucred **credanonp));
-static int nfs_vget __P((struct mount *, ino_t, struct vnode **));
+static int nfs_vget __P((struct mount *, void *, struct vnode **));
/*
* Calculate the size used for io buffers. Use the larger
* of the two sizes to minimise nfs requests but make sure
* that it is at least one VM page to avoid wasting buffer
- * space.
+ * space and to allow easy mmapping of I/O buffers.
+ * The read/write rpc calls handle the splitting up of
+ * buffers into multiple requests if the buffer size is
+ * larger than the I/O size.
*/
iosize = max(nmp->nm_rsize, nmp->nm_wsize);
if (iosize < PAGE_SIZE)
iosize = PAGE_SIZE;
- return (trunc_page(iosize));
+ return (trunc_page_32(iosize));
}
static void nfs_convert_oargs(args,oargs)
int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct ucred *cred;
- u_quad_t tquad;
extern int nfs_mount_type;
u_int64_t xid;
return(error);
cred = crget();
cred->cr_ngroups = 1;
- if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
+ if (v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0)
(void)nfs_fsinfo(nmp, vp, cred, p);
nfsstats.rpccnt[NFSPROC_FSSTAT]++;
nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3));
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_FSSTAT, p, cred, &xid);
- if (v3)
+ if (v3 && mrep)
nfsm_postop_attr(vp, retattr, &xid);
nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
sbp->f_flags = nmp->nm_flag;
sbp->f_iosize = nfs_iosize(nmp);
if (v3) {
- sbp->f_bsize = NFS_FABLKSIZE;
+ /*
+ * Adjust block size to get total block count to fit in a long.
+ * If we can't increase block size enough, clamp to max long.
+ */
+ u_quad_t tquad, tquad2, bsize;
+ bsize = NFS_FABLKSIZE;
+
fxdr_hyper(&sfp->sf_tbytes, &tquad);
- sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
+ tquad /= bsize;
+ while ((tquad & ~0x7fffffff) && (bsize < 0x40000000)) {
+ bsize <<= 1;
+ tquad >>= 1;
+ }
+ sbp->f_blocks = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
+
fxdr_hyper(&sfp->sf_fbytes, &tquad);
- sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
+ tquad /= bsize;
+ sbp->f_bfree = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
+
fxdr_hyper(&sfp->sf_abytes, &tquad);
- sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
- sbp->f_files = (fxdr_unsigned(long, sfp->sf_tfiles.nfsuquad[1])
- & 0x7fffffff);
- sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1])
- & 0x7fffffff);
+ tquad /= bsize;
+ sbp->f_bavail = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
+
+ sbp->f_bsize = (long)bsize;
+
+ /* adjust file slots too... */
+ fxdr_hyper(&sfp->sf_tfiles, &tquad);
+ fxdr_hyper(&sfp->sf_ffiles, &tquad2);
+ while (tquad & ~0x7fffffff) {
+ tquad >>= 1;
+ tquad2 >>= 1;
+ }
+ sbp->f_files = tquad;
+ sbp->f_ffree = tquad2;
} else {
sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1));
nfsm_fhtom(vp, 1);
nfsm_request(vp, NFSPROC_FSINFO, p, cred, &xid);
- nfsm_postop_attr(vp, retattr, &xid);
+ if (mrep) {
+ nfsm_postop_attr(vp, retattr, &xid);
+ }
if (!error) {
nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
pref = fxdr_unsigned(u_long, fsp->fs_wtpref);
if (max < nmp->nm_readdirsize) {
nmp->nm_readdirsize = max;
}
- nmp->nm_flag |= NFSMNT_GOTFSINFO;
+ nmp->nm_state |= NFSSTA_GOTFSINFO;
}
nfsm_reqdone;
return (error);
tryagain:
error = nfs_boot_getfh(&nd, procp, v3);
if (error) {
+ if (error == EHOSTDOWN || error == EHOSTUNREACH) {
+ if (nd.nd_root.ndm_path)
+ FREE_ZONE(nd.nd_root.ndm_path,
+ MAXPATHLEN, M_NAMEI);
+ if (nd.nd_private.ndm_path)
+ FREE_ZONE(nd.nd_private.ndm_path,
+ MAXPATHLEN, M_NAMEI);
+ return (error);
+ }
if (v3) {
printf("nfs_boot_getfh(v3) failed with %d, trying v2...\n", error);
v3 = 0;
if ((error = mountnfs(&args, mp, m, mntname, args.hostname, vpp))) {
printf("nfs_mountroot: mount %s failed: %d", mntname, error);
mp->mnt_vfc->vfc_refcount--;
+
+ if (mp->mnt_kern_flag & MNTK_IO_XINFO)
+ FREE(mp->mnt_xinfo_ptr, M_TEMP);
vfs_unbusy(mp, procp);
- _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
+
+ FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
return (error);
}
#if 0 /* Causes incorrect reporting of "mounted on" */
mp = _MALLOC_ZONE((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
bzero((char *)mp, (u_long)sizeof(struct mount));
- /* Initialize the default IO constraints */
- mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
- mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
+ /* Initialize the default IO constraints */
+ mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
+ mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0);
(void)vfs_busy(mp, LK_NOWAIT, 0, procp);
if ((error = mountnfs(&args, mp, m, mntname, args.hostname, &vp))) {
printf("nfs_mountroot: mount %s failed: %d", mntname, error);
mp->mnt_vfc->vfc_refcount--;
+
+ if (mp->mnt_kern_flag & MNTK_IO_XINFO)
+ FREE(mp->mnt_xinfo_ptr, M_TEMP);
vfs_unbusy(mp, procp);
- _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
+
+ FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
return (error);
}
struct mbuf *nam;
struct vnode *vp;
char pth[MNAMELEN], hst[MNAMELEN];
- u_int len;
+ size_t len;
u_char nfh[NFSX_V3FHMAX];
error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
return (EPROGMISMATCH);
#endif /* !NO_COMPAT_PRELITE2 */
}
- if (args.fhsize > NFSX_V3FHMAX)
+ if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX)
return (EINVAL);
error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
if (error)
error = NFSERR_NOTSUPP;
goto bad2;
}
+
+ /*
+ * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
+ * no sense in that context.
+ */
+ if (argp->sotype == SOCK_STREAM)
+ argp->flags &= ~NFSMNT_NOCONN;
if (mp->mnt_flag & MNT_UPDATE) {
nmp = VFSTONFS(mp);
mp->mnt_maxsymlinklen = 1;
nmp->nm_timeo = NFS_TIMEO;
nmp->nm_retry = NFS_RETRANS;
- nmp->nm_wsize = NFS_WSIZE;
- nmp->nm_rsize = NFS_RSIZE;
+ if (argp->sotype == SOCK_DGRAM) {
+ nmp->nm_wsize = NFS_DGRAM_WSIZE;
+ nmp->nm_rsize = NFS_DGRAM_RSIZE;
+ } else {
+ nmp->nm_wsize = NFS_WSIZE;
+ nmp->nm_rsize = NFS_RSIZE;
+ }
nmp->nm_readdirsize = NFS_READDIRSIZE;
nmp->nm_numgrps = NFS_MAXGRPS;
nmp->nm_readahead = NFS_DEFRAHEAD;
nmp->nm_leaseterm = NQ_DEFLEASE;
nmp->nm_deadthresh = NQ_DEADTHRESH;
+ nmp->nm_tprintf_delay = nfs_tprintf_delay;
+ if (nmp->nm_tprintf_delay < 0)
+ nmp->nm_tprintf_delay = 0;
+ nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
+ if (nmp->nm_tprintf_initial_delay < 0)
+ nmp->nm_tprintf_initial_delay = 0;
CIRCLEQ_INIT(&nmp->nm_timerhead);
nmp->nm_inprog = NULLVP;
bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
nmp->nm_nam = nam;
- /*
- * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
- * no sense in that context.
- */
- if (argp->sotype == SOCK_STREAM)
- argp->flags &= ~NFSMNT_NOCONN;
-
if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
if (nmp->nm_timeo < NFS_MINTIMEO)
(error = nfs_connect(nmp, (struct nfsreq *)0)))
goto bad;
- /*
- * This is silly, but it has to be set so that vinifod() works.
- * We do not want to do an nfs_statfs() here since we can get
- * stuck on a dead server and we are holding a lock on the mount
- * point.
- */
- mp->mnt_stat.f_iosize = nfs_iosize(nmp);
/*
* A reference count is needed on the nfsnode representing the
* remote root. If this object is not persistent, then backward
* effect of filling in (*vpp)->v_type with the correct value.
*/
curproc = current_proc();
- VOP_GETATTR(*vpp, &attrs, curproc->p_ucred, curproc);
+ error = VOP_GETATTR(*vpp, &attrs, curproc->p_ucred, curproc);
+ if (error) {
+ /*
+ * we got problems... we couldn't get the attributes
+ * from the NFS server... so the mount fails.
+ */
+ vput(*vpp);
+ goto bad;
+ }
+
+ /*
+ * Set the mount point's block I/O size.
+ * We really need to do this after we get info back from
+ * the server about what its preferred I/O sizes are.
+ */
+ if (nmp->nm_flag & NFSMNT_NFSV3)
+ nfs_fsinfo(nmp, *vpp, curproc->p_ucred, curproc);
+ mp->mnt_stat.f_iosize = nfs_iosize(nmp);
/*
* Lose the lock but keep the ref.
*/
VOP_UNLOCK(*vpp, 0, curproc);
+ if (nmp->nm_flag & NFSMNT_RESVPORT)
+ nfs_resv_mounts++;
+ nmp->nm_state |= NFSSTA_MOUNTED;
return (0);
bad:
nfs_disconnect(nmp);
- _FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
+ FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
bad2:
m_freem(nam);
return (error);
struct vnode *vp;
int error, flags = 0;
- if (mntflags & MNT_FORCE)
- flags |= FORCECLOSE;
nmp = VFSTONFS(mp);
+ /*
+ * During a force unmount we want to...
+ * Mark that we are doing a force unmount.
+ * Make the mountpoint soft.
+ */
+ if (mntflags & MNT_FORCE) {
+ flags |= FORCECLOSE;
+ nmp->nm_state |= NFSSTA_FORCE;
+ nmp->nm_flag |= NFSMNT_SOFT;
+ }
/*
* Goes something like this..
* - Call vflush() to clear out vnodes for this file system,
/*
* Must handshake with nqnfs_clientd() if it is active.
*/
- nmp->nm_flag |= NFSMNT_DISMINPROG;
+ nmp->nm_state |= NFSSTA_DISMINPROG;
while (nmp->nm_inprog != NULLVP)
(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
/*
* not get EBUSY back.
*/
error = vflush(mp, vp, SKIPSWAP | flags);
- if (mntflags & MNT_FORCE)
+ if (mntflags & MNT_FORCE) {
error = vflush(mp, NULLVP, flags); /* locks vp in the process */
- else {
+ } else {
if (vp->v_usecount > 1) {
- nmp->nm_flag &= ~NFSMNT_DISMINPROG;
+ nmp->nm_state &= ~NFSSTA_DISMINPROG;
return (EBUSY);
}
error = vflush(mp, vp, flags);
}
if (error) {
- nmp->nm_flag &= ~NFSMNT_DISMINPROG;
+ nmp->nm_state &= ~NFSSTA_DISMINPROG;
return (error);
}
* For NQNFS, let the server daemon free the nfsmount structure.
*/
if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
- nmp->nm_flag |= NFSMNT_DISMNT;
+ nmp->nm_state |= NFSSTA_DISMNT;
+ nmp->nm_state &= ~NFSSTA_MOUNTED;
+ if (nmp->nm_flag & NFSMNT_RESVPORT) {
+ if (--nfs_resv_mounts == 0)
+ nfs_bind_resv_thread_wake();
+ }
/*
* Release the root vnode reference held by mountnfs()
if (hw_atomic_sub(&nfsreqqusers, 1) != 0)
nfsatompanic("unmount sub");
#endif
- _FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
+ /* Need to wake up any rcvlock waiters so they notice the unmount. */
+ if (nmp->nm_state & NFSSTA_WANTRCV) {
+ nmp->nm_state &= ~NFSSTA_WANTRCV;
+ wakeup(&nmp->nm_state);
+ }
+ FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
}
return (0);
}
{
register struct vnode *vp;
struct nfsmount *nmp;
- int error;
+ int error, vpid;
nmp = VFSTONFS(mp);
vp = nmp->nm_dvp;
- error = vget(vp, LK_EXCLUSIVE, current_proc());
- if (error)
- return (error);
+ vpid = vp->v_id;
+ while (error = vget(vp, LK_EXCLUSIVE, current_proc())) {
+ /* vget may return ENOENT if the dir changes while in vget */
+ /* If that happens, try vget again, else return the error */
+ if ((error != ENOENT) || (vp->v_id == vpid))
+ return (error);
+ vpid = vp->v_id;
+ }
if (vp->v_type == VNON)
vp->v_type = VDIR;
vp->v_flag |= VROOT;
* Force stale buffer cache information to be flushed.
*/
loop:
- for (vp = mp->mnt_vnodelist.lh_first;
- vp != NULL;
- vp = vp->v_mntvnodes.le_next) {
- int didhold = 0;
+ LIST_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
+ int didhold;
/*
* If the vnode that we are about to sync is no longer
* associated with this mount point, start over.
*/
if (vp->v_mount != mp)
goto loop;
- if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
+ if (VOP_ISLOCKED(vp) || LIST_FIRST(&VTONFS(vp)->n_dirtyblkhd) == NULL)
continue;
if (vget(vp, LK_EXCLUSIVE, p))
goto loop;
static int
nfs_vget(mp, ino, vpp)
struct mount *mp;
- ino_t ino;
+ void *ino; /* XXX void* or ino_t? */
struct vnode **vpp;
{
nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
- int rv;
+ int error = 0, val;
+ struct sysctl_req *req;
+ struct vfsidctl vc;
+ struct mount *mp;
+ struct nfsmount *nmp;
+ struct vfsquery vq;
/*
* All names at this level are terminal.
if(namelen > 1)
return ENOTDIR; /* overloaded */
+ /* common code for "new style" VFS_CTL sysctl, get the mount. */
+ switch (name[0]) {
+ case VFS_CTL_TIMEO:
+ case VFS_CTL_QUERY:
+ case VFS_CTL_NOLOCKS:
+ req = oldp;
+ error = SYSCTL_IN(req, &vc, sizeof(vc));
+ if (error)
+ return (error);
+ mp = vfs_getvfs(&vc.vc_fsid);
+ if (mp == NULL)
+ return (ENOENT);
+ nmp = VFSTONFS(mp);
+ if (nmp == NULL)
+ return (ENOENT);
+ bzero(&vq, sizeof(vq));
+ VCTLTOREQ(&vc, req);
+ }
+
switch(name[0]) {
case NFS_NFSSTATS:
if(!oldp) {
return ENOMEM;
}
- rv = copyout(&nfsstats, oldp, sizeof nfsstats);
- if(rv) return rv;
+ error = copyout(&nfsstats, oldp, sizeof nfsstats);
+ if (error)
+ return (error);
if(newp && newlen != sizeof nfsstats)
return EINVAL;
return copyin(newp, &nfsstats, sizeof nfsstats);
}
return 0;
-
+ case VFS_CTL_NOLOCKS:
+ val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
+ if (req->oldptr != NULL) {
+ error = SYSCTL_OUT(req, &val, sizeof(val));
+ if (error)
+ return (error);
+ }
+ if (req->newptr != NULL) {
+ error = SYSCTL_IN(req, &val, sizeof(val));
+ if (error)
+ return (error);
+ if (val)
+ nmp->nm_flag |= NFSMNT_NOLOCKS;
+ else
+ nmp->nm_flag &= ~NFSMNT_NOLOCKS;
+ }
+ break;
+ case VFS_CTL_QUERY:
+ if ((nmp->nm_state & NFSSTA_TIMEO))
+ vq.vq_flags |= VQ_NOTRESP;
+ if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
+ (nmp->nm_state & NFSSTA_LOCKTIMEO))
+ vq.vq_flags |= VQ_NOTRESPLOCK;
+ error = SYSCTL_OUT(req, &vq, sizeof(vq));
+ break;
+ case VFS_CTL_TIMEO:
+ if (req->oldptr != NULL) {
+ error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
+ sizeof(nmp->nm_tprintf_initial_delay));
+ if (error)
+ return (error);
+ }
+ if (req->newptr != NULL) {
+ error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
+ sizeof(nmp->nm_tprintf_initial_delay));
+ if (error)
+ return (error);
+ if (nmp->nm_tprintf_initial_delay < 0)
+ nmp->nm_tprintf_initial_delay = 0;
+ }
+ break;
default:
- return EOPNOTSUPP;
+ return (ENOTSUP);
}
+ return (error);
}