]>
git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_nqlease.c
2 * Copyright (c) 2000-2003 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
;
435 newexpiry
= now
.tv_sec
+ duration
+ nqsrv_clockskew
;
436 if (lp
->lc_expiry
== newexpiry
)
438 if (lp
->lc_timer
.cqe_next
!= 0) {
439 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
441 lp
->lc_expiry
= newexpiry
;
444 * Find where in the queue it should be.
446 tlp
= nqtimerhead
.cqh_last
;
447 while (tlp
!= (void *)&nqtimerhead
&& tlp
->lc_expiry
> newexpiry
)
448 tlp
= tlp
->lc_timer
.cqe_prev
;
450 if (tlp
== nqtimerhead
.cqh_last
)
451 NQSTORENOVRAM(newexpiry
);
452 #endif /* HASNVRAM */
453 if (tlp
== (void *)&nqtimerhead
) {
454 CIRCLEQ_INSERT_HEAD(&nqtimerhead
, lp
, lc_timer
);
456 CIRCLEQ_INSERT_AFTER(&nqtimerhead
, tlp
, lp
, lc_timer
);
461 * Compare the requesting host address with the lph entry in the lease.
462 * Return true iff it is the same.
463 * This is somewhat messy due to the union in the nqhost structure.
464 * The local host is indicated by the special value of NQLOCALSLP for slp.
467 nqsrv_cmpnam(slp
, nam
, lph
)
468 register struct nfssvc_sock
*slp
;
470 register struct nqhost
*lph
;
472 register struct sockaddr_in
*saddr
;
474 union nethostaddr lhaddr
;
477 if (slp
== NQLOCALSLP
) {
478 if (lph
->lph_flag
& LC_LOCAL
)
483 if (slp
== nfs_udpsock
|| slp
== nfs_cltpsock
)
487 if (lph
->lph_flag
& LC_UDP
)
488 ret
= netaddr_match(AF_INET
, &lph
->lph_haddr
, addr
);
489 else if (lph
->lph_flag
& LC_CLTP
)
490 ret
= netaddr_match(AF_ISO
, &lph
->lph_claddr
, addr
);
492 if ((lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0)
494 saddr
= mtod(lph
->lph_slp
->ns_nam
, struct sockaddr_in
*);
495 if (saddr
->sin_family
== AF_INET
)
496 lhaddr
.had_inetaddr
= saddr
->sin_addr
.s_addr
;
498 lhaddr
.had_nam
= lph
->lph_slp
->ns_nam
;
499 ret
= netaddr_match(saddr
->sin_family
, &lhaddr
, addr
);
505 * Send out eviction notice messages to all other hosts for the lease.
508 nqsrv_send_eviction(vp
, lp
, slp
, nam
, cred
)
510 register struct nqlease
*lp
;
511 struct nfssvc_sock
*slp
;
515 register struct nqhost
*lph
= &lp
->lc_host
;
516 register struct mbuf
*m
;
518 struct nqm
*lphnext
= lp
->lc_morehosts
;
519 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
522 struct sockaddr_in
*saddr
;
527 int len
= 1, ok
= 1, i
= 0;
530 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
531 if (nqsrv_cmpnam(slp
, nam
, lph
))
532 lph
->lph_flag
|= LC_VACATED
;
533 else if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
534 if (lph
->lph_flag
& LC_UDP
) {
535 MGET(nam2
, M_WAIT
, MT_SONAME
);
536 saddr
= mtod(nam2
, struct sockaddr_in
*);
537 nam2
->m_len
= saddr
->sin_len
=
538 sizeof (struct sockaddr_in
);
539 saddr
->sin_family
= AF_INET
;
540 saddr
->sin_addr
.s_addr
= lph
->lph_inetaddr
;
541 saddr
->sin_port
= lph
->lph_port
;
542 so
= nfs_udpsock
->ns_so
;
543 } else if (lph
->lph_flag
& LC_CLTP
) {
545 so
= nfs_cltpsock
->ns_so
;
546 } else if (lph
->lph_slp
->ns_flag
& SLP_VALID
) {
547 nam2
= (struct mbuf
*)0;
548 so
= lph
->lph_slp
->ns_so
;
551 sotype
= so
->so_type
;
552 solock
= (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
);
553 nfsm_reqhead((struct vnode
*)0, NQNFSPROC_EVICTED
,
554 NFSX_V3FH
+ NFSX_UNSIGNED
);
555 fhp
= &nfh
.fh_generic
;
556 bzero((caddr_t
)fhp
, sizeof(nfh
));
557 fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
558 VFS_VPTOFH(vp
, &fhp
->fh_fid
);
559 nfsm_srvfhtom(fhp
, 1);
566 if (siz
<= 0 || siz
> NFS_MAXPACKET
) {
567 printf("mbuf siz=%d\n",siz
);
568 panic("Bad nfs svc reply");
570 m
= nfsm_rpchead(cred
, (NFSMNT_NFSV3
| NFSMNT_NQNFS
),
572 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
573 0, (char *)NULL
, mreq
, siz
, &mheadend
, &xid
);
575 * For stream protocols, prepend a Sun RPC
578 if (sotype
== SOCK_STREAM
) {
579 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
580 *mtod(m
, u_long
*) = htonl(0x80000000 |
581 (m
->m_pkthdr
.len
- NFSX_UNSIGNED
));
583 if (((lph
->lph_flag
& (LC_UDP
| LC_CLTP
)) == 0 &&
584 (lph
->lph_slp
->ns_flag
& SLP_VALID
) == 0) ||
585 (solock
&& nfs_slplock(lph
->lph_slp
, 0) == 0)) {
588 (void) nfs_send(so
, nam2
, m
,
591 nfs_slpunlock(lph
->lph_slp
);
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
;
627 if (now
.tv_sec
> lp
->lc_expiry
)
630 lphnext
= lp
->lc_morehosts
;
634 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
635 if ((lph
->lph_flag
& (LC_LOCAL
| LC_VACATED
)) == 0) {
636 lp
->lc_flag
|= LC_EXPIREDWANTED
;
637 (void) tsleep((caddr_t
)&lp
->lc_flag
, PSOCK
,
644 len
= LC_MOREHOSTSIZ
;
645 lph
= lphnext
->lpm_hosts
;
646 lphnext
= lphnext
->lpm_next
;
657 * Nqnfs server timer that maintains the server lease queue.
658 * Scan the lease queue for expired entries:
659 * - when one is found, wakeup anyone waiting for it
660 * else dequeue and free
665 register struct nqlease
*lp
;
666 register struct nqhost
*lph
;
667 struct nqlease
*nextlp
;
668 struct nqm
*lphnext
, *olphnext
;
674 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
676 if (lp
->lc_expiry
>= now
.tv_sec
)
678 nextlp
= lp
->lc_timer
.cqe_next
;
679 if (lp
->lc_flag
& LC_EXPIREDWANTED
) {
680 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
681 wakeup((caddr_t
)&lp
->lc_flag
);
682 } else if ((lp
->lc_flag
& (LC_LOCKED
| LC_WANTED
)) == 0) {
684 * Make a best effort at keeping a write caching lease long
685 * enough by not deleting it until it has been explicitly
686 * vacated or there have been no writes in the previous
687 * write_slack seconds since expiry and the nfsds are not
688 * all busy. The assumption is that if the nfsds are not
689 * all busy now (no queue of nfs requests), then the client
690 * would have been able to do at least one write to the
691 * file during the last write_slack seconds if it was still
692 * trying to push writes to the server.
694 if ((lp
->lc_flag
& (LC_WRITE
| LC_VACATED
)) == LC_WRITE
&&
695 ((lp
->lc_flag
& LC_WRITTEN
) || nfsd_waiting
== 0)) {
696 lp
->lc_flag
&= ~LC_WRITTEN
;
697 nqsrv_instimeq(lp
, nqsrv_writeslack
);
699 CIRCLEQ_REMOVE(&nqtimerhead
, lp
, lc_timer
);
700 LIST_REMOVE(lp
, lc_hash
);
702 * This soft reference may no longer be valid, but
703 * no harm done. The worst case is if the vnode was
704 * recycled and has another valid lease reference,
705 * which is dereferenced prematurely.
707 lp
->lc_vp
->v_lease
= (struct nqlease
*)0;
709 lphnext
= lp
->lc_morehosts
;
710 olphnext
= (struct nqm
*)0;
714 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
715 if (lph
->lph_flag
& LC_CLTP
)
716 MFREE(lph
->lph_nam
, n
);
717 if (lph
->lph_flag
& LC_SREF
)
718 nfsrv_slpderef(lph
->lph_slp
);
721 FREE_ZONE((caddr_t
)olphnext
,
724 olphnext
= (struct nqm
*)0;
729 len
= LC_MOREHOSTSIZ
;
730 lph
= lphnext
->lpm_hosts
;
731 lphnext
= lphnext
->lpm_next
;
737 FREE_ZONE((caddr_t
)lp
,
738 sizeof (struct nqlease
), M_NQLEASE
);
740 FREE_ZONE((caddr_t
)olphnext
,
741 sizeof (struct nqm
), M_NQMHOST
);
742 nfsstats
.srvnqnfs_leases
--;
749 * Called from nfssvc_nfsd() for a getlease rpc request.
750 * Do the from/to xdr translation and call nqsrv_getlease() to
754 nqnfsrv_getlease(nfsd
, slp
, procp
, mrq
)
755 struct nfsrv_descript
*nfsd
;
756 struct nfssvc_sock
*slp
;
760 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
761 struct mbuf
*nam
= nfsd
->nd_nam
;
762 caddr_t dpos
= nfsd
->nd_dpos
;
763 struct ucred
*cred
= &nfsd
->nd_cr
;
764 register struct nfs_fattr
*fp
;
766 register struct vattr
*vap
= &va
;
776 struct mbuf
*mb
, *mb2
, *mreq
;
777 int flags
, rdonly
, cache
;
779 fhp
= &nfh
.fh_generic
;
781 nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
782 flags
= fxdr_unsigned(int, *tl
++);
783 nfsd
->nd_duration
= fxdr_unsigned(int, *tl
);
784 error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
, &rdonly
,
785 (nfsd
->nd_flag
& ND_KERBAUTH
), TRUE
);
788 if (rdonly
&& flags
== ND_WRITE
) {
793 (void) nqsrv_getlease(vp
, &nfsd
->nd_duration
, flags
, slp
, procp
,
794 nam
, &cache
, &frev
, cred
);
795 error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
797 nfsm_reply(NFSX_V3FATTR
+ 4 * NFSX_UNSIGNED
);
798 nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
799 *tl
++ = txdr_unsigned(cache
);
800 *tl
++ = txdr_unsigned(nfsd
->nd_duration
);
801 txdr_hyper(&frev
, tl
);
802 nfsm_build(fp
, struct nfs_fattr
*, NFSX_V3FATTR
);
803 nfsm_srvfillattr(vap
, fp
);
808 * Called from nfssvc_nfsd() when a "vacated" message is received from a
809 * client. Find the entry and expire it.
812 nqnfsrv_vacated(nfsd
, slp
, procp
, mrq
)
813 struct nfsrv_descript
*nfsd
;
814 struct nfssvc_sock
*slp
;
818 struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
819 struct mbuf
*nam
= nfsd
->nd_nam
;
820 caddr_t dpos
= nfsd
->nd_dpos
;
821 register struct nqlease
*lp
;
822 register struct nqhost
*lph
;
823 struct nqlease
*tlp
= (struct nqlease
*)0;
829 struct mbuf
*mreq
, *mb
;
830 int error
= 0, i
, len
, ok
, gotit
= 0, cache
= 0;
834 fhp
= &nfh
.fh_generic
;
838 * Find the lease by searching the hash list.
840 for (lp
= NQFHHASH(fhp
->fh_fid
.fid_data
)->lh_first
; lp
!= 0;
841 lp
= lp
->lc_hash
.le_next
)
842 if (fhp
->fh_fsid
.val
[0] == lp
->lc_fsid
.val
[0] &&
843 fhp
->fh_fsid
.val
[1] == lp
->lc_fsid
.val
[1] &&
844 !bcmp(fhp
->fh_fid
.fid_data
, lp
->lc_fiddata
,
855 lphnext
= lp
->lc_morehosts
;
857 while (ok
&& (lph
->lph_flag
& LC_VALID
)) {
858 if (nqsrv_cmpnam(slp
, nam
, lph
)) {
859 lph
->lph_flag
|= LC_VACATED
;
865 len
= LC_MOREHOSTSIZ
;
867 lph
= lphnext
->lpm_hosts
;
868 lphnext
= lphnext
->lpm_next
;
874 if ((lp
->lc_flag
& LC_EXPIREDWANTED
) && gotit
) {
875 lp
->lc_flag
&= ~LC_EXPIREDWANTED
;
876 wakeup((caddr_t
)&lp
->lc_flag
);
884 #endif /* NFS_NOSERVER */
887 * Client get lease rpc function.
890 nqnfs_getlease(vp
, rwflag
, cred
, p
)
891 register struct vnode
*vp
;
898 register long t1
, t2
;
899 register struct nfsnode
*np
;
900 struct nfsmount
*nmp
;
901 caddr_t bpos
, dpos
, cp2
;
905 struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
910 nmp
= VFSTONFS(vp
->v_mount
);
914 nfsstats
.rpccnt
[NQNFSPROC_GETLEASE
]++;
915 mb
= mreq
= nfsm_reqh(vp
, NQNFSPROC_GETLEASE
, NFSX_V3FH
+2*NFSX_UNSIGNED
,
918 nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
919 *tl
++ = txdr_unsigned(rwflag
);
920 *tl
= txdr_unsigned(nmp
->nm_leaseterm
);
922 reqtime
= now
.tv_sec
;
923 nfsm_request(vp
, NQNFSPROC_GETLEASE
, p
, cred
, &xid
);
925 nfsm_dissect(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
926 cachable
= fxdr_unsigned(int, *tl
++);
927 reqtime
+= fxdr_unsigned(int, *tl
++);
929 if (reqtime
> now
.tv_sec
) {
930 nmp
= VFSTONFS(vp
->v_mount
);
934 fxdr_hyper(tl
, &frev
);
935 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
,
937 nfsm_loadattr(vp
, (struct vattr
*)0, &xid
);
940 error
= NQNFS_EXPIRED
;
946 * Client vacated message function.
949 nqnfs_vacated(vp
, cred
)
950 register struct vnode
*vp
;
954 register struct mbuf
*m
;
961 struct mbuf
*mreq
, *mb
, *mb2
, *mheadend
;
962 struct nfsmount
*nmp
;
967 nmp
= VFSTONFS(vp
->v_mount
);
970 nfsstats
.rpccnt
[NQNFSPROC_VACATED
]++;
971 nfsm_reqhead(vp
, NQNFSPROC_VACATED
, NFSX_FH(1));
979 m
= nfsm_rpchead(cred
, nmp
->nm_flag
, NQNFSPROC_VACATED
,
980 RPCAUTH_UNIX
, 5 * NFSX_UNSIGNED
, (char *)0,
981 0, (char *)NULL
, mreq
, i
, &mheadend
, &xid
);
982 if (nmp
->nm_sotype
== SOCK_STREAM
) {
983 M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
984 *mtod(m
, u_long
*) = htonl(0x80000000 | (m
->m_pkthdr
.len
-
990 connrequired
= (nmp
->nm_soflags
& PR_CONNREQUIRED
);
992 (void) nfs_sndlock(&myrep
);
994 (void) nfs_send(nmp
->nm_so
, nmp
->nm_nam
, m
, &myrep
);
997 nfs_sndunlock(&myrep
);
1002 #ifndef NFS_NOSERVER
1005 * Called for client side callbacks
1008 nqnfs_callback(nmp
, mrep
, md
, dpos
)
1009 struct nfsmount
*nmp
;
1010 struct mbuf
*mrep
, *md
;
1013 register struct vnode
*vp
;
1014 register u_long
*tl
;
1020 struct nfssvc_sock
*slp
;
1021 struct nfsrv_descript ndesc
;
1022 register struct nfsrv_descript
*nfsd
= &ndesc
;
1023 struct mbuf
**mrq
= (struct mbuf
**)0, *mb
, *mreq
;
1024 int error
= 0, cache
= 0;
1031 nfsd
->nd_mrep
= mrep
;
1033 nfsd
->nd_dpos
= dpos
;
1034 error
= nfs_getreq(nfsd
, &tnfsd
, FALSE
);
1038 dpos
= nfsd
->nd_dpos
;
1039 if (nfsd
->nd_procnum
!= NQNFSPROC_EVICTED
) {
1043 fhp
= &nfh
.fh_generic
;
1046 error
= nfs_nget(nmp
->nm_mountp
, (nfsfh_t
*)fhp
, NFSX_V3FH
, &np
);
1050 if (np
->n_timer
.cqe_next
!= 0) {
1052 np
->n_flag
|= NQNFSEVICTED
;
1053 if (nmp
->nm_timerhead
.cqh_first
!= np
) {
1054 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1055 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1064 * Nqnfs client helper daemon. Runs once a second to expire leases.
1065 * It also get authorization strings for "kerb" mounts.
1066 * It must start at the beginning of the list again after any potential
1067 * "sleep" since nfs_reclaim() called from vclean() can pull a node off
1068 * the list asynchronously.
1071 nqnfs_clientd(nmp
, cred
, ncd
, flag
, argp
, p
)
1072 register struct nfsmount
*nmp
;
1074 struct nfsd_cargs
*ncd
;
1079 register struct nfsnode
*np
;
1081 struct nfsreq myrep
;
1082 struct nfsuid
*nuidp
, *nnuidp
;
1083 int error
= 0, vpid
;
1084 register struct nfsreq
*rp
;
1088 * First initialize some variables
1093 * If an authorization string is being passed in, get it.
1095 if ((flag
& NFSSVC_GOTAUTH
) &&
1096 (nmp
->nm_state
& (NFSSTA_WAITAUTH
| NFSSTA_DISMNT
)) == 0) {
1097 if (nmp
->nm_state
& NFSSTA_HASAUTH
)
1099 if ((flag
& NFSSVC_AUTHINFAIL
) == 0) {
1100 if (ncd
->ncd_authlen
<= nmp
->nm_authlen
&&
1101 ncd
->ncd_verflen
<= nmp
->nm_verflen
&&
1102 !copyin(ncd
->ncd_authstr
,nmp
->nm_authstr
,ncd
->ncd_authlen
)&&
1103 !copyin(ncd
->ncd_verfstr
,nmp
->nm_verfstr
,ncd
->ncd_verflen
)){
1104 nmp
->nm_authtype
= ncd
->ncd_authtype
;
1105 nmp
->nm_authlen
= ncd
->ncd_authlen
;
1106 nmp
->nm_verflen
= ncd
->ncd_verflen
;
1108 nmp
->nm_key
= ncd
->ncd_key
;
1111 nmp
->nm_state
|= NFSSTA_AUTHERR
;
1113 nmp
->nm_state
|= NFSSTA_AUTHERR
;
1114 nmp
->nm_state
|= NFSSTA_HASAUTH
;
1115 wakeup((caddr_t
)&nmp
->nm_authlen
);
1117 nmp
->nm_state
|= NFSSTA_WAITAUTH
;
1120 * Loop every second updating queue until there is a termination sig.
1122 while ((nmp
->nm_state
& NFSSTA_DISMNT
) == 0) {
1123 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1125 * If there are no outstanding requests (and therefore no
1126 * processes in nfs_reply) and there is data in the receive
1127 * queue, poke for callbacks.
1129 if (nfs_reqq
.tqh_first
== 0 && nmp
->nm_so
&&
1130 nmp
->nm_so
->so_rcv
.sb_cc
> 0) {
1131 myrep
.r_flags
= R_GETONEREP
;
1133 myrep
.r_mrep
= (struct mbuf
*)0;
1134 myrep
.r_procp
= (struct proc
*)0;
1135 (void) nfs_reply(&myrep
);
1139 * Loop through the leases, updating as required.
1141 np
= nmp
->nm_timerhead
.cqh_first
;
1142 while (np
!= (void *)&nmp
->nm_timerhead
&&
1143 (nmp
->nm_state
& NFSSTA_DISMINPROG
) == 0) {
1146 if (np
->n_expiry
< now
.tv_sec
) {
1147 if (vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1148 nmp
->nm_inprog
= vp
;
1149 if (vpid
== vp
->v_id
) {
1150 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1151 np
->n_timer
.cqe_next
= 0;
1152 if (np
->n_flag
& (NMODIFIED
| NQNFSEVICTED
)) {
1153 if (np
->n_flag
& NQNFSEVICTED
) {
1154 if (vp
->v_type
== VDIR
)
1157 (void) nfs_vinvalbuf(vp
,
1158 V_SAVE
, cred
, p
, 0);
1159 np
->n_flag
&= ~NQNFSEVICTED
;
1160 (void) nqnfs_vacated(vp
, cred
);
1161 } else if (vp
->v_type
== VREG
) {
1162 (void) VOP_FSYNC(vp
, cred
,
1164 np
->n_flag
&= ~NMODIFIED
;
1169 nmp
->nm_inprog
= NULLVP
;
1171 } else if ((np
->n_expiry
- NQ_RENEWAL
) < now
.tv_sec
) {
1172 if ((np
->n_flag
& (NQNFSWRITE
| NQNFSNONCACHE
))
1173 == NQNFSWRITE
&& np
->n_dirtyblkhd
.lh_first
&&
1174 vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1175 nmp
->nm_inprog
= vp
;
1176 if (vpid
== vp
->v_id
&&
1177 nqnfs_getlease(vp
, ND_WRITE
, cred
, p
)==0)
1178 np
->n_brev
= np
->n_lrev
;
1180 nmp
->nm_inprog
= NULLVP
;
1184 if (np
== nmp
->nm_timerhead
.cqh_first
)
1186 np
= nmp
->nm_timerhead
.cqh_first
;
1191 * Get an authorization string, if required.
1193 if ((nmp
->nm_state
& (NFSSTA_WAITAUTH
| NFSSTA_DISMNT
| NFSSTA_HASAUTH
)) == 0) {
1194 ncd
->ncd_authuid
= nmp
->nm_authuid
;
1195 if (copyout((caddr_t
)ncd
, argp
, sizeof (struct nfsd_cargs
)))
1196 nmp
->nm_state
|= NFSSTA_WAITAUTH
;
1202 * Wait a bit (no pun) and do it again.
1204 if ((nmp
->nm_state
& NFSSTA_DISMNT
) == 0 &&
1205 (nmp
->nm_state
& (NFSSTA_WAITAUTH
| NFSSTA_HASAUTH
))) {
1206 error
= tsleep((caddr_t
)&nmp
->nm_authstr
, PSOCK
| PCATCH
,
1207 "nqnfstimr", hz
/ 3);
1208 if (error
== EINTR
|| error
== ERESTART
)
1209 (void) dounmount(nmp
->nm_mountp
, 0, p
);
1214 * Finally, we can free up the mount structure.
1216 for (nuidp
= nmp
->nm_uidlruhead
.tqh_first
; nuidp
!= 0; nuidp
= nnuidp
) {
1217 nnuidp
= nuidp
->nu_lru
.tqe_next
;
1218 LIST_REMOVE(nuidp
, nu_hash
);
1219 TAILQ_REMOVE(&nmp
->nm_uidlruhead
, nuidp
, nu_lru
);
1220 FREE_ZONE((caddr_t
)nuidp
, sizeof (struct nfsuid
), M_NFSUID
);
1223 * Loop through outstanding request list and remove dangling
1224 * references to defunct nfsmount struct
1226 for (rp
= nfs_reqq
.tqh_first
; rp
; rp
= rp
->r_chain
.tqe_next
)
1227 if (rp
->r_nmp
== nmp
)
1228 rp
->r_nmp
= (struct nfsmount
*)0;
1229 FREE_ZONE((caddr_t
)nmp
, sizeof (struct nfsmount
), M_NFSMNT
);
1230 if (error
== EWOULDBLOCK
)
1235 #endif /* NFS_NOSERVER */
1238 * Adjust all timer queue expiry times when the time of day clock is changed.
1239 * Called from the settimeofday() syscall.
1242 nqnfs_lease_updatetime(deltat
)
1243 register int deltat
;
1245 struct proc
*p
= current_proc(); /* XXX */
1248 struct mount
*mp
, *nxtmp
;
1249 struct nfsmount
*nmp
;
1252 if (nqnfsstarttime
!= 0)
1253 nqnfsstarttime
+= deltat
;
1255 for (lp
= nqtimerhead
.cqh_first
; lp
!= (void *)&nqtimerhead
;
1256 lp
= lp
->lc_timer
.cqe_next
)
1257 lp
->lc_expiry
+= deltat
;
1261 * Search the mount list for all nqnfs mounts and do their timer
1264 simple_lock(&mountlist_slock
);
1265 for (mp
= mountlist
.cqh_first
; mp
!= (void *)&mountlist
; mp
= nxtmp
) {
1266 if (vfs_busy(mp
, LK_NOWAIT
, &mountlist_slock
, p
)) {
1267 nxtmp
= mp
->mnt_list
.cqe_next
;
1270 if (mp
->mnt_stat
.f_type
== nfs_mount_type
) {
1272 if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
1273 for (np
= nmp
->nm_timerhead
.cqh_first
;
1274 np
!= (void *)&nmp
->nm_timerhead
;
1275 np
= np
->n_timer
.cqe_next
) {
1276 np
->n_expiry
+= deltat
;
1280 simple_lock(&mountlist_slock
);
1281 nxtmp
= mp
->mnt_list
.cqe_next
;
1284 simple_unlock(&mountlist_slock
);
1288 * Lock a server lease.
1295 while (lp
->lc_flag
& LC_LOCKED
) {
1296 lp
->lc_flag
|= LC_WANTED
;
1297 (void) tsleep((caddr_t
)lp
, PSOCK
, "nqlc", 0);
1299 lp
->lc_flag
|= LC_LOCKED
;
1300 lp
->lc_flag
&= ~LC_WANTED
;
1304 * Unlock a server lease.
1307 nqsrv_unlocklease(lp
)
1311 lp
->lc_flag
&= ~LC_LOCKED
;
1312 if (lp
->lc_flag
& LC_WANTED
)
1313 wakeup((caddr_t
)lp
);
1317 * Update a client lease.
1320 nqnfs_clientlease(nmp
, np
, rwflag
, cachable
, expiry
, frev
)
1321 register struct nfsmount
*nmp
;
1322 register struct nfsnode
*np
;
1323 int rwflag
, cachable
;
1327 register struct nfsnode
*tp
;
1329 if (np
->n_timer
.cqe_next
!= 0) {
1330 CIRCLEQ_REMOVE(&nmp
->nm_timerhead
, np
, n_timer
);
1331 if (rwflag
== ND_WRITE
)
1332 np
->n_flag
|= NQNFSWRITE
;
1333 } else if (rwflag
== ND_READ
)
1334 np
->n_flag
&= ~NQNFSWRITE
;
1336 np
->n_flag
|= NQNFSWRITE
;
1338 np
->n_flag
&= ~NQNFSNONCACHE
;
1340 np
->n_flag
|= NQNFSNONCACHE
;
1341 np
->n_expiry
= expiry
;
1343 tp
= nmp
->nm_timerhead
.cqh_last
;
1344 while (tp
!= (void *)&nmp
->nm_timerhead
&& tp
->n_expiry
> np
->n_expiry
)
1345 tp
= tp
->n_timer
.cqe_prev
;
1346 if (tp
== (void *)&nmp
->nm_timerhead
) {
1347 CIRCLEQ_INSERT_HEAD(&nmp
->nm_timerhead
, np
, n_timer
);
1349 CIRCLEQ_INSERT_AFTER(&nmp
->nm_timerhead
, tp
, np
, n_timer
);