xref: /csrg-svn/sys/nfs/nfs_nqlease.c (revision 54614)
1 /*
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)nfs_nqlease.c	7.8 (Berkeley) 07/02/92
11  */
12 
13 /*
14  * References:
15  *	Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
16  *		Mechanism for Distributed File Cache Consistency",
17  *		In Proc. of the Twelfth ACM Symposium on Operating Systems
18  *		Principals, pg. 202-210, Litchfield Park, AZ, Dec. 1989.
19  *	Michael N. Nelson, Brent B. Welch and John K. Ousterhout, "Caching
20  *		in the Sprite Network File System", ACM TOCS 6(1),
21  *		pages 134-154, February 1988.
22  *	V. Srinivasan and Jeffrey C. Mogul, "Spritely NFS: Implementation and
23  *		Performance of Cache-Consistency Protocols", Digital
24  *		Equipment Corporation WRL Research Report 89/5, May 1989.
25  */
26 #include <sys/param.h>
27 #include <sys/vnode.h>
28 #include <sys/mount.h>
29 #include <sys/kernel.h>
30 #include <sys/proc.h>
31 #include <sys/systm.h>
32 #include <sys/mbuf.h>
33 #include <sys/socket.h>
34 #include <sys/socketvar.h>
35 #include <sys/file.h>
36 #include <sys/buf.h>
37 #include <sys/stat.h>
38 #include <sys/protosw.h>
39 #include <netinet/in.h>
40 #include <nfs/rpcv2.h>
41 #include <nfs/nfsv2.h>
42 #include <nfs/nfs.h>
43 #include <nfs/nfsm_subs.h>
44 #include <nfs/xdr_subs.h>
45 #include <nfs/nqnfs.h>
46 #include <nfs/nfsnode.h>
47 #include <nfs/nfsmount.h>
48 
49 /*
50  * List head for the lease queue and other global data.
51  * At any time a lease is linked into a list ordered by increasing expiry time.
52  */
53 #if	((NQLCHSZ&(NQLCHSZ-1)) == 0)
54 #define	NQFHHASH(f)	((*((u_long *)(f)))&(NQLCHSZ-1))
55 #else
56 #define	NQFHHASH(f)	((*((u_long *)(f)))%NQLCHSZ)
57 #endif
58 
59 union nqsrvthead nqthead;
60 union nqsrvthead nqfhead[NQLCHSZ];
61 time_t nqnfsstarttime = (time_t)0;
62 u_long nqnfs_prog, nqnfs_vers;
63 int nqsrv_clockskew = NQ_CLOCKSKEW;
64 int nqsrv_writeslack = NQ_WRITESLACK;
65 int nqsrv_maxlease = NQ_MAXLEASE;
66 int nqsrv_maxnumlease = NQ_MAXNUMLEASE;
67 void nqsrv_instimeq(), nqsrv_send_eviction(), nfs_sndunlock();
68 void nqsrv_unlocklease(), nqsrv_waitfor_expiry(), nfsrv_slpderef();
69 void nqsrv_addhost(), nqsrv_locklease(), nqnfs_serverd();
70 struct mbuf *nfsm_rpchead();
71 
72 /*
73  * Signifies which rpcs can have piggybacked lease requests
74  */
75 int nqnfs_piggy[NFS_NPROCS] = {
76 	0,
77 	NQL_READ,
78 	NQL_WRITE,
79 	0,
80 	NQL_READ,
81 	NQL_READ,
82 	NQL_READ,
83 	0,
84 	NQL_WRITE,
85 	0,
86 	0,
87 	0,
88 	0,
89 	0,
90 	0,
91 	0,
92 	NQL_READ,
93 	0,
94 	NQL_READ,
95 	0,
96 	0,
97 	0,
98 };
99 
100 int nnnnnn = sizeof (struct nqlease);
101 int oooooo = sizeof (struct nfsnode);
102 extern nfstype nfs_type[9];
103 extern struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
104 extern struct nfsd nfsd_head;
105 extern int nfsd_waiting;
106 
107 #define TRUE	1
108 #define	FALSE	0
109 
110 /*
111  * Get or check for a lease for "vp", based on NQL_CHECK flag.
112  * The rules are as follows:
113  * - if a current non-caching lease, reply non-caching
114  * - if a current lease for same host only, extend lease
115  * - if a read cachable lease and a read lease request
116  *	add host to list any reply cachable
117  * - else { set non-cachable for read-write sharing }
118  *	send eviction notice messages to all other hosts that have lease
119  *	wait for lease termination { either by receiving vacated messages
120  *					from all the other hosts or expiry
121  *					via. timeout }
122  *	modify lease to non-cachable
123  * - else if no current lease, issue new one
124  * - reply
125  * - return boolean TRUE iff nam should be m_freem()'d
126  * NB: Since nqnfs_serverd() is called from a timer, any potential tsleep()
127  *     in here must be framed by nqsrv_locklease() and nqsrv_unlocklease().
128  *     nqsrv_locklease() is coded such that at least one of LC_LOCKED and
129  *     LC_WANTED is set whenever a process is tsleeping in it. The exception
130  *     is when a new lease is being allocated, since it is not in the timer
131  *     queue yet. (Ditto for the splsoftclock() and splx(s) calls)
132  */
133 nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
134 	struct vnode *vp;
135 	u_long *duration;
136 	int flags;
137 	struct nfsd *nd;
138 	struct mbuf *nam;
139 	int *cachablep;
140 	u_quad_t *frev;
141 	struct ucred *cred;
142 {
143 	USES_VOP_GETATTR;
144 	USES_VOP_LOCK;
145 	USES_VOP_UNLOCK;
146 	register struct nqlease *lp;
147 	register struct nqhost *lph;
148 	struct nqlease *tlp = (struct nqlease *)0;
149 	struct nqm **lphp;
150 	struct vattr vattr;
151 	union nqsrvthead *lhp;
152 	fhandle_t fh;
153 	int i, ok, error, s;
154 
155 	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
156 		return (0);
157 	if (*duration > nqsrv_maxlease)
158 		*duration = nqsrv_maxlease;
159 	if (error = VOP_GETATTR(vp, &vattr, cred, nd->nd_procp))
160 		return (error);
161 	*frev = vattr.va_filerev;
162 	s = splsoftclock();
163 	tlp = vp->v_lease;
164 	if ((flags & NQL_CHECK) == 0)
165 		nfsstats.srvnqnfs_getleases++;
166 	if (tlp == (struct nqlease *)0) {
167 
168 		/*
169 		 * Find the lease by searching the hash list.
170 		 */
171 		fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
172 		if (error = VFS_VPTOFH(vp, &fh.fh_fid)) {
173 			splx(s);
174 			return (error);
175 		}
176 		lhp = &nqfhead[NQFHHASH(fh.fh_fid.fid_data)];
177 		for (lp = lhp->th_chain[0]; lp != (struct nqlease *)lhp;
178 			lp = lp->lc_fhnext)
179 			if (fh.fh_fsid.val[0] == lp->lc_fsid.val[0] &&
180 			    fh.fh_fsid.val[1] == lp->lc_fsid.val[1] &&
181 			    !bcmp(fh.fh_fid.fid_data, lp->lc_fiddata,
182 				  fh.fh_fid.fid_len - sizeof (long))) {
183 				/* Found it */
184 				lp->lc_vp = vp;
185 				vp->v_lease = lp;
186 				tlp = lp;
187 				break;
188 			}
189 	}
190 	lp = tlp;
191 	if (lp) {
192 		if ((lp->lc_flag & LC_NONCACHABLE) ||
193 		    (lp->lc_morehosts == (struct nqm *)0 &&
194 		     nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host)))
195 			goto doreply;
196 		if ((flags & NQL_READ) && (lp->lc_flag & LC_WRITE)==0) {
197 			if (flags & NQL_CHECK)
198 				goto doreply;
199 			if (nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host))
200 				goto doreply;
201 			i = 0;
202 			if (lp->lc_morehosts) {
203 				lph = lp->lc_morehosts->lpm_hosts;
204 				lphp = &lp->lc_morehosts->lpm_next;
205 				ok = 1;
206 			} else {
207 				lphp = &lp->lc_morehosts;
208 				ok = 0;
209 			}
210 			while (ok && (lph->lph_flag & LC_VALID)) {
211 				if (nqsrv_cmpnam(nd->nd_slp, nam, lph))
212 					goto doreply;
213 				if (++i == LC_MOREHOSTSIZ) {
214 					i = 0;
215 					if (*lphp) {
216 						lph = (*lphp)->lpm_hosts;
217 						lphp = &((*lphp)->lpm_next);
218 					} else
219 						ok = 0;
220 				} else
221 					lph++;
222 			}
223 			nqsrv_locklease(lp);
224 			if (!ok) {
225 				*lphp = (struct nqm *)
226 					malloc(sizeof (struct nqm),
227 						M_NQMHOST, M_WAITOK);
228 				bzero((caddr_t)*lphp, sizeof (struct nqm));
229 				lph = (*lphp)->lpm_hosts;
230 			}
231 			nqsrv_addhost(lph, nd->nd_slp, nam);
232 			nqsrv_unlocklease(lp);
233 		} else {
234 			lp->lc_flag |= LC_NONCACHABLE;
235 			nqsrv_locklease(lp);
236 			VOP_UNLOCK(vp);
237 			nqsrv_send_eviction(vp, lp, nd->nd_slp, nam, cred);
238 			nqsrv_waitfor_expiry(lp);
239 			VOP_LOCK(vp);
240 			nqsrv_unlocklease(lp);
241 		}
242 doreply:
243 		/*
244 		 * Update the lease and return
245 		 */
246 		if ((flags & NQL_CHECK) == 0)
247 			nqsrv_instimeq(lp, *duration);
248 		if (lp->lc_flag & LC_NONCACHABLE)
249 			*cachablep = 0;
250 		else {
251 			*cachablep = 1;
252 			if (flags & NQL_WRITE)
253 				lp->lc_flag |= LC_WRITTEN;
254 		}
255 		splx(s);
256 		return (0);
257 	}
258 	splx(s);
259 	if (flags & NQL_CHECK)
260 		return (0);
261 
262 	/*
263 	 * Allocate new lease
264 	 * The value of nqsrv_maxnumlease should be set generously, so that
265 	 * the following "printf" happens infrequently.
266 	 */
267 	if (nfsstats.srvnqnfs_leases > nqsrv_maxnumlease) {
268 		printf("Nqnfs server, too many leases\n");
269 		do {
270 			(void) tsleep((caddr_t)&lbolt, PSOCK,
271 					"nqsrvnuml", 0);
272 		} while (nfsstats.srvnqnfs_leases > nqsrv_maxnumlease);
273 	}
274 	MALLOC(lp, struct nqlease *, sizeof (struct nqlease), M_NQLEASE, M_WAITOK);
275 	bzero((caddr_t)lp, sizeof (struct nqlease));
276 	if (flags & NQL_WRITE)
277 		lp->lc_flag |= (LC_WRITE | LC_WRITTEN);
278 	nqsrv_addhost(&lp->lc_host, nd->nd_slp, nam);
279 	lp->lc_vp = vp;
280 	lp->lc_fsid = fh.fh_fsid;
281 	bcopy(fh.fh_fid.fid_data, lp->lc_fiddata, fh.fh_fid.fid_len - sizeof (long));
282 	lp->lc_fhnext = lhp->th_chain[0];
283 	if (lhp->th_head[0] == lhp)
284 		lhp->th_chain[1] = lp;
285 	else
286 		lhp->th_chain[0]->lc_fhprev = lp;
287 	lp->lc_fhprev = (struct nqlease *)lhp;
288 	lhp->th_chain[0] = lp;
289 	vp->v_lease = lp;
290 	s = splsoftclock();
291 	nqsrv_instimeq(lp, *duration);
292 	splx(s);
293 	*cachablep = 1;
294 	if (++nfsstats.srvnqnfs_leases > nfsstats.srvnqnfs_maxleases)
295 		nfsstats.srvnqnfs_maxleases = nfsstats.srvnqnfs_leases;
296 	return (0);
297 }
298 
299 /*
300  * Local lease check for server syscalls.
301  * Just set up args and let nqsrv_getlease() do the rest.
302  */
303 void
304 lease_check(vp, p, cred, flag)
305 	struct vnode *vp;
306 	struct proc *p;
307 	struct ucred *cred;
308 	int flag;
309 {
310 	int duration, cache;
311 	struct nfsd nfsd;
312 	u_quad_t frev;
313 
314 	nfsd.nd_slp = NQLOCALSLP;
315 	nfsd.nd_procp = p;
316 	(void) nqsrv_getlease(vp, &duration, NQL_CHECK | flag, &nfsd,
317 		(struct mbuf *)0, &cache, &frev, cred);
318 }
319 
320 /*
321  * Add a host to an nqhost structure for a lease.
322  */
323 void
324 nqsrv_addhost(lph, slp, nam)
325 	register struct nqhost *lph;
326 	struct nfssvc_sock *slp;
327 	struct mbuf *nam;
328 {
329 	register struct sockaddr_in *saddr;
330 
331 	if (slp == NQLOCALSLP)
332 		lph->lph_flag |= (LC_VALID | LC_LOCAL);
333 	else if (slp == nfs_udpsock) {
334 		saddr = mtod(nam, struct sockaddr_in *);
335 		lph->lph_flag |= (LC_VALID | LC_UDP);
336 		lph->lph_inetaddr = saddr->sin_addr.s_addr;
337 		lph->lph_port = saddr->sin_port;
338 	} else if (slp == nfs_cltpsock) {
339 		lph->lph_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
340 		lph->lph_flag |= (LC_VALID | LC_CLTP);
341 	} else {
342 		lph->lph_flag |= (LC_VALID | LC_SREF);
343 		lph->lph_slp = slp;
344 		slp->ns_sref++;
345 	}
346 }
347 
348 /*
349  * Update the lease expiry time and position it in the timer queue correctly.
350  */
351 void
352 nqsrv_instimeq(lp, duration)
353 	register struct nqlease *lp;
354 	u_long duration;
355 {
356 	register struct nqlease *tlp;
357 	time_t newexpiry;
358 
359 	newexpiry = time.tv_sec + duration + nqsrv_clockskew;
360 	if (lp->lc_expiry == newexpiry)
361 		return;
362 	if (lp->lc_chain1[0])
363 		remque(lp);
364 	lp->lc_expiry = newexpiry;
365 
366 	/*
367 	 * Find where in the queue it should be.
368 	 */
369 	tlp = nqthead.th_chain[1];
370 	while (tlp->lc_expiry > newexpiry && tlp != (struct nqlease *)&nqthead)
371 		tlp = tlp->lc_chain1[1];
372 	if (tlp == nqthead.th_chain[1])
373 		NQSTORENOVRAM(newexpiry);
374 	insque(lp, tlp);
375 }
376 
377 /*
378  * Compare the requesting host address with the lph entry in the lease.
379  * Return true iff it is the same.
380  * This is somewhat messy due to the union in the nqhost structure.
381  * The local host is indicated by the special value of NQLOCALSLP for slp.
382  */
383 nqsrv_cmpnam(slp, nam, lph)
384 	register struct nfssvc_sock *slp;
385 	struct mbuf *nam;
386 	register struct nqhost *lph;
387 {
388 	register struct sockaddr_in *saddr;
389 	struct mbuf *addr;
390 	union nethostaddr lhaddr;
391 	int ret;
392 
393 	if (slp == NQLOCALSLP) {
394 		if (lph->lph_flag & LC_LOCAL)
395 			return (1);
396 		else
397 			return (0);
398 	}
399 	if (slp == nfs_udpsock || slp == nfs_cltpsock)
400 		addr = nam;
401 	else
402 		addr = slp->ns_nam;
403 	if (lph->lph_flag & LC_UDP)
404 		ret = netaddr_match(AF_INET, &lph->lph_haddr,
405 			(union nethostaddr *)0, addr);
406 	else if (lph->lph_flag & LC_CLTP)
407 		ret = netaddr_match(AF_ISO, &lph->lph_claddr,
408 			(union nethostaddr *)0, addr);
409 	else {
410 		if ((lph->lph_slp->ns_flag & SLP_VALID) == 0)
411 			return (0);
412 		saddr = mtod(lph->lph_slp->ns_nam, struct sockaddr_in *);
413 		if (saddr->sin_family == AF_INET)
414 			lhaddr.had_inetaddr = saddr->sin_addr.s_addr;
415 		else
416 			lhaddr.had_nam = lph->lph_slp->ns_nam;
417 		ret = netaddr_match(saddr->sin_family, &lhaddr,
418 			(union nethostaddr *)0, addr);
419 	}
420 	return (ret);
421 }
422 
423 /*
424  * Send out eviction notice messages to all other hosts for the lease.
425  */
426 void
427 nqsrv_send_eviction(vp, lp, slp, nam, cred)
428 	struct vnode *vp;
429 	register struct nqlease *lp;
430 	struct nfssvc_sock *slp;
431 	struct mbuf *nam;
432 	struct ucred *cred;
433 {
434 	register struct nqhost *lph = &lp->lc_host;
435 	register struct mbuf *m;
436 	register int siz;
437 	struct nqm *lphnext = lp->lc_morehosts;
438 	struct mbuf *mreq, *mb, *mb2, *nam2, *mheadend;
439 	struct socket *so;
440 	struct sockaddr_in *saddr;
441 	fhandle_t *fhp;
442 	caddr_t bpos, cp;
443 	u_long xid;
444 	int len = 1, ok = 1, i = 0;
445 	int sotype, *solockp;
446 
447 	while (ok && (lph->lph_flag & LC_VALID)) {
448 		if (nqsrv_cmpnam(slp, nam, lph))
449 			lph->lph_flag |= LC_VACATED;
450 		else if ((lph->lph_flag & (LC_LOCAL | LC_VACATED)) == 0) {
451 			if (lph->lph_flag & LC_UDP) {
452 				MGET(nam2, M_WAIT, MT_SONAME);
453 				saddr = mtod(nam2, struct sockaddr_in *);
454 				nam2->m_len = saddr->sin_len =
455 					sizeof (struct sockaddr_in);
456 				saddr->sin_family = AF_INET;
457 				saddr->sin_addr.s_addr = lph->lph_inetaddr;
458 				saddr->sin_port = lph->lph_port;
459 				so = nfs_udpsock->ns_so;
460 			} else if (lph->lph_flag & LC_CLTP) {
461 				nam2 = lph->lph_nam;
462 				so = nfs_cltpsock->ns_so;
463 			} else if (lph->lph_slp->ns_flag & SLP_VALID) {
464 				nam2 = (struct mbuf *)0;
465 				so = lph->lph_slp->ns_so;
466 			} else
467 				goto nextone;
468 			sotype = so->so_type;
469 			if (so->so_proto->pr_flags & PR_CONNREQUIRED)
470 				solockp = &lph->lph_slp->ns_solock;
471 			else
472 				solockp = (int *)0;
473 			nfsm_reqhead((struct vnode *)0, NQNFSPROC_EVICTED,
474 				NFSX_FH);
475 			nfsm_build(cp, caddr_t, NFSX_FH);
476 			bzero(cp, NFSX_FH);
477 			fhp = (fhandle_t *)cp;
478 			fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
479 			VFS_VPTOFH(vp, &fhp->fh_fid);
480 			m = mreq;
481 			siz = 0;
482 			while (m) {
483 				siz += m->m_len;
484 				m = m->m_next;
485 			}
486 			if (siz <= 0 || siz > NFS_MAXPACKET) {
487 				printf("mbuf siz=%d\n",siz);
488 				panic("Bad nfs svc reply");
489 			}
490 			m = nfsm_rpchead(cred, TRUE, NQNFSPROC_EVICTED,
491 				RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
492 				mreq, siz, &mheadend, &xid);
493 			/*
494 			 * For stream protocols, prepend a Sun RPC
495 			 * Record Mark.
496 			 */
497 			if (sotype == SOCK_STREAM) {
498 				M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
499 				*mtod(m, u_long *) = htonl(0x80000000 |
500 					(m->m_pkthdr.len - NFSX_UNSIGNED));
501 			}
502 			if (((lph->lph_flag & (LC_UDP | LC_CLTP)) == 0 &&
503 			    (lph->lph_slp->ns_flag & SLP_VALID) == 0) ||
504 			    (solockp && (*solockp & NFSMNT_SNDLOCK)))
505 				m_freem(m);
506 			else {
507 				if (solockp)
508 					*solockp |= NFSMNT_SNDLOCK;
509 				(void) nfs_send(so, nam2, m,
510 						(struct nfsreq *)0);
511 				if (solockp)
512 					nfs_sndunlock(solockp);
513 			}
514 			if (lph->lph_flag & LC_UDP)
515 				MFREE(nam2, m);
516 		}
517 nextone:
518 		if (++i == len) {
519 			if (lphnext) {
520 				i = 0;
521 				len = LC_MOREHOSTSIZ;
522 				lph = lphnext->lpm_hosts;
523 				lphnext = lphnext->lpm_next;
524 			} else
525 				ok = 0;
526 		} else
527 			lph++;
528 	}
529 }
530 
531 /*
532  * Wait for the lease to expire.
533  * This will occur when all clients have sent "vacated" messages to
534  * this server OR when it expires do to timeout.
535  */
536 void
537 nqsrv_waitfor_expiry(lp)
538 	register struct nqlease *lp;
539 {
540 	register struct nqhost *lph;
541 	register int i;
542 	struct nqm *lphnext;
543 	int len, ok;
544 
545 tryagain:
546 	if (time.tv_sec > lp->lc_expiry)
547 		return;
548 	lph = &lp->lc_host;
549 	lphnext = lp->lc_morehosts;
550 	len = 1;
551 	i = 0;
552 	ok = 1;
553 	while (ok && (lph->lph_flag & LC_VALID)) {
554 		if ((lph->lph_flag & (LC_LOCAL | LC_VACATED)) == 0) {
555 			lp->lc_flag |= LC_EXPIREDWANTED;
556 			(void) tsleep((caddr_t)&lp->lc_flag, PSOCK,
557 					"nqexp", 0);
558 			goto tryagain;
559 		}
560 		if (++i == len) {
561 			if (lphnext) {
562 				i = 0;
563 				len = LC_MOREHOSTSIZ;
564 				lph = lphnext->lpm_hosts;
565 				lphnext = lphnext->lpm_next;
566 			} else
567 				ok = 0;
568 		} else
569 			lph++;
570 	}
571 }
572 
573 /*
574  * Nqnfs server timer that maintains the server lease queue.
575  * Scan the lease queue for expired entries:
576  * - when one is found, wakeup anyone waiting for it
577  *   else dequeue and free
578  */
579 void
580 nqnfs_serverd()
581 {
582 	register struct nqlease *lp;
583 	register struct nqhost *lph;
584 	struct nqlease *nextlp;
585 	struct nqm *lphnext, *olphnext;
586 	struct mbuf *n;
587 	union nqsrvthead *lhp;
588 	int i, len, ok;
589 
590 	lp = nqthead.th_chain[0];
591 	while (lp != (struct nqlease *)&nqthead) {
592 		if (lp->lc_expiry >= time.tv_sec)
593 			break;
594 		nextlp = lp->lc_chain1[0];
595 		if (lp->lc_flag & LC_EXPIREDWANTED) {
596 			lp->lc_flag &= ~LC_EXPIREDWANTED;
597 			wakeup((caddr_t)&lp->lc_flag);
598 		} else if ((lp->lc_flag & (LC_LOCKED | LC_WANTED)) == 0) {
599 		    /*
600 		     * Make a best effort at keeping a write caching lease long
601 		     * enough by not deleting it until it has been explicitly
602 		     * vacated or there have been no writes in the previous
603 		     * write_slack seconds since expiry and the nfsds are not
604 		     * all busy. The assumption is that if the nfsds are not
605 		     * all busy now (no queue of nfs requests), then the client
606 		     * would have been able to do at least one write to the
607 		     * file during the last write_slack seconds if it was still
608 		     * trying to push writes to the server.
609 		     */
610 		    if ((lp->lc_flag & (LC_WRITE | LC_VACATED)) == LC_WRITE &&
611 			((lp->lc_flag & LC_WRITTEN) || nfsd_waiting == 0)) {
612 			lp->lc_flag &= ~LC_WRITTEN;
613 			nqsrv_instimeq(lp, nqsrv_writeslack);
614 		    } else {
615 			remque(lp);
616 			lhp = &nqfhead[NQFHHASH(lp->lc_fiddata)];
617 			if (lp->lc_fhprev == (struct nqlease *)lhp)
618 				lhp->th_chain[0] = lp->lc_fhnext;
619 			else
620 				lp->lc_fhprev->lc_fhnext = lp->lc_fhnext;
621 			if (lp->lc_fhnext == (struct nqlease *)lhp)
622 				lhp->th_chain[1] = lp->lc_fhprev;
623 			else
624 				lp->lc_fhnext->lc_fhprev = lp->lc_fhprev;
625 			/*
626 			 * This soft reference may no longer be valid, but
627 			 * no harm done. The worst case is if the vnode was
628 			 * recycled and has another valid lease reference,
629 			 * which is dereferenced prematurely.
630 			 */
631 			lp->lc_vp->v_lease = (struct nqlease *)0;
632 			lph = &lp->lc_host;
633 			lphnext = lp->lc_morehosts;
634 			olphnext = (struct nqm *)0;
635 			len = 1;
636 			i = 0;
637 			ok = 1;
638 			while (ok && (lph->lph_flag & LC_VALID)) {
639 				if (lph->lph_flag & LC_CLTP)
640 					MFREE(lph->lph_nam, n);
641 				if (lph->lph_flag & LC_SREF)
642 					nfsrv_slpderef(lph->lph_slp);
643 				if (++i == len) {
644 					if (olphnext) {
645 						free((caddr_t)olphnext, M_NQMHOST);
646 						olphnext = (struct nqm *)0;
647 					}
648 					if (lphnext) {
649 						olphnext = lphnext;
650 						i = 0;
651 						len = LC_MOREHOSTSIZ;
652 						lph = lphnext->lpm_hosts;
653 						lphnext = lphnext->lpm_next;
654 					} else
655 						ok = 0;
656 				} else
657 					lph++;
658 			}
659 			FREE((caddr_t)lp, M_NQLEASE);
660 			if (olphnext)
661 				free((caddr_t)olphnext, M_NQMHOST);
662 			nfsstats.srvnqnfs_leases--;
663 		    }
664 		}
665 		lp = nextlp;
666 	}
667 }
668 
669 /*
670  * Called from nfssvc_nfsd() for a getlease rpc request.
671  * Do the from/to xdr translation and call nqsrv_getlease() to
672  * do the real work.
673  */
674 nqnfsrv_getlease(nfsd, mrep, md, dpos, cred, nam, mrq)
675 	struct nfsd *nfsd;
676 	struct mbuf *mrep, *md;
677 	caddr_t dpos;
678 	struct ucred *cred;
679 	struct mbuf *nam, **mrq;
680 {
681 	USES_VOP_GETATTR;
682 	register struct nfsv2_fattr *fp;
683 	struct vattr va;
684 	register struct vattr *vap = &va;
685 	struct vnode *vp;
686 	nfsv2fh_t nfh;
687 	fhandle_t *fhp;
688 	register u_long *tl;
689 	register long t1;
690 	u_quad_t frev;
691 	caddr_t bpos;
692 	int error = 0;
693 	char *cp2;
694 	struct mbuf *mb, *mb2, *mreq;
695 	int flags, rdonly, cache;
696 
697 	fhp = &nfh.fh_generic;
698 	nfsm_srvmtofh(fhp);
699 	nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
700 	flags = fxdr_unsigned(int, *tl++);
701 	nfsd->nd_duration = fxdr_unsigned(int, *tl);
702 	if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
703 		nfsm_reply(0);
704 	if (rdonly && flags == NQL_WRITE) {
705 		error = EROFS;
706 		nfsm_reply(0);
707 	}
708 	(void) nqsrv_getlease(vp, &nfsd->nd_duration, flags, nfsd,
709 		nam, &cache, &frev, cred);
710 	error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
711 	vput(vp);
712 	nfsm_reply(NFSX_FATTR + 4*NFSX_UNSIGNED);
713 	nfsm_build(tl, u_long *, 4*NFSX_UNSIGNED);
714 	*tl++ = txdr_unsigned(cache);
715 	*tl++ = txdr_unsigned(nfsd->nd_duration);
716 	txdr_hyper(&frev, tl);
717 	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR);
718 	nfsm_srvfillattr;
719 	nfsm_srvdone;
720 }
721 
722 /*
723  * Called from nfssvc_nfsd() when a "vacated" message is received from a
724  * client. Find the entry and expire it.
725  */
726 nqnfsrv_vacated(nfsd, mrep, md, dpos, cred, nam, mrq)
727 	struct nfsd *nfsd;
728 	struct mbuf *mrep, *md;
729 	caddr_t dpos;
730 	struct ucred *cred;
731 	struct mbuf *nam, **mrq;
732 {
733 	register struct nqlease *lp;
734 	register struct nqhost *lph;
735 	struct nqlease *tlp = (struct nqlease *)0;
736 	struct vnode *vp;
737 	nfsv2fh_t nfh;
738 	fhandle_t *fhp;
739 	register u_long *tl;
740 	register long t1;
741 	struct nqm *lphnext;
742 	union nqsrvthead *lhp;
743 	u_quad_t frev;
744 	int error = 0, i, len, ok, rdonly, gotit = 0;
745 	char *cp2;
746 
747 	fhp = &nfh.fh_generic;
748 	nfsm_srvmtofh(fhp);
749 	if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
750 		return (error);
751 	m_freem(mrep);
752 	tlp = vp->v_lease;
753 	if (tlp == (struct nqlease *)0) {
754 		/*
755 		 * Find the lease by searching the hash list.
756 		 */
757 		lhp = &nqfhead[NQFHHASH(fhp->fh_fid.fid_data)];
758 		for (lp = lhp->th_chain[0]; lp != (struct nqlease *)lhp;
759 			lp = lp->lc_fhnext)
760 			if (fhp->fh_fsid.val[0] == lp->lc_fsid.val[0] &&
761 			    fhp->fh_fsid.val[1] == lp->lc_fsid.val[1] &&
762 			    !bcmp(fhp->fh_fid.fid_data, lp->lc_fiddata,
763 				  MAXFIDSZ)) {
764 				/* Found it */
765 				lp->lc_vp = vp;
766 				vp->v_lease = lp;
767 				tlp = lp;
768 				break;
769 			}
770 	}
771 	vrele(vp);
772 	if (tlp) {
773 		lp = tlp;
774 		len = 1;
775 		i = 0;
776 		lph = &lp->lc_host;
777 		lphnext = lp->lc_morehosts;
778 		ok = 1;
779 		while (ok && (lph->lph_flag & LC_VALID)) {
780 			if (nqsrv_cmpnam(nfsd->nd_slp, nam, lph)) {
781 				lph->lph_flag |= LC_VACATED;
782 				gotit++;
783 				break;
784 			}
785 			if (++i == len) {
786 				if (lphnext) {
787 					len = LC_MOREHOSTSIZ;
788 					i = 0;
789 					lph = lphnext->lpm_hosts;
790 					lphnext = lphnext->lpm_next;
791 				} else
792 					ok = 0;
793 			} else
794 				lph++;
795 		}
796 		if ((lp->lc_flag & LC_EXPIREDWANTED) && gotit) {
797 			lp->lc_flag &= ~LC_EXPIREDWANTED;
798 			wakeup((caddr_t)&lp->lc_flag);
799 		}
800 nfsmout:
801 		return (EPERM);
802 	}
803 	return (EPERM);
804 }
805 
806 /*
807  * Client get lease rpc function.
808  */
809 nqnfs_getlease(vp, rwflag, cred, p)
810 	register struct vnode *vp;
811 	int rwflag;
812 	struct ucred *cred;
813 	struct proc *p;
814 {
815 	register u_long *tl;
816 	register caddr_t cp;
817 	register long t1;
818 	register struct nfsnode *np, *tp;
819 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
820 	caddr_t bpos, dpos, cp2;
821 	time_t reqtime;
822 	int error = 0;
823 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
824 	int cachable;
825 
826 	nfsstats.rpccnt[NQNFSPROC_GETLEASE]++;
827 	mb = mreq = nfsm_reqh(vp, NQNFSPROC_GETLEASE, NFSX_FH+2*NFSX_UNSIGNED,
828 		 &bpos);
829 	nfsm_fhtom(vp);
830 	nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
831 	*tl++ = txdr_unsigned(rwflag);
832 	*tl = txdr_unsigned(nmp->nm_leaseterm);
833 	reqtime = time.tv_sec;
834 	nfsm_request(vp, NQNFSPROC_GETLEASE, p, cred);
835 	np = VTONFS(vp);
836 	nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
837 	cachable = fxdr_unsigned(int, *tl++);
838 	reqtime += fxdr_unsigned(int, *tl++);
839 	if (reqtime > time.tv_sec) {
840 		if (np->n_tnext) {
841 			if (np->n_tnext == (struct nfsnode *)nmp)
842 				nmp->nm_tprev = np->n_tprev;
843 			else
844 				np->n_tnext->n_tprev = np->n_tprev;
845 			if (np->n_tprev == (struct nfsnode *)nmp)
846 				nmp->nm_tnext = np->n_tnext;
847 			else
848 				np->n_tprev->n_tnext = np->n_tnext;
849 			if (rwflag == NQL_WRITE)
850 				np->n_flag |= NQNFSWRITE;
851 		} else if (rwflag == NQL_READ)
852 			np->n_flag &= ~NQNFSWRITE;
853 		else
854 			np->n_flag |= NQNFSWRITE;
855 		if (cachable)
856 			np->n_flag &= ~NQNFSNONCACHE;
857 		else
858 			np->n_flag |= NQNFSNONCACHE;
859 		np->n_expiry = reqtime;
860 		fxdr_hyper(tl, &np->n_lrev);
861 		tp = nmp->nm_tprev;
862 		while (tp != (struct nfsnode *)nmp && tp->n_expiry > np->n_expiry)
863 			tp = tp->n_tprev;
864 		if (tp == (struct nfsnode *)nmp) {
865 			np->n_tnext = nmp->nm_tnext;
866 			nmp->nm_tnext = np;
867 		} else {
868 			np->n_tnext = tp->n_tnext;
869 			tp->n_tnext = np;
870 		}
871 		np->n_tprev = tp;
872 		if (np->n_tnext == (struct nfsnode *)nmp)
873 			nmp->nm_tprev = np;
874 		else
875 			np->n_tnext->n_tprev = np;
876 		nfsm_loadattr(vp, (struct vattr *)0);
877 	} else
878 		error = NQNFS_EXPIRED;
879 	nfsm_reqdone;
880 	return (error);
881 }
882 
883 /*
884  * Client vacated message function.
885  */
886 nqnfs_vacated(vp, cred)
887 	register struct vnode *vp;
888 	struct ucred *cred;
889 {
890 	register caddr_t cp;
891 	register struct mbuf *m;
892 	register int i;
893 	caddr_t bpos;
894 	u_long xid;
895 	int error = 0;
896 	struct mbuf *mreq, *mb, *mb2, *mheadend;
897 	struct nfsmount *nmp;
898 	struct nfsreq myrep;
899 
900 	nmp = VFSTONFS(vp->v_mount);
901 	nfsstats.rpccnt[NQNFSPROC_VACATED]++;
902 	nfsm_reqhead(vp, NQNFSPROC_VACATED, NFSX_FH);
903 	nfsm_fhtom(vp);
904 	m = mreq;
905 	i = 0;
906 	while (m) {
907 		i += m->m_len;
908 		m = m->m_next;
909 	}
910 	m = nfsm_rpchead(cred, TRUE, NQNFSPROC_VACATED,
911 		RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
912 		mreq, i, &mheadend, &xid);
913 	if (nmp->nm_sotype == SOCK_STREAM) {
914 		M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
915 		*mtod(m, u_long *) = htonl(0x80000000 | (m->m_pkthdr.len -
916 			NFSX_UNSIGNED));
917 	}
918 	myrep.r_flags = 0;
919 	myrep.r_nmp = nmp;
920 	if (nmp->nm_soflags & PR_CONNREQUIRED)
921 		(void) nfs_sndlock(&nmp->nm_flag, (struct nfsreq *)0);
922 	(void) nfs_send(nmp->nm_so, nmp->nm_nam, m, &myrep);
923 	if (nmp->nm_soflags & PR_CONNREQUIRED)
924 		nfs_sndunlock(&nmp->nm_flag);
925 	return (error);
926 }
927 
928 /*
929  * Called for client side callbacks
930  */
931 nqnfs_callback(nmp, mrep, md, dpos)
932 	struct nfsmount *nmp;
933 	struct mbuf *mrep, *md;
934 	caddr_t dpos;
935 {
936 	register struct vnode *vp;
937 	register u_long *tl;
938 	register long t1;
939 	nfsv2fh_t nfh;
940 	fhandle_t *fhp;
941 	struct nfsnode *np;
942 	struct nfsd nd;
943 	int error;
944 	char *cp2;
945 
946 	nd.nd_mrep = mrep;
947 	nd.nd_md = md;
948 	nd.nd_dpos = dpos;
949 	if (error = nfs_getreq(&nd, FALSE))
950 		return (error);
951 	md = nd.nd_md;
952 	dpos = nd.nd_dpos;
953 	if (nd.nd_procnum != NQNFSPROC_EVICTED) {
954 		m_freem(mrep);
955 		return (EPERM);
956 	}
957 	fhp = &nfh.fh_generic;
958 	nfsm_srvmtofh(fhp);
959 	m_freem(mrep);
960 	if (error = nfs_nget(nmp->nm_mountp, fhp, &np))
961 		return (error);
962 	vp = NFSTOV(np);
963 	if (np->n_tnext) {
964 		np->n_expiry = 0;
965 		np->n_flag |= NQNFSEVICTED;
966 		if (np->n_tprev != (struct nfsnode *)nmp) {
967 			if (np->n_tnext == (struct nfsnode *)nmp)
968 				nmp->nm_tprev = np->n_tprev;
969 			else
970 				np->n_tnext->n_tprev = np->n_tprev;
971 			np->n_tprev->n_tnext = np->n_tnext;
972 			np->n_tnext = nmp->nm_tnext;
973 			nmp->nm_tnext = np;
974 			np->n_tprev = (struct nfsnode *)nmp;
975 			if (np->n_tnext == (struct nfsnode *)nmp)
976 				nmp->nm_tprev = np;
977 			else
978 				np->n_tnext->n_tprev = np;
979 		}
980 	}
981 	vrele(vp);
982 	nfsm_srvdone;
983 }
984 
985 /*
986  * Nqnfs client helper daemon. Runs once a second to expire leases.
987  * It also get authorization strings for "kerb" mounts.
988  * It must start at the beginning of the list again after any potential
989  * "sleep" since nfs_reclaim() called from vclean() can pull a node off
990  * the list asynchronously.
991  */
992 nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
993 	register struct nfsmount *nmp;
994 	struct ucred *cred;
995 	struct nfsd_cargs *ncd;
996 	int flag;
997 	caddr_t argp;
998 	struct proc *p;
999 {
1000 	USES_VOP_FSYNC;
1001 	register struct nfsnode *np;
1002 	struct vnode *vp;
1003 	int error, vpid;
1004 
1005 	/*
1006 	 * First initialize some variables
1007 	 */
1008 	nqnfs_prog = txdr_unsigned(NQNFS_PROG);
1009 	nqnfs_vers = txdr_unsigned(NQNFS_VER1);
1010 
1011 	/*
1012 	 * If an authorization string is being passed in, get it.
1013 	 */
1014 	if ((flag & NFSSVC_GOTAUTH) &&
1015 		(nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT)) == 0) {
1016 		if (nmp->nm_flag & NFSMNT_HASAUTH)
1017 			panic("cld kerb");
1018 		if ((flag & NFSSVC_AUTHINFAIL) == 0) {
1019 			if (ncd->ncd_authlen <= RPCAUTH_MAXSIZ &&
1020 				copyin(ncd->ncd_authstr, nmp->nm_authstr,
1021 				ncd->ncd_authlen) == 0) {
1022 				nmp->nm_authtype = ncd->ncd_authtype;
1023 				nmp->nm_authlen = ncd->ncd_authlen;
1024 			} else
1025 				nmp->nm_flag |= NFSMNT_AUTHERR;
1026 		} else
1027 			nmp->nm_flag |= NFSMNT_AUTHERR;
1028 		nmp->nm_flag |= NFSMNT_HASAUTH;
1029 		wakeup((caddr_t)&nmp->nm_authlen);
1030 	} else
1031 		nmp->nm_flag |= NFSMNT_WAITAUTH;
1032 
1033 	/*
1034 	 * Loop every second updating queue until there is a termination sig.
1035 	 */
1036 	while ((nmp->nm_flag & NFSMNT_DISMNT) == 0) {
1037 	    if (nmp->nm_flag & NFSMNT_NQNFS) {
1038 		np = nmp->nm_tnext;
1039 		while (np != (struct nfsnode *)nmp &&
1040 		       (nmp->nm_flag & NFSMNT_DISMINPROG) == 0) {
1041 			vp = NFSTOV(np);
1042 if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash2");
1043 			vpid = vp->v_id;
1044 			if (np->n_expiry < time.tv_sec) {
1045 			   if (vget(vp) == 0) {
1046 			     nmp->nm_inprog = vp;
1047 			     if (vpid == vp->v_id) {
1048 if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash3");
1049 				if (np->n_tnext == (struct nfsnode *)nmp)
1050 					nmp->nm_tprev = np->n_tprev;
1051 				else
1052 					np->n_tnext->n_tprev = np->n_tprev;
1053 				if (np->n_tprev == (struct nfsnode *)nmp)
1054 					nmp->nm_tnext = np->n_tnext;
1055 				else
1056 					np->n_tprev->n_tnext = np->n_tnext;
1057 				np->n_tnext = (struct nfsnode *)0;
1058 				if ((np->n_flag & (NMODIFIED | NQNFSEVICTED))
1059 				    && vp->v_type == VREG) {
1060 					np->n_flag &= ~NMODIFIED;
1061 					if (np->n_flag & NQNFSEVICTED) {
1062 						(void) vinvalbuf(vp, TRUE,
1063 						    cred, p);
1064 						np->n_flag &= ~NQNFSEVICTED;
1065 						(void) nqnfs_vacated(vp, cred);
1066 					} else
1067 						(void) VOP_FSYNC(vp, cred,
1068 						    MNT_WAIT, p);
1069 				}
1070 			      }
1071 			      vrele(vp);
1072 			      nmp->nm_inprog = NULLVP;
1073 			    }
1074 			    if (np != nmp->nm_tnext)
1075 				np = nmp->nm_tnext;
1076 			    else
1077 				break;
1078 			} else if ((np->n_expiry - NQ_RENEWAL) < time.tv_sec) {
1079 			    if ((np->n_flag & (NQNFSWRITE | NQNFSNONCACHE))
1080 				 == NQNFSWRITE && vp->v_dirtyblkhd &&
1081 				 vget(vp) == 0) {
1082 				 nmp->nm_inprog = vp;
1083 if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash4");
1084 				 if (vpid == vp->v_id &&
1085 				     nqnfs_getlease(vp, NQL_WRITE, cred, p)==0)
1086 					np->n_brev = np->n_lrev;
1087 				 vrele(vp);
1088 				 nmp->nm_inprog = NULLVP;
1089 			    }
1090 			    if (np != nmp->nm_tnext)
1091 				np = nmp->nm_tnext;
1092 			    else
1093 				break;
1094 			} else
1095 				break;
1096 		}
1097 	    }
1098 
1099 	    /*
1100 	     * Get an authorization string, if required.
1101 	     */
1102 	    if ((nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT | NFSMNT_HASAUTH)) == 0) {
1103 		ncd->ncd_authuid = nmp->nm_authuid;
1104 		if (copyout((caddr_t)ncd, argp, sizeof (*ncd)))
1105 			nmp->nm_flag |= NFSMNT_WAITAUTH;
1106 		else
1107 			return (ENEEDAUTH);
1108 	    }
1109 
1110 	    /*
1111 	     * Wait a bit (no pun) and do it again.
1112 	     */
1113 	    if ((nmp->nm_flag & NFSMNT_DISMNT) == 0 &&
1114 		(nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_HASAUTH))) {
1115 		    error = tsleep((caddr_t)&nmp->nm_authstr, PSOCK | PCATCH,
1116 			"nqnfstimr", hz / 3);
1117 		    if (error == EINTR || error == ERESTART)
1118 			(void) dounmount(nmp->nm_mountp, MNT_NOFORCE);
1119 	    }
1120 	}
1121 	free((caddr_t)nmp, M_NFSMNT);
1122 	if (error == EWOULDBLOCK)
1123 		error = 0;
1124 	return (error);
1125 }
1126 
1127 /*
1128  * Adjust all timer queue expiry times when the time of day clock is changed.
1129  * Called from the settimeofday() syscall.
1130  */
1131 void
1132 lease_updatetime(deltat)
1133 	register int deltat;
1134 {
1135 	register struct nqlease *lp;
1136 	register struct nfsnode *np;
1137 	struct mount *mp;
1138 	struct nfsmount *nmp;
1139 	int s;
1140 
1141 	if (nqnfsstarttime != 0)
1142 		nqnfsstarttime += deltat;
1143 	s = splsoftclock();
1144 	lp = nqthead.th_chain[0];
1145 	while (lp != (struct nqlease *)&nqthead) {
1146 		lp->lc_expiry += deltat;
1147 		lp = lp->lc_chain1[0];
1148 	}
1149 	splx(s);
1150 
1151 	/*
1152 	 * Search the mount list for all nqnfs mounts and do their timer
1153 	 * queues.
1154 	 */
1155 	mp = rootfs;
1156 	do {
1157 		if (mp->mnt_stat.f_fsid.val[1] == MOUNT_NFS) {
1158 			nmp = VFSTONFS(mp);
1159 			if (nmp->nm_flag & NFSMNT_NQNFS) {
1160 				np = nmp->nm_tnext;
1161 				while (np != (struct nfsnode *)nmp) {
1162 					np->n_expiry += deltat;
1163 					np = np->n_tnext;
1164 				}
1165 			}
1166 		}
1167 		mp = mp->mnt_next;
1168 	} while (mp != rootfs);
1169 }
1170 
1171 /*
1172  * Lock a server lease.
1173  */
1174 void
1175 nqsrv_locklease(lp)
1176 	struct nqlease *lp;
1177 {
1178 
1179 	while (lp->lc_flag & LC_LOCKED) {
1180 		lp->lc_flag |= LC_WANTED;
1181 		(void) tsleep((caddr_t)lp, PSOCK, "nqlc", 0);
1182 	}
1183 	lp->lc_flag |= LC_LOCKED;
1184 	lp->lc_flag &= ~LC_WANTED;
1185 }
1186 
1187 /*
1188  * Unlock a server lease.
1189  */
1190 void
1191 nqsrv_unlocklease(lp)
1192 	struct nqlease *lp;
1193 {
1194 
1195 	lp->lc_flag &= ~LC_LOCKED;
1196 	if (lp->lc_flag & LC_WANTED)
1197 		wakeup((caddr_t)lp);
1198 }
1199