]>
git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_nqlease.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
24 * Copyright (c) 1992, 1993
25 * The Regents of the University of California. All rights reserved.
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * @(#)nfs_nqlease.c 8.9 (Berkeley) 5/20/95
59 * FreeBSD-Id: nfs_nqlease.c,v 1.32 1997/11/07 08:53:23 phk Exp $
65 * Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
66 * Mechanism for Distributed File Cache Consistency",
67 * In Proc. of the Twelfth ACM Symposium on Operating Systems
68 * Principals, pg. 202-210, Litchfield Park, AZ, Dec. 1989.
69 * Michael N. Nelson, Brent B. Welch and John K. Ousterhout, "Caching
70 * in the Sprite Network File System", ACM TOCS 6(1),
71 * pages 134-154, February 1988.
72 * V. Srinivasan and Jeffrey C. Mogul, "Spritely NFS: Implementation and
73 * Performance of Cache-Consistency Protocols", Digital
74 * Equipment Corporation WRL Research Report 89/5, May 1989.
76 #include <sys/param.h>
77 #include <sys/vnode.h>
78 #include <sys/mount.h>
79 #include <sys/kernel.h>
81 #include <sys/systm.h>
82 #include <sys/malloc.h>
84 #include <sys/socket.h>
85 #include <sys/socketvar.h>
86 #include <sys/protosw.h>
87 #include <machine/spl.h>
89 #include <netinet/in.h>
90 #include <nfs/rpcv2.h>
91 #include <nfs/nfsproto.h>
93 #include <nfs/nfsm_subs.h>
94 #include <nfs/xdr_subs.h>
95 #include <nfs/nqnfs.h>
96 #include <nfs/nfsnode.h>
97 #include <nfs/nfsmount.h>
99 time_t nqnfsstarttime
= (time_t)0;
100 int nqsrv_clockskew
= NQ_CLOCKSKEW
;
101 int nqsrv_writeslack
= NQ_WRITESLACK
;
102 int nqsrv_maxlease
= NQ_MAXLEASE
;
103 static int nqsrv_maxnumlease
= NQ_MAXNUMLEASE
;
105 struct vop_lease_args
;
107 static int nqsrv_cmpnam
__P((struct nfssvc_sock
*, struct mbuf
*,
109 extern void nqnfs_lease_updatetime
__P((int deltat
));
110 static int nqnfs_vacated
__P((struct vnode
*vp
, struct ucred
*cred
));
111 static void nqsrv_addhost
__P((struct nqhost
*lph
, struct nfssvc_sock
*slp
,
113 static void nqsrv_instimeq
__P((struct nqlease
*lp
, u_long duration
));
114 static void nqsrv_locklease
__P((struct nqlease
*lp
));
115 static void nqsrv_send_eviction
__P((struct vnode
*vp
, struct nqlease
*lp
,
116 struct nfssvc_sock
*slp
,
117 struct mbuf
*nam
, struct ucred
*cred
));
118 static void nqsrv_unlocklease
__P((struct nqlease
*lp
));
119 static void nqsrv_waitfor_expiry
__P((struct nqlease
*lp
));
122 * Signifies which rpcs can have piggybacked lease requests
124 int nqnfs_piggy
[NFS_NPROCS
] = {
153 extern nfstype nfsv2_type
[9];
154 extern nfstype nfsv3_type
[9];
155 extern struct nfssvc_sock
*nfs_udpsock
, *nfs_cltpsock
;
156 extern int nfsd_waiting
;
157 extern struct nfsstats nfsstats
;
158 extern int nfs_mount_type
;
165 * Get or check for a lease for "vp", based on ND_CHECK flag.
166 * The rules are as follows:
167 * - if a current non-caching lease, reply non-caching
168 * - if a current lease for same host only, extend lease
169 * - if a read cachable lease and a read lease request
170 * add host to list any reply cachable
171 * - else { set non-cachable for read-write sharing }
172 * send eviction notice messages to all other hosts that have lease
173 * wait for lease termination { either by receiving vacated messages
174 * from all the other hosts or expiry
176 * modify lease to non-cachable
177 * - else if no current lease, issue new one
179 * - return boolean TRUE iff nam should be m_freem()'d
180 * NB: Since nqnfs_serverd() is called from a timer, any potential tsleep()
181 * in here must be framed by nqsrv_locklease() and nqsrv_unlocklease().
182 * nqsrv_locklease() is coded such that at least one of LC_LOCKED and
183 * LC_WANTED is set whenever a process is tsleeping in it. The exception
184 * is when a new lease is being allocated, since it is not in the timer
185 * queue yet. (Ditto for the splsoftclock() and splx(s) calls)
188 nqsrv_getlease(vp
, duration
, flags
, slp
, procp
, nam
, cachablep
, frev
, cred
)
192 struct nfssvc_sock
*slp
;
199 register struct nqlease
*lp
;
200 register struct nqfhhashhead
*lpp
= 0;
201 register struct nqhost
*lph
= 0;
208 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
&& vp
->v_type
!= VLNK
)
210 if (*duration
> nqsrv_maxlease
)
211 *duration
= nqsrv_maxlease
;
212 error
= VOP_GETATTR(vp
, &vattr
, cred
, procp
);
215 *frev
= vattr
.va_filerev
;
218 if ((flags
& ND_CHECK
) == 0)
219 nfsstats
.srvnqnfs_getleases
++;
220 if (tlp
== (struct nqlease
*)0) {
223 * Find the lease by searching the hash list.
225 fh
.fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
226 error
= VFS_VPTOFH(vp
, &fh
.fh_fid
);
231 lpp
= NQFHHASH(fh
.fh_fid
.fid_data
);
232 for (lp
= lpp
->lh_first
; lp
!= 0; lp
= lp
->lc_hash
.le_next
)
233 if (fh
.fh_fsid
.val
[0] == lp
->lc_fsid
.val
[0] &&
234 fh
.fh_fsid
.val
[1] == lp
->lc_fsid
.val
[1] &&
235 !bcmp(fh
.fh_fid
.fid_data
, lp
->lc_fiddata
,
236 fh
.fh_fid
.fid_len
- sizeof (long))) {
246 if ((lp
->lc_flag
& LC_NONCACHABLE
) ||
247 (lp
->lc_morehosts
== (struct nqm
*)0 &&
248 nqsrv_cmpnam(slp
, nam
, &lp
->lc_host
)))
250 if ((flags
& ND_READ
) && (lp
->lc_flag
& LC_WRITE
) == 0) {
251 if (flags
& ND_CHECK
)
253 if (nqsrv_cmpnam(slp
, nam
, &lp
->lc_host
))
256 if (lp
->lc_morehosts
) {
257 lph
= lp
->lc_morehosts
->lpm_hosts
;
258 lphp
= &lp
->lc_morehosts
->lpm_next
;
261 lphp
= &lp
->lc_morehosts
;
264 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
265 if (nqsrv_cmpnam(slp
, nam
, lph
))
267 if (++i
== LC_MOREHOSTSIZ
) {
270 lph
= (*lphp
)->lpm_hosts
;
271 lphp
= &((*lphp
)->lpm_next
);
279 MALLOC_ZONE(*lphp
, struct nqm
*,
281 M_NQMHOST
, M_WAITOK
);
282 bzero((caddr_t
)*lphp
, sizeof (struct nqm
));
283 lph
= (*lphp
)->lpm_hosts
;
285 nqsrv_addhost(lph
, slp
, nam
);
286 nqsrv_unlocklease(lp
);
288 lp
->lc_flag
|= LC_NONCACHABLE
;
290 nqsrv_send_eviction(vp
, lp
, slp
, nam
, cred
);
291 nqsrv_waitfor_expiry(lp
);
292 nqsrv_unlocklease(lp
);
296 * Update the lease and return
298 if ((flags
& ND_CHECK
) == 0)
299 nqsrv_instimeq(lp
, *duration
);
300 if (lp
->lc_flag
& LC_NONCACHABLE
)
304 if (flags
& ND_WRITE
)
305 lp
->lc_flag
|= LC_WRITTEN
;
311 if (flags
& ND_CHECK
)
316 * The value of nqsrv_maxnumlease should be set generously, so that
317 * the following "printf" happens infrequently.
319 if (nfsstats
.srvnqnfs_leases
> nqsrv_maxnumlease
) {
320 printf("Nqnfs server, too many leases\n");
322 (void) tsleep((caddr_t
)&lbolt
, PSOCK
,
324 } while (nfsstats
.srvnqnfs_leases
> nqsrv_maxnumlease
);
326 MALLOC_ZONE(lp
, struct nqlease
*,
327 sizeof (struct nqlease
), M_NQLEASE
, M_WAITOK
);
328 bzero((caddr_t
)lp
, sizeof (struct nqlease
));
329 if (flags
& ND_WRITE
)
330 lp
->lc_flag
|= (LC_WRITE
| LC_WRITTEN
);
331 nqsrv_addhost(&lp
->lc_host
, slp
, nam
);
333 lp
->lc_fsid
= fh
.fh_fsid
;
334 bcopy(fh
.fh_fid
.fid_data
, lp
->lc_fiddata
,
335 fh
.fh_fid
.fid_len
- sizeof (long));
337 panic("nfs_nqlease.c: Phoney lpp");
338 LIST_INSERT_HEAD(lpp
, lp
, lc_hash
);
341 nqsrv_instimeq(lp
, *duration
);
344 if (++nfsstats
.srvnqnfs_leases
> nfsstats
.srvnqnfs_maxleases
)
345 nfsstats
.srvnqnfs_maxleases
= nfsstats
.srvnqnfs_leases
;
350 * Local lease check for server syscalls.
351 * Just set up args and let nqsrv_getlease() do the rest.
352 * nqnfs_vop_lease_check() is the VOP_LEASE() form of the same routine.
353 * Ifdef'd code in nfsnode.h renames these routines to whatever a particular
357 nqnfs_lease_check(vp
, p
, cred
, flag
)
367 (void) nqsrv_getlease(vp
, &duration
, ND_CHECK
| flag
, NQLOCALSLP
,
368 p
, (struct mbuf
*)0, &cache
, &frev
, cred
);
372 nqnfs_vop_lease_check(ap
)
373 struct vop_lease_args
/* {
376 struct ucred *a_cred;
384 (void) nqsrv_getlease(ap
->a_vp
, &duration
, ND_CHECK
| ap
->a_flag
,
385 NQLOCALSLP
, ap
->a_p
, (struct mbuf
*)0, &cache
, &frev
, ap
->a_cred
);
389 #endif /* NFS_NOSERVER */
392 * Add a host to an nqhost structure for a lease.
395 nqsrv_addhost(lph
, slp
, nam
)
396 register struct nqhost
*lph
;
397 struct nfssvc_sock
*slp
;
400 register struct sockaddr_in
*saddr
;
402 if (slp
== NQLOCALSLP
)
403 lph
->lph_flag
|= (LC_VALID
| LC_LOCAL
);
404 else if (slp
== nfs_udpsock
) {
405 saddr
= mtod(nam
, struct sockaddr_in
*);
406 lph
->lph_flag
|= (LC_VALID
| LC_UDP
);
407 lph
->lph_inetaddr
= saddr
->sin_addr
.s_addr
;
408 lph
->lph_port
= saddr
->sin_port
;
409 } else if (slp
== nfs_cltpsock
) {
410 lph
->lph_nam
= m_copym(nam
, 0, M_COPYALL
, M_WAIT
);
411 lph
->lph_flag
|= (LC_VALID
| LC_CLTP
);
413 lph
->lph_flag
|= (LC_VALID
| LC_SREF
);
420 * Update the lease expiry time and position it in the timer queue correctly.
423 nqsrv_instimeq(lp
, duration
)
424 register struct nqlease
*lp
;
427 register struct nqlease
*tlp
;
430 newexpiry
= time
.tv_sec
+ duration
+ nqsrv_clockskew
;
431 if (lp
->lc_expiry
== newexpiry
)
433 if (lp
->lc_timer
.cqe_next
!= 0) {
434 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
436 lp
->lc_expiry
= newexpiry
;
439 * Find where in the queue it should be.
441 tlp
= nqtimerhead
.cqh_last
;
442 while (tlp
!= (void *)&nqtimerhead
&& tlp
->lc_expiry
> newexpiry
)
443 tlp
= tlp
->lc_timer
.cqe_prev
;
445 if (tlp
== nqtimerhead
.cqh_last
)
446 NQSTORENOVRAM(newexpiry
);
447 #endif /* HASNVRAM */
448 if (tlp
== (void *)&nqtimerhead
) {
449 CIRCLEQ_INSERT_HEAD(&nqtimerhead
, lp
, lc_timer
);
451 CIRCLEQ_INSERT_AFTER(&nqtimerhead
, tlp
, lp
, lc_timer
);
456 * Compare the requesting host address with the lph entry in the lease.
457 * Return true iff it is the same.
458 * This is somewhat messy due to the union in the nqhost structure.
459 * The local host is indicated by the special value of NQLOCALSLP for slp.
462 nqsrv_cmpnam(slp
, nam
, lph
)
463 register struct nfssvc_sock
*slp
;
465 register struct nqhost
*lph
;
467 register struct sockaddr_in
*saddr
;
469 union nethostaddr lhaddr
;
472 if (slp
== NQLOCALSLP
) {
473 if (lph
->lph_flag
& LC_LOCAL
)
478 if (slp
== nfs_udpsock
|| slp
== nfs_cltpsock
)
482 if (lph
->lph_flag
& LC_UDP
)
483 ret
= netaddr_match(AF_INET
, &lph
->lph_haddr
, addr
);
484 else if (lph
->lph_flag
& LC_CLTP
)
485 ret
= netaddr_match(AF_ISO
, &lph
->lph_claddr
, addr
);
487 if ((lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0)
489 saddr
= mtod(lph
->lph_slp
->ns_nam
, struct sockaddr_in
*);
490 if (saddr
->sin_family
== AF_INET
)
491 lhaddr
.had_inetaddr
= saddr
->sin_addr
.s_addr
;
493 lhaddr
.had_nam
= lph
->lph_slp
->ns_nam
;
494 ret
= netaddr_match(saddr
->sin_family
, &lhaddr
, addr
);
500 * Send out eviction notice messages to all other hosts for the lease.
503 nqsrv_send_eviction(vp
, lp
, slp
, nam
, cred
)
505 register struct nqlease
*lp
;
506 struct nfssvc_sock
*slp
;
510 register struct nqhost
*lph
= &lp
->lc_host
;
511 register struct mbuf
*m
;
513 struct nqm
*lphnext
= lp
->lc_morehosts
;
514 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
517 struct sockaddr_in
*saddr
;
522 int len
= 1, ok
= 1, i
= 0;
523 int sotype
, *solockp
;
525 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
526 if (nqsrv_cmpnam(slp
, nam
, lph
))
527 lph
->lph_flag
|= LC_VACATED
;
528 else if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
529 if (lph
->lph_flag
& LC_UDP
) {
530 MGET(nam2
, M_WAIT
, MT_SONAME
);
531 saddr
= mtod(nam2
, struct sockaddr_in
*);
532 nam2
->m_len
= saddr
->sin_len
=
533 sizeof (struct sockaddr_in
);
534 saddr
->sin_family
= AF_INET
;
535 saddr
->sin_addr
.s_addr
= lph
->lph_inetaddr
;
536 saddr
->sin_port
= lph
->lph_port
;
537 so
= nfs_udpsock
->ns_so
;
538 } else if (lph
->lph_flag
& LC_CLTP
) {
540 so
= nfs_cltpsock
->ns_so
;
541 } else if (lph
->lph_slp
->ns_flag
& SLP_VALID
) {
542 nam2
= (struct mbuf
*)0;
543 so
= lph
->lph_slp
->ns_so
;
546 sotype
= so
->so_type
;
547 if (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
)
548 solockp
= &lph
->lph_slp
->ns_solock
;
551 nfsm_reqhead((struct vnode
*)0, NQNFSPROC_EVICTED
,
552 NFSX_V3FH
+ NFSX_UNSIGNED
);
553 fhp
= &nfh
.fh_generic
;
554 bzero((caddr_t
)fhp
, sizeof(nfh
));
555 fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
556 VFS_VPTOFH(vp
, &fhp
->fh_fid
);
557 nfsm_srvfhtom(fhp
, 1);
564 if (siz
<= 0 || siz
> NFS_MAXPACKET
) {
565 printf("mbuf siz=%d\n",siz
);
566 panic("Bad nfs svc reply");
568 m
= nfsm_rpchead(cred
, (NFSMNT_NFSV3
| NFSMNT_NQNFS
),
570 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
571 0, (char *)NULL
, mreq
, siz
, &mheadend
, &xid
);
573 * For stream protocols, prepend a Sun RPC
576 if (sotype
== SOCK_STREAM
) {
577 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
578 *mtod(m
, u_long
*) = htonl(0x80000000 |
579 (m
->m_pkthdr
.len
- NFSX_UNSIGNED
));
581 if (((lph
->lph_flag
& (LC_UDP
| LC_CLTP
)) == 0 &&
582 (lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0) ||
583 (solockp
&& (*solockp
& NFSMNT_SNDLOCK
)))
587 *solockp
|= NFSMNT_SNDLOCK
;
588 (void) nfs_send(so
, nam2
, m
,
591 nfs_sndunlock(solockp
);
593 if (lph
->lph_flag
& LC_UDP
)
600 len
= LC_MOREHOSTSIZ
;
601 lph
= lphnext
->lpm_hosts
;
602 lphnext
= lphnext
->lpm_next
;
611 * Wait for the lease to expire.
612 * This will occur when all clients have sent "vacated" messages to
613 * this server OR when it expires do to timeout.
616 nqsrv_waitfor_expiry(lp
)
617 register struct nqlease
*lp
;
619 register struct nqhost
*lph
;
625 if (time
.tv_sec
> lp
->lc_expiry
)
628 lphnext
= lp
->lc_morehosts
;
632 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
633 if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
634 lp
->lc_flag
|= LC_EXPIREDWANTED
;
635 (void) tsleep((caddr_t
)&lp
->lc_flag
, PSOCK
,
642 len
= LC_MOREHOSTSIZ
;
643 lph
= lphnext
->lpm_hosts
;
644 lphnext
= lphnext
->lpm_next
;
655 * Nqnfs server timer that maintains the server lease queue.
656 * Scan the lease queue for expired entries:
657 * - when one is found, wakeup anyone waiting for it
658 * else dequeue and free
663 register struct nqlease
*lp
;
664 register struct nqhost
*lph
;
665 struct nqlease
*nextlp
;
666 struct nqm
*lphnext
, *olphnext
;
670 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
672 if (lp
->lc_expiry
>= time
.tv_sec
)
674 nextlp
= lp
->lc_timer
.cqe_next
;
675 if (lp
->lc_flag
& LC_EXPIREDWANTED
) {
676 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
677 wakeup((caddr_t
)&lp
->lc_flag
);
678 } else if ((lp
->lc_flag
& (LC_LOCKED
| LC_WANTED
)) == 0) {
680 * Make a best effort at keeping a write caching lease long
681 * enough by not deleting it until it has been explicitly
682 * vacated or there have been no writes in the previous
683 * write_slack seconds since expiry and the nfsds are not
684 * all busy. The assumption is that if the nfsds are not
685 * all busy now (no queue of nfs requests), then the client
686 * would have been able to do at least one write to the
687 * file during the last write_slack seconds if it was still
688 * trying to push writes to the server.
690 if ((lp
->lc_flag
& (LC_WRITE
| LC_VACATED
)) == LC_WRITE
&&
691 ((lp
->lc_flag
& LC_WRITTEN
) || nfsd_waiting
== 0)) {
692 lp
->lc_flag
&= ~LC_WRITTEN
;
693 nqsrv_instimeq(lp
, nqsrv_writeslack
);
695 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
696 LIST_REMOVE(lp
, lc_hash
);
698 * This soft reference may no longer be valid, but
699 * no harm done. The worst case is if the vnode was
700 * recycled and has another valid lease reference,
701 * which is dereferenced prematurely.
703 lp
->lc_vp
->v_lease
= (struct nqlease
*)0;
705 lphnext
= lp
->lc_morehosts
;
706 olphnext
= (struct nqm
*)0;
710 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
711 if (lph
->lph_flag
& LC_CLTP
)
712 MFREE(lph
->lph_nam
, n
);
713 if (lph
->lph_flag
& LC_SREF
)
714 nfsrv_slpderef(lph
->lph_slp
);
717 _FREE_ZONE((caddr_t
)olphnext
,
720 olphnext
= (struct nqm
*)0;
725 len
= LC_MOREHOSTSIZ
;
726 lph
= lphnext
->lpm_hosts
;
727 lphnext
= lphnext
->lpm_next
;
733 FREE_ZONE((caddr_t
)lp
,
734 sizeof (struct nqlease
), M_NQLEASE
);
736 _FREE_ZONE((caddr_t
)olphnext
,
737 sizeof (struct nqm
), M_NQMHOST
);
738 nfsstats
.srvnqnfs_leases
--;
745 * Called from nfssvc_nfsd() for a getlease rpc request.
746 * Do the from/to xdr translation and call nqsrv_getlease() to
750 nqnfsrv_getlease(nfsd
, slp
, procp
, mrq
)
751 struct nfsrv_descript
*nfsd
;
752 struct nfssvc_sock
*slp
;
756 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
757 struct mbuf
*nam
= nfsd
->nd_nam
;
758 caddr_t dpos
= nfsd
->nd_dpos
;
759 struct ucred
*cred
= &nfsd
->nd_cr
;
760 register struct nfs_fattr
*fp
;
762 register struct vattr
*vap
= &va
;
772 struct mbuf
*mb
, *mb2
, *mreq
;
773 int flags
, rdonly
, cache
;
775 fhp
= &nfh
.fh_generic
;
777 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
778 flags
= fxdr_unsigned(int, *tl
++);
779 nfsd
->nd_duration
= fxdr_unsigned(int, *tl
);
780 error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
, &rdonly
,
781 (nfsd
->nd_flag
& ND_KERBAUTH
), TRUE
);
784 if (rdonly
&& flags
== ND_WRITE
) {
789 (void) nqsrv_getlease(vp
, &nfsd
->nd_duration
, flags
, slp
, procp
,
790 nam
, &cache
, &frev
, cred
);
791 error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
793 nfsm_reply(NFSX_V3FATTR
+ 4 * NFSX_UNSIGNED
);
794 nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
795 *tl
++ = txdr_unsigned(cache
);
796 *tl
++ = txdr_unsigned(nfsd
->nd_duration
);
797 txdr_hyper(&frev
, tl
);
798 nfsm_build(fp
, struct nfs_fattr
*, NFSX_V3FATTR
);
799 nfsm_srvfillattr(vap
, fp
);
804 * Called from nfssvc_nfsd() when a "vacated" message is received from a
805 * client. Find the entry and expire it.
808 nqnfsrv_vacated(nfsd
, slp
, procp
, mrq
)
809 struct nfsrv_descript
*nfsd
;
810 struct nfssvc_sock
*slp
;
814 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
815 struct mbuf
*nam
= nfsd
->nd_nam
;
816 caddr_t dpos
= nfsd
->nd_dpos
;
817 register struct nqlease
*lp
;
818 register struct nqhost
*lph
;
819 struct nqlease
*tlp
= (struct nqlease
*)0;
825 struct mbuf
*mreq
, *mb
;
826 int error
= 0, i
, len
, ok
, gotit
= 0, cache
= 0;
830 fhp
= &nfh
.fh_generic
;
834 * Find the lease by searching the hash list.
836 for (lp
= NQFHHASH(fhp
->fh_fid
.fid_data
)->lh_first
; lp
!= 0;
837 lp
= lp
->lc_hash
.le_next
)
838 if (fhp
->fh_fsid
.val
[0] == lp
->lc_fsid
.val
[0] &&
839 fhp
->fh_fsid
.val
[1] == lp
->lc_fsid
.val
[1] &&
840 !bcmp(fhp
->fh_fid
.fid_data
, lp
->lc_fiddata
,
851 lphnext
= lp
->lc_morehosts
;
853 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
854 if (nqsrv_cmpnam(slp
, nam
, lph
)) {
855 lph
->lph_flag
|= LC_VACATED
;
861 len
= LC_MOREHOSTSIZ
;
863 lph
= lphnext
->lpm_hosts
;
864 lphnext
= lphnext
->lpm_next
;
870 if ((lp
->lc_flag
& LC_EXPIREDWANTED
) && gotit
) {
871 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
872 wakeup((caddr_t
)&lp
->lc_flag
);
880 #endif /* NFS_NOSERVER */
883 * Client get lease rpc function.
886 nqnfs_getlease(vp
, rwflag
, cred
, p
)
887 register struct vnode
*vp
;
894 register long t1
, t2
;
895 register struct nfsnode
*np
;
896 struct nfsmount
*nmp
= VFSTONFS(vp
->v_mount
);
897 caddr_t bpos
, dpos
, cp2
;
900 struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
904 nfsstats
.rpccnt
[NQNFSPROC_GETLEASE
]++;
905 mb
= mreq
= nfsm_reqh(vp
, NQNFSPROC_GETLEASE
, NFSX_V3FH
+2*NFSX_UNSIGNED
,
908 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
909 *tl
++ = txdr_unsigned(rwflag
);
910 *tl
= txdr_unsigned(nmp
->nm_leaseterm
);
911 reqtime
= time
.tv_sec
;
912 nfsm_request(vp
, NQNFSPROC_GETLEASE
, p
, cred
);
914 nfsm_dissect(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
915 cachable
= fxdr_unsigned(int, *tl
++);
916 reqtime
+= fxdr_unsigned(int, *tl
++);
917 if (reqtime
> time
.tv_sec
) {
918 fxdr_hyper(tl
, &frev
);
919 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
, reqtime
, frev
);
920 nfsm_loadattr(vp
, (struct vattr
*)0);
922 error
= NQNFS_EXPIRED
;
928 * Client vacated message function.
931 nqnfs_vacated(vp
, cred
)
932 register struct vnode
*vp
;
936 register struct mbuf
*m
;
943 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
944 struct nfsmount
*nmp
;
947 nmp
= VFSTONFS(vp
->v_mount
);
948 nfsstats
.rpccnt
[NQNFSPROC_VACATED
]++;
949 nfsm_reqhead(vp
, NQNFSPROC_VACATED
, NFSX_FH(1));
957 m
= nfsm_rpchead(cred
, nmp
->nm_flag
, NQNFSPROC_VACATED
,
958 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
959 0, (char *)NULL
, mreq
, i
, &mheadend
, &xid
);
960 if (nmp
->nm_sotype
== SOCK_STREAM
) {
961 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
962 *mtod(m
, u_long
*) = htonl(0x80000000 | (m
->m_pkthdr
.len
-
967 if (nmp
->nm_soflags
& PR_CONNREQUIRED
)
968 (void) nfs_sndlock(&nmp
->nm_flag
, (struct nfsreq
*)0);
969 (void) nfs_send(nmp
->nm_so
, nmp
->nm_nam
, m
, &myrep
);
970 if (nmp
->nm_soflags
& PR_CONNREQUIRED
)
971 nfs_sndunlock(&nmp
->nm_flag
);
979 * Called for client side callbacks
982 nqnfs_callback(nmp
, mrep
, md
, dpos
)
983 struct nfsmount
*nmp
;
984 struct mbuf
*mrep
, *md
;
987 register struct vnode
*vp
;
994 struct nfssvc_sock
*slp
;
995 struct nfsrv_descript ndesc
;
996 register struct nfsrv_descript
*nfsd
= &ndesc
;
997 struct mbuf
**mrq
= (struct mbuf
**)0, *mb
, *mreq
;
998 int error
= 0, cache
= 0;
1005 nfsd
->nd_mrep
= mrep
;
1007 nfsd
->nd_dpos
= dpos
;
1008 error
= nfs_getreq(nfsd
, &tnfsd
, FALSE
);
1012 dpos
= nfsd
->nd_dpos
;
1013 if (nfsd
->nd_procnum
!= NQNFSPROC_EVICTED
) {
1017 fhp
= &nfh
.fh_generic
;
1020 error
= nfs_nget(nmp
->nm_mountp
, (nfsfh_t
*)fhp
, NFSX_V3FH
, &np
);
1024 if (np
->n_timer
.cqe_next
!= 0) {
1026 np
->n_flag
|= NQNFSEVICTED
;
1027 if (nmp
->nm_timerhead
.cqh_first
!= np
) {
1028 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1029 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1038 * Nqnfs client helper daemon. Runs once a second to expire leases.
1039 * It also get authorization strings for "kerb" mounts.
1040 * It must start at the beginning of the list again after any potential
1041 * "sleep" since nfs_reclaim() called from vclean() can pull a node off
1042 * the list asynchronously.
1045 nqnfs_clientd(nmp
, cred
, ncd
, flag
, argp
, p
)
1046 register struct nfsmount
*nmp
;
1048 struct nfsd_cargs
*ncd
;
1053 register struct nfsnode
*np
;
1055 struct nfsreq myrep
;
1056 struct nfsuid
*nuidp
, *nnuidp
;
1057 int error
= 0, vpid
;
1058 register struct nfsreq
*rp
;
1061 * First initialize some variables
1065 * If an authorization string is being passed in, get it.
1067 if ((flag
& NFSSVC_GOTAUTH
) &&
1068 (nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_DISMNT
)) == 0) {
1069 if (nmp
->nm_flag
& NFSMNT_HASAUTH
)
1071 if ((flag
& NFSSVC_AUTHINFAIL
) == 0) {
1072 if (ncd
->ncd_authlen
<= nmp
->nm_authlen
&&
1073 ncd
->ncd_verflen
<= nmp
->nm_verflen
&&
1074 !copyin(ncd
->ncd_authstr
,nmp
->nm_authstr
,ncd
->ncd_authlen
)&&
1075 !copyin(ncd
->ncd_verfstr
,nmp
->nm_verfstr
,ncd
->ncd_verflen
)){
1076 nmp
->nm_authtype
= ncd
->ncd_authtype
;
1077 nmp
->nm_authlen
= ncd
->ncd_authlen
;
1078 nmp
->nm_verflen
= ncd
->ncd_verflen
;
1080 nmp
->nm_key
= ncd
->ncd_key
;
1083 nmp
->nm_flag
|= NFSMNT_AUTHERR
;
1085 nmp
->nm_flag
|= NFSMNT_AUTHERR
;
1086 nmp
->nm_flag
|= NFSMNT_HASAUTH
;
1087 wakeup((caddr_t
)&nmp
->nm_authlen
);
1089 nmp
->nm_flag
|= NFSMNT_WAITAUTH
;
1092 * Loop every second updating queue until there is a termination sig.
1094 while ((nmp
->nm_flag
& NFSMNT_DISMNT
) == 0) {
1095 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1097 * If there are no outstanding requests (and therefore no
1098 * processes in nfs_reply) and there is data in the receive
1099 * queue, poke for callbacks.
1101 if (nfs_reqq
.tqh_first
== 0 && nmp
->nm_so
&&
1102 nmp
->nm_so
->so_rcv
.sb_cc
> 0) {
1103 myrep
.r_flags
= R_GETONEREP
;
1105 myrep
.r_mrep
= (struct mbuf
*)0;
1106 myrep
.r_procp
= (struct proc
*)0;
1107 (void) nfs_reply(&myrep
);
1111 * Loop through the leases, updating as required.
1113 np
= nmp
->nm_timerhead
.cqh_first
;
1114 while (np
!= (void *)&nmp
->nm_timerhead
&&
1115 (nmp
->nm_flag
& NFSMNT_DISMINPROG
) == 0) {
1118 if (np
->n_expiry
< time
.tv_sec
) {
1119 if (vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1120 nmp
->nm_inprog
= vp
;
1121 if (vpid
== vp
->v_id
) {
1122 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1123 np
->n_timer
.cqe_next
= 0;
1124 if (np
->n_flag
& (NMODIFIED
| NQNFSEVICTED
)) {
1125 if (np
->n_flag
& NQNFSEVICTED
) {
1126 if (vp
->v_type
== VDIR
)
1129 (void) nfs_vinvalbuf(vp
,
1130 V_SAVE
, cred
, p
, 0);
1131 np
->n_flag
&= ~NQNFSEVICTED
;
1132 (void) nqnfs_vacated(vp
, cred
);
1133 } else if (vp
->v_type
== VREG
) {
1134 (void) VOP_FSYNC(vp
, cred
,
1136 np
->n_flag
&= ~NMODIFIED
;
1141 nmp
->nm_inprog
= NULLVP
;
1143 } else if ((np
->n_expiry
- NQ_RENEWAL
) < time
.tv_sec
) {
1144 if ((np
->n_flag
& (NQNFSWRITE
| NQNFSNONCACHE
))
1145 == NQNFSWRITE
&& vp
->v_dirtyblkhd
.lh_first
&&
1146 vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1147 nmp
->nm_inprog
= vp
;
1148 if (vpid
== vp
->v_id
&&
1149 nqnfs_getlease(vp
, ND_WRITE
, cred
, p
)==0)
1150 np
->n_brev
= np
->n_lrev
;
1152 nmp
->nm_inprog
= NULLVP
;
1156 if (np
== nmp
->nm_timerhead
.cqh_first
)
1158 np
= nmp
->nm_timerhead
.cqh_first
;
1163 * Get an authorization string, if required.
1165 if ((nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_DISMNT
| NFSMNT_HASAUTH
)) == 0) {
1166 ncd
->ncd_authuid
= nmp
->nm_authuid
;
1167 if (copyout((caddr_t
)ncd
, argp
, sizeof (struct nfsd_cargs
)))
1168 nmp
->nm_flag
|= NFSMNT_WAITAUTH
;
1174 * Wait a bit (no pun) and do it again.
1176 if ((nmp
->nm_flag
& NFSMNT_DISMNT
) == 0 &&
1177 (nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_HASAUTH
))) {
1178 error
= tsleep((caddr_t
)&nmp
->nm_authstr
, PSOCK
| PCATCH
,
1179 "nqnfstimr", hz
/ 3);
1180 if (error
== EINTR
|| error
== ERESTART
)
1181 (void) dounmount(nmp
->nm_mountp
, 0, p
);
1186 * Finally, we can free up the mount structure.
1188 for (nuidp
= nmp
->nm_uidlruhead
.tqh_first
; nuidp
!= 0; nuidp
= nnuidp
) {
1189 nnuidp
= nuidp
->nu_lru
.tqe_next
;
1190 LIST_REMOVE(nuidp
, nu_hash
);
1191 TAILQ_REMOVE(&nmp
->nm_uidlruhead
, nuidp
, nu_lru
);
1192 _FREE_ZONE((caddr_t
)nuidp
, sizeof (struct nfsuid
), M_NFSUID
);
1195 * Loop through outstanding request list and remove dangling
1196 * references to defunct nfsmount struct
1198 for (rp
= nfs_reqq
.tqh_first
; rp
; rp
= rp
->r_chain
.tqe_next
)
1199 if (rp
->r_nmp
== nmp
)
1200 rp
->r_nmp
= (struct nfsmount
*)0;
1201 _FREE_ZONE((caddr_t
)nmp
, sizeof (struct nfsmount
), M_NFSMNT
);
1202 if (error
== EWOULDBLOCK
)
1207 #endif /* NFS_NOSERVER */
1210 * Adjust all timer queue expiry times when the time of day clock is changed.
1211 * Called from the settimeofday() syscall.
1214 nqnfs_lease_updatetime(deltat
)
1215 register int deltat
;
1217 struct proc
*p
= current_proc(); /* XXX */
1220 struct mount
*mp
, *nxtmp
;
1221 struct nfsmount
*nmp
;
1224 if (nqnfsstarttime
!= 0)
1225 nqnfsstarttime
+= deltat
;
1227 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
1228 lp
= lp
->lc_timer
.cqe_next
)
1229 lp
->lc_expiry
+= deltat
;
1233 * Search the mount list for all nqnfs mounts and do their timer
1236 simple_lock(&mountlist_slock
);
1237 for (mp
= mountlist
.cqh_first
; mp
!= (void *)&mountlist
; mp
= nxtmp
) {
1238 if (vfs_busy(mp
, LK_NOWAIT
, &mountlist_slock
, p
)) {
1239 nxtmp
= mp
->mnt_list
.cqe_next
;
1242 if (mp
->mnt_stat
.f_type
== nfs_mount_type
) {
1244 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1245 for (np
= nmp
->nm_timerhead
.cqh_first
;
1246 np
!= (void *)&nmp
->nm_timerhead
;
1247 np
= np
->n_timer
.cqe_next
) {
1248 np
->n_expiry
+= deltat
;
1252 simple_lock(&mountlist_slock
);
1253 nxtmp
= mp
->mnt_list
.cqe_next
;
1256 simple_unlock(&mountlist_slock
);
1260 * Lock a server lease.
1267 while (lp
->lc_flag
& LC_LOCKED
) {
1268 lp
->lc_flag
|= LC_WANTED
;
1269 (void) tsleep((caddr_t
)lp
, PSOCK
, "nqlc", 0);
1271 lp
->lc_flag
|= LC_LOCKED
;
1272 lp
->lc_flag
&= ~LC_WANTED
;
1276 * Unlock a server lease.
1279 nqsrv_unlocklease(lp
)
1283 lp
->lc_flag
&= ~LC_LOCKED
;
1284 if (lp
->lc_flag
& LC_WANTED
)
1285 wakeup((caddr_t
)lp
);
1289 * Update a client lease.
1292 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
, expiry
, frev
)
1293 register struct nfsmount
*nmp
;
1294 register struct nfsnode
*np
;
1295 int rwflag
, cachable
;
1299 register struct nfsnode
*tp
;
1301 if (np
->n_timer
.cqe_next
!= 0) {
1302 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1303 if (rwflag
== ND_WRITE
)
1304 np
->n_flag
|= NQNFSWRITE
;
1305 } else if (rwflag
== ND_READ
)
1306 np
->n_flag
&= ~NQNFSWRITE
;
1308 np
->n_flag
|= NQNFSWRITE
;
1310 np
->n_flag
&= ~NQNFSNONCACHE
;
1312 np
->n_flag
|= NQNFSNONCACHE
;
1313 np
->n_expiry
= expiry
;
1315 tp
= nmp
->nm_timerhead
.cqh_last
;
1316 while (tp
!= (void *)&nmp
->nm_timerhead
&& tp
->n_expiry
> np
->n_expiry
)
1317 tp
= tp
->n_timer
.cqe_prev
;
1318 if (tp
== (void *)&nmp
->nm_timerhead
) {
1319 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1321 CIRCLEQ_INSERT_AFTER(&nmp
->nm_timerhead
, tp
, np
, n_timer
);