xref: /original-bsd/usr.sbin/amd/amd/map.c (revision 05cf3734)
1 /*
2  * $Id: map.c,v 5.2 90/06/23 22:19:35 jsp Rel $
3  *
4  * Copyright (c) 1990 Jan-Simon Pendry
5  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * %sccs.include.redist.c%
13  *
14  *	@(#)map.c	5.1 (Berkeley) 06/29/90
15  */
16 
17 #include "am.h"
18 
19 /*
20  * Generation Numbers.
21  *
22  * Generation numbers are allocated to every node created
23  * by amd.  When a filehandle is computed and sent to the
24  * kernel, the generation number makes sure that it is safe
25  * to reallocate a node slot even when the kernel has a cached
26  * reference to its old incarnation.
27  * No garbage collection is done, since it is assumed that
28  * there is no way that 2^32 generation numbers could ever
29  * be allocated by a single run of amd - there is simply
30  * not enough cpu time available.
31  */
32 static unsigned int am_gen = 2;	/* Initial generation number */
33 #define new_gen() (am_gen++)
34 
35 struct am_node *exported_ap[NEXP_AP];
36 int first_free_map = 0;		/* First available free slot */
37 int last_used_map = -1;		/* Last unavailable used slot */
38 static int timeout_mp_id;	/* Id from last call to timeout */
39 
40 /*
41  * The root of the mount tree.
42  */
43 static am_node *root_node;
44 
45 /*
46  * Allocate a new mount slot and create
47  * a new node.
48  * Fills in the map number of the node,
49  * but leaves everything else uninitialised.
50  */
51 struct am_node *exported_ap_alloc(P_void)
52 {
53 	struct am_node *mp, **mpp;
54 
55 	/*
56 	 * First check if there are any slots left
57 	 */
58 	if (first_free_map >= NEXP_AP)
59 		return 0;
60 
61 	/*
62 	 * Grab the next free slot
63 	 */
64 	mpp = exported_ap + first_free_map;
65 	mp = *mpp = ALLOC(am_node);
66 	bzero((char *) mp, sizeof(*mp));
67 
68 	mp->am_mapno = first_free_map++;
69 
70 	/*
71 	 * Update free pointer
72 	 */
73 	while (first_free_map < NEXP_AP && exported_ap[first_free_map])
74 		first_free_map++;
75 
76 	if (first_free_map > last_used_map)
77 		last_used_map = first_free_map - 1;
78 
79 #ifdef DEBUG
80 	/*dlog("alloc_exp: last_used_map = %d, first_free_map = %d\n",
81 		last_used_map, first_free_map);*/
82 #endif /* DEBUG */
83 
84 	return mp;
85 }
86 
87 /*
88  * Free a mount slot
89  */
90 void exported_ap_free(mp)
91 struct am_node *mp;
92 {
93 	/*
94 	 * Sanity check
95 	 */
96 	if (!mp)
97 		return;
98 
99 	/*
100 	 * Zero the slot pointer to avoid double free's
101 	 */
102 	exported_ap[mp->am_mapno] = 0;
103 
104 	/*
105 	 * Update the free and last_used indices
106 	 */
107 	if (mp->am_mapno == last_used_map)
108 		while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
109 			--last_used_map;
110 
111 	if (first_free_map > mp->am_mapno)
112 		first_free_map = mp->am_mapno;
113 
114 #ifdef DEBUG
115 	/*dlog("free_exp: last_used_map = %d, first_free_map = %d\n",
116 		last_used_map, first_free_map);*/
117 #endif /* DEBUG */
118 
119 	/*
120 	 * Free the mount node
121 	 */
122 	free(mp);
123 }
124 
125 /*
126  * Insert mp into the correct place,
127  * where p_mp is its parent node.
128  * A new node gets placed as the youngest sibling
129  * of any other children, and the parent's child
130  * pointer is adjusted to point to the new child node.
131  */
132 void insert_am(mp, p_mp)
133 am_node *mp;
134 am_node *p_mp;
135 {
136 	/*
137 	 * If this is going in at the root then flag it
138 	 * so that it cannot be unmounted by amq.
139 	 */
140 	if (p_mp == root_node)
141 		mp->am_flags |= AMF_ROOT;
142 	/*
143 	 * Fill in n-way links
144 	 */
145 	mp->am_parent = p_mp;
146 	mp->am_osib = p_mp->am_child;
147 	if (mp->am_osib)
148 		mp->am_osib->am_ysib = mp;
149 	p_mp->am_child = mp;
150 }
151 
152 /*
153  * Remove am from its place in the mount tree
154  */
155 void remove_am(mp)
156 am_node *mp;
157 {
158 	/*
159 	 * 1.  Consistency check
160 	 */
161 	if (mp->am_child && mp->am_parent) {
162 		plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
163 	}
164 
165 	/*
166 	 * 2.  Update parent's child pointer
167 	 */
168 	if (mp->am_parent && mp->am_parent->am_child == mp)
169 		mp->am_parent->am_child = mp->am_osib;
170 
171 	/*
172 	 * 3.  Unlink from sibling chain
173 	 */
174 	if (mp->am_ysib)
175 		mp->am_ysib->am_osib = mp->am_osib;
176 	if (mp->am_osib)
177 		mp->am_osib->am_ysib = mp->am_ysib;
178 }
179 
180 /*
181  * Compute a new time to live value for a node.
182  */
183 void new_ttl(mp)
184 am_node *mp;
185 {
186 	mp->am_timeo_w = 0;
187 
188 	mp->am_ttl = clocktime();
189 	mp->am_mnt->mf_fattr.atime.seconds = mp->am_ttl;
190 	mp->am_ttl += mp->am_timeo;	/* sun's -tl option */
191 }
192 
193 /*
194  * Initialise an allocated mount node.
195  * It is assumed that the mount node was bzero'd
196  * before getting here so anything that would
197  * be set to zero isn't done here.
198  */
199 void init_map(mp, dir)
200 am_node *mp;
201 char *dir;
202 {
203 	/* mp->am_mapno initalised by exported_ap_alloc */
204 	mp->am_mnt = new_mntfs();
205 	mp->am_name = strdup(dir);
206 	mp->am_path = strdup(dir);
207 	/*mp->am_link = 0;*/
208 	/*mp->am_parent = 0;*/
209 	/*mp->am_ysib = 0;*/
210 	/*mp->am_osib = 0;*/
211 	/*mp->am_child = 0;*/
212 	/*mp->am_flags = 0;*/
213 	/*mp->am_error = 0;*/
214 	mp->am_gen = new_gen();
215 	/*mp->am_pref = 0;*/
216 
217 	mp->am_timeo = am_timeo;
218 	new_ttl(mp);
219 	mp->am_stats.s_mtime = mp->am_mnt->mf_fattr.atime.seconds;
220 	/*mp->am_private = 0;*/
221 }
222 
223 /*
224  * Free a mount node.
225  * The node must be already unmounted.
226  */
227 void free_map(mp)
228 am_node *mp;
229 {
230 	remove_am(mp);
231 
232 	if (mp->am_link)
233 		free(mp->am_link);
234 	if (mp->am_name)
235 		free(mp->am_name);
236 	if (mp->am_path)
237 		free(mp->am_path);
238 	if (mp->am_pref)
239 		free(mp->am_pref);
240 
241 	if (mp->am_mnt)
242 		free_mntfs(mp->am_mnt);
243 
244 	if (mp->am_flags & AMF_MKPATH)
245 		rmdirs(mp->am_path);
246 	exported_ap_free(mp);
247 }
248 
249 /*
250  * Convert from file handle to
251  * automount node.
252  */
253 am_node *fh_to_mp3(fhp, rp, c_or_d)
254 nfs_fh *fhp;
255 int *rp;
256 int c_or_d;
257 {
258 	struct am_fh *fp = (struct am_fh *) fhp;
259 	am_node *ap = 0;
260 
261 	/*
262 	 * Check process id matches
263 	 * If it doesn't then it is probably
264 	 * from an old kernel cached filehandle
265 	 * which is now out of date.
266 	 */
267 	if (fp->fhh_pid != mypid)
268 		goto drop;
269 
270 	/*
271 	 * Make sure the index is valid before
272 	 * exported_ap is referenced.
273 	 */
274 	if (fp->fhh_id < 0 || fp->fhh_id >= NEXP_AP)
275 		goto drop;
276 
277 	/*
278 	 * Get hold of the supposed mount node
279 	 */
280 	ap = exported_ap[fp->fhh_id];
281 
282 	/*
283 	 * If it exists then maybe...
284 	 */
285 	if (ap) {
286 		/*
287 		 * Check the generation number in the node
288 		 * matches the one from the kernel.  If not
289 		 * then the old node has been timed out and
290 		 * a new one allocated.
291 		 */
292 		if (ap->am_gen != fp->fhh_gen) {
293 			ap = 0;
294 			goto drop;
295 		}
296 
297 		/*
298 		 * If the node is hung then locate a new node
299 		 * for it.  This implements the replicated filesystem
300 		 * retries.
301 		 */
302 		if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
303 			int error;
304 #ifdef DEBUG
305 			dlog("fh_to_mp3: %s (%s) is hung:- call lookup", ap->am_path, ap->am_mnt->mf_info);
306 #endif /* DEBUG */
307 			/*
308 			 * Update last access to original node.  This
309 			 * avoids timing it out and so sending ESTALE
310 			 * back to the kernel.
311 			 */
312 			new_ttl(ap);
313 
314 			/*
315 			 * Call the parent's lookup routine for an object
316 			 * with the same name.  This may return -1 in error
317 			 * if a mount is in progress.  In any case, if no
318 			 * mount node is returned the error code is propagated
319 			 * to the caller.
320 			 */
321 			if (c_or_d == VLOOK_CREATE) {
322 				ap = (*ap->am_parent->am_mnt->mf_ops->lookuppn)(ap->am_parent,
323 						ap->am_name, &error, c_or_d);
324 			} else {
325 				ap = 0;
326 				error = ESTALE;
327 			}
328 			if (ap == 0) {
329 				if (error < 0 && amd_state == Finishing)
330 					error = ENOENT;
331 				*rp = error;
332 				return 0;
333 			}
334 		}
335 		/*
336 		 * Disallow references to objects being unmounted, unless
337 		 * they are automount points.
338 		 */
339 		if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
340 				!(ap->am_flags & AMF_ROOT)) {
341 			if (amd_state == Finishing)
342 				*rp = ENOENT;
343 			else
344 				*rp = -1;
345 			return 0;
346 		}
347 		new_ttl(ap);
348 	}
349 
350 drop:
351 	if (!ap || !ap->am_mnt) {
352 		/*
353 		 * If we are shutting down then it is likely
354 		 * that this node has disappeared because of
355 		 * a fast timeout.  To avoid things thrashing
356 		 * just pretend it doesn't exist at all.  If
357 		 * ESTALE is returned, some NFS clients just
358 		 * keep retrying (stupid or what - if it's
359 		 * stale now, what's it going to be in 5 minutes?)
360 		 */
361 		if (amd_state == Finishing)
362 			*rp = ENOENT;
363 		else
364 			*rp = ESTALE;
365 		amd_stats.d_stale++;
366 	}
367 
368 	return ap;
369 }
370 
371 am_node *fh_to_mp(fhp)
372 nfs_fh *fhp;
373 {
374 	int dummy;
375 	return fh_to_mp2(fhp, &dummy);
376 }
377 
378 /*
379  * Convert from automount node to
380  * file handle.
381  */
382 void mp_to_fh(mp, fhp)
383 am_node *mp;
384 struct nfs_fh *fhp;
385 {
386 	struct am_fh *fp = (struct am_fh *) fhp;
387 
388 	/*
389 	 * Take the process id
390 	 */
391 	fp->fhh_pid = mypid;
392 	/*
393 	 * .. the map number
394 	 */
395 	fp->fhh_id = mp->am_mapno;
396 	/*
397 	 * .. and the generation number
398 	 */
399 	fp->fhh_gen = mp->am_gen;
400 	/*
401 	 * .. to make a "unique" triple that will never
402 	 * be reallocated except across reboots (which doesn't matter)
403 	 * or if we are unlucky enough to be given the same
404 	 * pid as a previous amd (very unlikely).
405 	 */
406 }
407 
408 static am_node *find_ap2(dir, mp)
409 char *dir;
410 am_node *mp;
411 {
412 	if (mp) {
413 		am_node *mp2;
414 		if (strcmp(mp->am_path, dir) == 0)
415 			return mp;
416 
417 		if ((mp->am_mnt->mf_flags & MFF_MOUNTED) &&
418 			strcmp(mp->am_mnt->mf_mount, dir) == 0)
419 			return mp;
420 
421 		mp2 = find_ap2(dir, mp->am_osib);
422 		if (mp2)
423 			return mp2;
424 		return find_ap2(dir, mp->am_child);
425 	}
426 
427 	return 0;
428 }
429 
430 /*
431  * Find the mount node corresponding
432  * to dir.  dir can match either the
433  * automount path or, if the node is
434  * mounted, the mount location.
435  */
436 am_node *find_ap(dir)
437 char *dir;
438 {
439 	int i;
440 
441 	for (i = last_used_map; i >= 0; --i) {
442 		am_node *mp = exported_ap[i];
443 		if (mp && (mp->am_flags & AMF_ROOT)) {
444 			mp = find_ap2(dir, exported_ap[i]);
445 			if (mp)
446 				return mp;
447 		}
448 	}
449 
450 	return 0;
451 }
452 
453 /*
454  * Get the filehandle for a particular named directory.
455  * This is used during the bootstrap to tell the kernel
456  * the filehandles of the initial automount points.
457  */
458 nfs_fh *root_fh(dir)
459 char *dir;
460 {
461 	static nfs_fh nfh;
462 	am_node *mp = root_ap(dir, TRUE);
463 	if (mp) {
464 		mp_to_fh(mp, &nfh);
465 		return &nfh;
466 	}
467 
468 	/*
469 	 * Should never get here...
470 	 */
471 	plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
472 	return 0;
473 }
474 
475 am_node *root_ap(dir, path)
476 char *dir;
477 int path;
478 {
479 	am_node *mp = find_ap(dir);
480 	if (mp && mp->am_parent == root_node)
481 		return mp;
482 
483 	return 0;
484 }
485 
486 /*
487  * Mount a top level automount node
488  * by calling lookup in the parent
489  * (root) node which will cause the
490  * automount node to be automounted (!)
491  */
492 static void mount_auto_node(dir)
493 char *dir;
494 {
495 	int error = 0;
496 	(void) afs_ops.lookuppn(root_node, dir, &error, VLOOK_CREATE);
497 	if (error) {
498 		errno = error; /* XXX */
499 		plog(XLOG_ERROR, "Could not start server on %s: %m", dir);
500 	}
501 }
502 
503 /*
504  * Cause all the top-level mount nodes
505  * to be automounted
506  */
507 int mount_exported()
508 {
509 	/*
510 	 * Iterate over all the nodes to be started
511 	 */
512 	return root_keyiter(mount_auto_node);
513 }
514 
515 /*
516  * Construct top-level node
517  */
518 void make_root_node()
519 {
520 	mntfs *root_mnt;
521 	char *rootmap = ROOT_MAP;
522 	root_node = exported_ap_alloc();
523 
524 	init_map(root_node, "");
525 	root_mnt = find_mntfs(&afs_ops, (am_opts *) 0, "", rootmap, "");
526 	root_mnt->mf_mount = strealloc(root_mnt->mf_mount, pid_fsname);
527 	root_mnt->mf_private = (voidp) mapc_find(rootmap, "");
528 	root_mnt->mf_prfree = mapc_free;
529 	free_mntfs(root_node->am_mnt);
530 	root_node->am_mnt = root_mnt;
531 	root_node->am_flags |= AMF_NOTIMEOUT;
532 	root_mnt->mf_error = 0;
533 }
534 
535 /*
536  * Cause all the nodes to be unmounted by timing
537  * them out.
538  */
539 void umount_exported(P_void)
540 {
541 	int i;
542 	for (i = last_used_map; i >= 0; --i) {
543 		am_node *mp = exported_ap[i];
544 		if (mp) {
545 			mntfs *mf = mp->am_mnt;
546 			if (mf->mf_flags & MFF_UNMOUNTING) {
547 				/*
548 				 * If this node is being unmounted then
549 				 * just ignore it.  However, this could
550 				 * prevent amd from finishing if the
551 				 * unmount gets blocked since the am_node
552 				 * will never be free'd.  am_unmounted needs
553 				 * telling about this possibility. - XXX
554 				 */
555 				continue;
556 			}
557 			if (mf && mf->mf_ops == &dfs_ops) {
558 				/*
559 				 * When shutting down this had better
560 				 * look like a directory, otherwise it
561 				 * can't be unmounted!
562 				 */
563 				mf->mf_fattr.type = NFDIR;
564 				mf->mf_fattr.mode = NFSMODE_DIR | 0555;
565 			}
566 			if ((--immediate_abort < 0 && !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
567 			    (mf->mf_flags & MFF_RESTART)) {
568 				/*
569 				 * Just throw this node away without
570 				 * bothering to unmount it.  If the
571 				 * server is not known to be up then
572 				 * don't discard the mounted on directory
573 				 * or Amd might hang...
574 				 */
575 				if (mf->mf_server &&
576 					(mf->mf_server->fs_flags & (FSF_DOWN|FSF_VALID)) != FSF_VALID)
577 					mf->mf_flags &= ~MFF_MKMNT;
578 				am_unmounted(mp);
579 			} else {
580 				/*
581 				 * Any other node gets forcibly
582 				 * timed out
583 				 */
584 				mp->am_flags &= ~AMF_NOTIMEOUT;
585 				mp->am_mnt->mf_flags &= ~MFF_RSTKEEP;
586 				mp->am_ttl = 0;
587 				mp->am_timeo = 1;
588 				mp->am_timeo_w = 0;
589 			}
590 		}
591 	}
592 }
593 
594 static int unmount_node P((am_node *mp));
595 static int unmount_node(mp)
596 am_node *mp;
597 {
598 	mntfs *mf = mp->am_mnt;
599 	int error;
600 
601 	if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) {
602 		/*
603 		 * Just unlink
604 		 */
605 #ifdef DEBUG
606 		if (mf->mf_flags & MFF_ERROR)
607 			dlog("No-op unmount of error node %s", mf->mf_info);
608 #endif /* DEBUG */
609 		error = 0;
610 	} else {
611 #ifdef DEBUG
612 		dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info);
613 #endif /* DEBUG */
614 		error = (*mf->mf_ops->umount_fs)(mp);
615 	}
616 
617 	if (error) {
618 #ifdef DEBUG
619 		errno = error; /* XXX */
620 		dlog("%s: unmount: %m", mf->mf_mount);
621 #endif /* DEBUG */
622 	}
623 
624 	return error;
625 }
626 
627 #ifdef FLUSH_KERNEL_NAME_CACHE
628 static void flush_kernel_name_cache(mp)
629 am_node *mp;
630 {
631 	int islink = (mp->am_mnt->mf_fattr.type == NFLNK);
632 	int isdir = (mp->am_mnt->mf_fattr.type == NFDIR);
633 	int elog = 0;
634 	if (islink) {
635 		if (unlink(mp->am_path) < 0)
636 			elog = 1;
637 	} else if (isdir) {
638 		if (rmdir(mp->am_path) < 0)
639 			elog = 1;
640 	}
641 	if (elog)
642 		plog(XLOG_WARNING, "failed to clear \"%s\" from dnlc: %m", mp->am_path);
643 }
644 #endif /* FLUSH_KERNEL_NAME_CACHE */
645 
646 static int unmount_node_wrap P((voidp vp));
647 static int unmount_node_wrap(vp)
648 voidp vp;
649 {
650 #ifndef FLUSH_KERNEL_NAME_CACHE
651 	return unmount_node((am_node*) vp);
652 #else /* FLUSH_KERNEL_NAME_CACHE */
653 	/*
654 	 * This code should just say:
655 	 * return unmount_node((am_node *) vp);
656 	 *
657 	 * However...
658 	 * The kernel keeps a cached copy of filehandles,
659 	 * and doesn't ever uncache them (apparently).  So
660 	 * when Amd times out a node the kernel will have a
661 	 * stale filehandle.  When the kernel next uses the
662 	 * filehandle it gets ESTALE.
663 	 *
664 	 * The workaround:
665 	 * Arrange that when a node is removed an unlink or
666 	 * rmdir is done on that path so that the kernel
667 	 * cache is done.  Yes - yuck.
668 	 *
669 	 * This can all be removed (and the background
670 	 * unmount flag in sfs_ops) if/when the kernel does
671 	 * something smarter.
672 	 *
673 	 * If the unlink or rmdir failed then just log a warning,
674 	 * don't fail the unmount.  This can occur if the kernel
675 	 * client code decides that the object is still referenced
676 	 * and should be renamed rather than discarded.
677 	 *
678 	 * There is still a race condition here...
679 	 * if another process is trying to access the same
680 	 * filesystem at the time we get here, then
681 	 * it will block, since the MF_UNMOUNTING flag will
682 	 * be set.  That may, or may not, cause the entire
683 	 * system to deadlock.  Hmmm...
684 	 */
685 	am_node *mp = (am_node *) vp;
686 	int isauto = mp->am_parent && (mp->am_parent->am_mnt->mf_fattr.type == NFDIR);
687 	int error = unmount_node(mp);
688 	if (error)
689 		return error;
690 	if (isauto && (int)amd_state < (int)Finishing)
691 		flush_kernel_name_cache(mp);
692 
693 	return 0;
694 #endif /* FLUSH_KERNEL_NAME_CACHE */
695 }
696 
697 static void free_map_if_success(rc, term, closure)
698 int rc;
699 int term;
700 voidp closure;
701 {
702 	am_node *mp = (am_node *) closure;
703 	mntfs *mf = mp->am_mnt;
704 
705 	/*
706 	 * Not unmounting any more
707 	 */
708 	mf->mf_flags &= ~MFF_UNMOUNTING;
709 
710 	/*
711 	 * If a timeout was defered because the underlying filesystem
712 	 * was busy then arrange for a timeout as soon as possible.
713 	 */
714 	if (mf->mf_flags & MFF_WANTTIMO) {
715 		mf->mf_flags &= ~MFF_WANTTIMO;
716 		reschedule_timeout_mp();
717 	}
718 
719 	if (term) {
720 		plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
721 #ifdef DEBUG
722 		/*
723 		 * dbx likes to put a trap on exit().
724 		 * Pretend it succeeded for now...
725 		 */
726 		if (term == SIGTRAP) {
727 			am_unmounted(mp);
728 		}
729 #endif /* DEBUG */
730 		amd_stats.d_uerr++;
731 	} else if (rc) {
732 		if (rc == EBUSY) {
733 			plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
734 		} else {
735 			errno = rc;	/* XXX */
736 			plog(XLOG_ERROR, "%s: unmount: %m", mp->am_path);
737 		}
738 		amd_stats.d_uerr++;
739 	} else {
740 		am_unmounted(mp);
741 	}
742 
743 	/*
744 	 * Wakeup anything waiting for this mount
745 	 */
746 	wakeup((voidp) mf);
747 }
748 
749 static void unmount_mp(mp)
750 am_node *mp;
751 {
752 	mntfs *mf = mp->am_mnt;
753 #ifdef notdef
754 	plog(XLOG_INFO, "\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
755 #endif /* notdef */
756 	if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) &&
757 			(mf->mf_flags & MFF_MOUNTED)) {
758 		if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
759 			/*
760 			 * Don't try to unmount from a server that is known to be down
761 			 */
762 			if (!(mf->mf_flags & MFF_LOGDOWN)) {
763 				/* Only log this once, otherwise gets a bit boring */
764 				plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
765 				mf->mf_flags |= MFF_LOGDOWN;
766 			}
767 		} else {
768 			/* Clear logdown flag - since the server must be up */
769 			mf->mf_flags &= ~MFF_LOGDOWN;
770 #ifdef DEBUG
771 			dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
772 			/*dlog("Will background the unmount attempt");*/
773 #endif /* DEBUG */
774 			/*
775 			 * Note that we are unmounting this node
776 			 */
777 			mf->mf_flags |= MFF_UNMOUNTING;
778 			run_task(unmount_node_wrap, (voidp) mp,
779 				 free_map_if_success, (voidp) mp);
780 #ifdef DEBUG
781 			dlog("unmount attempt backgrounded");
782 #endif /* DEBUG */
783 		}
784 	} else {
785 #ifdef DEBUG
786 		dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
787 		dlog("Trying unmount in foreground");
788 #endif
789 		mf->mf_flags |= MFF_UNMOUNTING;
790 		free_map_if_success(unmount_node(mp), 0, (voidp) mp);
791 #ifdef DEBUG
792 		dlog("unmount attempt done");
793 #endif /* DEBUG */
794 	}
795 }
796 
797 void timeout_mp()
798 {
799 #define NEVER (time_t) 0
800 #define	smallest_t(t1, t2) \
801 	(t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
802 #define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
803 
804 	int i;
805 	time_t t = NEVER;
806 	time_t now = clocktime();
807 
808 #ifdef DEBUG
809 	dlog("Timing out automount points...");
810 #endif /* DEBUG */
811 	for (i = last_used_map; i >= 0; --i) {
812 		am_node *mp = exported_ap[i];
813 		mntfs *mf;
814 		/*
815 		 * Just continue if nothing mounted, or can't be timed out.
816 		 */
817 		if (!mp || (mp->am_flags & AMF_NOTIMEOUT))
818 			continue;
819 		/*
820 		 * Pick up mounted filesystem
821 		 */
822 		mf = mp->am_mnt;
823 		if (!mf)
824 			continue;
825 		/*
826 		 * Don't delete last reference to a restarted filesystem.
827 		 */
828 		if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
829 			continue;
830 		/*
831 		 * If there is action on this filesystem then ignore it
832 		 */
833 		if (!(mf->mf_flags & IGNORE_FLAGS)) {
834 			int expired = 0;
835 			mf->mf_flags &= ~MFF_WANTTIMO;
836 #ifdef DEBUG
837 			/*dlog("t is initially @%d, zero in %d secs", t, t - now);*/
838 #endif /* DEBUG */
839 			if (now >= mp->am_ttl) {
840 				expired = 1;
841 				/*
842 				 * Move the ttl forward to avoid thrashing effects
843 				 * on the next call to timeout!
844 				 */
845 				/* sun's -tw option */
846 				if (mp->am_timeo_w < 4 * am_timeo_w)
847 					mp->am_timeo_w += am_timeo_w;
848 				mp->am_ttl = now + mp->am_timeo_w;
849 			}
850 			/*
851 			 * If the next ttl is smallest, use that
852 			 */
853 			t = smallest_t(t, mp->am_ttl);
854 
855 #ifdef DEBUG
856 			/*dlog("after ttl t is @%d, zero in %d secs", t, t - now);*/
857 #endif /* DEBUG */
858 
859 			if (!mp->am_child && mf->mf_error >= 0 && expired)
860 				unmount_mp(mp);
861 		} else if (mf->mf_flags & MFF_UNMOUNTING) {
862 			mf->mf_flags |= MFF_WANTTIMO;
863 		}
864 	}
865 
866 	if (t == NEVER) {
867 #ifdef DEBUG
868 		dlog("No further timeouts");
869 #endif /* DEBUG */
870 		t = now + ONE_HOUR;
871 	}
872 
873 	/*
874 	 * Sanity check to avoid runaways.
875 	 * Absolutely should never get this but
876 	 * if you do without this trap amd will thrash.
877 	 */
878 	if (t <= now) {
879 		t = now + 6;	/* XXX */
880 		plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
881 	}
882 	/*
883 	 * XXX - when shutting down, make things happen faster
884 	 */
885 	if ((int)amd_state >= (int)Finishing)
886 		t = now + 1;
887 #ifdef DEBUG
888 	dlog("Next mount timeout in %ds", t - now);
889 #endif /* DEBUG */
890 
891 	timeout_mp_id = timeout(t - now, timeout_mp, 0);
892 
893 #undef NEVER
894 #undef smallest_t
895 #undef IGNORE_FLAGS
896 }
897 
898 /*
899  * Cause timeout_mp to be called soonest
900  */
901 void reschedule_timeout_mp()
902 {
903 	if (timeout_mp_id)
904 		untimeout(timeout_mp_id);
905 	timeout_mp_id = timeout(0, timeout_mp, 0);
906 }
907