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