/*
- * Copyright (c) 2000-2003 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/time.h>
#include <kern/clock.h>
+#include <kern/task.h>
+#include <kern/thread.h>
#include <sys/user.h>
#include <netinet/in.h>
struct nfsrtt nfsrtt;
static int nfs_msg __P((struct proc *, const char *, const char *, int));
-static void nfs_up(struct nfsreq *, const char *, int);
-static void nfs_down(struct nfsreq *, const char *, int);
static int nfs_rcvlock __P((struct nfsreq *));
static void nfs_rcvunlock __P((struct nfsreq *));
static int nfs_receive __P((struct nfsreq *rep, struct mbuf **aname,
static void nfs_repbusy(struct nfsreq *rep);
static struct nfsreq * nfs_repnext(struct nfsreq *rep);
static void nfs_repdequeue(struct nfsreq *rep);
+
+/* XXX */
+boolean_t current_thread_aborted(void);
+kern_return_t thread_terminate(thread_act_t);
+
#ifndef NFS_NOSERVER
static int nfsrv_getstream __P((struct nfssvc_sock *,int));
}
#endif /* NFSDIAG */
+
+/*
+ * attempt to bind a socket to a reserved port
+ */
+static int
+nfs_bind_resv(struct nfsmount *nmp)
+{
+ struct socket *so = nmp->nm_so;
+ struct sockaddr_in sin;
+ int error;
+ u_short tport;
+
+ if (!so)
+ return (EINVAL);
+
+ sin.sin_len = sizeof (struct sockaddr_in);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ tport = IPPORT_RESERVED - 1;
+ sin.sin_port = htons(tport);
+
+ while (((error = sobind(so, (struct sockaddr *) &sin)) == EADDRINUSE) &&
+ (--tport > IPPORT_RESERVED / 2))
+ sin.sin_port = htons(tport);
+ return (error);
+}
+
+/*
+ * variables for managing the nfs_bind_resv_thread
+ */
+int nfs_resv_mounts = 0;
+static int nfs_bind_resv_thread_state = 0;
+#define NFS_BIND_RESV_THREAD_STATE_INITTED 1
+#define NFS_BIND_RESV_THREAD_STATE_RUNNING 2
+static struct slock nfs_bind_resv_slock;
+struct nfs_bind_resv_request {
+ TAILQ_ENTRY(nfs_bind_resv_request) brr_chain;
+ struct nfsmount *brr_nmp;
+ int brr_error;
+};
+static TAILQ_HEAD(, nfs_bind_resv_request) nfs_bind_resv_request_queue;
+
+/*
+ * thread to handle any reserved port bind requests
+ */
+static void
+nfs_bind_resv_thread(void)
+{
+ struct nfs_bind_resv_request *brreq;
+ boolean_t funnel_state;
+
+ funnel_state = thread_funnel_set(network_flock, TRUE);
+ nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_RUNNING;
+
+ while (nfs_resv_mounts > 0) {
+ simple_lock(&nfs_bind_resv_slock);
+ while ((brreq = TAILQ_FIRST(&nfs_bind_resv_request_queue))) {
+ TAILQ_REMOVE(&nfs_bind_resv_request_queue, brreq, brr_chain);
+ simple_unlock(&nfs_bind_resv_slock);
+ brreq->brr_error = nfs_bind_resv(brreq->brr_nmp);
+ wakeup(brreq);
+ simple_lock(&nfs_bind_resv_slock);
+ }
+ simple_unlock(&nfs_bind_resv_slock);
+ (void)tsleep((caddr_t)&nfs_bind_resv_request_queue, PSOCK,
+ "nfs_bind_resv_request_queue", 0);
+ }
+
+ nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_INITTED;
+ (void) thread_funnel_set(network_flock, funnel_state);
+ (void) thread_terminate(current_act());
+}
+
+int
+nfs_bind_resv_thread_wake(void)
+{
+ if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_RUNNING)
+ return (EIO);
+ wakeup(&nfs_bind_resv_request_queue);
+ return (0);
+}
+
+/*
+ * underprivileged procs call this to request nfs_bind_resv_thread
+ * to perform the reserved port binding for them.
+ */
+static int
+nfs_bind_resv_nopriv(struct nfsmount *nmp)
+{
+ struct nfs_bind_resv_request brreq;
+ int error;
+
+ if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_RUNNING) {
+ if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_INITTED) {
+ simple_lock_init(&nfs_bind_resv_slock);
+ TAILQ_INIT(&nfs_bind_resv_request_queue);
+ nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_INITTED;
+ }
+ kernel_thread(kernel_task, nfs_bind_resv_thread);
+ nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_RUNNING;
+ }
+
+ brreq.brr_nmp = nmp;
+ brreq.brr_error = 0;
+
+ simple_lock(&nfs_bind_resv_slock);
+ TAILQ_INSERT_TAIL(&nfs_bind_resv_request_queue, &brreq, brr_chain);
+ simple_unlock(&nfs_bind_resv_slock);
+
+ error = nfs_bind_resv_thread_wake();
+ if (error) {
+ TAILQ_REMOVE(&nfs_bind_resv_request_queue, &brreq, brr_chain);
+ /* Note: we might be able to simply restart the thread */
+ return (error);
+ }
+
+ (void) tsleep((caddr_t)&brreq, PSOCK, "nfsbindresv", 0);
+
+ return (brreq.brr_error);
+}
+
/*
* Initialize sockets and congestion for a new NFS connection.
* We do not free the sockaddr if error.
*/
int
nfs_connect(nmp, rep)
- register struct nfsmount *nmp;
+ struct nfsmount *nmp;
struct nfsreq *rep;
{
- register struct socket *so;
+ struct socket *so;
int s, error, rcvreserve, sndreserve;
struct sockaddr *saddr;
- struct sockaddr_in sin;
- u_short tport;
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
nmp->nm_so = (struct socket *)0;
* Some servers require that the client port be a reserved port number.
*/
if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) {
- sin.sin_len = sizeof (struct sockaddr_in);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- tport = IPPORT_RESERVED - 1;
- sin.sin_port = htons(tport);
-
- while ((error = sobind(so, (struct sockaddr *) &sin) == EADDRINUSE) &&
- (--tport > IPPORT_RESERVED / 2))
- sin.sin_port = htons(tport);
- if (error) {
- goto bad;
+ struct proc *p;
+ /*
+ * sobind() requires current_proc() to have superuser privs.
+ * If this bind is part of a reconnect, and the current proc
+ * doesn't have superuser privs, we hand the sobind() off to
+ * a kernel thread to process.
+ */
+ if ((nmp->nm_state & NFSSTA_MOUNTED) &&
+ (p = current_proc()) && suser(p->p_ucred, &p->p_acflag)) {
+ /* request nfs_bind_resv_thread() to do bind */
+ error = nfs_bind_resv_nopriv(nmp);
+ } else {
+ error = nfs_bind_resv(nmp);
}
+ if (error)
+ goto bad;
}
/*
return (EINTR);
if (error == EIO)
return (EIO);
- nfs_down(rep, "can not connect", error);
+ nfs_down(rep, rep->r_nmp, rep->r_procp, "can not connect",
+ error, NFSSTA_TIMEO);
+ if (!(nmp->nm_state & NFSSTA_MOUNTED)) {
+ /* we're not yet completely mounted and */
+ /* we can't reconnect, so we fail */
+ return (error);
+ }
+ if ((error = nfs_sigintr(rep->r_nmp, rep, rep->r_procp)))
+ return (error);
(void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
}
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
do {
+ control = NULL;
rcvflg = 0;
error = soreceive(so, (struct sockaddr **)0,
&auio, mp, &control, &rcvflg);
error,
rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
error = nfs_sndlock(rep);
- if (!error)
+ if (!error) {
error = nfs_reconnect(rep);
- if (!error)
- goto tryagain;
+ if (!error)
+ goto tryagain;
+ nfs_sndunlock(rep);
+ }
}
} else {
/*
* Get the next Rpc reply off the socket. Assume myrep->r_nmp
* is still intact by checks done in nfs_rcvlock.
*/
+ /* XXX why do we ask for nam here? we don't use it! */
error = nfs_receive(myrep, &nam, &mrep);
if (nam)
m_freem(nam);
/*
* Bailout asap if nfsmount struct gone (unmounted).
*/
- if (!myrep->r_nmp || !nmp->nm_so) {
+ if (!myrep->r_nmp) {
FSDBG(530, myrep->r_xid, myrep, nmp, -2);
return (ENXIO);
}
nfs_rcvunlock(myrep);
/* Bailout asap if nfsmount struct gone (unmounted). */
- if (!myrep->r_nmp || !nmp->nm_so)
+ if (!myrep->r_nmp)
return (ENXIO);
/*
* Ignore routing errors on connectionless protocols??
*/
if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) {
- nmp->nm_so->so_error = 0;
+ if (nmp->nm_so)
+ nmp->nm_so->so_error = 0;
if (myrep->r_flags & R_GETONEREP)
return (0);
continue;
* just check here and get out. (ekn)
*/
if (!mrep) {
+ nfs_rcvunlock(myrep);
FSDBG(530, myrep->r_xid, myrep, nmp, -3);
return (ENXIO); /* sounds good */
}
int nmsotype;
struct timeval now;
+ if (mrp)
+ *mrp = NULL;
if (xidp)
*xidp = 0;
* If there was a successful reply and a tprintf msg.
* tprintf a response.
*/
- nfs_up(rep, "is alive again", error);
+ if (!error)
+ nfs_up(rep, nmp, procp, "is alive again", NFSSTA_TIMEO);
mrep = rep->r_mrep;
md = rep->r_md;
dpos = rep->r_dpos;
*mdp = md;
*dposp = dpos;
error |= NFSERR_RETERR;
- } else
+ } else {
m_freem(mrep);
+ error &= ~NFSERR_RETERR;
+ }
m_freem(rep->r_mreq);
FSDBG_BOT(531, error, rep->r_xid, nmp, rep);
FREE_ZONE((caddr_t)rep,
(rep->r_rexmit > 2 || (rep->r_flags & R_RESENDERR)) &&
rep->r_lastmsg + nmp->nm_tprintf_delay < now.tv_sec) {
rep->r_lastmsg = now.tv_sec;
- nfs_down(rep, "not responding", 0);
+ nfs_down(rep, rep->r_nmp, rep->r_procp, "not responding",
+ 0, NFSSTA_TIMEO);
+ if (!(nmp->nm_state & NFSSTA_MOUNTED)) {
+ /* we're not yet completely mounted and */
+ /* we can't complete an RPC, so we fail */
+ nfsstats.rpctimeouts++;
+ nfs_softterm(rep);
+ continue;
+ }
}
if (rep->r_rtt >= 0) {
rep->r_rtt++;
rep->r_rtt = 0;
}
}
+ microuptime(&now);
#ifndef NFS_NOSERVER
/*
* Call the nqnfs server timer once a second to handle leases.
*/
- microuptime(&now);
if (lasttime != now.tv_sec) {
lasttime = now.tv_sec;
nqnfs_serverd();
}
#endif /* NFS_NOSERVER */
splx(s);
+
+ if (nfsbuffreeuptimestamp + 30 <= now.tv_sec) {
+ /*
+ * We haven't called nfs_buf_freeup() in a little while.
+ * So, see if we can free up any stale/unused bufs now.
+ */
+ nfs_buf_freeup(1);
+ }
+
timeout(nfs_timer_funnel, (void *)0, nfs_ticks);
}
register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
register struct mbuf *m;
struct mbuf *mp, *mhck;
- struct sockaddr *nam=0;
+ struct sockaddr *nam;
struct uio auio;
int flags, ns_nflag=0, error;
struct sockaddr_in *sin;
} else {
do {
auio.uio_resid = 1000000000;
- flags = MSG_DONTWAIT;
+ flags = MSG_DONTWAIT | MSG_NEEDSA;
nam = 0;
+ mp = 0;
error = soreceive(so, &nam, &auio, &mp,
(struct mbuf **)0, &flags);
sin = mtod(mhck, struct sockaddr_in *);
bcopy(nam, sin, sizeof(struct sockaddr_in));
mhck->m_hdr.mh_len = sizeof(struct sockaddr_in);
- FREE(nam, M_SONAME);
m = mhck;
m->m_next = mp;
slp->ns_recend = m;
m->m_nextpkt = (struct mbuf *)0;
}
+ if (nam) {
+ FREE(nam, M_SONAME);
+ }
if (error) {
if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
&& error != EWOULDBLOCK) {
return (0);
}
-static void
-nfs_down(rep, msg, error)
+void
+nfs_down(rep, nmp, proc, msg, error, flags)
struct nfsreq *rep;
+ struct nfsmount *nmp;
+ struct proc *proc;
const char *msg;
- int error;
+ int error, flags;
{
- int dosignal;
-
- if (rep == NULL || rep->r_nmp == NULL)
+ if (nmp == NULL)
return;
- if (!(rep->r_nmp->nm_state & NFSSTA_TIMEO)) {
- vfs_event_signal(&rep->r_nmp->nm_mountp->mnt_stat.f_fsid,
+ if ((flags & NFSSTA_TIMEO) && !(nmp->nm_state & NFSSTA_TIMEO)) {
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
VQ_NOTRESP, 0);
- rep->r_nmp->nm_state |= NFSSTA_TIMEO;
+ nmp->nm_state |= NFSSTA_TIMEO;
+ }
+ if ((flags & NFSSTA_LOCKTIMEO) && !(nmp->nm_state & NFSSTA_LOCKTIMEO)) {
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESPLOCK, 0);
+ nmp->nm_state |= NFSSTA_LOCKTIMEO;
}
- rep->r_flags |= R_TPRINTFMSG;
- nfs_msg(rep->r_procp, rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname,
- msg, error);
+ if (rep)
+ rep->r_flags |= R_TPRINTFMSG;
+ nfs_msg(proc, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, error);
}
-static void
-nfs_up(rep, msg, error)
+void
+nfs_up(rep, nmp, proc, msg, flags)
struct nfsreq *rep;
+ struct nfsmount *nmp;
+ struct proc *proc;
const char *msg;
- int error;
+ int flags;
{
-
- if (error != 0 || rep == NULL || rep->r_nmp == NULL)
+ if (nmp == NULL)
return;
- if ((rep->r_flags & R_TPRINTFMSG) != 0)
- nfs_msg(rep->r_procp,
- rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname, msg, 0);
- if ((rep->r_nmp->nm_state & NFSSTA_TIMEO)) {
- rep->r_nmp->nm_state &= ~NFSSTA_TIMEO;
- vfs_event_signal(&rep->r_nmp->nm_mountp->mnt_stat.f_fsid,
+ if ((rep == NULL) || (rep->r_flags & R_TPRINTFMSG) != 0)
+ nfs_msg(proc, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, 0);
+ if ((flags & NFSSTA_TIMEO) && (nmp->nm_state & NFSSTA_TIMEO)) {
+ nmp->nm_state &= ~NFSSTA_TIMEO;
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
VQ_NOTRESP, 1);
}
+ if ((flags & NFSSTA_LOCKTIMEO) && (nmp->nm_state & NFSSTA_LOCKTIMEO)) {
+ nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESPLOCK, 1);
+ }
}
+