X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8ad349bb6ed4a0be06e34c92be0d98b92e078db4..e2fac8b15b12a7979f72090454d850e612fc5b13:/bsd/nfs/nfs_lock.c diff --git a/bsd/nfs/nfs_lock.c b/bsd/nfs/nfs_lock.c index f52d547ba..68125dd26 100644 --- a/bsd/nfs/nfs_lock.c +++ b/bsd/nfs/nfs_lock.c @@ -1,31 +1,29 @@ /* - * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * 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. The rights granted to you under the - * License may not be used to create, or enable the creation or - * redistribution of, unlawful or unlicensed copies of an Apple operating - * system, or to circumvent, violate, or enable the circumvention or - * violation of, any terms of an Apple operating system software license - * agreement. - * - * 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 - * 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 + * 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. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * 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 + * 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. - * - * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /*- * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. @@ -76,6 +74,7 @@ #include #include +#include #include @@ -84,28 +83,26 @@ #include #include #include +#include #include #include #include -#define OFF_MAX QUAD_MAX +#include +#include +#include +#include -/* - * globals for managing the lockd fifo - */ -vnode_t nfslockdvnode = 0; -int nfslockdwaiting = 0; -time_t nfslockdstarttimeout = 0; -int nfslockdfifolock = 0; -#define NFSLOCKDFIFOLOCK_LOCKED 1 -#define NFSLOCKDFIFOLOCK_WANT 2 +extern void ipc_port_release_send(ipc_port_t); + +#define OFF_MAX QUAD_MAX /* * pending lock request messages are kept in this queue which is * kept sorted by transaction ID (xid). */ -uint64_t nfs_lockxid = 0; -LOCKD_MSG_QUEUE nfs_pendlockq; +static uint64_t nfs_lockxid = 0; +static LOCKD_MSG_QUEUE nfs_pendlockq; /* * This structure is used to identify processes which have acquired NFS locks. @@ -125,10 +122,12 @@ struct nfs_lock_pid { #define NFS_LOCK_PID_HASH_SIZE 64 // XXX tune me #define NFS_LOCK_PID_HASH(pid) \ (&nfs_lock_pid_hash_tbl[(pid) & nfs_lock_pid_hash]) -LIST_HEAD(, nfs_lock_pid) *nfs_lock_pid_hash_tbl; -TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_lru; -u_long nfs_lock_pid_hash; -int nfs_lock_pid_lock; +static LIST_HEAD(, nfs_lock_pid) *nfs_lock_pid_hash_tbl; +static TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_lru; +static u_long nfs_lock_pid_hash, nfs_lock_pid_hash_trusted; + +static lck_grp_t *nfs_lock_lck_grp; +static lck_mtx_t *nfs_lock_mutex; /* @@ -138,14 +137,63 @@ void nfs_lockinit(void) { TAILQ_INIT(&nfs_pendlockq); - nfs_lock_pid_lock = 0; + nfs_lock_pid_hash_trusted = 1; nfs_lock_pid_hash_tbl = hashinit(NFS_LOCK_PID_HASH_SIZE, M_TEMP, &nfs_lock_pid_hash); TAILQ_INIT(&nfs_lock_pid_lru); + + nfs_lock_lck_grp = lck_grp_alloc_init("nfs_lock", LCK_GRP_ATTR_NULL); + nfs_lock_mutex = lck_mtx_alloc_init(nfs_lock_lck_grp, LCK_ATTR_NULL); +} + +/* + * change the count of NFS mounts that may need to make lockd requests + * + * If the mount count drops to zero, then send a shutdown request to + * lockd if we've sent any requests to it. + */ +void +nfs_lockd_mount_change(int i) +{ + mach_port_t lockd_port = IPC_PORT_NULL; + kern_return_t kr; + int send_shutdown; + + lck_mtx_lock(nfs_lock_mutex); + + nfs_lockd_mounts += i; + + /* send a shutdown request if there are no more lockd mounts */ + send_shutdown = ((nfs_lockd_mounts == 0) && nfs_lockd_request_sent); + if (send_shutdown) + nfs_lockd_request_sent = 0; + + lck_mtx_unlock(nfs_lock_mutex); + + if (!send_shutdown) + return; + + /* + * Let lockd know that it is no longer need for any NFS mounts + */ + kr = host_get_lockd_port(host_priv_self(), &lockd_port); + if ((kr != KERN_SUCCESS) || !IPC_PORT_VALID(lockd_port)) { + printf("nfs_lockd_mount_change: shutdown couldn't get port, kr %d, port %s\n", + kr, (lockd_port == IPC_PORT_NULL) ? "NULL" : + (lockd_port == IPC_PORT_DEAD) ? "DEAD" : "VALID"); + return; + } + + kr = lockd_shutdown(lockd_port); + if (kr != KERN_SUCCESS) + printf("nfs_lockd_mount_change: shutdown %d\n", kr); + + ipc_port_release_send(lockd_port); } /* * insert a lock request message into the pending queue + * (nfs_lock_mutex must be held) */ static inline void nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *msgreq) @@ -171,6 +219,7 @@ nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *msgreq) /* * remove a lock request message from the pending queue + * (nfs_lock_mutex must be held) */ static inline void nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *msgreq) @@ -187,6 +236,8 @@ nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *msgreq) * However, this may not be the case if there are blocked requests. We may * want to move blocked requests to a separate queue (but that'll complicate * duplicate xid checking). + * + * (nfs_lock_mutex must be held) */ static inline LOCKD_MSG_REQUEST * nfs_lockdmsg_find_by_xid(uint64_t lockxid) @@ -244,6 +295,8 @@ nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *msgreq, struct lockd_ans *ansp * However, this may not be the case if there are blocked requests. We may * want to move blocked requests to a separate queue (but that'll complicate * duplicate xid checking). + * + * (nfs_lock_mutex must be held) */ static inline LOCKD_MSG_REQUEST * nfs_lockdmsg_find_by_answer(struct lockd_ans *ansp) @@ -261,6 +314,7 @@ nfs_lockdmsg_find_by_answer(struct lockd_ans *ansp) /* * return the next unique lock request transaction ID + * (nfs_lock_mutex must be held) */ static inline uint64_t nfs_lockxid_get(void) @@ -302,36 +356,30 @@ nfs_lockxid_get(void) * add the entry if it is not found. * * (Also, if adding, try to clean up some stale entries.) + * (nfs_lock_mutex must be held) */ static int -nfs_lock_pid_check(proc_t p, int addflag, vnode_t vp) +nfs_lock_pid_check(proc_t p, int addflag) { - struct nfs_lock_pid *lp, *lplru, *lplru_next; - proc_t plru; + struct nfs_lock_pid *lp, *lplru, *lplru_next, *mlp; + TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_free; + proc_t plru = PROC_NULL; + pid_t pid; int error = 0; struct timeval now; - /* lock hash */ -loop: - if (nfs_lock_pid_lock) { - struct nfsmount *nmp = VFSTONFS(vnode_mount(vp)); - while (nfs_lock_pid_lock) { - nfs_lock_pid_lock = -1; - tsleep(&nfs_lock_pid_lock, PCATCH, "nfslockpid", 0); - if ((error = nfs_sigintr(nmp, NULL, p))) - return (error); - } - goto loop; - } - nfs_lock_pid_lock = 1; + TAILQ_INIT(&nfs_lock_pid_free); + mlp = NULL; +loop: /* Search hash chain */ + pid = proc_pid(p); error = ENOENT; - lp = NFS_LOCK_PID_HASH(proc_pid(p))->lh_first; + lp = NFS_LOCK_PID_HASH(pid)->lh_first; for (; lp != NULL; lp = lp->lp_hash.le_next) - if (lp->lp_pid == proc_pid(p)) { + if (lp->lp_pid == pid) { /* found pid... */ - if (timevalcmp(&lp->lp_pid_start, &p->p_stats->p_start, ==)) { + if (timevalcmp(&lp->lp_pid_start, &p->p_start, ==)) { /* ...and it's valid */ /* move to tail of LRU */ TAILQ_REMOVE(&nfs_lock_pid_lru, lp, lp_lru); @@ -351,8 +399,12 @@ loop: break; } - /* if we didn't find it (valid) and we've been asked to add it */ - if ((error == ENOENT) && addflag) { + /* if we didn't find it (valid), use any newly allocated one */ + if (!lp) + lp = mlp; + + /* if we don't have an lp and we've been asked to add it */ + if ((error == ENOENT) && addflag && !lp) { /* scan lru list for invalid, stale entries to reuse/free */ int lrucnt = 0; microuptime(&now); @@ -367,19 +419,27 @@ loop: } /* remove entry from LRU, and check if it's still in use */ TAILQ_REMOVE(&nfs_lock_pid_lru, lplru, lp_lru); - if (!lplru->lp_valid || !(plru = pfind(lplru->lp_pid)) || - timevalcmp(&lplru->lp_pid_start, &plru->p_stats->p_start, !=)) { + if (!lplru->lp_valid || !(plru = proc_find(lplru->lp_pid)) || + timevalcmp(&lplru->lp_pid_start, &plru->p_start, !=)) { + if (plru != PROC_NULL) { + proc_rele(plru); + plru = PROC_NULL; + } /* no longer in use */ LIST_REMOVE(lplru, lp_hash); if (!lp) { /* we'll reuse this one */ lp = lplru; } else { - /* we can free this one */ - FREE(lplru, M_TEMP); + /* queue it up for freeing */ + TAILQ_INSERT_HEAD(&nfs_lock_pid_free, lplru, lp_lru); } } else { /* still in use */ + if (plru != PROC_NULL) { + proc_rele(plru); + plru = PROC_NULL; + } lplru->lp_time = now.tv_sec; TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lplru, lp_lru); } @@ -389,80 +449,144 @@ loop: } if (!lp) { /* we need to allocate a new one */ - MALLOC(lp, struct nfs_lock_pid *, sizeof(struct nfs_lock_pid), + lck_mtx_unlock(nfs_lock_mutex); + MALLOC(mlp, struct nfs_lock_pid *, sizeof(struct nfs_lock_pid), M_TEMP, M_WAITOK | M_ZERO); - } - if (!lp) { + lck_mtx_lock(nfs_lock_mutex); + if (mlp) /* make sure somebody hasn't already added this guy */ + goto loop; error = ENOMEM; - } else { - /* (re)initialize nfs_lock_pid info */ - lp->lp_pid = proc_pid(p); - lp->lp_pid_start = p->p_stats->p_start; - /* insert pid in hash */ - LIST_INSERT_HEAD(NFS_LOCK_PID_HASH(lp->lp_pid), lp, lp_hash); - lp->lp_valid = 1; - lp->lp_time = now.tv_sec; - TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lp, lp_lru); - error = 0; } } + if ((error == ENOENT) && addflag && lp) { + /* (re)initialize nfs_lock_pid info */ + lp->lp_pid = pid; + lp->lp_pid_start = p->p_start; + /* insert pid in hash */ + LIST_INSERT_HEAD(NFS_LOCK_PID_HASH(lp->lp_pid), lp, lp_hash); + lp->lp_valid = 1; + lp->lp_time = now.tv_sec; + TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lp, lp_lru); + error = 0; + } - /* unlock hash */ - if (nfs_lock_pid_lock < 0) { - nfs_lock_pid_lock = 0; - wakeup(&nfs_lock_pid_lock); - } else - nfs_lock_pid_lock = 0; + if ((mlp && (lp != mlp)) || TAILQ_FIRST(&nfs_lock_pid_free)) { + lck_mtx_unlock(nfs_lock_mutex); + if (mlp && (lp != mlp)) { + /* we didn't need this one, so we can free it */ + FREE(mlp, M_TEMP); + } + /* free up any stale entries */ + while ((lp = TAILQ_FIRST(&nfs_lock_pid_free))) { + TAILQ_REMOVE(&nfs_lock_pid_free, lp, lp_lru); + FREE(lp, M_TEMP); + } + lck_mtx_lock(nfs_lock_mutex); + } return (error); } +#define MACH_MAX_TRIES 3 + +static int +send_request(LOCKD_MSG *msg, int interruptable) +{ + kern_return_t kr; + int retries = 0; + mach_port_t lockd_port = IPC_PORT_NULL; + + kr = host_get_lockd_port(host_priv_self(), &lockd_port); + if (kr != KERN_SUCCESS || !IPC_PORT_VALID(lockd_port)) + return (ENOTSUP); + + do { + /* In the kernel all mach messaging is interruptable */ + do { + kr = lockd_request( + lockd_port, + msg->lm_version, + msg->lm_flags, + msg->lm_xid, + msg->lm_fl.l_start, + msg->lm_fl.l_len, + msg->lm_fl.l_pid, + msg->lm_fl.l_type, + msg->lm_fl.l_whence, + (uint32_t *)&msg->lm_addr, + (uint32_t *)&msg->lm_cred, + msg->lm_fh_len, + msg->lm_fh); + if (kr != KERN_SUCCESS) + printf("lockd_request received %d!\n", kr); + } while (!interruptable && kr == MACH_SEND_INTERRUPTED); + } while (kr == MIG_SERVER_DIED && retries++ < MACH_MAX_TRIES); + + ipc_port_release_send(lockd_port); + switch (kr) { + case MACH_SEND_INTERRUPTED: + return (EINTR); + default: + /* + * Other MACH or MIG errors we will retry. Eventually + * we will call nfs_down and allow the user to disable + * locking. + */ + return (EAGAIN); + } + return (kr); +} + /* - * nfs_advlock -- - * NFS advisory byte-level locks. + * NFS advisory byte-level locks (client) */ int -nfs_dolock(struct vnop_advlock_args *ap) -/* struct vnop_advlock_args { - struct vnodeop_desc *a_desc; - vnode_t a_vp; - caddr_t a_id; - int a_op; - struct flock *a_fl; - int a_flags; - vfs_context_t a_context; -}; */ +nfs3_vnop_advlock( + struct vnop_advlock_args /* { + struct vnodeop_desc *a_desc; + vnode_t a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + vfs_context_t a_context; + } */ *ap) { + vfs_context_t ctx; + proc_t p; LOCKD_MSG_REQUEST msgreq; LOCKD_MSG *msg; - vnode_t vp, wvp; - struct nfsnode *np; - int error, error1; + vnode_t vp; + nfsnode_t np; + int error, error2; + int interruptable; struct flock *fl; - int fmode, ioflg; struct nfsmount *nmp; struct nfs_vattr nvattr; off_t start, end; struct timeval now; int timeo, endtime, lastmsg, wentdown = 0; - int lockpidcheck; - kauth_cred_t cred; - proc_t p; + int lockpidcheck, nfsvers; struct sockaddr *saddr; + struct timespec ts; - p = vfs_context_proc(ap->a_context); - cred = vfs_context_ucred(ap->a_context); - + ctx = ap->a_context; + p = vfs_context_proc(ctx); vp = ap->a_vp; fl = ap->a_fl; np = VTONFS(vp); - nmp = VFSTONFS(vnode_mount(vp)); + nmp = VTONMP(vp); if (!nmp) return (ENXIO); - if (nmp->nm_flag & NFSMNT_NOLOCKS) + lck_mtx_lock(&nmp->nm_lock); + if (nmp->nm_flag & NFSMNT_NOLOCKS) { + lck_mtx_unlock(&nmp->nm_lock); return (ENOTSUP); + } + nfsvers = nmp->nm_vers; + lck_mtx_unlock(&nmp->nm_lock); /* * The NLM protocol doesn't allow the server to return an error @@ -483,75 +607,20 @@ nfs_dolock(struct vnop_advlock_args *ap) (fl->l_len < 0 && fl->l_start + fl->l_len < 0)) return (EINVAL); } - /* - * If daemon is running take a ref on its fifo vnode - */ - if (!(wvp = nfslockdvnode)) { - if (!nfslockdwaiting && !nfslockdstarttimeout) - return (ENOTSUP); - /* - * Don't wake lock daemon if it hasn't been started yet and - * this is an unlock request (since we couldn't possibly - * actually have a lock on the file). This could be an - * uninformed unlock request due to closef()'s behavior of doing - * unlocks on all files if a process has had a lock on ANY file. - */ - if (!nfslockdvnode && (fl->l_type == F_UNLCK)) - return (EINVAL); - microuptime(&now); - if (nfslockdwaiting) { - /* wake up lock daemon */ - nfslockdstarttimeout = now.tv_sec + 60; - (void)wakeup((void *)&nfslockdwaiting); - } - /* wait on nfslockdvnode for a while to allow daemon to start */ - while (!nfslockdvnode && (now.tv_sec < nfslockdstarttimeout)) { - error = tsleep((void *)&nfslockdvnode, PCATCH | PUSER, "lockdstart", 2*hz); - if (error && (error != EWOULDBLOCK)) - return (error); - /* check that we still have our mount... */ - /* ...and that we still support locks */ - nmp = VFSTONFS(vnode_mount(vp)); - if (!nmp) - return (ENXIO); - if (nmp->nm_flag & NFSMNT_NOLOCKS) - return (ENOTSUP); - if (!error) - break; - microuptime(&now); - } - /* - * check for nfslockdvnode - * If it hasn't started by now, there's a problem. - */ - if (!(wvp = nfslockdvnode)) - return (ENOTSUP); - } - error = vnode_getwithref(wvp); - if (error) - return (ENOTSUP); - error = vnode_ref(wvp); - if (error) { - vnode_put(wvp); - return (ENOTSUP); - } + + lck_mtx_lock(nfs_lock_mutex); /* * Need to check if this process has successfully acquired an NFS lock before. * If not, and this is an unlock request we can simply return success here. */ - lockpidcheck = nfs_lock_pid_check(p, 0, vp); + lockpidcheck = nfs_lock_pid_check(p, 0); + lck_mtx_unlock(nfs_lock_mutex); if (lockpidcheck) { - if (lockpidcheck != ENOENT) { - vnode_rele(wvp); - vnode_put(wvp); + if (lockpidcheck != ENOENT) return (lockpidcheck); - } - if (ap->a_op == F_UNLCK) { - vnode_rele(wvp); - vnode_put(wvp); + if ((ap->a_op == F_UNLCK) && nfs_lock_pid_hash_trusted) return (0); - } } /* @@ -573,28 +642,31 @@ nfs_dolock(struct vnop_advlock_args *ap) case SEEK_END: /* need to flush, and refetch attributes to make */ /* sure we have the correct end of file offset */ + error = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE); + if (error) + return (error); + NATTRINVALIDATE(np); if (np->n_flag & NMODIFIED) { - NATTRINVALIDATE(np); - error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); - if (error) { - vnode_rele(wvp); - vnode_put(wvp); + nfs_unlock(np); + error = nfs_vinvalbuf(vp, V_SAVE, ctx, 1); + if (error) return (error); - } - } - NATTRINVALIDATE(np); + } else + nfs_unlock(np); - error = nfs_getattr(vp, &nvattr, cred, p); + error = nfs_getattr(np, &nvattr, ctx, 0); + nfs_data_lock(np, NFS_NODE_LOCK_SHARED); + if (!error) + error = nfs_lock(np, NFS_NODE_LOCK_SHARED); if (error) { - vnode_rele(wvp); - vnode_put(wvp); + nfs_data_unlock(np); return (error); } start = np->n_size + fl->l_start; + nfs_unlock(np); + nfs_data_unlock(np); break; default: - vnode_rele(wvp); - vnode_put(wvp); return (EINVAL); } if (fl->l_len == 0) @@ -605,24 +677,23 @@ nfs_dolock(struct vnop_advlock_args *ap) end = start - 1; start += fl->l_len; } - if (start < 0) { - vnode_rele(wvp); - vnode_put(wvp); + if (start < 0) return (EINVAL); - } - if (!NFS_ISV3(vp) && - ((start >= 0x80000000) || (end >= 0x80000000))) { - vnode_rele(wvp); - vnode_put(wvp); + + if ((nfsvers == NFS_VER2) && + ((start >= 0x80000000) || (end >= 0x80000000))) return (EINVAL); - } /* * Fill in the information structure. + * We set all values to zero with bzero to clear + * out any information in the sockaddr_storage + * and nfs_filehandle contained in msgreq so that + * we will not leak extraneous information out of + * the kernel when calling up to lockd via our mig + * generated routine. */ - msgreq.lmr_answered = 0; - msgreq.lmr_errno = 0; - msgreq.lmr_saved_errno = 0; + bzero(&msgreq, sizeof(msgreq)); msg = &msgreq.lmr_msg; msg->lm_version = LOCKD_MSG_VERSION; msg->lm_flags = 0; @@ -631,73 +702,48 @@ nfs_dolock(struct vnop_advlock_args *ap) msg->lm_fl.l_start = start; if (end != -1) msg->lm_fl.l_len = end - start + 1; - msg->lm_fl.l_pid = proc_pid(p); + msg->lm_fl.l_pid = vfs_context_pid(ctx); if (ap->a_flags & F_WAIT) msg->lm_flags |= LOCKD_MSG_BLOCK; if (ap->a_op == F_GETLK) msg->lm_flags |= LOCKD_MSG_TEST; - nmp = VFSTONFS(vnode_mount(vp)); - if (!nmp) { - vnode_rele(wvp); - vnode_put(wvp); + nmp = VTONMP(vp); + if (!nmp) return (ENXIO); - } + lck_mtx_lock(&nmp->nm_lock); saddr = mbuf_data(nmp->nm_nam); bcopy(saddr, &msg->lm_addr, min(sizeof msg->lm_addr, saddr->sa_len)); - msg->lm_fh_len = NFS_ISV3(vp) ? VTONFS(vp)->n_fhsize : NFSX_V2FH; - bcopy(VTONFS(vp)->n_fhp, msg->lm_fh, msg->lm_fh_len); - if (NFS_ISV3(vp)) + msg->lm_fh_len = (nfsvers == NFS_VER2) ? NFSX_V2FH : np->n_fhsize; + bcopy(np->n_fhp, msg->lm_fh, msg->lm_fh_len); + if (nfsvers == NFS_VER3) msg->lm_flags |= LOCKD_MSG_NFSV3; - cru2x(cred, &msg->lm_cred); + cru2x(vfs_context_ucred(ctx), &msg->lm_cred); microuptime(&now); lastmsg = now.tv_sec - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay)); + interruptable = nmp->nm_flag & NFSMNT_INT; + lck_mtx_unlock(&nmp->nm_lock); - fmode = FFLAGS(O_WRONLY); - if ((error = VNOP_OPEN(wvp, fmode, ap->a_context))) { - vnode_rele(wvp); - vnode_put(wvp); - return (error); - } - vnode_lock(wvp); - ++wvp->v_writecount; - vnode_unlock(wvp); + lck_mtx_lock(nfs_lock_mutex); /* allocate unique xid */ msg->lm_xid = nfs_lockxid_get(); nfs_lockdmsg_enqueue(&msgreq); - timeo = 2*hz; -#define IO_NOMACCHECK 0; - ioflg = IO_UNIT | IO_NOMACCHECK; - for (;;) { - error = 0; - while (nfslockdfifolock & NFSLOCKDFIFOLOCK_LOCKED) { - nfslockdfifolock |= NFSLOCKDFIFOLOCK_WANT; - error = tsleep((void *)&nfslockdfifolock, - PCATCH | PUSER, "lockdfifo", 20*hz); - if (error) - break; - } - if (error) - break; - nfslockdfifolock |= NFSLOCKDFIFOLOCK_LOCKED; - - error = vn_rdwr(UIO_WRITE, wvp, (caddr_t)msg, sizeof(*msg), 0, - UIO_SYSSPACE32, ioflg, proc_ucred(kernproc), NULL, p); + timeo = 2; - nfslockdfifolock &= ~NFSLOCKDFIFOLOCK_LOCKED; - if (nfslockdfifolock & NFSLOCKDFIFOLOCK_WANT) { - nfslockdfifolock &= ~NFSLOCKDFIFOLOCK_WANT; - wakeup((void *)&nfslockdfifolock); - } + for (;;) { + nfs_lockd_request_sent = 1; - if (error && (((ioflg & IO_NDELAY) == 0) || error != EAGAIN)) { + /* need to drop nfs_lock_mutex while calling send_request() */ + lck_mtx_unlock(nfs_lock_mutex); + error = send_request(msg, interruptable); + lck_mtx_lock(nfs_lock_mutex); + if (error && error != EAGAIN) break; - } /* * Always wait for an answer. Not waiting for unlocks could @@ -711,18 +757,19 @@ nfs_dolock(struct vnop_advlock_args *ap) * at 2 and double each timeout with a max of 60 seconds. * * In order to maintain responsiveness, we pass a small timeout - * to tsleep and calculate the timeouts ourselves. This allows + * to msleep and calculate the timeouts ourselves. This allows * us to pick up on mount changes quicker. */ wait_for_granted: error = EWOULDBLOCK; + ts.tv_sec = 2; + ts.tv_nsec = 0; microuptime(&now); - if ((timeo/hz) > 0) - endtime = now.tv_sec + timeo/hz; - else - endtime = now.tv_sec + 1; + endtime = now.tv_sec + timeo; while (now.tv_sec < endtime) { - error = tsleep((void *)&msgreq, PCATCH | PUSER, "lockd", 2*hz); + error = error2 = 0; + if (!msgreq.lmr_answered) + error = msleep(&msgreq, nfs_lock_mutex, PCATCH | PUSER, "lockd", &ts); if (msgreq.lmr_answered) { /* * Note: it's possible to have a lock granted at @@ -731,58 +778,66 @@ wait_for_granted: * error from this request or we might not unlock the * lock that's been granted. */ - error = 0; + nmp = VTONMP(vp); + if ((msgreq.lmr_errno == ENOTSUP) && nmp && + (nmp->nm_state & NFSSTA_LOCKSWORK)) { + /* + * We have evidence that locks work, yet lockd + * returned ENOTSUP. This is probably because + * it was unable to contact the server's lockd + * to send it the request. + * + * Because we know locks work, we'll consider + * this failure to be a timeout. + */ + error = EWOULDBLOCK; + } else { + error = 0; + } break; } if (error != EWOULDBLOCK) break; /* check that we still have our mount... */ /* ...and that we still support locks */ - nmp = VFSTONFS(vnode_mount(vp)); - if (!nmp || (nmp->nm_flag & NFSMNT_NOLOCKS)) - break; - /* - * If the mount is hung and we've requested not to hang - * on remote filesystems, then bail now. - */ - if ((p != NULL) && ((proc_noremotehang(p)) != 0) && - ((nmp->nm_state & (NFSSTA_TIMEO|NFSSTA_LOCKTIMEO)) != 0)) { + nmp = VTONMP(vp); + if ((error2 = nfs_sigintr(nmp, NULL, vfs_context_thread(ctx), 0))) { + error = error2; if (fl->l_type == F_UNLCK) - printf("nfs_dolock: aborting unlock request " - "due to timeout (noremotehang)\n"); - error = EIO; + printf("nfs_vnop_advlock: aborting unlock request, error %d\n", error); + break; + } + lck_mtx_lock(&nmp->nm_lock); + if (nmp->nm_flag & NFSMNT_NOLOCKS) { + lck_mtx_unlock(&nmp->nm_lock); break; } + interruptable = nmp->nm_flag & NFSMNT_INT; + lck_mtx_unlock(&nmp->nm_lock); microuptime(&now); } if (error) { /* check that we still have our mount... */ - nmp = VFSTONFS(vnode_mount(vp)); - if (!nmp) { - if (error == EWOULDBLOCK) - error = ENXIO; - break; + nmp = VTONMP(vp); + if ((error2 = nfs_sigintr(nmp, NULL, vfs_context_thread(ctx), 0))) { + error = error2; + if (error2 != EINTR) { + if (fl->l_type == F_UNLCK) + printf("nfs_vnop_advlock: aborting unlock request, error %d\n", error); + break; + } } /* ...and that we still support locks */ + lck_mtx_lock(&nmp->nm_lock); if (nmp->nm_flag & NFSMNT_NOLOCKS) { if (error == EWOULDBLOCK) error = ENOTSUP; + lck_mtx_unlock(&nmp->nm_lock); break; } - if ((error == ENOTSUP) && - (nmp->nm_state & NFSSTA_LOCKSWORK)) { - /* - * We have evidence that locks work, yet lockd - * returned ENOTSUP. This is probably because - * it was unable to contact the server's lockd to - * send it the request. - * - * Because we know locks work, we'll consider - * this failure to be a timeout. - */ - error = EWOULDBLOCK; - } + interruptable = nmp->nm_flag & NFSMNT_INT; if (error != EWOULDBLOCK) { + lck_mtx_unlock(&nmp->nm_lock); /* * We're going to bail on this request. * If we were a blocked lock request, send a cancel. @@ -798,34 +853,25 @@ wait_for_granted: msgreq.lmr_errno = 0; msgreq.lmr_answered = 0; /* reset timeout */ - timeo = 2*hz; + timeo = 2; /* send cancel request */ continue; } break; } - /* - * If the mount is hung and we've requested not to hang - * on remote filesystems, then bail now. - */ - if ((p != NULL) && ((proc_noremotehang(p)) != 0) && - ((nmp->nm_state & (NFSSTA_TIMEO|NFSSTA_LOCKTIMEO)) != 0)) { - if (fl->l_type == F_UNLCK) - printf("nfs_dolock: aborting unlock request " - "due to timeout (noremotehang)\n"); - error = EIO; - break; - } /* warn if we're not getting any response */ microuptime(&now); if ((msgreq.lmr_errno != EINPROGRESS) && (nmp->nm_tprintf_initial_delay != 0) && ((lastmsg + nmp->nm_tprintf_delay) < now.tv_sec)) { + lck_mtx_unlock(&nmp->nm_lock); lastmsg = now.tv_sec; - nfs_down(nmp, p, 0, NFSSTA_LOCKTIMEO, "lockd not responding"); + nfs_down(nmp, vfs_context_thread(ctx), 0, NFSSTA_LOCKTIMEO, "lockd not responding"); wentdown = 1; - } + } else + lck_mtx_unlock(&nmp->nm_lock); + if (msgreq.lmr_errno == EINPROGRESS) { /* * We've got a blocked lock request that we are @@ -845,31 +891,29 @@ wait_for_granted: msgreq.lmr_saved_errno = msgreq.lmr_errno; msgreq.lmr_errno = 0; msgreq.lmr_answered = 0; - timeo = 2*hz; + timeo = 2; /* send cancel then resend request */ continue; } /* - * We timed out, so we will rewrite the request - * to the fifo, but only if it isn't already full. + * We timed out, so we will resend the request. */ - ioflg |= IO_NDELAY; timeo *= 2; - if (timeo > 60*hz) - timeo = 60*hz; + if (timeo > 60) + timeo = 60; /* resend request */ continue; } /* we got a reponse, so the server's lockd is OK */ - nfs_up(VFSTONFS(vnode_mount(vp)), p, NFSSTA_LOCKTIMEO, + nfs_up(VTONMP(vp), vfs_context_thread(ctx), NFSSTA_LOCKTIMEO, wentdown ? "lockd alive again" : NULL); wentdown = 0; if (msgreq.lmr_errno == EINPROGRESS) { /* got NLM_BLOCKED response */ /* need to wait for NLM_GRANTED */ - timeo = 60*hz; + timeo = 60; msgreq.lmr_answered = 0; goto wait_for_granted; } @@ -888,7 +932,7 @@ wait_for_granted: msgreq.lmr_saved_errno = 0; msgreq.lmr_errno = 0; msgreq.lmr_answered = 0; - timeo = 2*hz; + timeo = 2; /* resend request */ continue; } @@ -900,9 +944,8 @@ wait_for_granted: fl->l_start = msg->lm_fl.l_start; fl->l_len = msg->lm_fl.l_len; fl->l_whence = SEEK_SET; - } else { + } else fl->l_type = F_UNLCK; - } } /* @@ -916,31 +959,56 @@ wait_for_granted: } else error = msgreq.lmr_errno; + nmp = VTONMP(vp); + if ((error == ENOTSUP) && nmp && !(nmp->nm_state & NFSSTA_LOCKSWORK)) { + /* + * We have NO evidence that locks work and lockd + * returned ENOTSUP. Let's take this as a hint + * that locks aren't supported and disable them + * for this mount. + */ + lck_mtx_lock(&nmp->nm_lock); + nmp->nm_flag |= NFSMNT_NOLOCKS; + nmp->nm_state &= ~NFSSTA_LOCKTIMEO; + lck_mtx_unlock(&nmp->nm_lock); + printf("lockd returned ENOTSUP, disabling locks for nfs server: %s\n", + vfs_statfs(nmp->nm_mountp)->f_mntfromname); + } if (!error) { /* record that NFS file locking has worked on this mount */ - nmp = VFSTONFS(vnode_mount(vp)); - if (nmp && !(nmp->nm_state & NFSSTA_LOCKSWORK)) - nmp->nm_state |= NFSSTA_LOCKSWORK; + if (nmp) { + lck_mtx_lock(&nmp->nm_lock); + if (!(nmp->nm_state & NFSSTA_LOCKSWORK)) + nmp->nm_state |= NFSSTA_LOCKSWORK; + lck_mtx_unlock(&nmp->nm_lock); + } /* * If we successfully acquired a lock, make sure this pid * is in the nfs_lock_pid hash table so we know we can't * short-circuit unlock requests. */ if ((lockpidcheck == ENOENT) && - ((ap->a_op == F_SETLK) || (ap->a_op == F_SETLKW))) - nfs_lock_pid_check(p, 1, vp); - + ((ap->a_op == F_SETLK) || (ap->a_op == F_SETLKW))) { + error = nfs_lock_pid_check(p, 1); + if (error) { + /* + * We couldn't add the pid to the table, + * so we can no longer trust that a pid + * not in the table has no locks. + */ + nfs_lock_pid_hash_trusted = 0; + printf("nfs_vnop_advlock: pid add failed - no longer trusted\n"); + } + } } break; } nfs_lockdmsg_dequeue(&msgreq); - error1 = VNOP_CLOSE(wvp, FWRITE, ap->a_context); - vnode_rele(wvp); - vnode_put(wvp); - /* prefer any previous 'error' to our vn_close 'error1'. */ - return (error != 0 ? error : error1); + lck_mtx_unlock(nfs_lock_mutex); + + return (error); } /* @@ -962,6 +1030,8 @@ nfslockdans(proc_t p, struct lockd_ans *ansp) if (ansp->la_version != LOCKD_ANS_VERSION) return (EINVAL); + lck_mtx_lock(nfs_lock_mutex); + /* try to find the lockd message by transaction id (cookie) */ msgreq = nfs_lockdmsg_find_by_xid(ansp->la_xid); if (ansp->la_flags & LOCKD_ANS_GRANTED) { @@ -980,8 +1050,10 @@ nfslockdans(proc_t p, struct lockd_ans *ansp) if (msgreq && (msgreq->lmr_msg.lm_flags & LOCKD_MSG_CANCEL)) msgreq = NULL; } - if (!msgreq) + if (!msgreq) { + lck_mtx_unlock(nfs_lock_mutex); return (EPIPE); + } msgreq->lmr_errno = ansp->la_errno; if ((msgreq->lmr_msg.lm_flags & LOCKD_MSG_TEST) && msgreq->lmr_errno == 0) { @@ -999,70 +1071,9 @@ nfslockdans(proc_t p, struct lockd_ans *ansp) } msgreq->lmr_answered = 1; - (void)wakeup((void *)msgreq); + lck_mtx_unlock(nfs_lock_mutex); + wakeup(msgreq); return (0); } -/* - * nfslockdfd -- - * NFS advisory byte-level locks: fifo file# from the lock daemon. - */ -int -nfslockdfd(proc_t p, int fd) -{ - int error; - vnode_t vp, oldvp; - - error = proc_suser(p); - if (error) - return (error); - if (fd < 0) { - vp = NULL; - } else { - error = file_vnode(fd, &vp); - if (error) - return (error); - error = vnode_getwithref(vp); - if (error) - return (error); - error = vnode_ref(vp); - if (error) { - vnode_put(vp); - return (error); - } - } - oldvp = nfslockdvnode; - nfslockdvnode = vp; - if (oldvp) { - vnode_rele(oldvp); - } - (void)wakeup((void *)&nfslockdvnode); - if (vp) { - vnode_put(vp); - } - return (0); -} - -/* - * nfslockdwait -- - * lock daemon waiting for lock request - */ -int -nfslockdwait(proc_t p) -{ - int error; - - error = proc_suser(p); - if (error) - return (error); - if (nfslockdwaiting || nfslockdvnode) - return (EBUSY); - - nfslockdstarttimeout = 0; - nfslockdwaiting = 1; - tsleep((void *)&nfslockdwaiting, PCATCH | PUSER, "lockd", 0); - nfslockdwaiting = 0; - - return (0); -}