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