kern_shutdown.c revision 40751
117658Sjulian/*-
217658Sjulian * Copyright (c) 1986, 1988, 1991, 1993
317658Sjulian *	The Regents of the University of California.  All rights reserved.
417658Sjulian * (c) UNIX System Laboratories, Inc.
517658Sjulian * All or some portions of this file are derived from material licensed
617658Sjulian * to the University of California by American Telephone and Telegraph
717658Sjulian * Co. or Unix System Laboratories, Inc. and are reproduced herein with
817658Sjulian * the permission of UNIX System Laboratories, Inc.
917658Sjulian *
1017658Sjulian * Redistribution and use in source and binary forms, with or without
1117658Sjulian * modification, are permitted provided that the following conditions
1217658Sjulian * are met:
1317658Sjulian * 1. Redistributions of source code must retain the above copyright
1417658Sjulian *    notice, this list of conditions and the following disclaimer.
1517658Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1617658Sjulian *    notice, this list of conditions and the following disclaimer in the
1717658Sjulian *    documentation and/or other materials provided with the distribution.
1817658Sjulian * 3. All advertising materials mentioning features or use of this software
1917658Sjulian *    must display the following acknowledgement:
2017658Sjulian *	This product includes software developed by the University of
2117658Sjulian *	California, Berkeley and its contributors.
2217658Sjulian * 4. Neither the name of the University nor the names of its contributors
2317658Sjulian *    may be used to endorse or promote products derived from this software
2417658Sjulian *    without specific prior written permission.
2517658Sjulian *
2617658Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2717658Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2817658Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2917658Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3017658Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3117658Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3217658Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3317658Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3417658Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3517658Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3617658Sjulian * SUCH DAMAGE.
3717658Sjulian *
3817658Sjulian *	@(#)kern_shutdown.c	8.3 (Berkeley) 1/21/94
3940751Smsmith * $Id: kern_shutdown.c,v 1.40 1998/09/20 16:50:31 dt Exp $
4017658Sjulian */
4117658Sjulian
4217658Sjulian#include "opt_ddb.h"
4333445Seivind#include "opt_hw_wdog.h"
4428976Sbde#include "opt_panic.h"
4528976Sbde#include "opt_show_busybufs.h"
4617658Sjulian
4717658Sjulian#include <sys/param.h>
4817658Sjulian#include <sys/systm.h>
4931275Sbde#include <sys/buf.h>
5017658Sjulian#include <sys/reboot.h>
5117658Sjulian#include <sys/proc.h>
5217658Sjulian#include <sys/malloc.h>
5317658Sjulian#include <sys/kernel.h>
5421776Sbde#include <sys/mount.h>
5539237Sgibbs#include <sys/queue.h>
5617658Sjulian#include <sys/sysctl.h>
5717658Sjulian#include <sys/conf.h>
5817658Sjulian#include <sys/sysproto.h>
5917658Sjulian
6017658Sjulian#include <machine/pcb.h>
6117658Sjulian#include <machine/clock.h>
6217658Sjulian#include <machine/cons.h>
6317658Sjulian#include <machine/md_var.h>
6426812Speter#ifdef SMP
6526812Speter#include <machine/smp.h>		/* smp_active, cpuid */
6626812Speter#endif
6717658Sjulian
6817658Sjulian#include <sys/signalvar.h>
6917658Sjulian
7017658Sjulian#ifndef PANIC_REBOOT_WAIT_TIME
7117658Sjulian#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */
7217658Sjulian#endif
7317658Sjulian
7417658Sjulian/*
7517658Sjulian * Note that stdarg.h and the ANSI style va_start macro is used for both
7617658Sjulian * ANSI and traditional C compilers.
7717658Sjulian */
7817658Sjulian#include <machine/stdarg.h>
7917658Sjulian
8028769Sbde#ifdef DDB
8117658Sjulian#ifdef DDB_UNATTENDED
8228769Sbdestatic int debugger_on_panic = 0;
8317658Sjulian#else
8428769Sbdestatic int debugger_on_panic = 1;
8517658Sjulian#endif
8617658SjulianSYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, CTLFLAG_RW,
8717658Sjulian	&debugger_on_panic, 0, "");
8817658Sjulian#endif
8917658Sjulian
9028000Sjulian#ifdef	HW_WDOG
9117658Sjulian/*
9227997Sjulian * If there is a hardware watchdog, point this at the function needed to
9327997Sjulian * hold it off.
9427997Sjulian * It's needed when the kernel needs to do some lengthy operations.
9527997Sjulian * e.g. in wd.c when dumping core.. It's most annoying to have
9627997Sjulian * your precious core-dump only half written because the wdog kicked in.
9727997Sjulian */
9827997Sjulianwatchdog_tickle_fn wdog_tickler = NULL;
9928000Sjulian#endif	/* HW_WDOG */
10027997Sjulian
10127997Sjulian/*
10217658Sjulian * Variable panicstr contains argument to first call to panic; used as flag
10317658Sjulian * to indicate that the kernel has already called panic.
10417658Sjulian */
10517658Sjulianconst char *panicstr;
10617658Sjulian
10717658Sjulian/*
10817658Sjulian * callout list for things to do a shutdown
10917658Sjulian */
11017658Sjuliantypedef struct shutdown_list_element {
11139237Sgibbs	LIST_ENTRY(shutdown_list_element) links;
11217658Sjulian	bootlist_fn function;
11317658Sjulian	void *arg;
11440751Smsmith	int priority;
11517658Sjulian} *sle_p;
11617658Sjulian
11717768Sjulian/*
11839237Sgibbs * There are three shutdown lists. Some things need to be shut down
11939237Sgibbs * earlier than others.
12017768Sjulian */
12139237SgibbsLIST_HEAD(shutdown_list, shutdown_list_element);
12217658Sjulian
12339237Sgibbsstatic struct shutdown_list shutdown_lists[SHUTDOWN_FINAL + 1];
12439237Sgibbs
12531275Sbdestatic void boot __P((int)) __dead2;
12631275Sbdestatic void dumpsys __P((void));
12717658Sjulian
12817658Sjulian#ifndef _SYS_SYSPROTO_H_
12917658Sjulianstruct reboot_args {
13017658Sjulian	int	opt;
13117658Sjulian};
13217658Sjulian#endif
13317658Sjulian/* ARGSUSED */
13417658Sjulian
13517658Sjulian/*
13617658Sjulian * The system call that results in a reboot
13717658Sjulian */
13817658Sjulianint
13930994Sphkreboot(p, uap)
14017658Sjulian	struct proc *p;
14117658Sjulian	struct reboot_args *uap;
14217658Sjulian{
14317658Sjulian	int error;
14417658Sjulian
14517658Sjulian	if ((error = suser(p->p_ucred, &p->p_acflag)))
14617658Sjulian		return (error);
14717658Sjulian
14817658Sjulian	boot(uap->opt);
14917658Sjulian	return (0);
15017658Sjulian}
15117658Sjulian
15217658Sjulian/*
15317658Sjulian * Called by events that want to shut down.. e.g  <CTL><ALT><DEL> on a PC
15417658Sjulian */
15517658Sjulianvoid
15628769Sbdeshutdown_nice()
15717658Sjulian{
15817658Sjulian	/* Send a signal to init(8) and have it shutdown the world */
15917658Sjulian	if (initproc != NULL) {
16017658Sjulian		psignal(initproc, SIGINT);
16117658Sjulian	} else {
16217658Sjulian		/* No init(8) running, so simply reboot */
16317658Sjulian		boot(RB_NOSYNC);
16417658Sjulian	}
16517658Sjulian	return;
16617658Sjulian}
16717658Sjulianstatic int	waittime = -1;
16817658Sjulianstatic struct pcb dumppcb;
16917658Sjulian
17017658Sjulian/*
17117658Sjulian *  Go through the rigmarole of shutting down..
17217658Sjulian * this used to be in machdep.c but I'll be dammned if I could see
17317658Sjulian * anything machine dependant in it.
17417658Sjulian */
17531275Sbdestatic void
17617658Sjulianboot(howto)
17717658Sjulian	int howto;
17817658Sjulian{
17917768Sjulian	sle_p ep;
18017658Sjulian
18125164Speter#ifdef SMP
18225164Speter	if (smp_active) {
18326812Speter		printf("boot() called on cpu#%d\n", cpuid);
18425164Speter	}
18525164Speter#endif
18627997Sjulian	/*
18727997Sjulian	 * Do any callouts that should be done BEFORE syncing the filesystems.
18827997Sjulian	 */
18939237Sgibbs	LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_PRE_SYNC], links)
19017658Sjulian		(*ep->function)(howto, ep->arg);
19127997Sjulian
19227997Sjulian	/*
19327997Sjulian	 * Now sync filesystems
19427997Sjulian	 */
19517658Sjulian	if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) {
19617658Sjulian		register struct buf *bp;
19717658Sjulian		int iter, nbusy;
19817658Sjulian
19917658Sjulian		waittime = 0;
20017658Sjulian		printf("\nsyncing disks... ");
20117658Sjulian
20230994Sphk		sync(&proc0, NULL);
20317658Sjulian
20434266Sjulian		/*
20534266Sjulian		 * With soft updates, some buffers that are
20634266Sjulian		 * written will be remarked as dirty until other
20734266Sjulian		 * buffers are written.
20834266Sjulian		 */
20917658Sjulian		for (iter = 0; iter < 20; iter++) {
21017658Sjulian			nbusy = 0;
21117658Sjulian			for (bp = &buf[nbuf]; --bp >= buf; ) {
21239237Sgibbs				if ((bp->b_flags & (B_BUSY | B_INVAL))
21339237Sgibbs						== B_BUSY) {
21417658Sjulian					nbusy++;
21534266Sjulian				} else if ((bp->b_flags & (B_DELWRI | B_INVAL))
21634266Sjulian						== B_DELWRI) {
21734266Sjulian					/* bawrite(bp);*/
21834266Sjulian					nbusy++;
21917658Sjulian				}
22017658Sjulian			}
22117658Sjulian			if (nbusy == 0)
22217658Sjulian				break;
22317658Sjulian			printf("%d ", nbusy);
22434266Sjulian			sync(&proc0, NULL);
22534266Sjulian			DELAY(50000 * iter);
22617658Sjulian		}
22717658Sjulian		if (nbusy) {
22817658Sjulian			/*
22917658Sjulian			 * Failed to sync all blocks. Indicate this and don't
23017658Sjulian			 * unmount filesystems (thus forcing an fsck on reboot).
23117658Sjulian			 */
23217658Sjulian			printf("giving up\n");
23317658Sjulian#ifdef SHOW_BUSYBUFS
23417658Sjulian			nbusy = 0;
23517658Sjulian			for (bp = &buf[nbuf]; --bp >= buf; ) {
23639237Sgibbs				if ((bp->b_flags & (B_BUSY | B_INVAL))
23739237Sgibbs						== B_BUSY) {
23817658Sjulian					nbusy++;
23937555Sbde					printf(
24037555Sbde			"%d: dev:%08lx, flags:%08lx, blkno:%ld, lblkno:%ld\n",
24137555Sbde					    nbusy, (u_long)bp->b_dev,
24237555Sbde					    bp->b_flags, (long)bp->b_blkno,
24337555Sbde					    (long)bp->b_lblkno);
24417658Sjulian				}
24517658Sjulian			}
24617658Sjulian			DELAY(5000000);	/* 5 seconds */
24717658Sjulian#endif
24817658Sjulian		} else {
24917658Sjulian			printf("done\n");
25017658Sjulian			/*
25117658Sjulian			 * Unmount filesystems
25217658Sjulian			 */
25317658Sjulian			if (panicstr == 0)
25417658Sjulian				vfs_unmountall();
25517658Sjulian		}
25639237Sgibbs		DELAY(100000);		/* wait for console output to finish */
25717658Sjulian	}
25827997Sjulian
25927997Sjulian	/*
26027997Sjulian	 * Ok, now do things that assume all filesystem activity has
26127997Sjulian	 * been completed.
26227997Sjulian	 */
26339237Sgibbs	LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_POST_SYNC], links)
26417768Sjulian		(*ep->function)(howto, ep->arg);
26539237Sgibbs	splhigh();
26639522Sdt	if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold) {
26739237Sgibbs		savectx(&dumppcb);
26839237Sgibbs#ifdef __i386__
26939237Sgibbs		dumppcb.pcb_cr3 = rcr3();
27039237Sgibbs#endif
27139237Sgibbs		dumpsys();
27217768Sjulian	}
27339237Sgibbs
27439237Sgibbs	/* Now that we're going to really halt the system... */
27539237Sgibbs	LIST_FOREACH(ep, &shutdown_lists[SHUTDOWN_FINAL], links)
27639237Sgibbs		(*ep->function)(howto, ep->arg);
27739237Sgibbs
27817658Sjulian	if (howto & RB_HALT) {
27917658Sjulian		printf("\n");
28017658Sjulian		printf("The operating system has halted.\n");
28117658Sjulian		printf("Please press any key to reboot.\n\n");
28219274Sjulian		switch (cngetc()) {
28319274Sjulian		case -1:		/* No console, just die */
28419274Sjulian			cpu_halt();
28519274Sjulian			/* NOTREACHED */
28619274Sjulian		default:
28739237Sgibbs			howto &= ~RB_HALT;
28819274Sjulian			break;
28919274Sjulian		}
29039237Sgibbs	} else if (howto & RB_DUMP) {
29139237Sgibbs		/* System Paniced */
29217658Sjulian
29339237Sgibbs		if (PANIC_REBOOT_WAIT_TIME != 0) {
29439237Sgibbs			if (PANIC_REBOOT_WAIT_TIME != -1) {
29539237Sgibbs				int loop;
29639237Sgibbs				printf("Automatic reboot in %d seconds - "
29739237Sgibbs				       "press a key on the console to abort\n",
29839237Sgibbs					PANIC_REBOOT_WAIT_TIME);
29939237Sgibbs				for (loop = PANIC_REBOOT_WAIT_TIME * 10;
30039237Sgibbs				     loop > 0; --loop) {
30139237Sgibbs					DELAY(1000 * 100); /* 1/10th second */
30239237Sgibbs					/* Did user type a key? */
30339237Sgibbs					if (cncheckc() != -1)
30439237Sgibbs						break;
30517658Sjulian				}
30639237Sgibbs				if (!loop)
30739237Sgibbs					goto die;
30817658Sjulian			}
30939237Sgibbs		} else { /* zero time specified - reboot NOW */
31039237Sgibbs			goto die;
31117658Sjulian		}
31239237Sgibbs		printf("--> Press a key on the console to reboot <--\n");
31339237Sgibbs		cngetc();
31417658Sjulian	}
31517658Sjuliandie:
31617658Sjulian	printf("Rebooting...\n");
31717658Sjulian	DELAY(1000000);	/* wait 1 sec for printf's to complete and be read */
31817677Sjulian	/* cpu_boot(howto); */ /* doesn't do anything at the moment */
31917658Sjulian	cpu_reset();
32017658Sjulian	for(;;) ;
32117658Sjulian	/* NOTREACHED */
32217658Sjulian}
32317658Sjulian
32417658Sjulian/*
32517658Sjulian * Magic number for savecore
32617658Sjulian *
32717658Sjulian * exported (symorder) and used at least by savecore(8)
32817658Sjulian *
32917658Sjulian */
33017658Sjulianstatic u_long const	dumpmag = 0x8fca0101UL;
33117658Sjulian
33217658Sjulianstatic int	dumpsize = 0;		/* also for savecore */
33317658Sjulian
33417658Sjulianstatic int	dodump = 1;
33517658SjulianSYSCTL_INT(_machdep, OID_AUTO, do_dump, CTLFLAG_RW, &dodump, 0, "");
33617658Sjulian
33731403Sjulian/* ARGSUSED */
33831403Sjulianstatic void dump_conf __P((void *dummy));
33931403Sjulianstatic void
34031403Sjuliandump_conf(dummy)
34131403Sjulian	void *dummy;
34231403Sjulian{
34331403Sjulian	cpu_dumpconf();
34431403Sjulian}
34531403SjulianSYSINIT(dump_conf, SI_SUB_DUMP_CONF, SI_ORDER_FIRST, dump_conf, NULL)
34631403Sjulian
34717658Sjulian/*
34817658Sjulian * Doadump comes here after turning off memory management and
34917658Sjulian * getting on the dump stack, either when called above, or by
35017658Sjulian * the auto-restart code.
35117658Sjulian */
35217658Sjulianstatic void
35317658Sjuliandumpsys(void)
35417658Sjulian{
35517658Sjulian
35617658Sjulian	if (!dodump)
35717658Sjulian		return;
35817658Sjulian	if (dumpdev == NODEV)
35917658Sjulian		return;
36017658Sjulian	if (!(bdevsw[major(dumpdev)]))
36117658Sjulian		return;
36217658Sjulian	if (!(bdevsw[major(dumpdev)]->d_dump))
36317658Sjulian		return;
36417658Sjulian	dumpsize = Maxmem;
36537555Sbde	printf("\ndumping to dev %lx, offset %ld\n", (u_long)dumpdev, dumplo);
36617658Sjulian	printf("dump ");
36717658Sjulian	switch ((*bdevsw[major(dumpdev)]->d_dump)(dumpdev)) {
36817658Sjulian
36917658Sjulian	case ENXIO:
37017658Sjulian		printf("device bad\n");
37117658Sjulian		break;
37217658Sjulian
37317658Sjulian	case EFAULT:
37417658Sjulian		printf("device not ready\n");
37517658Sjulian		break;
37617658Sjulian
37717658Sjulian	case EINVAL:
37817658Sjulian		printf("area improper\n");
37917658Sjulian		break;
38017658Sjulian
38117658Sjulian	case EIO:
38217658Sjulian		printf("i/o error\n");
38317658Sjulian		break;
38417658Sjulian
38517658Sjulian	case EINTR:
38617658Sjulian		printf("aborted from console\n");
38717658Sjulian		break;
38817658Sjulian
38917658Sjulian	default:
39017658Sjulian		printf("succeeded\n");
39117658Sjulian		break;
39217658Sjulian	}
39317658Sjulian}
39417658Sjulian
39517658Sjulian/*
39617658Sjulian * Panic is called on unresolvable fatal errors.  It prints "panic: mesg",
39717658Sjulian * and then reboots.  If we are called twice, then we avoid trying to sync
39817658Sjulian * the disks as this often leads to recursive panics.
39917658Sjulian */
40017658Sjulianvoid
40117658Sjulianpanic(const char *fmt, ...)
40217658Sjulian{
40317658Sjulian	int bootopt;
40417658Sjulian	va_list ap;
40538874Sache	static char buf[256];
40617658Sjulian
40717658Sjulian	bootopt = RB_AUTOBOOT | RB_DUMP;
40817658Sjulian	if (panicstr)
40917658Sjulian		bootopt |= RB_NOSYNC;
41017658Sjulian	else
41117658Sjulian		panicstr = fmt;
41217658Sjulian
41317658Sjulian	va_start(ap, fmt);
41438874Sache	(void)vsprintf(buf, fmt, ap);
41538874Sache	if (panicstr == fmt)
41638874Sache		panicstr = buf;
41717658Sjulian	va_end(ap);
41838874Sache	printf("panic: %s\n", buf);
41926100Sfsmp#ifdef SMP
42029128Speter	/* three seperate prints in case of an unmapped page and trap */
42129128Speter	printf("mp_lock = %08x; ", mp_lock);
42229128Speter	printf("cpuid = %d; ", cpuid);
42329128Speter	printf("lapic.id = %08x\n", lapic.id);
42426100Sfsmp#endif
42517658Sjulian
42617658Sjulian#if defined(DDB)
42717658Sjulian	if (debugger_on_panic)
42817658Sjulian		Debugger ("panic");
42917658Sjulian#endif
43017658Sjulian	boot(bootopt);
43117658Sjulian}
43217658Sjulian
43317768Sjulian/*
43440751Smsmith * Three routines to handle adding/deleting items on the
43517768Sjulian * shutdown callout lists
43617768Sjulian *
43717768Sjulian * at_shutdown():
43817658Sjulian * Take the arguments given and put them onto the shutdown callout list.
43917658Sjulian * However first make sure that it's not already there.
44017658Sjulian * returns 0 on success.
44117658Sjulian */
44217658Sjulianint
44339237Sgibbsat_shutdown(bootlist_fn function, void *arg, int queue)
44417658Sjulian{
44540751Smsmith	return(at_shutdown_pri(function, arg, queue, SHUTDOWN_PRI_DEFAULT));
44640751Smsmith}
44717768Sjulian
44840751Smsmith/*
44940751Smsmith * at_shutdown_pri():
45040751Smsmith * Take the arguments given and put them onto the shutdown callout list
45140751Smsmith * with the given execution priority.
45240751Smsmith * returns 0 on success.
45340751Smsmith */
45440751Smsmithint
45540751Smsmithat_shutdown_pri(bootlist_fn function, void *arg, int queue, int pri)
45640751Smsmith{
45740751Smsmith	sle_p ep, ip;
45840751Smsmith
45939237Sgibbs	if (queue < SHUTDOWN_PRE_SYNC
46039237Sgibbs	 || queue > SHUTDOWN_FINAL) {
46139237Sgibbs		printf("at_shutdown: bad exit callout queue %d specified\n",
46239237Sgibbs		       queue);
46317768Sjulian		return (EINVAL);
46417768Sjulian	}
46517768Sjulian	if (rm_at_shutdown(function, arg))
46639237Sgibbs		printf("at_shutdown: exit callout entry was already present\n");
46717768Sjulian	ep = malloc(sizeof(*ep), M_TEMP, M_NOWAIT);
46817768Sjulian	if (ep == NULL)
46917768Sjulian		return (ENOMEM);
47017658Sjulian	ep->function = function;
47117658Sjulian	ep->arg = arg;
47240751Smsmith	ep->priority = pri;
47340751Smsmith
47440751Smsmith	/* Sort into list of items on this queue */
47540751Smsmith	ip = LIST_FIRST(&shutdown_lists[queue]);
47640751Smsmith	if (ip == NULL) {
47740751Smsmith		LIST_INSERT_HEAD(&shutdown_lists[queue], ep, links);
47840751Smsmith	} else {
47940751Smsmith		for (; LIST_NEXT(ip, links) != NULL; ip = LIST_NEXT(ip, links)) {
48040751Smsmith			if (ep->priority < ip->priority) {
48140751Smsmith				LIST_INSERT_BEFORE(ip, ep, links);
48240751Smsmith				ep = NULL;
48340751Smsmith				break;
48440751Smsmith			}
48540751Smsmith		}
48640751Smsmith		if (ep != NULL)
48740751Smsmith			LIST_INSERT_AFTER(ip, ep, links);
48840751Smsmith	}
48917768Sjulian	return (0);
49017658Sjulian}
49117768Sjulian
49217658Sjulian/*
49317768Sjulian * Scan the exit callout lists for the given items and remove them.
49417658Sjulian * Returns the number of items removed.
49517658Sjulian */
49617658Sjulianint
49717658Sjulianrm_at_shutdown(bootlist_fn function, void *arg)
49817658Sjulian{
49939237Sgibbs	sle_p ep;
50039237Sgibbs	int   count;
50139237Sgibbs	int   queue;
50217658Sjulian
50317768Sjulian	count = 0;
50439237Sgibbs	for (queue = SHUTDOWN_PRE_SYNC; queue < SHUTDOWN_FINAL; queue++) {
50539237Sgibbs		LIST_FOREACH(ep, &shutdown_lists[queue], links) {
50639237Sgibbs			if ((ep->function == function) && (ep->arg == arg)) {
50739237Sgibbs				LIST_REMOVE(ep, links);
50839237Sgibbs				free(ep, M_TEMP);
50939237Sgibbs				count++;
51039237Sgibbs			}
51117658Sjulian		}
51217658Sjulian	}
51317768Sjulian	return (count);
51417658Sjulian}
515