]>
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 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
27 * Copyright (c) 1992, 1993
28 * The Regents of the University of California. All rights reserved.
30 * This code is derived from software contributed to Berkeley by
31 * Rick Macklem at The University of Guelph.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * @(#)nfs_nqlease.c 8.9 (Berkeley) 5/20/95
62 * FreeBSD-Id: nfs_nqlease.c,v 1.32 1997/11/07 08:53:23 phk Exp $
68 * Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
69 * Mechanism for Distributed File Cache Consistency",
70 * In Proc. of the Twelfth ACM Symposium on Operating Systems
71 * Principals, pg. 202-210, Litchfield Park, AZ, Dec. 1989.
72 * Michael N. Nelson, Brent B. Welch and John K. Ousterhout, "Caching
73 * in the Sprite Network File System", ACM TOCS 6(1),
74 * pages 134-154, February 1988.
75 * V. Srinivasan and Jeffrey C. Mogul, "Spritely NFS: Implementation and
76 * Performance of Cache-Consistency Protocols", Digital
77 * Equipment Corporation WRL Research Report 89/5, May 1989.
79 #include <sys/param.h>
80 #include <sys/vnode.h>
81 #include <sys/mount.h>
82 #include <sys/kernel.h>
84 #include <sys/systm.h>
85 #include <sys/malloc.h>
87 #include <sys/socket.h>
88 #include <sys/socketvar.h>
89 #include <sys/protosw.h>
90 #include <machine/spl.h>
92 #include <netinet/in.h>
93 #include <nfs/rpcv2.h>
94 #include <nfs/nfsproto.h>
96 #include <nfs/nfsm_subs.h>
97 #include <nfs/xdr_subs.h>
98 #include <nfs/nqnfs.h>
99 #include <nfs/nfsnode.h>
100 #include <nfs/nfsmount.h>
102 time_t nqnfsstarttime
= (time_t)0;
103 int nqsrv_clockskew
= NQ_CLOCKSKEW
;
104 int nqsrv_writeslack
= NQ_WRITESLACK
;
105 int nqsrv_maxlease
= NQ_MAXLEASE
;
106 static int nqsrv_maxnumlease
= NQ_MAXNUMLEASE
;
108 struct vop_lease_args
;
110 static int nqsrv_cmpnam
__P((struct nfssvc_sock
*, struct mbuf
*,
112 extern void nqnfs_lease_updatetime
__P((int deltat
));
113 static int nqnfs_vacated
__P((struct vnode
*vp
, struct ucred
*cred
));
114 static void nqsrv_addhost
__P((struct nqhost
*lph
, struct nfssvc_sock
*slp
,
116 static void nqsrv_instimeq
__P((struct nqlease
*lp
, u_long duration
));
117 static void nqsrv_locklease
__P((struct nqlease
*lp
));
118 static void nqsrv_send_eviction
__P((struct vnode
*vp
, struct nqlease
*lp
,
119 struct nfssvc_sock
*slp
,
120 struct mbuf
*nam
, struct ucred
*cred
));
121 static void nqsrv_unlocklease
__P((struct nqlease
*lp
));
122 static void nqsrv_waitfor_expiry
__P((struct nqlease
*lp
));
125 * Signifies which rpcs can have piggybacked lease requests
127 int nqnfs_piggy
[NFS_NPROCS
] = {
156 extern nfstype nfsv2_type
[9];
157 extern nfstype nfsv3_type
[9];
158 extern struct nfssvc_sock
*nfs_udpsock
, *nfs_cltpsock
;
159 extern int nfsd_waiting
;
160 extern struct nfsstats nfsstats
;
161 extern int nfs_mount_type
;
168 * Get or check for a lease for "vp", based on ND_CHECK flag.
169 * The rules are as follows:
170 * - if a current non-caching lease, reply non-caching
171 * - if a current lease for same host only, extend lease
172 * - if a read cachable lease and a read lease request
173 * add host to list any reply cachable
174 * - else { set non-cachable for read-write sharing }
175 * send eviction notice messages to all other hosts that have lease
176 * wait for lease termination { either by receiving vacated messages
177 * from all the other hosts or expiry
179 * modify lease to non-cachable
180 * - else if no current lease, issue new one
182 * - return boolean TRUE iff nam should be m_freem()'d
183 * NB: Since nqnfs_serverd() is called from a timer, any potential tsleep()
184 * in here must be framed by nqsrv_locklease() and nqsrv_unlocklease().
185 * nqsrv_locklease() is coded such that at least one of LC_LOCKED and
186 * LC_WANTED is set whenever a process is tsleeping in it. The exception
187 * is when a new lease is being allocated, since it is not in the timer
188 * queue yet. (Ditto for the splsoftclock() and splx(s) calls)
191 nqsrv_getlease(vp
, duration
, flags
, slp
, procp
, nam
, cachablep
, frev
, cred
)
195 struct nfssvc_sock
*slp
;
202 register struct nqlease
*lp
;
203 register struct nqfhhashhead
*lpp
= 0;
204 register struct nqhost
*lph
= 0;
211 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
&& vp
->v_type
!= VLNK
)
213 if (*duration
> nqsrv_maxlease
)
214 *duration
= nqsrv_maxlease
;
215 error
= VOP_GETATTR(vp
, &vattr
, cred
, procp
);
218 *frev
= vattr
.va_filerev
;
221 if ((flags
& ND_CHECK
) == 0)
222 nfsstats
.srvnqnfs_getleases
++;
223 if (tlp
== (struct nqlease
*)0) {
226 * Find the lease by searching the hash list.
228 fh
.fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
229 error
= VFS_VPTOFH(vp
, &fh
.fh_fid
);
234 lpp
= NQFHHASH(fh
.fh_fid
.fid_data
);
235 for (lp
= lpp
->lh_first
; lp
!= 0; lp
= lp
->lc_hash
.le_next
)
236 if (fh
.fh_fsid
.val
[0] == lp
->lc_fsid
.val
[0] &&
237 fh
.fh_fsid
.val
[1] == lp
->lc_fsid
.val
[1] &&
238 !bcmp(fh
.fh_fid
.fid_data
, lp
->lc_fiddata
,
239 fh
.fh_fid
.fid_len
- sizeof (long))) {
249 if ((lp
->lc_flag
& LC_NONCACHABLE
) ||
250 (lp
->lc_morehosts
== (struct nqm
*)0 &&
251 nqsrv_cmpnam(slp
, nam
, &lp
->lc_host
)))
253 if ((flags
& ND_READ
) && (lp
->lc_flag
& LC_WRITE
) == 0) {
254 if (flags
& ND_CHECK
)
256 if (nqsrv_cmpnam(slp
, nam
, &lp
->lc_host
))
259 if (lp
->lc_morehosts
) {
260 lph
= lp
->lc_morehosts
->lpm_hosts
;
261 lphp
= &lp
->lc_morehosts
->lpm_next
;
264 lphp
= &lp
->lc_morehosts
;
267 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
268 if (nqsrv_cmpnam(slp
, nam
, lph
))
270 if (++i
== LC_MOREHOSTSIZ
) {
273 lph
= (*lphp
)->lpm_hosts
;
274 lphp
= &((*lphp
)->lpm_next
);
282 MALLOC_ZONE(*lphp
, struct nqm
*,
284 M_NQMHOST
, M_WAITOK
);
285 bzero((caddr_t
)*lphp
, sizeof (struct nqm
));
286 lph
= (*lphp
)->lpm_hosts
;
288 nqsrv_addhost(lph
, slp
, nam
);
289 nqsrv_unlocklease(lp
);
291 lp
->lc_flag
|= LC_NONCACHABLE
;
293 nqsrv_send_eviction(vp
, lp
, slp
, nam
, cred
);
294 nqsrv_waitfor_expiry(lp
);
295 nqsrv_unlocklease(lp
);
299 * Update the lease and return
301 if ((flags
& ND_CHECK
) == 0)
302 nqsrv_instimeq(lp
, *duration
);
303 if (lp
->lc_flag
& LC_NONCACHABLE
)
307 if (flags
& ND_WRITE
)
308 lp
->lc_flag
|= LC_WRITTEN
;
314 if (flags
& ND_CHECK
)
319 * The value of nqsrv_maxnumlease should be set generously, so that
320 * the following "printf" happens infrequently.
322 if (nfsstats
.srvnqnfs_leases
> nqsrv_maxnumlease
) {
323 printf("Nqnfs server, too many leases\n");
325 (void) tsleep((caddr_t
)&lbolt
, PSOCK
,
327 } while (nfsstats
.srvnqnfs_leases
> nqsrv_maxnumlease
);
329 MALLOC_ZONE(lp
, struct nqlease
*,
330 sizeof (struct nqlease
), M_NQLEASE
, M_WAITOK
);
331 bzero((caddr_t
)lp
, sizeof (struct nqlease
));
332 if (flags
& ND_WRITE
)
333 lp
->lc_flag
|= (LC_WRITE
| LC_WRITTEN
);
334 nqsrv_addhost(&lp
->lc_host
, slp
, nam
);
336 lp
->lc_fsid
= fh
.fh_fsid
;
337 bcopy(fh
.fh_fid
.fid_data
, lp
->lc_fiddata
,
338 fh
.fh_fid
.fid_len
- sizeof (long));
340 panic("nfs_nqlease.c: Phoney lpp");
341 LIST_INSERT_HEAD(lpp
, lp
, lc_hash
);
344 nqsrv_instimeq(lp
, *duration
);
347 if (++nfsstats
.srvnqnfs_leases
> nfsstats
.srvnqnfs_maxleases
)
348 nfsstats
.srvnqnfs_maxleases
= nfsstats
.srvnqnfs_leases
;
353 * Local lease check for server syscalls.
354 * Just set up args and let nqsrv_getlease() do the rest.
355 * nqnfs_vop_lease_check() is the VOP_LEASE() form of the same routine.
356 * Ifdef'd code in nfsnode.h renames these routines to whatever a particular
360 nqnfs_lease_check(vp
, p
, cred
, flag
)
370 (void) nqsrv_getlease(vp
, &duration
, ND_CHECK
| flag
, NQLOCALSLP
,
371 p
, (struct mbuf
*)0, &cache
, &frev
, cred
);
375 nqnfs_vop_lease_check(ap
)
376 struct vop_lease_args
/* {
379 struct ucred *a_cred;
387 (void) nqsrv_getlease(ap
->a_vp
, &duration
, ND_CHECK
| ap
->a_flag
,
388 NQLOCALSLP
, ap
->a_p
, (struct mbuf
*)0, &cache
, &frev
, ap
->a_cred
);
392 #endif /* NFS_NOSERVER */
395 * Add a host to an nqhost structure for a lease.
398 nqsrv_addhost(lph
, slp
, nam
)
399 register struct nqhost
*lph
;
400 struct nfssvc_sock
*slp
;
403 register struct sockaddr_in
*saddr
;
405 if (slp
== NQLOCALSLP
)
406 lph
->lph_flag
|= (LC_VALID
| LC_LOCAL
);
407 else if (slp
== nfs_udpsock
) {
408 saddr
= mtod(nam
, struct sockaddr_in
*);
409 lph
->lph_flag
|= (LC_VALID
| LC_UDP
);
410 lph
->lph_inetaddr
= saddr
->sin_addr
.s_addr
;
411 lph
->lph_port
= saddr
->sin_port
;
412 } else if (slp
== nfs_cltpsock
) {
413 lph
->lph_nam
= m_copym(nam
, 0, M_COPYALL
, M_WAIT
);
414 lph
->lph_flag
|= (LC_VALID
| LC_CLTP
);
416 lph
->lph_flag
|= (LC_VALID
| LC_SREF
);
423 * Update the lease expiry time and position it in the timer queue correctly.
426 nqsrv_instimeq(lp
, duration
)
427 register struct nqlease
*lp
;
430 register struct nqlease
*tlp
;
433 newexpiry
= time
.tv_sec
+ duration
+ nqsrv_clockskew
;
434 if (lp
->lc_expiry
== newexpiry
)
436 if (lp
->lc_timer
.cqe_next
!= 0) {
437 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
439 lp
->lc_expiry
= newexpiry
;
442 * Find where in the queue it should be.
444 tlp
= nqtimerhead
.cqh_last
;
445 while (tlp
!= (void *)&nqtimerhead
&& tlp
->lc_expiry
> newexpiry
)
446 tlp
= tlp
->lc_timer
.cqe_prev
;
448 if (tlp
== nqtimerhead
.cqh_last
)
449 NQSTORENOVRAM(newexpiry
);
450 #endif /* HASNVRAM */
451 if (tlp
== (void *)&nqtimerhead
) {
452 CIRCLEQ_INSERT_HEAD(&nqtimerhead
, lp
, lc_timer
);
454 CIRCLEQ_INSERT_AFTER(&nqtimerhead
, tlp
, lp
, lc_timer
);
459 * Compare the requesting host address with the lph entry in the lease.
460 * Return true iff it is the same.
461 * This is somewhat messy due to the union in the nqhost structure.
462 * The local host is indicated by the special value of NQLOCALSLP for slp.
465 nqsrv_cmpnam(slp
, nam
, lph
)
466 register struct nfssvc_sock
*slp
;
468 register struct nqhost
*lph
;
470 register struct sockaddr_in
*saddr
;
472 union nethostaddr lhaddr
;
475 if (slp
== NQLOCALSLP
) {
476 if (lph
->lph_flag
& LC_LOCAL
)
481 if (slp
== nfs_udpsock
|| slp
== nfs_cltpsock
)
485 if (lph
->lph_flag
& LC_UDP
)
486 ret
= netaddr_match(AF_INET
, &lph
->lph_haddr
, addr
);
487 else if (lph
->lph_flag
& LC_CLTP
)
488 ret
= netaddr_match(AF_ISO
, &lph
->lph_claddr
, addr
);
490 if ((lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0)
492 saddr
= mtod(lph
->lph_slp
->ns_nam
, struct sockaddr_in
*);
493 if (saddr
->sin_family
== AF_INET
)
494 lhaddr
.had_inetaddr
= saddr
->sin_addr
.s_addr
;
496 lhaddr
.had_nam
= lph
->lph_slp
->ns_nam
;
497 ret
= netaddr_match(saddr
->sin_family
, &lhaddr
, addr
);
503 * Send out eviction notice messages to all other hosts for the lease.
506 nqsrv_send_eviction(vp
, lp
, slp
, nam
, cred
)
508 register struct nqlease
*lp
;
509 struct nfssvc_sock
*slp
;
513 register struct nqhost
*lph
= &lp
->lc_host
;
514 register struct mbuf
*m
;
516 struct nqm
*lphnext
= lp
->lc_morehosts
;
517 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
520 struct sockaddr_in
*saddr
;
525 int len
= 1, ok
= 1, i
= 0;
526 int sotype
, *solockp
;
528 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
529 if (nqsrv_cmpnam(slp
, nam
, lph
))
530 lph
->lph_flag
|= LC_VACATED
;
531 else if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
532 if (lph
->lph_flag
& LC_UDP
) {
533 MGET(nam2
, M_WAIT
, MT_SONAME
);
534 saddr
= mtod(nam2
, struct sockaddr_in
*);
535 nam2
->m_len
= saddr
->sin_len
=
536 sizeof (struct sockaddr_in
);
537 saddr
->sin_family
= AF_INET
;
538 saddr
->sin_addr
.s_addr
= lph
->lph_inetaddr
;
539 saddr
->sin_port
= lph
->lph_port
;
540 so
= nfs_udpsock
->ns_so
;
541 } else if (lph
->lph_flag
& LC_CLTP
) {
543 so
= nfs_cltpsock
->ns_so
;
544 } else if (lph
->lph_slp
->ns_flag
& SLP_VALID
) {
545 nam2
= (struct mbuf
*)0;
546 so
= lph
->lph_slp
->ns_so
;
549 sotype
= so
->so_type
;
550 if (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
)
551 solockp
= &lph
->lph_slp
->ns_solock
;
554 nfsm_reqhead((struct vnode
*)0, NQNFSPROC_EVICTED
,
555 NFSX_V3FH
+ NFSX_UNSIGNED
);
556 fhp
= &nfh
.fh_generic
;
557 bzero((caddr_t
)fhp
, sizeof(nfh
));
558 fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
559 VFS_VPTOFH(vp
, &fhp
->fh_fid
);
560 nfsm_srvfhtom(fhp
, 1);
567 if (siz
<= 0 || siz
> NFS_MAXPACKET
) {
568 printf("mbuf siz=%d\n",siz
);
569 panic("Bad nfs svc reply");
571 m
= nfsm_rpchead(cred
, (NFSMNT_NFSV3
| NFSMNT_NQNFS
),
573 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
574 0, (char *)NULL
, mreq
, siz
, &mheadend
, &xid
);
576 * For stream protocols, prepend a Sun RPC
579 if (sotype
== SOCK_STREAM
) {
580 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
581 *mtod(m
, u_long
*) = htonl(0x80000000 |
582 (m
->m_pkthdr
.len
- NFSX_UNSIGNED
));
584 if (((lph
->lph_flag
& (LC_UDP
| LC_CLTP
)) == 0 &&
585 (lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0) ||
586 (solockp
&& (*solockp
& NFSMNT_SNDLOCK
)))
590 *solockp
|= NFSMNT_SNDLOCK
;
591 (void) nfs_send(so
, nam2
, m
,
594 nfs_sndunlock(solockp
);
596 if (lph
->lph_flag
& LC_UDP
)
603 len
= LC_MOREHOSTSIZ
;
604 lph
= lphnext
->lpm_hosts
;
605 lphnext
= lphnext
->lpm_next
;
614 * Wait for the lease to expire.
615 * This will occur when all clients have sent "vacated" messages to
616 * this server OR when it expires do to timeout.
619 nqsrv_waitfor_expiry(lp
)
620 register struct nqlease
*lp
;
622 register struct nqhost
*lph
;
628 if (time
.tv_sec
> lp
->lc_expiry
)
631 lphnext
= lp
->lc_morehosts
;
635 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
636 if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
637 lp
->lc_flag
|= LC_EXPIREDWANTED
;
638 (void) tsleep((caddr_t
)&lp
->lc_flag
, PSOCK
,
645 len
= LC_MOREHOSTSIZ
;
646 lph
= lphnext
->lpm_hosts
;
647 lphnext
= lphnext
->lpm_next
;
658 * Nqnfs server timer that maintains the server lease queue.
659 * Scan the lease queue for expired entries:
660 * - when one is found, wakeup anyone waiting for it
661 * else dequeue and free
666 register struct nqlease
*lp
;
667 register struct nqhost
*lph
;
668 struct nqlease
*nextlp
;
669 struct nqm
*lphnext
, *olphnext
;
673 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
675 if (lp
->lc_expiry
>= time
.tv_sec
)
677 nextlp
= lp
->lc_timer
.cqe_next
;
678 if (lp
->lc_flag
& LC_EXPIREDWANTED
) {
679 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
680 wakeup((caddr_t
)&lp
->lc_flag
);
681 } else if ((lp
->lc_flag
& (LC_LOCKED
| LC_WANTED
)) == 0) {
683 * Make a best effort at keeping a write caching lease long
684 * enough by not deleting it until it has been explicitly
685 * vacated or there have been no writes in the previous
686 * write_slack seconds since expiry and the nfsds are not
687 * all busy. The assumption is that if the nfsds are not
688 * all busy now (no queue of nfs requests), then the client
689 * would have been able to do at least one write to the
690 * file during the last write_slack seconds if it was still
691 * trying to push writes to the server.
693 if ((lp
->lc_flag
& (LC_WRITE
| LC_VACATED
)) == LC_WRITE
&&
694 ((lp
->lc_flag
& LC_WRITTEN
) || nfsd_waiting
== 0)) {
695 lp
->lc_flag
&= ~LC_WRITTEN
;
696 nqsrv_instimeq(lp
, nqsrv_writeslack
);
698 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
699 LIST_REMOVE(lp
, lc_hash
);
701 * This soft reference may no longer be valid, but
702 * no harm done. The worst case is if the vnode was
703 * recycled and has another valid lease reference,
704 * which is dereferenced prematurely.
706 lp
->lc_vp
->v_lease
= (struct nqlease
*)0;
708 lphnext
= lp
->lc_morehosts
;
709 olphnext
= (struct nqm
*)0;
713 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
714 if (lph
->lph_flag
& LC_CLTP
)
715 MFREE(lph
->lph_nam
, n
);
716 if (lph
->lph_flag
& LC_SREF
)
717 nfsrv_slpderef(lph
->lph_slp
);
720 _FREE_ZONE((caddr_t
)olphnext
,
723 olphnext
= (struct nqm
*)0;
728 len
= LC_MOREHOSTSIZ
;
729 lph
= lphnext
->lpm_hosts
;
730 lphnext
= lphnext
->lpm_next
;
736 FREE_ZONE((caddr_t
)lp
,
737 sizeof (struct nqlease
), M_NQLEASE
);
739 _FREE_ZONE((caddr_t
)olphnext
,
740 sizeof (struct nqm
), M_NQMHOST
);
741 nfsstats
.srvnqnfs_leases
--;
748 * Called from nfssvc_nfsd() for a getlease rpc request.
749 * Do the from/to xdr translation and call nqsrv_getlease() to
753 nqnfsrv_getlease(nfsd
, slp
, procp
, mrq
)
754 struct nfsrv_descript
*nfsd
;
755 struct nfssvc_sock
*slp
;
759 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
760 struct mbuf
*nam
= nfsd
->nd_nam
;
761 caddr_t dpos
= nfsd
->nd_dpos
;
762 struct ucred
*cred
= &nfsd
->nd_cr
;
763 register struct nfs_fattr
*fp
;
765 register struct vattr
*vap
= &va
;
775 struct mbuf
*mb
, *mb2
, *mreq
;
776 int flags
, rdonly
, cache
;
778 fhp
= &nfh
.fh_generic
;
780 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
781 flags
= fxdr_unsigned(int, *tl
++);
782 nfsd
->nd_duration
= fxdr_unsigned(int, *tl
);
783 error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
, &rdonly
,
784 (nfsd
->nd_flag
& ND_KERBAUTH
), TRUE
);
787 if (rdonly
&& flags
== ND_WRITE
) {
792 (void) nqsrv_getlease(vp
, &nfsd
->nd_duration
, flags
, slp
, procp
,
793 nam
, &cache
, &frev
, cred
);
794 error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
796 nfsm_reply(NFSX_V3FATTR
+ 4 * NFSX_UNSIGNED
);
797 nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
798 *tl
++ = txdr_unsigned(cache
);
799 *tl
++ = txdr_unsigned(nfsd
->nd_duration
);
800 txdr_hyper(&frev
, tl
);
801 nfsm_build(fp
, struct nfs_fattr
*, NFSX_V3FATTR
);
802 nfsm_srvfillattr(vap
, fp
);
807 * Called from nfssvc_nfsd() when a "vacated" message is received from a
808 * client. Find the entry and expire it.
811 nqnfsrv_vacated(nfsd
, slp
, procp
, mrq
)
812 struct nfsrv_descript
*nfsd
;
813 struct nfssvc_sock
*slp
;
817 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
818 struct mbuf
*nam
= nfsd
->nd_nam
;
819 caddr_t dpos
= nfsd
->nd_dpos
;
820 register struct nqlease
*lp
;
821 register struct nqhost
*lph
;
822 struct nqlease
*tlp
= (struct nqlease
*)0;
828 struct mbuf
*mreq
, *mb
;
829 int error
= 0, i
, len
, ok
, gotit
= 0, cache
= 0;
833 fhp
= &nfh
.fh_generic
;
837 * Find the lease by searching the hash list.
839 for (lp
= NQFHHASH(fhp
->fh_fid
.fid_data
)->lh_first
; lp
!= 0;
840 lp
= lp
->lc_hash
.le_next
)
841 if (fhp
->fh_fsid
.val
[0] == lp
->lc_fsid
.val
[0] &&
842 fhp
->fh_fsid
.val
[1] == lp
->lc_fsid
.val
[1] &&
843 !bcmp(fhp
->fh_fid
.fid_data
, lp
->lc_fiddata
,
854 lphnext
= lp
->lc_morehosts
;
856 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
857 if (nqsrv_cmpnam(slp
, nam
, lph
)) {
858 lph
->lph_flag
|= LC_VACATED
;
864 len
= LC_MOREHOSTSIZ
;
866 lph
= lphnext
->lpm_hosts
;
867 lphnext
= lphnext
->lpm_next
;
873 if ((lp
->lc_flag
& LC_EXPIREDWANTED
) && gotit
) {
874 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
875 wakeup((caddr_t
)&lp
->lc_flag
);
883 #endif /* NFS_NOSERVER */
886 * Client get lease rpc function.
889 nqnfs_getlease(vp
, rwflag
, cred
, p
)
890 register struct vnode
*vp
;
897 register long t1
, t2
;
898 register struct nfsnode
*np
;
899 struct nfsmount
*nmp
= VFSTONFS(vp
->v_mount
);
900 caddr_t bpos
, dpos
, cp2
;
903 struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
908 nfsstats
.rpccnt
[NQNFSPROC_GETLEASE
]++;
909 mb
= mreq
= nfsm_reqh(vp
, NQNFSPROC_GETLEASE
, NFSX_V3FH
+2*NFSX_UNSIGNED
,
912 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
913 *tl
++ = txdr_unsigned(rwflag
);
914 *tl
= txdr_unsigned(nmp
->nm_leaseterm
);
915 reqtime
= time
.tv_sec
;
916 nfsm_request(vp
, NQNFSPROC_GETLEASE
, p
, cred
, &xid
);
918 nfsm_dissect(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
919 cachable
= fxdr_unsigned(int, *tl
++);
920 reqtime
+= fxdr_unsigned(int, *tl
++);
921 if (reqtime
> time
.tv_sec
) {
922 fxdr_hyper(tl
, &frev
);
923 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
, reqtime
, frev
);
924 nfsm_loadattr(vp
, (struct vattr
*)0, &xid
);
926 error
= NQNFS_EXPIRED
;
932 * Client vacated message function.
935 nqnfs_vacated(vp
, cred
)
936 register struct vnode
*vp
;
940 register struct mbuf
*m
;
947 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
948 struct nfsmount
*nmp
;
951 nmp
= VFSTONFS(vp
->v_mount
);
952 nfsstats
.rpccnt
[NQNFSPROC_VACATED
]++;
953 nfsm_reqhead(vp
, NQNFSPROC_VACATED
, NFSX_FH(1));
961 m
= nfsm_rpchead(cred
, nmp
->nm_flag
, NQNFSPROC_VACATED
,
962 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
963 0, (char *)NULL
, mreq
, i
, &mheadend
, &xid
);
964 if (nmp
->nm_sotype
== SOCK_STREAM
) {
965 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
966 *mtod(m
, u_long
*) = htonl(0x80000000 | (m
->m_pkthdr
.len
-
971 if (nmp
->nm_soflags
& PR_CONNREQUIRED
)
972 (void) nfs_sndlock(&nmp
->nm_flag
, (struct nfsreq
*)0);
973 (void) nfs_send(nmp
->nm_so
, nmp
->nm_nam
, m
, &myrep
);
974 if (nmp
->nm_soflags
& PR_CONNREQUIRED
)
975 nfs_sndunlock(&nmp
->nm_flag
);
983 * Called for client side callbacks
986 nqnfs_callback(nmp
, mrep
, md
, dpos
)
987 struct nfsmount
*nmp
;
988 struct mbuf
*mrep
, *md
;
991 register struct vnode
*vp
;
998 struct nfssvc_sock
*slp
;
999 struct nfsrv_descript ndesc
;
1000 register struct nfsrv_descript
*nfsd
= &ndesc
;
1001 struct mbuf
**mrq
= (struct mbuf
**)0, *mb
, *mreq
;
1002 int error
= 0, cache
= 0;
1009 nfsd
->nd_mrep
= mrep
;
1011 nfsd
->nd_dpos
= dpos
;
1012 error
= nfs_getreq(nfsd
, &tnfsd
, FALSE
);
1016 dpos
= nfsd
->nd_dpos
;
1017 if (nfsd
->nd_procnum
!= NQNFSPROC_EVICTED
) {
1021 fhp
= &nfh
.fh_generic
;
1024 error
= nfs_nget(nmp
->nm_mountp
, (nfsfh_t
*)fhp
, NFSX_V3FH
, &np
);
1028 if (np
->n_timer
.cqe_next
!= 0) {
1030 np
->n_flag
|= NQNFSEVICTED
;
1031 if (nmp
->nm_timerhead
.cqh_first
!= np
) {
1032 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1033 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1042 * Nqnfs client helper daemon. Runs once a second to expire leases.
1043 * It also get authorization strings for "kerb" mounts.
1044 * It must start at the beginning of the list again after any potential
1045 * "sleep" since nfs_reclaim() called from vclean() can pull a node off
1046 * the list asynchronously.
1049 nqnfs_clientd(nmp
, cred
, ncd
, flag
, argp
, p
)
1050 register struct nfsmount
*nmp
;
1052 struct nfsd_cargs
*ncd
;
1057 register struct nfsnode
*np
;
1059 struct nfsreq myrep
;
1060 struct nfsuid
*nuidp
, *nnuidp
;
1061 int error
= 0, vpid
;
1062 register struct nfsreq
*rp
;
1065 * First initialize some variables
1069 * If an authorization string is being passed in, get it.
1071 if ((flag
& NFSSVC_GOTAUTH
) &&
1072 (nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_DISMNT
)) == 0) {
1073 if (nmp
->nm_flag
& NFSMNT_HASAUTH
)
1075 if ((flag
& NFSSVC_AUTHINFAIL
) == 0) {
1076 if (ncd
->ncd_authlen
<= nmp
->nm_authlen
&&
1077 ncd
->ncd_verflen
<= nmp
->nm_verflen
&&
1078 !copyin(ncd
->ncd_authstr
,nmp
->nm_authstr
,ncd
->ncd_authlen
)&&
1079 !copyin(ncd
->ncd_verfstr
,nmp
->nm_verfstr
,ncd
->ncd_verflen
)){
1080 nmp
->nm_authtype
= ncd
->ncd_authtype
;
1081 nmp
->nm_authlen
= ncd
->ncd_authlen
;
1082 nmp
->nm_verflen
= ncd
->ncd_verflen
;
1084 nmp
->nm_key
= ncd
->ncd_key
;
1087 nmp
->nm_flag
|= NFSMNT_AUTHERR
;
1089 nmp
->nm_flag
|= NFSMNT_AUTHERR
;
1090 nmp
->nm_flag
|= NFSMNT_HASAUTH
;
1091 wakeup((caddr_t
)&nmp
->nm_authlen
);
1093 nmp
->nm_flag
|= NFSMNT_WAITAUTH
;
1096 * Loop every second updating queue until there is a termination sig.
1098 while ((nmp
->nm_flag
& NFSMNT_DISMNT
) == 0) {
1099 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1101 * If there are no outstanding requests (and therefore no
1102 * processes in nfs_reply) and there is data in the receive
1103 * queue, poke for callbacks.
1105 if (nfs_reqq
.tqh_first
== 0 && nmp
->nm_so
&&
1106 nmp
->nm_so
->so_rcv
.sb_cc
> 0) {
1107 myrep
.r_flags
= R_GETONEREP
;
1109 myrep
.r_mrep
= (struct mbuf
*)0;
1110 myrep
.r_procp
= (struct proc
*)0;
1111 (void) nfs_reply(&myrep
);
1115 * Loop through the leases, updating as required.
1117 np
= nmp
->nm_timerhead
.cqh_first
;
1118 while (np
!= (void *)&nmp
->nm_timerhead
&&
1119 (nmp
->nm_flag
& NFSMNT_DISMINPROG
) == 0) {
1122 if (np
->n_expiry
< time
.tv_sec
) {
1123 if (vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1124 nmp
->nm_inprog
= vp
;
1125 if (vpid
== vp
->v_id
) {
1126 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1127 np
->n_timer
.cqe_next
= 0;
1128 if (np
->n_flag
& (NMODIFIED
| NQNFSEVICTED
)) {
1129 if (np
->n_flag
& NQNFSEVICTED
) {
1130 if (vp
->v_type
== VDIR
)
1133 (void) nfs_vinvalbuf(vp
,
1134 V_SAVE
, cred
, p
, 0);
1135 np
->n_flag
&= ~NQNFSEVICTED
;
1136 (void) nqnfs_vacated(vp
, cred
);
1137 } else if (vp
->v_type
== VREG
) {
1138 (void) VOP_FSYNC(vp
, cred
,
1140 np
->n_flag
&= ~NMODIFIED
;
1145 nmp
->nm_inprog
= NULLVP
;
1147 } else if ((np
->n_expiry
- NQ_RENEWAL
) < time
.tv_sec
) {
1148 if ((np
->n_flag
& (NQNFSWRITE
| NQNFSNONCACHE
))
1149 == NQNFSWRITE
&& vp
->v_dirtyblkhd
.lh_first
&&
1150 vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1151 nmp
->nm_inprog
= vp
;
1152 if (vpid
== vp
->v_id
&&
1153 nqnfs_getlease(vp
, ND_WRITE
, cred
, p
)==0)
1154 np
->n_brev
= np
->n_lrev
;
1156 nmp
->nm_inprog
= NULLVP
;
1160 if (np
== nmp
->nm_timerhead
.cqh_first
)
1162 np
= nmp
->nm_timerhead
.cqh_first
;
1167 * Get an authorization string, if required.
1169 if ((nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_DISMNT
| NFSMNT_HASAUTH
)) == 0) {
1170 ncd
->ncd_authuid
= nmp
->nm_authuid
;
1171 if (copyout((caddr_t
)ncd
, argp
, sizeof (struct nfsd_cargs
)))
1172 nmp
->nm_flag
|= NFSMNT_WAITAUTH
;
1178 * Wait a bit (no pun) and do it again.
1180 if ((nmp
->nm_flag
& NFSMNT_DISMNT
) == 0 &&
1181 (nmp
->nm_flag
& (NFSMNT_WAITAUTH
| NFSMNT_HASAUTH
))) {
1182 error
= tsleep((caddr_t
)&nmp
->nm_authstr
, PSOCK
| PCATCH
,
1183 "nqnfstimr", hz
/ 3);
1184 if (error
== EINTR
|| error
== ERESTART
)
1185 (void) dounmount(nmp
->nm_mountp
, 0, p
);
1190 * Finally, we can free up the mount structure.
1192 for (nuidp
= nmp
->nm_uidlruhead
.tqh_first
; nuidp
!= 0; nuidp
= nnuidp
) {
1193 nnuidp
= nuidp
->nu_lru
.tqe_next
;
1194 LIST_REMOVE(nuidp
, nu_hash
);
1195 TAILQ_REMOVE(&nmp
->nm_uidlruhead
, nuidp
, nu_lru
);
1196 _FREE_ZONE((caddr_t
)nuidp
, sizeof (struct nfsuid
), M_NFSUID
);
1199 * Loop through outstanding request list and remove dangling
1200 * references to defunct nfsmount struct
1202 for (rp
= nfs_reqq
.tqh_first
; rp
; rp
= rp
->r_chain
.tqe_next
)
1203 if (rp
->r_nmp
== nmp
)
1204 rp
->r_nmp
= (struct nfsmount
*)0;
1205 _FREE_ZONE((caddr_t
)nmp
, sizeof (struct nfsmount
), M_NFSMNT
);
1206 if (error
== EWOULDBLOCK
)
1211 #endif /* NFS_NOSERVER */
1214 * Adjust all timer queue expiry times when the time of day clock is changed.
1215 * Called from the settimeofday() syscall.
1218 nqnfs_lease_updatetime(deltat
)
1219 register int deltat
;
1221 struct proc
*p
= current_proc(); /* XXX */
1224 struct mount
*mp
, *nxtmp
;
1225 struct nfsmount
*nmp
;
1228 if (nqnfsstarttime
!= 0)
1229 nqnfsstarttime
+= deltat
;
1231 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
1232 lp
= lp
->lc_timer
.cqe_next
)
1233 lp
->lc_expiry
+= deltat
;
1237 * Search the mount list for all nqnfs mounts and do their timer
1240 simple_lock(&mountlist_slock
);
1241 for (mp
= mountlist
.cqh_first
; mp
!= (void *)&mountlist
; mp
= nxtmp
) {
1242 if (vfs_busy(mp
, LK_NOWAIT
, &mountlist_slock
, p
)) {
1243 nxtmp
= mp
->mnt_list
.cqe_next
;
1246 if (mp
->mnt_stat
.f_type
== nfs_mount_type
) {
1248 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1249 for (np
= nmp
->nm_timerhead
.cqh_first
;
1250 np
!= (void *)&nmp
->nm_timerhead
;
1251 np
= np
->n_timer
.cqe_next
) {
1252 np
->n_expiry
+= deltat
;
1256 simple_lock(&mountlist_slock
);
1257 nxtmp
= mp
->mnt_list
.cqe_next
;
1260 simple_unlock(&mountlist_slock
);
1264 * Lock a server lease.
1271 while (lp
->lc_flag
& LC_LOCKED
) {
1272 lp
->lc_flag
|= LC_WANTED
;
1273 (void) tsleep((caddr_t
)lp
, PSOCK
, "nqlc", 0);
1275 lp
->lc_flag
|= LC_LOCKED
;
1276 lp
->lc_flag
&= ~LC_WANTED
;
1280 * Unlock a server lease.
1283 nqsrv_unlocklease(lp
)
1287 lp
->lc_flag
&= ~LC_LOCKED
;
1288 if (lp
->lc_flag
& LC_WANTED
)
1289 wakeup((caddr_t
)lp
);
1293 * Update a client lease.
1296 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
, expiry
, frev
)
1297 register struct nfsmount
*nmp
;
1298 register struct nfsnode
*np
;
1299 int rwflag
, cachable
;
1303 register struct nfsnode
*tp
;
1305 if (np
->n_timer
.cqe_next
!= 0) {
1306 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1307 if (rwflag
== ND_WRITE
)
1308 np
->n_flag
|= NQNFSWRITE
;
1309 } else if (rwflag
== ND_READ
)
1310 np
->n_flag
&= ~NQNFSWRITE
;
1312 np
->n_flag
|= NQNFSWRITE
;
1314 np
->n_flag
&= ~NQNFSNONCACHE
;
1316 np
->n_flag
|= NQNFSNONCACHE
;
1317 np
->n_expiry
= expiry
;
1319 tp
= nmp
->nm_timerhead
.cqh_last
;
1320 while (tp
!= (void *)&nmp
->nm_timerhead
&& tp
->n_expiry
> np
->n_expiry
)
1321 tp
= tp
->n_timer
.cqe_prev
;
1322 if (tp
== (void *)&nmp
->nm_timerhead
) {
1323 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1325 CIRCLEQ_INSERT_AFTER(&nmp
->nm_timerhead
, tp
, np
, n_timer
);