]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_lock.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_lock.c
CommitLineData
55e303ae 1/*
39037602 2 * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
55e303ae 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
55e303ae
A
27 */
28/*-
29 * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. Berkeley Software Design Inc's name may not be used to endorse or
40 * promote products derived from this software without specific prior
41 * written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 * from BSDI nfs_lock.c,v 2.4 1998/12/14 23:49:56 jch Exp
56 */
57
ea3f0419
A
58#include <nfs/nfs_conf.h>
59#if CONFIG_NFS_CLIENT
60
55e303ae
A
61#include <sys/cdefs.h>
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/fcntl.h>
0a7de745 65#include <sys/kernel.h> /* for hz */
91447636 66#include <sys/file_internal.h>
55e303ae 67#include <sys/malloc.h>
0a7de745 68#include <sys/lockf.h> /* for hz */ /* Must come after sys/malloc.h */
91447636
A
69#include <sys/kpi_mbuf.h>
70#include <sys/mount_internal.h>
0a7de745 71#include <sys/proc_internal.h> /* for p_start */
91447636 72#include <sys/kauth.h>
55e303ae
A
73#include <sys/resourcevar.h>
74#include <sys/socket.h>
55e303ae
A
75#include <sys/unistd.h>
76#include <sys/user.h>
91447636 77#include <sys/vnode_internal.h>
55e303ae 78
91447636 79#include <kern/thread.h>
2d21ac55 80#include <kern/host.h>
55e303ae
A
81
82#include <machine/limits.h>
83
84#include <net/if.h>
85
86#include <nfs/rpcv2.h>
87#include <nfs/nfsproto.h>
88#include <nfs/nfs.h>
2d21ac55 89#include <nfs/nfs_gss.h>
55e303ae
A
90#include <nfs/nfsmount.h>
91#include <nfs/nfsnode.h>
92#include <nfs/nfs_lock.h>
55e303ae 93
2d21ac55
A
94#include <mach/host_priv.h>
95#include <mach/mig_errors.h>
96#include <mach/host_special_ports.h>
97#include <lockd/lockd_mach.h>
55e303ae 98
2d21ac55
A
99extern void ipc_port_release_send(ipc_port_t);
100
55e303ae 101/*
e5568f75
A
102 * pending lock request messages are kept in this queue which is
103 * kept sorted by transaction ID (xid).
104 */
2d21ac55
A
105static uint64_t nfs_lockxid = 0;
106static LOCKD_MSG_QUEUE nfs_pendlockq;
e5568f75 107
6d2010ae 108/* list of mounts that are (potentially) making lockd requests */
0a7de745 109TAILQ_HEAD(nfs_lockd_mount_list, nfsmount) nfs_lockd_mount_list;
2d21ac55
A
110
111static lck_grp_t *nfs_lock_lck_grp;
112static lck_mtx_t *nfs_lock_mutex;
e5568f75 113
b0d623f7
A
114void nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *);
115void nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *);
116int nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *, struct lockd_ans *);
117LOCKD_MSG_REQUEST *nfs_lockdmsg_find_by_answer(struct lockd_ans *);
118LOCKD_MSG_REQUEST *nfs_lockdmsg_find_by_xid(uint64_t);
119uint64_t nfs_lockxid_get(void);
b0d623f7 120int nfs_lockd_send_request(LOCKD_MSG *, int);
e5568f75
A
121
122/*
123 * initialize global nfs lock state
124 */
125void
126nfs_lockinit(void)
127{
128 TAILQ_INIT(&nfs_pendlockq);
6d2010ae 129 TAILQ_INIT(&nfs_lockd_mount_list);
2d21ac55
A
130
131 nfs_lock_lck_grp = lck_grp_alloc_init("nfs_lock", LCK_GRP_ATTR_NULL);
132 nfs_lock_mutex = lck_mtx_alloc_init(nfs_lock_lck_grp, LCK_ATTR_NULL);
133}
134
135/*
6d2010ae
A
136 * Register a mount as (potentially) making lockd requests.
137 */
138void
139nfs_lockd_mount_register(struct nfsmount *nmp)
140{
141 lck_mtx_lock(nfs_lock_mutex);
142 TAILQ_INSERT_HEAD(&nfs_lockd_mount_list, nmp, nm_ldlink);
143 nfs_lockd_mounts++;
144 lck_mtx_unlock(nfs_lock_mutex);
145}
146
147/*
148 * Unregister a mount as (potentially) making lockd requests.
2d21ac55 149 *
6d2010ae 150 * When the lockd mount count drops to zero, then send a shutdown request to
2d21ac55
A
151 * lockd if we've sent any requests to it.
152 */
153void
6d2010ae 154nfs_lockd_mount_unregister(struct nfsmount *nmp)
2d21ac55 155{
6d2010ae 156 int send_shutdown;
2d21ac55
A
157 mach_port_t lockd_port = IPC_PORT_NULL;
158 kern_return_t kr;
2d21ac55
A
159
160 lck_mtx_lock(nfs_lock_mutex);
fe8ab488
A
161 if (nmp->nm_ldlink.tqe_next == NFSNOLIST) {
162 lck_mtx_unlock(nfs_lock_mutex);
163 return;
164 }
0a7de745 165
6d2010ae 166 TAILQ_REMOVE(&nfs_lockd_mount_list, nmp, nm_ldlink);
fe8ab488
A
167 nmp->nm_ldlink.tqe_next = NFSNOLIST;
168
6d2010ae 169 nfs_lockd_mounts--;
2d21ac55
A
170
171 /* send a shutdown request if there are no more lockd mounts */
172 send_shutdown = ((nfs_lockd_mounts == 0) && nfs_lockd_request_sent);
0a7de745 173 if (send_shutdown) {
2d21ac55 174 nfs_lockd_request_sent = 0;
0a7de745 175 }
2d21ac55
A
176
177 lck_mtx_unlock(nfs_lock_mutex);
178
0a7de745 179 if (!send_shutdown) {
2d21ac55 180 return;
0a7de745 181 }
2d21ac55
A
182
183 /*
6d2010ae 184 * Let lockd know that it is no longer needed for any NFS mounts
2d21ac55
A
185 */
186 kr = host_get_lockd_port(host_priv_self(), &lockd_port);
187 if ((kr != KERN_SUCCESS) || !IPC_PORT_VALID(lockd_port)) {
188 printf("nfs_lockd_mount_change: shutdown couldn't get port, kr %d, port %s\n",
0a7de745
A
189 kr, (lockd_port == IPC_PORT_NULL) ? "NULL" :
190 (lockd_port == IPC_PORT_DEAD) ? "DEAD" : "VALID");
2d21ac55
A
191 return;
192 }
193
194 kr = lockd_shutdown(lockd_port);
0a7de745 195 if (kr != KERN_SUCCESS) {
2d21ac55 196 printf("nfs_lockd_mount_change: shutdown %d\n", kr);
0a7de745 197 }
2d21ac55
A
198
199 ipc_port_release_send(lockd_port);
e5568f75
A
200}
201
202/*
203 * insert a lock request message into the pending queue
2d21ac55 204 * (nfs_lock_mutex must be held)
e5568f75 205 */
6d2010ae 206void
e5568f75
A
207nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *msgreq)
208{
209 LOCKD_MSG_REQUEST *mr;
210
211 mr = TAILQ_LAST(&nfs_pendlockq, nfs_lock_msg_queue);
212 if (!mr || (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
213 /* fast path: empty queue or new largest xid */
214 TAILQ_INSERT_TAIL(&nfs_pendlockq, msgreq, lmr_next);
215 return;
216 }
217 /* slow path: need to walk list to find insertion point */
218 while (mr && (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
219 mr = TAILQ_PREV(mr, nfs_lock_msg_queue, lmr_next);
220 }
221 if (mr) {
222 TAILQ_INSERT_AFTER(&nfs_pendlockq, mr, msgreq, lmr_next);
223 } else {
224 TAILQ_INSERT_HEAD(&nfs_pendlockq, msgreq, lmr_next);
225 }
226}
227
228/*
229 * remove a lock request message from the pending queue
2d21ac55 230 * (nfs_lock_mutex must be held)
e5568f75 231 */
6d2010ae 232void
e5568f75
A
233nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *msgreq)
234{
235 TAILQ_REMOVE(&nfs_pendlockq, msgreq, lmr_next);
236}
237
238/*
239 * find a pending lock request message by xid
240 *
241 * We search from the head of the list assuming that the message we're
242 * looking for is for an older request (because we have an answer to it).
243 * This assumes that lock request will be answered primarily in FIFO order.
244 * However, this may not be the case if there are blocked requests. We may
245 * want to move blocked requests to a separate queue (but that'll complicate
246 * duplicate xid checking).
2d21ac55
A
247 *
248 * (nfs_lock_mutex must be held)
e5568f75 249 */
6d2010ae 250LOCKD_MSG_REQUEST *
e5568f75
A
251nfs_lockdmsg_find_by_xid(uint64_t lockxid)
252{
253 LOCKD_MSG_REQUEST *mr;
254
255 TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
0a7de745 256 if (mr->lmr_msg.lm_xid == lockxid) {
e5568f75 257 return mr;
0a7de745
A
258 }
259 if (mr->lmr_msg.lm_xid > lockxid) {
e5568f75 260 return NULL;
0a7de745 261 }
e5568f75
A
262 }
263 return mr;
264}
265
266/*
267 * Because we can't depend on nlm_granted messages containing the same
6d2010ae
A
268 * cookie we sent with the original lock request, we need code to test
269 * if an nlm_granted answer matches the lock request. We also need code
e5568f75
A
270 * that can find a lockd message based solely on the nlm_granted answer.
271 */
272
273/*
274 * compare lockd message to answer
275 *
276 * returns 0 on equality and 1 if different
277 */
6d2010ae 278int
e5568f75
A
279nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *msgreq, struct lockd_ans *ansp)
280{
0a7de745 281 if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO)) {
e5568f75 282 return 1;
0a7de745
A
283 }
284 if (msgreq->lmr_msg.lm_fl.l_pid != ansp->la_pid) {
e5568f75 285 return 1;
0a7de745
A
286 }
287 if (msgreq->lmr_msg.lm_fl.l_start != ansp->la_start) {
e5568f75 288 return 1;
0a7de745
A
289 }
290 if (msgreq->lmr_msg.lm_fl.l_len != ansp->la_len) {
e5568f75 291 return 1;
0a7de745
A
292 }
293 if (msgreq->lmr_msg.lm_fh_len != ansp->la_fh_len) {
e5568f75 294 return 1;
0a7de745
A
295 }
296 if (bcmp(msgreq->lmr_msg.lm_fh, ansp->la_fh, ansp->la_fh_len)) {
e5568f75 297 return 1;
0a7de745 298 }
e5568f75
A
299 return 0;
300}
301
302/*
303 * find a pending lock request message based on the lock info provided
304 * in the lockd_ans/nlm_granted data. We need this because we can't
305 * depend on nlm_granted messages containing the same cookie we sent
306 * with the original lock request.
307 *
308 * We search from the head of the list assuming that the message we're
309 * looking for is for an older request (because we have an answer to it).
310 * This assumes that lock request will be answered primarily in FIFO order.
311 * However, this may not be the case if there are blocked requests. We may
312 * want to move blocked requests to a separate queue (but that'll complicate
313 * duplicate xid checking).
2d21ac55
A
314 *
315 * (nfs_lock_mutex must be held)
55e303ae 316 */
6d2010ae 317LOCKD_MSG_REQUEST *
e5568f75
A
318nfs_lockdmsg_find_by_answer(struct lockd_ans *ansp)
319{
320 LOCKD_MSG_REQUEST *mr;
321
0a7de745 322 if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO)) {
e5568f75 323 return NULL;
0a7de745 324 }
e5568f75 325 TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
0a7de745 326 if (!nfs_lockdmsg_compare_to_answer(mr, ansp)) {
e5568f75 327 break;
0a7de745 328 }
e5568f75
A
329 }
330 return mr;
331}
332
333/*
334 * return the next unique lock request transaction ID
2d21ac55 335 * (nfs_lock_mutex must be held)
e5568f75 336 */
6d2010ae 337uint64_t
e5568f75
A
338nfs_lockxid_get(void)
339{
340 LOCKD_MSG_REQUEST *mr;
341
342 /* derive initial lock xid from system time */
343 if (!nfs_lockxid) {
344 /*
345 * Note: it's OK if this code inits nfs_lockxid to 0 (for example,
346 * due to a broken clock) because we immediately increment it
347 * and we guarantee to never use xid 0. So, nfs_lockxid should only
348 * ever be 0 the first time this function is called.
349 */
350 struct timeval tv;
351 microtime(&tv);
352 nfs_lockxid = (uint64_t)tv.tv_sec << 12;
353 }
354
355 /* make sure we get a unique xid */
356 do {
357 /* Skip zero xid if it should ever happen. */
0a7de745 358 if (++nfs_lockxid == 0) {
e5568f75 359 nfs_lockxid++;
0a7de745 360 }
e5568f75 361 if (!(mr = TAILQ_LAST(&nfs_pendlockq, nfs_lock_msg_queue)) ||
0a7de745 362 (mr->lmr_msg.lm_xid < nfs_lockxid)) {
e5568f75
A
363 /* fast path: empty queue or new largest xid */
364 break;
365 }
366 /* check if xid is already in use */
367 } while (nfs_lockdmsg_find_by_xid(nfs_lockxid));
368
369 return nfs_lockxid;
370}
371
2d21ac55
A
372#define MACH_MAX_TRIES 3
373
b0d623f7
A
374int
375nfs_lockd_send_request(LOCKD_MSG *msg, int interruptable)
2d21ac55
A
376{
377 kern_return_t kr;
378 int retries = 0;
379 mach_port_t lockd_port = IPC_PORT_NULL;
380
381 kr = host_get_lockd_port(host_priv_self(), &lockd_port);
0a7de745
A
382 if (kr != KERN_SUCCESS || !IPC_PORT_VALID(lockd_port)) {
383 return ENOTSUP;
384 }
2d21ac55
A
385
386 do {
387 /* In the kernel all mach messaging is interruptable */
388 do {
389 kr = lockd_request(
390 lockd_port,
391 msg->lm_version,
392 msg->lm_flags,
393 msg->lm_xid,
394 msg->lm_fl.l_start,
395 msg->lm_fl.l_len,
396 msg->lm_fl.l_pid,
397 msg->lm_fl.l_type,
398 msg->lm_fl.l_whence,
399 (uint32_t *)&msg->lm_addr,
400 (uint32_t *)&msg->lm_cred,
401 msg->lm_fh_len,
402 msg->lm_fh);
0a7de745 403 if (kr != KERN_SUCCESS) {
2d21ac55 404 printf("lockd_request received %d!\n", kr);
0a7de745 405 }
2d21ac55
A
406 } while (!interruptable && kr == MACH_SEND_INTERRUPTED);
407 } while (kr == MIG_SERVER_DIED && retries++ < MACH_MAX_TRIES);
408
409 ipc_port_release_send(lockd_port);
410 switch (kr) {
0a7de745
A
411 case MACH_SEND_INTERRUPTED:
412 return EINTR;
2d21ac55
A
413 default:
414 /*
415 * Other MACH or MIG errors we will retry. Eventually
0a7de745 416 * we will call nfs_down and allow the user to disable
2d21ac55
A
417 * locking.
418 */
0a7de745 419 return EAGAIN;
2d21ac55 420 }
2d21ac55 421}
55e303ae
A
422
423/*
2d21ac55 424 * NFS advisory byte-level locks (client)
55e303ae
A
425 */
426int
6d2010ae
A
427nfs3_lockd_request(
428 nfsnode_t np,
429 int type,
430 LOCKD_MSG_REQUEST *msgreq,
431 int flags,
432 thread_t thd)
55e303ae 433{
6d2010ae 434 LOCKD_MSG *msg = &msgreq->lmr_msg;
2d21ac55 435 int error, error2;
6d2010ae 436 int interruptable, slpflag;
55e303ae 437 struct nfsmount *nmp;
e5568f75 438 struct timeval now;
f427ee49
A
439 int timeo, wentdown = 0;
440 long starttime, endtime, lastmsg;
2d21ac55 441 struct timespec ts;
6d2010ae 442 struct sockaddr *saddr;
55e303ae 443
6d2010ae 444 nmp = NFSTONMP(np);
0a7de745
A
445 if (!nmp || !nmp->nm_saddr) {
446 return ENXIO;
447 }
55e303ae 448
2d21ac55 449 lck_mtx_lock(&nmp->nm_lock);
6d2010ae 450 saddr = nmp->nm_saddr;
91447636 451 bcopy(saddr, &msg->lm_addr, min(sizeof msg->lm_addr, saddr->sa_len));
0a7de745 452 if (nmp->nm_vers == NFS_VER3) {
e5568f75 453 msg->lm_flags |= LOCKD_MSG_NFSV3;
0a7de745 454 }
316670eb 455
0a7de745 456 if (nmp->nm_sotype != SOCK_DGRAM) {
6d2010ae 457 msg->lm_flags |= LOCKD_MSG_TCP;
0a7de745 458 }
55e303ae 459
e5568f75 460 microuptime(&now);
6d2010ae 461 starttime = now.tv_sec;
e5568f75 462 lastmsg = now.tv_sec - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay));
6d2010ae 463 interruptable = NMFLAG(nmp, INTR);
2d21ac55 464 lck_mtx_unlock(&nmp->nm_lock);
55e303ae 465
2d21ac55 466 lck_mtx_lock(nfs_lock_mutex);
55e303ae 467
e5568f75
A
468 /* allocate unique xid */
469 msg->lm_xid = nfs_lockxid_get();
6d2010ae 470 nfs_lockdmsg_enqueue(msgreq);
e5568f75 471
6d2010ae 472 timeo = 4;
55e303ae 473
2d21ac55
A
474 for (;;) {
475 nfs_lockd_request_sent = 1;
55e303ae 476
b0d623f7 477 /* need to drop nfs_lock_mutex while calling nfs_lockd_send_request() */
2d21ac55 478 lck_mtx_unlock(nfs_lock_mutex);
b0d623f7 479 error = nfs_lockd_send_request(msg, interruptable);
2d21ac55 480 lck_mtx_lock(nfs_lock_mutex);
0a7de745 481 if (error && error != EAGAIN) {
55e303ae 482 break;
0a7de745 483 }
e5568f75 484
55e303ae 485 /*
e5568f75
A
486 * Always wait for an answer. Not waiting for unlocks could
487 * cause a lock to be left if the unlock request gets dropped.
55e303ae 488 */
55e303ae
A
489
490 /*
e5568f75
A
491 * Retry if it takes too long to get a response.
492 *
493 * The timeout numbers were picked out of thin air... they start
6d2010ae 494 * at 4 and double each timeout with a max of 30 seconds.
e5568f75
A
495 *
496 * In order to maintain responsiveness, we pass a small timeout
2d21ac55 497 * to msleep and calculate the timeouts ourselves. This allows
e5568f75 498 * us to pick up on mount changes quicker.
55e303ae 499 */
e5568f75
A
500wait_for_granted:
501 error = EWOULDBLOCK;
6d2010ae 502 slpflag = (interruptable && (type != F_UNLCK)) ? PCATCH : 0;
2d21ac55
A
503 ts.tv_sec = 2;
504 ts.tv_nsec = 0;
e5568f75 505 microuptime(&now);
2d21ac55 506 endtime = now.tv_sec + timeo;
e5568f75 507 while (now.tv_sec < endtime) {
2d21ac55 508 error = error2 = 0;
6d2010ae
A
509 if (!msgreq->lmr_answered) {
510 error = msleep(msgreq, nfs_lock_mutex, slpflag | PUSER, "lockd", &ts);
511 slpflag = 0;
512 }
513 if (msgreq->lmr_answered) {
55e303ae 514 /*
e5568f75
A
515 * Note: it's possible to have a lock granted at
516 * essentially the same time that we get interrupted.
517 * Since the lock may be granted, we can't return an
518 * error from this request or we might not unlock the
519 * lock that's been granted.
55e303ae 520 */
6d2010ae
A
521 nmp = NFSTONMP(np);
522 if ((msgreq->lmr_errno == ENOTSUP) && nmp &&
2d21ac55
A
523 (nmp->nm_state & NFSSTA_LOCKSWORK)) {
524 /*
525 * We have evidence that locks work, yet lockd
526 * returned ENOTSUP. This is probably because
527 * it was unable to contact the server's lockd
528 * to send it the request.
529 *
530 * Because we know locks work, we'll consider
531 * this failure to be a timeout.
532 */
533 error = EWOULDBLOCK;
534 } else {
535 error = 0;
536 }
e5568f75
A
537 break;
538 }
0a7de745 539 if (error != EWOULDBLOCK) {
e5568f75 540 break;
0a7de745 541 }
e5568f75
A
542 /* check that we still have our mount... */
543 /* ...and that we still support locks */
6d2010ae
A
544 /* ...and that there isn't a recovery pending */
545 nmp = NFSTONMP(np);
546 if ((error2 = nfs_sigintr(nmp, NULL, NULL, 0))) {
2d21ac55 547 error = error2;
0a7de745 548 if (type == F_UNLCK) {
6d2010ae 549 printf("nfs3_lockd_request: aborting unlock request, error %d\n", error);
0a7de745 550 }
2d21ac55
A
551 break;
552 }
553 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
554 if (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED) {
555 lck_mtx_unlock(&nmp->nm_lock);
556 break;
557 }
558 if ((nmp->nm_state & NFSSTA_RECOVER) && !(flags & R_RECOVER)) {
559 /* recovery pending... return an error that'll get this operation restarted */
560 error = NFSERR_GRACE;
2d21ac55 561 lck_mtx_unlock(&nmp->nm_lock);
e5568f75
A
562 break;
563 }
6d2010ae 564 interruptable = NMFLAG(nmp, INTR);
2d21ac55 565 lck_mtx_unlock(&nmp->nm_lock);
e5568f75
A
566 microuptime(&now);
567 }
568 if (error) {
569 /* check that we still have our mount... */
6d2010ae
A
570 nmp = NFSTONMP(np);
571 if ((error2 = nfs_sigintr(nmp, NULL, NULL, 0))) {
2d21ac55
A
572 error = error2;
573 if (error2 != EINTR) {
0a7de745 574 if (type == F_UNLCK) {
6d2010ae 575 printf("nfs3_lockd_request: aborting unlock request, error %d\n", error);
0a7de745 576 }
2d21ac55
A
577 break;
578 }
e5568f75
A
579 }
580 /* ...and that we still support locks */
2d21ac55 581 lck_mtx_lock(&nmp->nm_lock);
6d2010ae 582 if (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED) {
0a7de745 583 if (error == EWOULDBLOCK) {
91447636 584 error = ENOTSUP;
0a7de745 585 }
2d21ac55 586 lck_mtx_unlock(&nmp->nm_lock);
e5568f75
A
587 break;
588 }
6d2010ae
A
589 /* ...and that there isn't a recovery pending */
590 if ((error == EWOULDBLOCK) && (nmp->nm_state & NFSSTA_RECOVER) && !(flags & R_RECOVER)) {
591 /* recovery pending... return to allow recovery to occur */
592 error = NFSERR_DENIED;
593 lck_mtx_unlock(&nmp->nm_lock);
594 break;
595 }
596 interruptable = NMFLAG(nmp, INTR);
597 if ((error != EWOULDBLOCK) ||
598 ((nmp->nm_state & NFSSTA_RECOVER) && !(flags & R_RECOVER)) ||
599 ((flags & R_RECOVER) && ((now.tv_sec - starttime) > 30))) {
600 if ((error == EWOULDBLOCK) && (flags & R_RECOVER)) {
601 /* give up if this is for recovery and taking too long */
602 error = ETIMEDOUT;
603 } else if ((nmp->nm_state & NFSSTA_RECOVER) && !(flags & R_RECOVER)) {
604 /* recovery pending... return an error that'll get this operation restarted */
605 error = NFSERR_GRACE;
606 }
2d21ac55 607 lck_mtx_unlock(&nmp->nm_lock);
e5568f75
A
608 /*
609 * We're going to bail on this request.
610 * If we were a blocked lock request, send a cancel.
611 */
6d2010ae 612 if ((msgreq->lmr_errno == EINPROGRESS) &&
e5568f75
A
613 !(msg->lm_flags & LOCKD_MSG_CANCEL)) {
614 /* set this request up as a cancel */
615 msg->lm_flags |= LOCKD_MSG_CANCEL;
6d2010ae 616 nfs_lockdmsg_dequeue(msgreq);
e5568f75 617 msg->lm_xid = nfs_lockxid_get();
6d2010ae
A
618 nfs_lockdmsg_enqueue(msgreq);
619 msgreq->lmr_saved_errno = error;
620 msgreq->lmr_errno = 0;
621 msgreq->lmr_answered = 0;
e5568f75 622 /* reset timeout */
2d21ac55 623 timeo = 2;
e5568f75
A
624 /* send cancel request */
625 continue;
626 }
627 break;
628 }
629
e5568f75
A
630 /* warn if we're not getting any response */
631 microuptime(&now);
6d2010ae 632 if ((msgreq->lmr_errno != EINPROGRESS) &&
b0d623f7 633 !(msg->lm_flags & LOCKD_MSG_DENIED_GRACE) &&
e5568f75
A
634 (nmp->nm_tprintf_initial_delay != 0) &&
635 ((lastmsg + nmp->nm_tprintf_delay) < now.tv_sec)) {
2d21ac55 636 lck_mtx_unlock(&nmp->nm_lock);
e5568f75 637 lastmsg = now.tv_sec;
5ba3f43e 638 nfs_down(nmp, thd, 0, NFSSTA_LOCKTIMEO, "lockd not responding", 1);
e5568f75 639 wentdown = 1;
0a7de745 640 } else {
2d21ac55 641 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 642 }
2d21ac55 643
6d2010ae 644 if (msgreq->lmr_errno == EINPROGRESS) {
e5568f75
A
645 /*
646 * We've got a blocked lock request that we are
647 * going to retry. First, we'll want to try to
648 * send a cancel for the previous request.
649 *
650 * Clear errno so if we don't get a response
651 * to the resend we'll call nfs_down().
652 * Also reset timeout because we'll expect a
653 * quick response to the cancel/resend (even if
654 * it is NLM_BLOCKED).
655 */
656 msg->lm_flags |= LOCKD_MSG_CANCEL;
6d2010ae 657 nfs_lockdmsg_dequeue(msgreq);
e5568f75 658 msg->lm_xid = nfs_lockxid_get();
6d2010ae
A
659 nfs_lockdmsg_enqueue(msgreq);
660 msgreq->lmr_saved_errno = msgreq->lmr_errno;
661 msgreq->lmr_errno = 0;
662 msgreq->lmr_answered = 0;
2d21ac55 663 timeo = 2;
e5568f75 664 /* send cancel then resend request */
55e303ae
A
665 continue;
666 }
b0d623f7 667
e5568f75 668 /*
2d21ac55 669 * We timed out, so we will resend the request.
e5568f75 670 */
0a7de745 671 if (!(flags & R_RECOVER)) {
6d2010ae 672 timeo *= 2;
0a7de745
A
673 }
674 if (timeo > 30) {
6d2010ae 675 timeo = 30;
0a7de745 676 }
e5568f75
A
677 /* resend request */
678 continue;
679 }
55e303ae 680
91447636 681 /* we got a reponse, so the server's lockd is OK */
6d2010ae 682 nfs_up(NFSTONMP(np), thd, NFSSTA_LOCKTIMEO,
0a7de745 683 wentdown ? "lockd alive again" : NULL);
91447636 684 wentdown = 0;
55e303ae 685
6d2010ae 686 if (msgreq->lmr_answered && (msg->lm_flags & LOCKD_MSG_DENIED_GRACE)) {
b0d623f7
A
687 /*
688 * The lock request was denied because the server lockd is
689 * still in its grace period. So, we need to try the
6d2010ae
A
690 * request again in a little bit. Return the GRACE error so
691 * the higher levels can perform the retry.
b0d623f7 692 */
6d2010ae 693 msgreq->lmr_saved_errno = msgreq->lmr_errno = error = NFSERR_GRACE;
b0d623f7
A
694 }
695
6d2010ae 696 if (msgreq->lmr_errno == EINPROGRESS) {
e5568f75
A
697 /* got NLM_BLOCKED response */
698 /* need to wait for NLM_GRANTED */
6d2010ae
A
699 timeo = 30;
700 msgreq->lmr_answered = 0;
e5568f75
A
701 goto wait_for_granted;
702 }
703
704 if ((msg->lm_flags & LOCKD_MSG_CANCEL) &&
6d2010ae 705 (msgreq->lmr_saved_errno == EINPROGRESS)) {
e5568f75
A
706 /*
707 * We just got a successful reply to the
708 * cancel of the previous blocked lock request.
6d2010ae
A
709 * Now, go ahead and return a DENIED error so the
710 * higher levels can resend the request.
e5568f75
A
711 */
712 msg->lm_flags &= ~LOCKD_MSG_CANCEL;
6d2010ae 713 error = NFSERR_DENIED;
d9a64523 714 /* Will dequeue msgreq after the following break at the end of this routine */
6d2010ae 715 break;
55e303ae 716 }
e5568f75
A
717
718 /*
719 * If the blocked lock request was cancelled.
720 * Restore the error condition from when we
721 * originally bailed on the request.
722 */
723 if (msg->lm_flags & LOCKD_MSG_CANCEL) {
724 msg->lm_flags &= ~LOCKD_MSG_CANCEL;
6d2010ae
A
725 error = msgreq->lmr_saved_errno;
726 } else {
727 error = msgreq->lmr_errno;
728 }
e5568f75 729
6d2010ae 730 nmp = NFSTONMP(np);
2d21ac55
A
731 if ((error == ENOTSUP) && nmp && !(nmp->nm_state & NFSSTA_LOCKSWORK)) {
732 /*
733 * We have NO evidence that locks work and lockd
734 * returned ENOTSUP. Let's take this as a hint
735 * that locks aren't supported and disable them
736 * for this mount.
737 */
6d2010ae
A
738 nfs_lockdmsg_dequeue(msgreq);
739 lck_mtx_unlock(nfs_lock_mutex);
2d21ac55 740 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
741 if (nmp->nm_lockmode == NFS_LOCK_MODE_ENABLED) {
742 nmp->nm_lockmode = NFS_LOCK_MODE_DISABLED;
743 nfs_lockd_mount_unregister(nmp);
744 }
2d21ac55
A
745 nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
746 lck_mtx_unlock(&nmp->nm_lock);
747 printf("lockd returned ENOTSUP, disabling locks for nfs server: %s\n",
0a7de745
A
748 vfs_statfs(nmp->nm_mountp)->f_mntfromname);
749 return error;
2d21ac55 750 }
e5568f75
A
751 if (!error) {
752 /* record that NFS file locking has worked on this mount */
2d21ac55
A
753 if (nmp) {
754 lck_mtx_lock(&nmp->nm_lock);
0a7de745 755 if (!(nmp->nm_state & NFSSTA_LOCKSWORK)) {
2d21ac55 756 nmp->nm_state |= NFSSTA_LOCKSWORK;
0a7de745 757 }
2d21ac55
A
758 lck_mtx_unlock(&nmp->nm_lock);
759 }
e5568f75 760 }
55e303ae
A
761 break;
762 }
91447636 763
6d2010ae 764 nfs_lockdmsg_dequeue(msgreq);
55e303ae 765
2d21ac55
A
766 lck_mtx_unlock(nfs_lock_mutex);
767
0a7de745 768 return error;
55e303ae
A
769}
770
6d2010ae
A
771/*
772 * Send an NLM LOCK message to the server
773 */
774int
775nfs3_setlock_rpc(
776 nfsnode_t np,
777 struct nfs_open_file *nofp,
778 struct nfs_file_lock *nflp,
779 int reclaim,
780 int flags,
781 thread_t thd,
782 kauth_cred_t cred)
783{
784 struct nfs_lock_owner *nlop = nflp->nfl_owner;
785 struct nfsmount *nmp;
786 int error;
787 LOCKD_MSG_REQUEST msgreq;
788 LOCKD_MSG *msg;
789
790 nmp = NFSTONMP(np);
0a7de745
A
791 if (nfs_mount_gone(nmp)) {
792 return ENXIO;
793 }
6d2010ae
A
794
795 if (!nlop->nlo_open_owner) {
796 nfs_open_owner_ref(nofp->nof_owner);
797 nlop->nlo_open_owner = nofp->nof_owner;
798 }
0a7de745
A
799 if ((error = nfs_lock_owner_set_busy(nlop, thd))) {
800 return error;
801 }
6d2010ae
A
802
803 /* set up lock message request structure */
804 bzero(&msgreq, sizeof(msgreq));
805 msg = &msgreq.lmr_msg;
806 msg->lm_version = LOCKD_MSG_VERSION;
0a7de745 807 if ((nflp->nfl_flags & NFS_FILE_LOCK_WAIT) && !reclaim) {
6d2010ae 808 msg->lm_flags |= LOCKD_MSG_BLOCK;
0a7de745
A
809 }
810 if (reclaim) {
6d2010ae 811 msg->lm_flags |= LOCKD_MSG_RECLAIM;
0a7de745 812 }
6d2010ae
A
813 msg->lm_fh_len = (nmp->nm_vers == NFS_VER2) ? NFSX_V2FH : np->n_fhsize;
814 bcopy(np->n_fhp, msg->lm_fh, msg->lm_fh_len);
815 cru2x(cred, &msg->lm_cred);
816
817 msg->lm_fl.l_whence = SEEK_SET;
818 msg->lm_fl.l_start = nflp->nfl_start;
819 msg->lm_fl.l_len = NFS_FLOCK_LENGTH(nflp->nfl_start, nflp->nfl_end);
820 msg->lm_fl.l_type = nflp->nfl_type;
821 msg->lm_fl.l_pid = nlop->nlo_pid;
822
823 error = nfs3_lockd_request(np, 0, &msgreq, flags, thd);
824
825 nfs_lock_owner_clear_busy(nlop);
0a7de745 826 return error;
6d2010ae
A
827}
828
829/*
830 * Send an NLM UNLOCK message to the server
831 */
832int
833nfs3_unlock_rpc(
834 nfsnode_t np,
835 struct nfs_lock_owner *nlop,
836 __unused int type,
837 uint64_t start,
838 uint64_t end,
839 int flags,
840 thread_t thd,
841 kauth_cred_t cred)
842{
843 struct nfsmount *nmp;
844 LOCKD_MSG_REQUEST msgreq;
845 LOCKD_MSG *msg;
846
847 nmp = NFSTONMP(np);
0a7de745
A
848 if (!nmp) {
849 return ENXIO;
850 }
6d2010ae
A
851
852 /* set up lock message request structure */
853 bzero(&msgreq, sizeof(msgreq));
854 msg = &msgreq.lmr_msg;
855 msg->lm_version = LOCKD_MSG_VERSION;
856 msg->lm_fh_len = (nmp->nm_vers == NFS_VER2) ? NFSX_V2FH : np->n_fhsize;
857 bcopy(np->n_fhp, msg->lm_fh, msg->lm_fh_len);
858 cru2x(cred, &msg->lm_cred);
859
860 msg->lm_fl.l_whence = SEEK_SET;
861 msg->lm_fl.l_start = start;
862 msg->lm_fl.l_len = NFS_FLOCK_LENGTH(start, end);
863 msg->lm_fl.l_type = F_UNLCK;
864 msg->lm_fl.l_pid = nlop->nlo_pid;
865
0a7de745 866 return nfs3_lockd_request(np, F_UNLCK, &msgreq, flags, thd);
6d2010ae
A
867}
868
869/*
870 * Send an NLM LOCK TEST message to the server
871 */
872int
873nfs3_getlock_rpc(
874 nfsnode_t np,
875 struct nfs_lock_owner *nlop,
876 struct flock *fl,
877 uint64_t start,
878 uint64_t end,
879 vfs_context_t ctx)
880{
881 struct nfsmount *nmp;
882 int error;
883 LOCKD_MSG_REQUEST msgreq;
884 LOCKD_MSG *msg;
885
886 nmp = NFSTONMP(np);
0a7de745
A
887 if (nfs_mount_gone(nmp)) {
888 return ENXIO;
889 }
6d2010ae
A
890
891 /* set up lock message request structure */
892 bzero(&msgreq, sizeof(msgreq));
893 msg = &msgreq.lmr_msg;
894 msg->lm_version = LOCKD_MSG_VERSION;
895 msg->lm_flags |= LOCKD_MSG_TEST;
896 msg->lm_fh_len = (nmp->nm_vers == NFS_VER2) ? NFSX_V2FH : np->n_fhsize;
897 bcopy(np->n_fhp, msg->lm_fh, msg->lm_fh_len);
898 cru2x(vfs_context_ucred(ctx), &msg->lm_cred);
899
900 msg->lm_fl.l_whence = SEEK_SET;
901 msg->lm_fl.l_start = start;
902 msg->lm_fl.l_len = NFS_FLOCK_LENGTH(start, end);
903 msg->lm_fl.l_type = fl->l_type;
904 msg->lm_fl.l_pid = nlop->nlo_pid;
905
906 error = nfs3_lockd_request(np, 0, &msgreq, 0, vfs_context_thread(ctx));
907
908 if (!error && (msg->lm_flags & LOCKD_MSG_TEST) && !msgreq.lmr_errno) {
909 if (msg->lm_fl.l_type != F_UNLCK) {
910 fl->l_type = msg->lm_fl.l_type;
911 fl->l_pid = msg->lm_fl.l_pid;
912 fl->l_start = msg->lm_fl.l_start;
913 fl->l_len = msg->lm_fl.l_len;
914 fl->l_whence = SEEK_SET;
0a7de745 915 } else {
6d2010ae 916 fl->l_type = F_UNLCK;
0a7de745 917 }
6d2010ae
A
918 }
919
0a7de745 920 return error;
6d2010ae
A
921}
922
55e303ae
A
923/*
924 * nfslockdans --
925 * NFS advisory byte-level locks answer from the lock daemon.
926 */
927int
91447636 928nfslockdans(proc_t p, struct lockd_ans *ansp)
55e303ae 929{
e5568f75 930 LOCKD_MSG_REQUEST *msgreq;
55e303ae
A
931 int error;
932
91447636
A
933 /* Let root make this call. */
934 error = proc_suser(p);
0a7de745
A
935 if (error) {
936 return error;
937 }
55e303ae
A
938
939 /* the version should match, or we're out of sync */
0a7de745
A
940 if (ansp->la_version != LOCKD_ANS_VERSION) {
941 return EINVAL;
942 }
55e303ae 943
2d21ac55
A
944 lck_mtx_lock(nfs_lock_mutex);
945
e5568f75
A
946 /* try to find the lockd message by transaction id (cookie) */
947 msgreq = nfs_lockdmsg_find_by_xid(ansp->la_xid);
948 if (ansp->la_flags & LOCKD_ANS_GRANTED) {
949 /*
950 * We can't depend on the granted message having our cookie,
951 * so we check the answer against the lockd message found.
952 * If no message was found or it doesn't match the answer,
953 * we look for the lockd message by the answer's lock info.
954 */
0a7de745 955 if (!msgreq || nfs_lockdmsg_compare_to_answer(msgreq, ansp)) {
e5568f75 956 msgreq = nfs_lockdmsg_find_by_answer(ansp);
0a7de745 957 }
e5568f75
A
958 /*
959 * We need to make sure this request isn't being cancelled
960 * If it is, we don't want to accept the granted message.
961 */
0a7de745 962 if (msgreq && (msgreq->lmr_msg.lm_flags & LOCKD_MSG_CANCEL)) {
e5568f75 963 msgreq = NULL;
0a7de745 964 }
55e303ae 965 }
2d21ac55
A
966 if (!msgreq) {
967 lck_mtx_unlock(nfs_lock_mutex);
0a7de745 968 return EPIPE;
2d21ac55 969 }
55e303ae 970
e5568f75
A
971 msgreq->lmr_errno = ansp->la_errno;
972 if ((msgreq->lmr_msg.lm_flags & LOCKD_MSG_TEST) && msgreq->lmr_errno == 0) {
973 if (ansp->la_flags & LOCKD_ANS_LOCK_INFO) {
0a7de745 974 if (ansp->la_flags & LOCKD_ANS_LOCK_EXCL) {
e5568f75 975 msgreq->lmr_msg.lm_fl.l_type = F_WRLCK;
0a7de745 976 } else {
e5568f75 977 msgreq->lmr_msg.lm_fl.l_type = F_RDLCK;
0a7de745 978 }
e5568f75
A
979 msgreq->lmr_msg.lm_fl.l_pid = ansp->la_pid;
980 msgreq->lmr_msg.lm_fl.l_start = ansp->la_start;
981 msgreq->lmr_msg.lm_fl.l_len = ansp->la_len;
982 } else {
983 msgreq->lmr_msg.lm_fl.l_type = F_UNLCK;
984 }
985 }
0a7de745 986 if (ansp->la_flags & LOCKD_ANS_DENIED_GRACE) {
b0d623f7 987 msgreq->lmr_msg.lm_flags |= LOCKD_MSG_DENIED_GRACE;
0a7de745 988 }
55e303ae 989
e5568f75 990 msgreq->lmr_answered = 1;
2d21ac55
A
991 lck_mtx_unlock(nfs_lock_mutex);
992 wakeup(msgreq);
55e303ae 993
0a7de745 994 return 0;
55e303ae
A
995}
996
6d2010ae
A
997/*
998 * nfslockdnotify --
999 * NFS host restart notification from the lock daemon.
1000 *
1001 * Used to initiate reclaiming of held locks when a server we
1002 * have mounted reboots.
1003 */
1004int
1005nfslockdnotify(proc_t p, user_addr_t argp)
1006{
1007 int error, i, headsize;
1008 struct lockd_notify ln;
1009 struct nfsmount *nmp;
1010 struct sockaddr *saddr;
1011
1012 /* Let root make this call. */
1013 error = proc_suser(p);
0a7de745
A
1014 if (error) {
1015 return error;
1016 }
6d2010ae
A
1017
1018 headsize = (char*)&ln.ln_addr[0] - (char*)&ln.ln_version;
1019 error = copyin(argp, &ln, headsize);
0a7de745
A
1020 if (error) {
1021 return error;
1022 }
1023 if (ln.ln_version != LOCKD_NOTIFY_VERSION) {
1024 return EINVAL;
1025 }
1026 if ((ln.ln_addrcount < 1) || (ln.ln_addrcount > 128)) {
1027 return EINVAL;
1028 }
6d2010ae
A
1029 argp += headsize;
1030 saddr = (struct sockaddr *)&ln.ln_addr[0];
1031
1032 lck_mtx_lock(nfs_lock_mutex);
1033
0a7de745 1034 for (i = 0; i < ln.ln_addrcount; i++) {
6d2010ae 1035 error = copyin(argp, &ln.ln_addr[0], sizeof(ln.ln_addr[0]));
0a7de745 1036 if (error) {
6d2010ae 1037 break;
0a7de745 1038 }
6d2010ae
A
1039 argp += sizeof(ln.ln_addr[0]);
1040 /* scan lockd mount list for match to this address */
1041 TAILQ_FOREACH(nmp, &nfs_lockd_mount_list, nm_ldlink) {
1042 /* check if address matches this mount's server address */
0a7de745 1043 if (!nmp->nm_saddr || nfs_sockaddr_cmp(saddr, nmp->nm_saddr)) {
6d2010ae 1044 continue;
0a7de745 1045 }
6d2010ae
A
1046 /* We have a match! Mark it as needing recovery. */
1047 lck_mtx_lock(&nmp->nm_lock);
1048 nfs_need_recover(nmp, 0);
1049 lck_mtx_unlock(&nmp->nm_lock);
1050 }
1051 }
1052
1053 lck_mtx_unlock(nfs_lock_mutex);
1054
0a7de745 1055 return error;
6d2010ae 1056}
ea3f0419
A
1057
1058#endif /* CONFIG_NFS_CLIENT */