X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..5eebf7385fedb1517b66b53c28e5aa6bb0a2be50:/bsd/nfs/nfs_socket.c diff --git a/bsd/nfs/nfs_socket.c b/bsd/nfs/nfs_socket.c index 9b55ac48f..ff2f55066 100644 --- a/bsd/nfs/nfs_socket.c +++ b/bsd/nfs/nfs_socket.c @@ -1,24 +1,21 @@ /* - * 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@ */ @@ -84,6 +81,8 @@ #include #include +#include +#include #include #include @@ -179,8 +178,6 @@ int nfsrtton = 0; 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, @@ -189,6 +186,11 @@ static int nfs_reconnect __P((struct nfsreq *rep)); 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)); @@ -349,20 +351,139 @@ nfsdup(struct nfsreq *rep) } #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; @@ -379,18 +500,22 @@ nfs_connect(nmp, rep) * 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; } /* @@ -540,7 +665,15 @@ nfs_reconnect(rep) 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); } @@ -864,6 +997,7 @@ tryagain: thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL); do { + control = NULL; rcvflg = 0; error = soreceive(so, (struct sockaddr **)0, &auio, mp, &control, &rcvflg); @@ -902,10 +1036,12 @@ errout: 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 { /* @@ -1049,13 +1185,14 @@ nfs_reply(myrep) * 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); } @@ -1064,14 +1201,15 @@ nfs_reply(myrep) 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; @@ -1093,6 +1231,7 @@ nfs_reply(myrep) * just check here and get out. (ekn) */ if (!mrep) { + nfs_rcvunlock(myrep); FSDBG(530, myrep->r_xid, myrep, nmp, -3); return (ENXIO); /* sounds good */ } @@ -1267,6 +1406,8 @@ nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp, xidp) int nmsotype; struct timeval now; + if (mrp) + *mrp = NULL; if (xidp) *xidp = 0; @@ -1461,7 +1602,8 @@ tryagain: * 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; @@ -1551,8 +1693,10 @@ tryagain: *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, @@ -1907,7 +2051,15 @@ nfs_timer(arg) (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++; @@ -2024,11 +2176,11 @@ nfs_timer(arg) 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(); @@ -2046,6 +2198,15 @@ nfs_timer(arg) } #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); } @@ -2306,7 +2467,7 @@ nfsrv_rcv(so, arg, waitflag) 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; @@ -2372,8 +2533,9 @@ nfsrv_rcv(so, arg, waitflag) } 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); @@ -2384,7 +2546,6 @@ nfsrv_rcv(so, arg, waitflag) 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; @@ -2397,6 +2558,9 @@ nfsrv_rcv(so, arg, waitflag) 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) { @@ -2891,41 +3055,52 @@ nfs_msg(p, server, msg, error) 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); + } } +