Index: projects/sv/sys/kern/kern_shutdown.c =================================================================== --- projects/sv/sys/kern/kern_shutdown.c (revision 220571) +++ projects/sv/sys/kern/kern_shutdown.c (revision 220572) @@ -1,718 +1,747 @@ /*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_shutdown.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_kdb.h" +#include "opt_netdump.h" #include "opt_panic.h" #include "opt_show_busybufs.h" #include "opt_sched.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#ifdef NETDUMP_CLIENT +#include + +#include +#include + +#include +#endif + #include #ifndef PANIC_REBOOT_WAIT_TIME #define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ #endif /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #include #ifdef KDB #ifdef KDB_UNATTENDED int debugger_on_panic = 0; #else int debugger_on_panic = 1; #endif SYSCTL_INT(_debug, OID_AUTO, debugger_on_panic, CTLFLAG_RW | CTLFLAG_TUN, &debugger_on_panic, 0, "Run debugger on kernel panic"); TUNABLE_INT("debug.debugger_on_panic", &debugger_on_panic); #ifdef KDB_TRACE static int trace_on_panic = 1; #else static int trace_on_panic = 0; #endif SYSCTL_INT(_debug, OID_AUTO, trace_on_panic, CTLFLAG_RW | CTLFLAG_TUN, &trace_on_panic, 0, "Print stack trace on kernel panic"); TUNABLE_INT("debug.trace_on_panic", &trace_on_panic); #endif /* KDB */ static int sync_on_panic = 0; SYSCTL_INT(_kern, OID_AUTO, sync_on_panic, CTLFLAG_RW | CTLFLAG_TUN, &sync_on_panic, 0, "Do a sync before rebooting from a panic"); TUNABLE_INT("kern.sync_on_panic", &sync_on_panic); SYSCTL_NODE(_kern, OID_AUTO, shutdown, CTLFLAG_RW, 0, "Shutdown environment"); /* * Variable panicstr contains argument to first call to panic; used as flag * to indicate that the kernel has already called panic. */ const char *panicstr; int dumping; /* system is dumping */ int rebooting; /* system is rebooting */ static struct dumperinfo dumper; /* our selected dumper */ /* Context information for dump-debuggers. */ -struct pcb dumppcb; /* Registers. */ -lwpid_t dumptid; /* Thread ID. */ +static struct pcb dumppcb; /* Registers. */ +static lwpid_t dumptid; /* Thread ID. */ static void poweroff_wait(void *, int); static void shutdown_halt(void *junk, int howto); static void shutdown_panic(void *junk, int howto); static void shutdown_reset(void *junk, int howto); /* register various local shutdown events */ static void shutdown_conf(void *unused) { EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL, SHUTDOWN_PRI_FIRST); EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_reset, NULL, SHUTDOWN_PRI_LAST + 200); } SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL); /* * The system call that results in a reboot. */ /* ARGSUSED */ int reboot(struct thread *td, struct reboot_args *uap) { int error; error = 0; #ifdef MAC error = mac_system_check_reboot(td->td_ucred, uap->opt); #endif if (error == 0) error = priv_check(td, PRIV_REBOOT); if (error == 0) { mtx_lock(&Giant); kern_reboot(uap->opt); mtx_unlock(&Giant); } return (error); } /* * Called by events that want to shut down.. e.g on a PC */ static int shutdown_howto = 0; void shutdown_nice(int howto) { shutdown_howto = howto; /* Send a signal to init(8) and have it shutdown the world */ if (initproc != NULL) { PROC_LOCK(initproc); psignal(initproc, SIGINT); PROC_UNLOCK(initproc); } else { /* No init(8) running, so simply reboot */ kern_reboot(RB_NOSYNC); } return; } static int waittime = -1; static void print_uptime(void) { int f; struct timespec ts; getnanouptime(&ts); printf("Uptime: "); f = 0; if (ts.tv_sec >= 86400) { printf("%ldd", (long)ts.tv_sec / 86400); ts.tv_sec %= 86400; f = 1; } if (f || ts.tv_sec >= 3600) { printf("%ldh", (long)ts.tv_sec / 3600); ts.tv_sec %= 3600; f = 1; } if (f || ts.tv_sec >= 60) { printf("%ldm", (long)ts.tv_sec / 60); ts.tv_sec %= 60; f = 1; } printf("%lds\n", (long)ts.tv_sec); } static void doadump(void) { + savectx(&dumppcb); + dumptid = curthread->td_tid; + dumping++; + +#ifdef NETDUMP_CLIENT /* + * If netdump finished successfully just return, otherwise give + * traditional disk dumping a chance. + * + * Avoid a POLA breakage by skipping netdump when calling it from + * within KDB. That may change in the future. + */ +#ifdef KDB + if (kdb_why == KDB_WHY_UNSET) +#endif + if (netdumpsys() == 0) { + dumping--; + return; + } +#endif + + /* * Sometimes people have to call this from the kernel debugger. * (if 'panic' can not dump) * Give them a clue as to why they can't dump. */ if (dumper.dumper == NULL) { + dumping--; printf("Cannot dump. Device not defined or unavailable.\n"); return; } - savectx(&dumppcb); - dumptid = curthread->td_tid; - dumping++; #ifdef DDB if (textdump_pending) textdump_dumpsys(&dumper); else #endif dumpsys(&dumper); dumping--; } static int isbufbusy(struct buf *bp) { if (((bp->b_flags & (B_INVAL | B_PERSISTENT)) == 0 && BUF_ISLOCKED(bp)) || ((bp->b_flags & (B_DELWRI | B_INVAL)) == B_DELWRI)) return (1); return (0); } /* * Shutdown the system cleanly to prepare for reboot, halt, or power off. */ void kern_reboot(int howto) { static int first_buf_printf = 1; #if defined(SMP) /* * Bind us to CPU 0 so that all shutdown code runs there. Some * systems don't shutdown properly (i.e., ACPI power off) if we * run on another processor. */ thread_lock(curthread); sched_bind(curthread, 0); thread_unlock(curthread); KASSERT(PCPU_GET(cpuid) == 0, ("%s: not running on cpu 0", __func__)); #endif /* We're in the process of rebooting. */ rebooting = 1; /* collect extra flags that shutdown_nice might have set */ howto |= shutdown_howto; /* We are out of the debugger now. */ kdb_active = 0; /* * Do any callouts that should be done BEFORE syncing the filesystems. */ EVENTHANDLER_INVOKE(shutdown_pre_sync, howto); /* * Now sync filesystems */ if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) { register struct buf *bp; int iter, nbusy, pbusy; #ifndef PREEMPTION int subiter; #endif waittime = 0; sync(curthread, NULL); /* * With soft updates, some buffers that are * written will be remarked as dirty until other * buffers are written. */ for (iter = pbusy = 0; iter < 20; iter++) { nbusy = 0; for (bp = &buf[nbuf]; --bp >= buf; ) if (isbufbusy(bp)) nbusy++; if (nbusy == 0) { if (first_buf_printf) printf("All buffers synced."); break; } if (first_buf_printf) { printf("Syncing disks, buffers remaining... "); first_buf_printf = 0; } printf("%d ", nbusy); if (nbusy < pbusy) iter = 0; pbusy = nbusy; sync(curthread, NULL); #ifdef PREEMPTION /* * Drop Giant and spin for a while to allow * interrupt threads to run. */ DROP_GIANT(); DELAY(50000 * iter); PICKUP_GIANT(); #else /* * Drop Giant and context switch several times to * allow interrupt threads to run. */ DROP_GIANT(); for (subiter = 0; subiter < 50 * iter; subiter++) { thread_lock(curthread); mi_switch(SW_VOL, NULL); thread_unlock(curthread); DELAY(1000); } PICKUP_GIANT(); #endif } printf("\n"); /* * Count only busy local buffers to prevent forcing * a fsck if we're just a client of a wedged NFS server */ nbusy = 0; for (bp = &buf[nbuf]; --bp >= buf; ) { if (isbufbusy(bp)) { #if 0 /* XXX: This is bogus. We should probably have a BO_REMOTE flag instead */ if (bp->b_dev == NULL) { TAILQ_REMOVE(&mountlist, bp->b_vp->v_mount, mnt_list); continue; } #endif nbusy++; #if defined(SHOW_BUSYBUFS) || defined(DIAGNOSTIC) printf( "%d: bufobj:%p, flags:%0x, blkno:%ld, lblkno:%ld\n", nbusy, bp->b_bufobj, bp->b_flags, (long)bp->b_blkno, (long)bp->b_lblkno); #endif } } if (nbusy) { /* * Failed to sync all blocks. Indicate this and don't * unmount filesystems (thus forcing an fsck on reboot). */ printf("Giving up on %d buffers\n", nbusy); DELAY(5000000); /* 5 seconds */ } else { if (!first_buf_printf) printf("Final sync complete\n"); /* * Unmount filesystems */ if (panicstr == 0) vfs_unmountall(); } swapoff_all(); DELAY(100000); /* wait for console output to finish */ } print_uptime(); /* * Ok, now do things that assume all filesystem activity has * been completed. */ EVENTHANDLER_INVOKE(shutdown_post_sync, howto); if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold && !dumping) doadump(); /* Now that we're going to really halt the system... */ EVENTHANDLER_INVOKE(shutdown_final, howto); for(;;) ; /* safety against shutdown_reset not working */ /* NOTREACHED */ } /* * If the shutdown was a clean halt, behave accordingly. */ static void shutdown_halt(void *junk, int howto) { if (howto & RB_HALT) { printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); switch (cngetc()) { case -1: /* No console, just die */ cpu_halt(); /* NOTREACHED */ default: howto &= ~RB_HALT; break; } } } /* * Check to see if the system paniced, pause and then reboot * according to the specified delay. */ static void shutdown_panic(void *junk, int howto) { int loop; if (howto & RB_DUMP) { if (PANIC_REBOOT_WAIT_TIME != 0) { if (PANIC_REBOOT_WAIT_TIME != -1) { printf("Automatic reboot in %d seconds - " "press a key on the console to abort\n", PANIC_REBOOT_WAIT_TIME); for (loop = PANIC_REBOOT_WAIT_TIME * 10; loop > 0; --loop) { DELAY(1000 * 100); /* 1/10th second */ /* Did user type a key? */ if (cncheckc() != -1) break; } if (!loop) return; } } else { /* zero time specified - reboot NOW */ return; } printf("--> Press a key on the console to reboot,\n"); printf("--> or switch off the system now.\n"); cngetc(); } } /* * Everything done, now reset */ static void shutdown_reset(void *junk, int howto) { printf("Rebooting...\n"); DELAY(1000000); /* wait 1 sec for printf's to complete and be read */ /* * Acquiring smp_ipi_mtx here has a double effect: * - it disables interrupts avoiding CPU0 preemption * by fast handlers (thus deadlocking against other CPUs) * - it avoids deadlocks against smp_rendezvous() or, more * generally, threads busy-waiting, with this spinlock held, * and waiting for responses by threads on other CPUs * (ie. smp_tlb_shootdown()). * * For the !SMP case it just needs to handle the former problem. */ #ifdef SMP mtx_lock_spin(&smp_ipi_mtx); #else spinlock_enter(); #endif /* cpu_boot(howto); */ /* doesn't do anything at the moment */ cpu_reset(); /* NOTREACHED */ /* assuming reset worked */ } /* * Panic is called on unresolvable fatal errors. It prints "panic: mesg", * and then reboots. If we are called twice, then we avoid trying to sync * the disks as this often leads to recursive panics. */ void panic(const char *fmt, ...) { #ifdef SMP static volatile u_int panic_cpu = NOCPU; #endif struct thread *td = curthread; int bootopt, newpanic; va_list ap; static char buf[256]; critical_enter(); #ifdef SMP /* * We don't want multiple CPU's to panic at the same time, so we * use panic_cpu as a simple spinlock. We have to keep checking * panic_cpu if we are spinning in case the panic on the first * CPU is canceled. */ if (panic_cpu != PCPU_GET(cpuid)) while (atomic_cmpset_int(&panic_cpu, NOCPU, PCPU_GET(cpuid)) == 0) while (panic_cpu != NOCPU) ; /* nothing */ #endif bootopt = RB_AUTOBOOT | RB_DUMP; newpanic = 0; if (panicstr) bootopt |= RB_NOSYNC; else { panicstr = fmt; newpanic = 1; } va_start(ap, fmt); if (newpanic) { (void)vsnprintf(buf, sizeof(buf), fmt, ap); panicstr = buf; printf("panic: %s\n", buf); } else { printf("panic: "); vprintf(fmt, ap); printf("\n"); } va_end(ap); #ifdef SMP printf("cpuid = %d\n", PCPU_GET(cpuid)); #endif #ifdef KDB if (newpanic && trace_on_panic) kdb_backtrace(); if (debugger_on_panic) kdb_enter(KDB_WHY_PANIC, "panic"); #ifdef RESTARTABLE_PANICS /* See if the user aborted the panic, in which case we continue. */ if (panicstr == NULL) { #ifdef SMP atomic_store_rel_int(&panic_cpu, NOCPU); #endif return; } #endif #endif /*thread_lock(td); */ td->td_flags |= TDF_INPANIC; /* thread_unlock(td); */ if (!sync_on_panic) bootopt |= RB_NOSYNC; critical_exit(); kern_reboot(bootopt); } /* * Support for poweroff delay. * * Please note that setting this delay too short might power off your machine * before the write cache on your hard disk has been flushed, leading to * soft-updates inconsistencies. */ #ifndef POWEROFF_DELAY # define POWEROFF_DELAY 5000 #endif static int poweroff_delay = POWEROFF_DELAY; SYSCTL_INT(_kern_shutdown, OID_AUTO, poweroff_delay, CTLFLAG_RW, &poweroff_delay, 0, ""); static void poweroff_wait(void *junk, int howto) { if (!(howto & RB_POWEROFF) || poweroff_delay <= 0) return; DELAY(poweroff_delay * 1000); } /* * Some system processes (e.g. syncer) need to be stopped at appropriate * points in their main loops prior to a system shutdown, so that they * won't interfere with the shutdown process (e.g. by holding a disk buf * to cause sync to fail). For each of these system processes, register * shutdown_kproc() as a handler for one of shutdown events. */ static int kproc_shutdown_wait = 60; SYSCTL_INT(_kern_shutdown, OID_AUTO, kproc_shutdown_wait, CTLFLAG_RW, &kproc_shutdown_wait, 0, ""); void kproc_shutdown(void *arg, int howto) { struct proc *p; int error; if (panicstr) return; p = (struct proc *)arg; printf("Waiting (max %d seconds) for system process `%s' to stop...", kproc_shutdown_wait, p->p_comm); error = kproc_suspend(p, kproc_shutdown_wait * hz); if (error == EWOULDBLOCK) printf("timed out\n"); else printf("done\n"); } void kthread_shutdown(void *arg, int howto) { struct thread *td; int error; if (panicstr) return; td = (struct thread *)arg; printf("Waiting (max %d seconds) for system thread `%s' to stop...", kproc_shutdown_wait, td->td_name); error = kthread_suspend(td, kproc_shutdown_wait * hz); if (error == EWOULDBLOCK) printf("timed out\n"); else printf("done\n"); } /* Registration of dumpers */ int set_dumper(struct dumperinfo *di) { if (di == NULL) { bzero(&dumper, sizeof dumper); return (0); } if (dumper.dumper != NULL) return (EBUSY); dumper = *di; return (0); } /* Call dumper with bounds checking. */ int dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, off_t offset, size_t length) { /* If the upper bound is 0, dumper likely will not use disks. */ if ((di->mediaoffset + di->mediasize) != 0 && length != 0 && (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { printf("Attempt to write outside dump device boundaries.\n"); return (ENXIO); } return (di->dumper(di->priv, virtual, physical, offset, length)); } void mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, uint64_t dumplen, uint32_t blksz) { bzero(kdh, sizeof(*kdh)); strncpy(kdh->magic, magic, sizeof(kdh->magic)); strncpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); kdh->version = htod32(KERNELDUMPVERSION); kdh->architectureversion = htod32(archver); kdh->dumplength = htod64(dumplen); kdh->dumptime = htod64(time_second); kdh->blocksize = htod32(blksz); strncpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname)); strncpy(kdh->versionstring, version, sizeof(kdh->versionstring)); if (panicstr != NULL) strncpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); kdh->parity = kerneldump_parity(kdh); } Index: projects/sv/sys/netinet/netdump.h =================================================================== --- projects/sv/sys/netinet/netdump.h (revision 220571) +++ projects/sv/sys/netinet/netdump.h (revision 220572) @@ -1,75 +1,77 @@ /* * Copyright (c) 2005-2011 Sandvine Incorporated * Copyright (c) 2000 Darrell Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _NETINET_NETDUMP_H_ #define _NETINET_NETDUMP_H_ #include #define NETDUMP_PORT 20023 /* Server udp port number for data. */ #define NETDUMP_ACKPORT 20024 /* Client udp port number for acks. */ #define NETDUMP_HERALD 1 /* Broadcast before starting a dump. */ #define NETDUMP_FINISHED 2 /* Send after finishing a dump. */ #define NETDUMP_VMCORE 3 /* Contains dump datas. */ #define NETDUMP_KDH 4 /* Contains kernel dump header. */ #define NETDUMP_DATASIZE 8192 /* Packets payload. */ struct netdump_msg_hdr { uint32_t mh_type; /* NETDUMP_HERALD, _FINISHED, _VMCORE, _KDH. */ uint32_t mh_seqno; /* Match acks with msgs. */ uint64_t mh_offset; /* vmcore offset (bytes). */ uint32_t mh_len; /* Attached data (bytes). */ uint32_t mh__pad; /* Pad space matching 32- and 64-bits archs. */ }; struct netdump_ack { uint32_t na_seqno; /* Match acks with msgs. */ }; struct netdump_msg { struct netdump_msg_hdr nm_hdr; uint8_t nm_data[NETDUMP_DATASIZE]; }; #ifdef _KERNEL typedef void ndumplock_handler_t(struct ifnet *); struct netdump_methods { poll_handler_t *ne_poll_locked; poll_handler_t *ne_poll_unlocked; ndumplock_handler_t *ne_disable_intr; ndumplock_handler_t *ne_enable_intr; }; +int netdumpsys(void); + #endif #endif /* !_NETINET_NETDUMP_H_ */ Index: projects/sv/sys/netinet/netdump_client.c =================================================================== --- projects/sv/sys/netinet/netdump_client.c (revision 220571) +++ projects/sv/sys/netinet/netdump_client.c (revision 220572) @@ -1,1262 +1,1208 @@ /*- * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved. * Copyright (c) 2000 Darrell Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + /* * netdump_client.c - * FreeBSD kernel module supporting netdump network dumps. - * netdump_server must be running to accept client dumps. + * FreeBSD subsystem supporting netdump network dumps. + * A dedicated server must be running to accept client dumps. * XXX: This should be split into machdep and non-machdep parts * */ #include "opt_ddb.h" #include "opt_kdb.h" #include "opt_netdump.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #ifdef NETDUMP_CLIENT_DEBUG #define NETDDEBUG(f, ...) printf((f), ## __VA_ARGS__) #define NETDDEBUG_IF(i, f, ...) if_printf((i), (f), ## __VA_ARGS__) #if NETDUMP_CLIENT_DEBUG > 1 #define NETDDEBUGV(f, ...) printf((f), ## __VA_ARGS__) #define NETDDEBUGV_IF(i, f, ...) if_printf((i), (f), ## __VA_ARGS__) #else #define NETDDEBUGV(f, ...) #define NETDDEBUGV_IF(i, f, ...) #endif #else #define NETDDEBUG(f, ...) #define NETDDEBUG_IF(i, f, ...) #define NETDDEBUGV(f, ...) #define NETDDEBUGV_IF(i, f, ...) #endif static void nd_handle_arp(struct mbuf **mb); static void nd_handle_ip(struct mbuf **mb); static int netdump_arp_server(void); -static void netdump_config_defaults(void); +static void netdump_config_defaults(void *dummy __unused); static int netdump_dumper(void *priv __unused, void *virtual, vm_offset_t physical __unused, off_t offset, size_t length); static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst, u_short etype); static void netdump_mbuf_nop(void *ptr __unused, void *opt_args __unused); -static int netdump_modevent(module_t mod, int type, void *unused); static void netdump_network_poll(void); static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); static int netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen); static int netdump_send_arp(void); -static void netdump_trigger(void *arg, int howto); static int netdump_udp_output(struct mbuf *m); static int sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS); static int sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS); /* Must be at least as big as the chunks dumpsys() gives us. */ static unsigned char buf[MAXDUMPPGS * PAGE_SIZE]; -static eventhandler_tag nd_tag; static uint64_t rcvd_acks; static uint32_t nd_seqno = 1; static int dump_failed, have_server_mac; /* * Times to poll the NIC (0.5ms each poll) before assuming packetloss * occurred (default to 5s). */ static int nd_polls = 10000; /* Times to retransmit lost packets. */ static int nd_retries = 10; /* General dynamic settings. */ static char nd_ifp_str[IFNAMSIZ]; static struct ether_addr nd_gw_mac; static struct in_addr nd_server = {INADDR_ANY}; static struct in_addr nd_client = {INADDR_ANY}; static struct in_addr nd_gw = {INADDR_ANY}; struct ifnet *nd_ifp; static int nd_enable = 0; static uint16_t nd_server_port = NETDUMP_PORT; /* Tunables storages. */ static char nd_server_tun[INET_ADDRSTRLEN]; static char nd_client_tun[INET_ADDRSTRLEN]; static char nd_gw_tun[INET_ADDRSTRLEN]; static char nd_nic_tun[IFNAMSIZ]; /* * Checks for netdump support on a network interface * * Parameters: * ifp The network interface that is being tested for support * * Returns: * int 1 if the interface is supported, 0 if not */ static __inline int netdump_supported_nic(struct ifnet *ifp) { return (ifp->if_ndumpfuncs != NULL); } /*- * Sysctls specific code. */ /* * Sysctl handler converting a string sysctl to/from an ifaddr name. * * Parameters: * SYSCTL_HANDLER_ARGS * - arg1 is a pointer to the if structure name * - arg2 is unused * * Returns: * int see errno.h, 0 for success */ static int sysctl_handle_ifxname(SYSCTL_HANDLER_ARGS) { char buf[IFNAMSIZ]; struct ifnet *ifp; int error, found; strlcpy(buf, arg1, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); found = 0; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (!strncmp(ifp->if_xname, buf, strlen(ifp->if_xname)) && netdump_supported_nic(ifp)) { found = 1; break; } } IFNET_RUNLOCK_NOSLEEP(); if (found == 0) return (EINVAL); strlcpy(arg1, buf, strlen(buf) + 1); return (error); } /* * Sysctl handler converting a string sysctl to/from an in_addr. * * Parameters: * SYSCTL_HANDLER_ARGS * - arg1 is a pointer to the struct in_addr holding the IP * - arg2 is unused * * Returns: * int see errno.h, 0 for success */ static int sysctl_handle_inaddr(SYSCTL_HANDLER_ARGS) { struct in_addr addr; char buf[INET_ADDRSTRLEN]; int error; inet_ntoa_r(*(struct in_addr *)arg1, buf); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error == 0) { if (!inet_aton(buf, &addr)) error = EINVAL; else *(struct in_addr *)arg1 = addr; } return (error); } SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "netdump"); SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_server, 0, sysctl_handle_inaddr, "A", "dump server"); SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING |CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_client, 0, sysctl_handle_inaddr, "A", "dump client"); SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_gw, 0, sysctl_handle_inaddr, "A", "dump default gateway"); SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_ifp_str, 0, sysctl_handle_ifxname, "A", "dumping interface name"); SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_polls, 0, "times to poll NIC per retry"); SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_retries, 0, "times to retransmit lost packets"); SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, &nd_enable, 0, "enable network dump"); TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun)); TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun)); TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun)); TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun)); TUNABLE_INT("net.dump.enable", &nd_enable); /*- * Network specific primitives. * Following down the code they are divided ordered as: * - Output primitives * - Input primitives * - Polling primitives */ /* * Netdump wraps external mbufs around address ranges. unlike most sane * counterparts, netdump uses a stop-and-wait approach to flow control and * retransmission, so the ack obviates the need for mbuf reference * counting. We still need to tell other mbuf handlers not to do anything * special with our mbufs, so specify this nop handler. * * Parameters: * ptr data to free (ignored) * opt_args callback pointer (ignored) * * Returns: * void */ static void netdump_mbuf_nop(void *ptr __unused, void *opt_args __unused) { } /* * Handles creation of the ethernet header, then places outgoing packets into * the tx buffer for the NIC * * Parameters: * m The mbuf containing the packet to be sent (will be freed by * this function or the NIC driver) * ifp The interface to send on * dst The destination ethernet address (source address will be looked * up using ifp) * etype The ETHERTYPE_* value for the protocol that is being sent * * Returns: * int see errno.h, 0 for success */ static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst, u_short etype) { struct ether_header *eh; /* Fill in the ethernet header. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) { printf("netdump_ether_output: Out of mbufs\n"); return (ENOBUFS); } eh = mtod(m, struct ether_header *); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); eh->ether_type = htons(etype); if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) || (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { if_printf(ifp, "netdump_ether_output: Interface isn't up\n"); m_freem(m); return (ENETDOWN); } return ((ifp->if_transmit)(ifp, m)); } /* * Unreliable transmission of an mbuf chain to the netdump server * Note: can't handle fragmentation; fails if the packet is larger than * nd_ifp->if_mtu after adding the UDP/IP headers * * Parameters: * m mbuf chain * * Returns: * int see errno.h, 0 for success */ static int netdump_udp_output(struct mbuf *m) { struct udpiphdr *ui; struct ip *ip; MPASS(nd_ifp != NULL); M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); if (m == NULL) { printf("netdump_udp_output: Out of mbufs\n"); return (ENOBUFS); } ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); ui->ui_pr = IPPROTO_UDP; ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip)); ui->ui_ulen = ui->ui_len; ui->ui_src = nd_client; ui->ui_dst = nd_server; /* Use this src port so that the server can connect() the socket */ ui->ui_sport = htons(NETDUMP_ACKPORT); ui->ui_dport = htons(nd_server_port); ui->ui_sum = 0; if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0) ui->ui_sum = 0xffff; ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_tos = 0; ip->ip_len = htons(m->m_pkthdr.len); ip->ip_id = 0; ip->ip_off = htons(IP_DF); ip->ip_ttl = 32; ip->ip_sum = 0; ip->ip_sum = in_cksum(m, sizeof(struct ip)); if (m->m_pkthdr.len > nd_ifp->if_mtu) { printf("netdump_udp_output: Packet is too big: %u > MTU %lu\n", m->m_pkthdr.len, nd_ifp->if_mtu); m_freem(m); return (ENOBUFS); } return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP)); } /* * Builds and sends a single ARP request to locate the server * * Parameters: * void * * Return value: * 0 on success * errno on error */ static int netdump_send_arp() { struct ether_addr bcast; struct mbuf *m; struct arphdr *ah; int pktlen; MPASS(nd_ifp != NULL); /* Fill-up a broadcast address. */ memset(&bcast, 0xFF, ETHER_ADDR_LEN); MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { printf("netdump_send_arp: Out of mbufs"); return (ENOBUFS); } pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); m->m_len = pktlen; m->m_pkthdr.len = pktlen; MH_ALIGN(m, pktlen); ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ETHER_ADDR_LEN; ah->ar_pln = sizeof(struct in_addr); ah->ar_op = htons(ARPOP_REQUEST); memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); ((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr; bzero(ar_tha(ah), ETHER_ADDR_LEN); ((struct in_addr *)ar_tpa(ah))->s_addr = nd_gw.s_addr; return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); } /* * Sends ARP requests to locate the server and waits for a response * * Parameters: * void * * Return value: * 0 on success * errno on error */ static int netdump_arp_server() { int err, polls, retries; for (retries = 0; retries < nd_retries && have_server_mac == 0; retries++) { err = netdump_send_arp(); if (err != 0) return (err); for (polls = 0; polls < nd_polls && have_server_mac == 0; polls++) { netdump_network_poll(); DELAY(500); } if (have_server_mac == 0) printf("(ARP retry)"); } if (have_server_mac != 0) return (0); printf("\nARP timed out.\n"); return (ETIMEDOUT); } /* * Construct and reliably send a netdump packet. May fail from a resource * shortage or extreme number of unacknowledged retransmissions. Wait for * an acknowledgement before returning. Splits packets into chunks small * enough to be sent without fragmentation (looks up the interface MTU) * * Parameters: * type netdump packet type (HERALD, FINISHED, or VMCORE) * offset vmcore data offset (bytes) * data vmcore data * datalen vmcore data size (bytes) * * Returns: * int see errno.h, 0 for success */ static int netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen) { uint64_t want_acks; struct netdump_msg_hdr *nd_msg_hdr; struct mbuf *m, *m2; uint32_t i, pktlen, sent_so_far; int retries, polls, error; want_acks = 0; rcvd_acks = 0; retries = 0; MPASS(nd_ifp != NULL); retransmit: /* Chunks can be too big to fit in packets. */ for (i = sent_so_far = 0; sent_so_far < datalen || (i == 0 && datalen == 0); i++) { pktlen = datalen - sent_so_far; /* First bound: the packet structure. */ pktlen = min(pktlen, NETDUMP_DATASIZE); /* Second bound: the interface MTU (assume no IP options). */ pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) - sizeof(struct netdump_msg_hdr)); /* * Check if it is retransmitting and this has been ACKed * already. */ if ((rcvd_acks & (1 << i)) != 0) { sent_so_far += pktlen; continue; } /* * Get and fill a header mbuf, then chain data as an extended * mbuf. */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { printf("netdump_send: Out of mbufs!\n"); return (ENOBUFS); } m->m_len = sizeof(struct netdump_msg_hdr); m->m_pkthdr.len = sizeof(struct netdump_msg_hdr); MH_ALIGN(m, sizeof(struct netdump_msg_hdr)); nd_msg_hdr = mtod(m, struct netdump_msg_hdr *); nd_msg_hdr->mh_seqno = htonl(nd_seqno+i); nd_msg_hdr->mh_type = htonl(type); nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far); nd_msg_hdr->mh_len = htonl(pktlen); nd_msg_hdr->mh__pad = 0; if (pktlen != 0) { m2 = m_get(M_DONTWAIT, MT_DATA); if (m2 == NULL) { m_freem(m); printf("netdump_send: Out of mbufs!\n"); return (ENOBUFS); } MEXTADD(m2, data+sent_so_far, pktlen, netdump_mbuf_nop, NULL, NULL, M_RDONLY, EXT_MOD_TYPE); m2->m_len = pktlen; m->m_next = m2; m->m_pkthdr.len += m2->m_len; } error = netdump_udp_output(m); if (error != 0) return (error); /* Note that we're waiting for this packet in the bitfield. */ want_acks |= 1 << i; sent_so_far += pktlen; } if (i >= sizeof(want_acks) * 8) printf("Warning: Sent more than %zd packets (%d). " "Acknowledgements will fail unless the size of " "rcvd_acks/want_acks is increased.\n", sizeof(want_acks) * 8, i); /* * Wait for acks. A *real* window would speed things up considerably. */ polls = 0; while (rcvd_acks != want_acks) { if (polls++ > nd_polls) { if (retries++ > nd_retries) return (ETIMEDOUT); printf(". "); goto retransmit; } netdump_network_poll(); DELAY(500); } nd_seqno += i; return (0); } /* * Handler for IP packets: checks their sanity and then processes any netdump * ACK packets it finds. * * It needs to replicate partially the behaviour of ip_input() and * udp_input(). * * Parameters: * mb a pointer to an mbuf * containing the packet received * Updates *mb if m_pullup et al change the pointer * Assumes the calling function will take care of freeing the mbuf * * Return value: * void */ static void nd_handle_ip(struct mbuf **mb) { struct ip *ip; struct udpiphdr *udp; struct netdump_ack *nd_ack; struct mbuf *m; int rcv_ackno; unsigned short hlen; /* IP processing. */ m = *mb; if (m->m_pkthdr.len < sizeof(struct ip)) { NETDDEBUG("nd_handle_ip: dropping packet too small for IP " "header\n"); return; } if (m->m_len < sizeof(struct ip)) { m = m_pullup(m, sizeof(struct ip)); *mb = m; if (m == NULL) { NETDDEBUG("nd_handle_ip: m_pullup failed\n"); return; } } ip = mtod(m, struct ip *); /* IP version. */ if (ip->ip_v != IPVERSION) { NETDDEBUG("nd_handle_ip: Bad IP version %d\n", ip->ip_v); return; } /* Header length. */ hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", hlen); return; } if (hlen > m->m_len) { m = m_pullup(m, hlen); *mb = m; if (m == NULL) { NETDDEBUG("nd_handle_ip: m_pullup failed\n"); return; } ip = mtod(m, struct ip *); } #ifdef INVARIANTS if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) && (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n"); return; } #endif /* Checksum. */ if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) { if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) { NETDDEBUG("nd_handle_ip: Bad IP checksum\n"); return; } } else NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n"); /* Convert fields to host byte order. */ ip->ip_len = ntohs(ip->ip_len); if (ip->ip_len < hlen) { NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than header (%hu)\n", ip->ip_len, hlen); return; } ip->ip_off = ntohs(ip->ip_off); if (m->m_pkthdr.len < ip->ip_len) { NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than ethernet packet (%hu)\n", ip->ip_len, m->m_pkthdr.len); return; } if (m->m_pkthdr.len > ip->ip_len) { /* Truncate the packet to the IP length. */ if (m->m_len == m->m_pkthdr.len) { m->m_len = ip->ip_len; m->m_pkthdr.len = ip->ip_len; } else m_adj(m, ip->ip_len - m->m_pkthdr.len); } /* Ignore packets with IP options. */ if (hlen > sizeof(struct ip)) { NETDDEBUG("nd_handle_ip: Drop packet with IP options\n"); return; } /* Check that the source is the server's IP. */ if (ip->ip_src.s_addr != nd_server.s_addr) { NETDDEBUG("nd_handle_ip: Drop packet not from server\n"); return; } /* Check if the destination IP is ours. */ if (ip->ip_dst.s_addr != nd_client.s_addr) { NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n"); return; } if (ip->ip_p != IPPROTO_UDP) { NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n"); return; } /* Do not deal with fragments. */ if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) { NETDDEBUG("nd_handle_ip: Drop fragmented packet\n"); return; } /* UDP custom is to have packet length not include IP header. */ ip->ip_len -= hlen; /* UDP processing. */ /* Get IP and UDP headers together, along with the netdump packet. */ if (m->m_pkthdr.len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { NETDDEBUG("nd_handle_ip: Ignoring small packet\n"); return; } if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { m = m_pullup(m, sizeof(struct udpiphdr) + sizeof(struct netdump_ack)); *mb = m; if (m == NULL) { NETDDEBUG("nd_handle_ip: m_pullup failed\n"); return; } } udp = mtod(m, struct udpiphdr *); if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) { NETDDEBUG("not on the netdump port.\n"); return; } /* Netdump processing. */ /* * Packet is meant for us. Extract the ack sequence number and the * port number if necessary. */ nd_ack = (struct netdump_ack *)(mtod(m, caddr_t) + sizeof(struct udpiphdr)); rcv_ackno = ntohl(nd_ack->na_seqno); if (nd_server_port == NETDUMP_PORT) nd_server_port = ntohs(udp->ui_u.uh_sport); if (rcv_ackno >= nd_seqno + 64) printf("nd_handle_ip: ACK %d too far in future!\n", rcv_ackno); else if (rcv_ackno >= nd_seqno) { /* We're interested in this ack. Record it. */ rcvd_acks |= 1 << (rcv_ackno-nd_seqno); } } /* * Handler for ARP packets: checks their sanity and then * 1. If the ARP is a request for our IP, respond with our MAC address * 2. If the ARP is a response from our server, record its MAC address * * It needs to replicate partially the behaviour of arpintr() and * in_arpinput(). * * Parameters: * mb a pointer to an mbuf * containing the packet received * Updates *mb if m_pullup et al change the pointer * Assumes the calling function will take care of freeing the mbuf * * Return value: * void */ static void nd_handle_arp(struct mbuf **mb) { char buf[INET_ADDRSTRLEN]; struct in_addr isaddr, itaddr, myaddr; struct ether_addr dst; struct mbuf *m; struct arphdr *ah; struct ifnet *ifp; uint8_t *enaddr; int req_len, op; m = *mb; ifp = m->m_pkthdr.rcvif; if (m->m_len < sizeof(struct arphdr)) { m = m_pullup(m, sizeof(struct arphdr)); *mb = m; if (m == NULL) { NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n"); return; } } ah = mtod(m, struct arphdr *); if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) { NETDDEBUG("nd_handle_arp: unknown hardware address 0x%2D)\n", (unsigned char *)&ah->ar_hrd, ""); return; } if (ntohs(ah->ar_pro) != ETHERTYPE_IP) { NETDDEBUG("nd_handle_arp: Drop ARP for unknown protocol %d\n", ntohs(ah->ar_pro)); return; } req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr)); if (m->m_len < req_len) { m = m_pullup(m, req_len); *mb = m; if (m == NULL) { NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n"); return; } } ah = mtod(m, struct arphdr *); op = ntohs(ah->ar_op); memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); enaddr = (uint8_t *)IF_LLADDR(ifp); myaddr = nd_client; if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) { NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n"); return; } if (isaddr.s_addr == nd_client.s_addr) { printf("nd_handle_arp: %*D is using my IP address %s!\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr)); return; } if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast address\n"); return; } if (op == ARPOP_REPLY) { if (isaddr.s_addr != nd_gw.s_addr) { inet_ntoa_r(isaddr, buf); NETDDEBUG("nd_handle_arp: ignoring ARP reply from %s (not netdump server)\n", buf); return; } memcpy(nd_gw_mac.octet, ar_sha(ah), min(ah->ar_hln, ETHER_ADDR_LEN)); have_server_mac = 1; NETDDEBUG("\nnd_handle_arp: Got server MAC address %6D\n", nd_gw_mac.octet, ":"); return; } if (op != ARPOP_REQUEST) { NETDDEBUG("nd_handle_arp: Ignoring ARP non-request/reply\n"); return; } if (itaddr.s_addr != nd_client.s_addr) { NETDDEBUG("nd_handle_arp: ignoring ARP not to our IP\n"); return; } memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); memcpy(ar_sha(ah), enaddr, ah->ar_hln); memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); memcpy(ar_spa(ah), &itaddr, ah->ar_pln); ah->ar_op = htons(ARPOP_REPLY); ah->ar_pro = htons(ETHERTYPE_IP); m->m_flags &= ~(M_BCAST|M_MCAST); m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); m->m_pkthdr.len = m->m_len; memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP); *mb = NULL; } /* * Handler for incoming packets directly from the network adapter * Identifies the packet type (IP or ARP) and passes it along to one of the * helper functions nd_handle_ip or nd_handle_arp. * * It needs to replicate partially the behaviour of ether_input() and * ether_demux(). * * Parameters: * ifp the interface the packet came from (should be nd_ifp) * m an mbuf containing the packet received * * Return value: * void */ static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; /* Ethernet processing. */ if ((m->m_flags & M_PKTHDR) == 0) { NETDDEBUG_IF(ifp, "netdump_pkt_in: Discard frame without packet header\n"); goto done; } if (m->m_len < ETHER_HDR_LEN) { NETDDEBUG_IF(ifp, "netdump_pkt_in: Discard frame without leading eth header (len %u pktlen %u)\n", m->m_len, m->m_pkthdr.len); goto done; } if ((m->m_flags & M_HASFCS) != 0) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } eh = mtod(m, struct ether_header *); m->m_pkthdr.header = eh; etype = ntohs(eh->ether_type); if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) { NETDDEBUG_IF(ifp, "netdump_pkt_in: Ignoring vlan packets\n"); goto done; } /* XXX: Probably must also check if we're the recipient MAC address. */ /* Done ethernet processing. Strip off the ethernet header. */ m_adj(m, ETHER_HDR_LEN); switch (etype) { case ETHERTYPE_ARP: nd_handle_arp(&m); break; case ETHERTYPE_IP: nd_handle_ip(&m); break; default: NETDDEBUG_IF(ifp, "netdump_pkt_in: Dropping unknown ethertype %hu\n", etype); break; } done: if (m != NULL) m_freem(m); } /* * After trapping, instead of assuming that most of the network stack is sane * just poll the driver directly for packets. * * Parameters: * void * * Returns: * void */ static void netdump_network_poll() { MPASS(nd_ifp != NULL); #if defined(KDB) && !defined(KDB_UNATTENDED) if (panicstr != NULL) nd_ifp->if_ndumpfuncs->ne_poll_unlocked(nd_ifp, POLL_AND_CHECK_STATUS, 1000); else #endif nd_ifp->if_ndumpfuncs->ne_poll_locked(nd_ifp, POLL_AND_CHECK_STATUS, 1000); } /*- * Dumping specific primitives. */ /* * Callback from dumpsys() to dump a chunk of memory. * Copies it out to our static buffer then sends it across the network. * Detects the initial KDH and makes sure it is given a special packet type. * * Parameters: * priv Unused. Optional private pointer. * virtual Virtual address (where to read the data from) * physical Unused. Physical memory address. * offset Offset from start of core file * length Data length * * Return value: * 0 on success * errno on error */ static int netdump_dumper(void *priv __unused, void *virtual, vm_offset_t physical __unused, off_t offset, size_t length) { int err, msgtype; NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n", virtual, (uintmax_t)offset, length); if (length > sizeof(buf)) return (ENOSPC); /* * The first write (at offset 0) is the kernel dump header. Flag it * for the server to treat specially. * XXX: This doesn't strip out the footer KDH, although it * should not hurt anything. */ msgtype = NETDUMP_VMCORE; if (offset == 0 && length > 0) msgtype = NETDUMP_KDH; else if (offset > 0) offset -= sizeof(struct kerneldumpheader); memcpy(buf, virtual, length); err = netdump_send(msgtype, offset, buf, length); if (err != 0) { dump_failed = 1; return (err); } return (0); } /* - * Handler going into shutdown_pre_sync hook. - * Overrides a standard disk dumping activity. + * Dumper routine, specular to dumpsys(). * * Parameters: - * arg unused - * howto boot flags (only dump if RB_DUMP set) + * void * * Returns: - * void + * int see errno.h, 0 for success */ -static void -netdump_trigger(void *arg, int howto) +int +netdumpsys() { struct dumperinfo dumper; void (*old_if_input)(struct ifnet *, struct mbuf *); - int found, must_lock, nd_gw_unset; + int error, found, must_lock, nd_gw_unset; old_if_input = NULL; + error = 0; found = 0; nd_gw_unset = 0; must_lock = 1; #if defined(KDB) && !defined(KDB_UNATTENDED) if (panicstr != NULL) must_lock = 0; #endif /* Check if the dumping is allowed to continue. */ - if ((howto & (RB_HALT | RB_DUMP)) != RB_DUMP || nd_enable == 0 || - cold != 0 || dumping != 0) - return; + if (nd_enable == 0) + return (EINVAL); /* Lookup the right if device to be used in the dump. */ if (must_lock != 0) IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(nd_ifp, &V_ifnet, if_link) { if (!strncmp(nd_ifp->if_xname, nd_ifp_str, strlen(nd_ifp->if_xname)) && netdump_supported_nic(nd_ifp)) { found = 1; break; } } if (must_lock != 0) IFNET_RUNLOCK_NOSLEEP(); if (found == 0) { - printf("netdump_trigger: Can't netdump: no valid NIC given\n"); - return; + printf("netdumpsys: Can't netdump: no valid NIC given\n"); + return (EINVAL); } MPASS(nd_ifp != NULL); if (nd_server.s_addr == INADDR_ANY) { - printf("netdump_trigger: Can't netdump; no server IP given\n"); - return; + printf("netdumpsys: Can't netdump; no server IP given\n"); + return (EINVAL); } if (nd_client.s_addr == INADDR_ANY) { - printf("netdump_trigger: Can't netdump; no client IP given\n"); - return; + printf("netdumpsys: Can't netdump; no client IP given\n"); + return (EINVAL); } /* - * netdump is invoked as a pre-sync handler instead of as - * a real dumpdev dump routine (that is because shutdown handlers - * run as post-sync handlers, earlier than dumping routines - * taking place, and thus network and devices may not be further - * available). - * Make sure, artificially, the dump context is set so a debugger - * can find the stack trace. - */ - savectx(&dumppcb); - dumptid = curthread->td_tid; - dumping++; - - /* * nd_server_port could have switched after the first ack the * first time it gets called. Adjust it accordingly. */ nd_server_port = NETDUMP_PORT; if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0) nd_ifp->if_ndumpfuncs->ne_disable_intr(nd_ifp); /* Make the card use *our* receive callback. */ old_if_input = nd_ifp->if_input; nd_ifp->if_input = netdump_pkt_in; if (nd_gw.s_addr == INADDR_ANY) { nd_gw.s_addr = nd_server.s_addr; nd_gw_unset = 1; } printf("\n-----------------------------------\n"); printf("netdump in progress. searching for server.. "); if (netdump_arp_server()) { printf("Failed to locate server MAC address\n"); + error = EINVAL; goto trig_abort; } if (netdump_send(NETDUMP_HERALD, 0, NULL, 0) != 0) { printf("Failed to contact netdump server\n"); + error = EINVAL; goto trig_abort; } printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), nd_gw_mac.octet, ":"); printf("-----------------------------------\n"); /* Call the dumping routine. */ dumper.dumper = netdump_dumper; dumper.priv = NULL; dumper.blocksize = NETDUMP_DATASIZE; dumper.mediasize = 0; dumper.mediaoffset = 0; dumpsys(&dumper); if (dump_failed != 0) { printf("Failed to dump the actual raw datas\n"); + error = EINVAL; goto trig_abort; } if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0) { printf("Failed to close the transaction\n"); + error = EINVAL; goto trig_abort; } printf("\nnetdump finished.\n"); - printf("cancelling normal dump\n"); - set_dumper(NULL); trig_abort: if (nd_gw_unset != 0) nd_gw.s_addr = INADDR_ANY; if (old_if_input) nd_ifp->if_input = old_if_input; if ((nd_ifp->if_capenable & IFCAP_POLLING) == 0 && must_lock != 0) nd_ifp->if_ndumpfuncs->ne_enable_intr(nd_ifp); - dumping--; + return (error); } /*- * KLD specific code. */ /* - * Called upon module load. Initializes the sysctl variables to sane defaults + * Called upon system init. Initializes the sysctl variables to sane defaults * (locates the first available NIC and uses the first IPv4 IP on that card as * the client IP). Leaves the server IP unconfigured. * * Parameters: - * void + * void *, unused * * Returns: * void */ static void -netdump_config_defaults() +netdump_config_defaults(void *dummy __unused) { struct ifnet *ifp; int found; nd_ifp = NULL; nd_server.s_addr = INADDR_ANY; nd_client.s_addr = INADDR_ANY; nd_gw.s_addr = INADDR_ANY; if (nd_server_tun[0] != '\0') inet_aton(nd_server_tun, &nd_server); if (nd_client_tun[0] != '\0') inet_aton(nd_client_tun, &nd_client); if (nd_gw_tun[0] != '\0') inet_aton(nd_gw_tun, &nd_gw); if (nd_nic_tun[0] != '\0') { found = 0; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (!strncmp(ifp->if_xname, nd_nic_tun, strlen(ifp->if_xname))) { found = 1; break; } } IFNET_RUNLOCK_NOSLEEP(); if (found != 0 && netdump_supported_nic(ifp)) nd_ifp = ifp; } } +SYSINIT(netdump, SI_SUB_KLD, SI_ORDER_ANY, netdump_config_defaults, NULL); -static int -netdump_modevent(module_t mod, int type, void *unused) -{ -#ifdef NETDUMP_CLIENT_DEBUG - char buf[INET_ADDRSTRLEN]; -#endif - - switch (type) { - case MOD_LOAD: - netdump_config_defaults(); - nd_tag = EVENTHANDLER_REGISTER(shutdown_pre_sync, - netdump_trigger, NULL, SHUTDOWN_PRI_FIRST); - -#ifdef NETDUMP_CLIENT_DEBUG - if (nd_ifp == NULL) { - printf("netdump: Warning: No default interface found."); - printf("Manual configuration required.\n"); - } else { - inet_ntoa_r(nd_client, buf); - printf("netdump: Using interface %s; client IP %s\n", - nd_ifp->if_xname, buf); - } -#endif - printf("netdump initialized\n"); - break; - case MOD_UNLOAD: - if (nd_tag != NULL) { - EVENTHANDLER_DEREGISTER(shutdown_pre_sync, nd_tag); - nd_tag = NULL; - } - printf("netdump unloaded\n"); - break; - default: - break; - } - return (0); -} -static moduledata_t netdump_mod = {"netdump", netdump_modevent, 0}; -DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PROTO_END, SI_ORDER_ANY); - #ifdef DDB DB_COMMAND(netdump, ddb_force_netdump) { - netdump_trigger(NULL, RB_DUMP); + netdumpsys(); } #endif Index: projects/sv/sys/sys/conf.h =================================================================== --- projects/sv/sys/sys/conf.h (revision 220571) +++ projects/sv/sys/sys/conf.h (revision 220572) @@ -1,340 +1,337 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2000 * Poul-Henning Kamp. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)conf.h 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _SYS_CONF_H_ #define _SYS_CONF_H_ #ifdef _KERNEL #include #else #include #endif struct snapdata; struct devfs_dirent; struct cdevsw; struct file; struct cdev { void *__si_reserved; u_int si_flags; #define SI_ETERNAL 0x0001 /* never destroyed */ #define SI_ALIAS 0x0002 /* carrier of alias name */ #define SI_NAMED 0x0004 /* make_dev{_alias} has been called */ #define SI_CHEAPCLONE 0x0008 /* can be removed_dev'ed when vnode reclaims */ #define SI_CHILD 0x0010 /* child of another struct cdev **/ #define SI_DEVOPEN 0x0020 /* opened by device */ #define SI_CONSOPEN 0x0040 /* opened by console */ #define SI_DUMPDEV 0x0080 /* is kernel dumpdev */ #define SI_CANDELETE 0x0100 /* can do BIO_DELETE */ #define SI_CLONELIST 0x0200 /* on a clone list */ struct timespec si_atime; struct timespec si_ctime; struct timespec si_mtime; uid_t si_uid; gid_t si_gid; mode_t si_mode; struct ucred *si_cred; /* cached clone-time credential */ int si_drv0; int si_refcount; LIST_ENTRY(cdev) si_list; LIST_ENTRY(cdev) si_clone; LIST_HEAD(, cdev) si_children; LIST_ENTRY(cdev) si_siblings; struct cdev *si_parent; char *si_name; void *si_drv1, *si_drv2; struct cdevsw *si_devsw; int si_iosize_max; /* maximum I/O size (for physio &al) */ u_long si_usecount; u_long si_threadcount; union { struct snapdata *__sid_snapdata; } __si_u; char __si_namebuf[SPECNAMELEN + 1]; }; #define si_snapdata __si_u.__sid_snapdata #ifdef _KERNEL /* * Definitions of device driver entry switches */ struct bio; struct buf; struct thread; struct uio; struct knote; struct clonedevs; struct vm_object; struct vnode; /* * Note: d_thread_t is provided as a transition aid for those drivers * that treat struct proc/struct thread as an opaque data type and * exist in substantially the same form in both 4.x and 5.x. Writers * of drivers that dips into the d_thread_t structure should use * struct thread or struct proc as appropriate for the version of the * OS they are using. It is provided in lieu of each device driver * inventing its own way of doing this. While it does violate style(9) * in a number of ways, this violation is deemed to be less * important than the benefits that a uniform API between releases * gives. * * Users of struct thread/struct proc that aren't device drivers should * not use d_thread_t. */ typedef struct thread d_thread_t; typedef int d_open_t(struct cdev *dev, int oflags, int devtype, struct thread *td); typedef int d_fdopen_t(struct cdev *dev, int oflags, struct thread *td, struct file *fp); typedef int d_close_t(struct cdev *dev, int fflag, int devtype, struct thread *td); typedef void d_strategy_t(struct bio *bp); typedef int d_ioctl_t(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td); typedef int d_read_t(struct cdev *dev, struct uio *uio, int ioflag); typedef int d_write_t(struct cdev *dev, struct uio *uio, int ioflag); typedef int d_poll_t(struct cdev *dev, int events, struct thread *td); typedef int d_kqfilter_t(struct cdev *dev, struct knote *kn); typedef int d_mmap_t(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr); typedef int d_mmap_single_t(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot); typedef void d_purge_t(struct cdev *dev); typedef int dumper_t( void *_priv, /* Private to the driver. */ void *_virtual, /* Virtual (mapped) address. */ vm_offset_t _physical, /* Physical address of virtual. */ off_t _offset, /* Byte-offset to write at. */ size_t _length); /* Number of bytes to dump. */ #endif /* _KERNEL */ /* * Types for d_flags. */ #define D_TAPE 0x0001 #define D_DISK 0x0002 #define D_TTY 0x0004 #define D_MEM 0x0008 #ifdef _KERNEL #define D_TYPEMASK 0xffff /* * Flags for d_flags which the drivers can set. */ #define D_TRACKCLOSE 0x00080000 /* track all closes */ #define D_MMAP_ANON 0x00100000 /* special treatment in vm_mmap.c */ #define D_PSEUDO 0x00200000 /* make_dev() can return NULL */ #define D_NEEDGIANT 0x00400000 /* driver want Giant */ #define D_NEEDMINOR 0x00800000 /* driver uses clone_create() */ /* * Version numbers. */ #define D_VERSION_00 0x20011966 #define D_VERSION_01 0x17032005 /* Add d_uid,gid,mode & kind */ #define D_VERSION_02 0x28042009 /* Add d_mmap_single */ #define D_VERSION_03 0x17122009 /* d_mmap takes memattr,vm_ooffset_t */ #define D_VERSION D_VERSION_03 /* * Flags used for internal housekeeping */ #define D_INIT 0x80000000 /* cdevsw initialized */ /* * Character device switch table */ struct cdevsw { int d_version; u_int d_flags; const char *d_name; d_open_t *d_open; d_fdopen_t *d_fdopen; d_close_t *d_close; d_read_t *d_read; d_write_t *d_write; d_ioctl_t *d_ioctl; d_poll_t *d_poll; d_mmap_t *d_mmap; d_strategy_t *d_strategy; dumper_t *d_dump; d_kqfilter_t *d_kqfilter; d_purge_t *d_purge; d_mmap_single_t *d_mmap_single; int32_t d_spare0[3]; void *d_spare1[3]; /* These fields should not be messed with by drivers */ LIST_HEAD(, cdev) d_devs; int d_spare2; union { struct cdevsw *gianttrick; SLIST_ENTRY(cdevsw) postfree_list; } __d_giant; }; #define d_gianttrick __d_giant.gianttrick #define d_postfree_list __d_giant.postfree_list struct module; struct devsw_module_data { int (*chainevh)(struct module *, int, void *); /* next handler */ void *chainarg; /* arg for next event handler */ /* Do not initialize fields hereafter */ }; #define DEV_MODULE(name, evh, arg) \ static moduledata_t name##_mod = { \ #name, \ evh, \ arg \ }; \ DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE) void clone_setup(struct clonedevs **cdp); void clone_cleanup(struct clonedevs **); #define CLONE_UNITMASK 0xfffff #define CLONE_FLAG0 (CLONE_UNITMASK + 1) int clone_create(struct clonedevs **, struct cdevsw *, int *unit, struct cdev **dev, int extra); int count_dev(struct cdev *_dev); void destroy_dev(struct cdev *_dev); int destroy_dev_sched(struct cdev *dev); int destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg); void destroy_dev_drain(struct cdevsw *csw); void drain_dev_clone_events(void); struct cdevsw *dev_refthread(struct cdev *_dev, int *_ref); struct cdevsw *devvn_refthread(struct vnode *vp, struct cdev **devp, int *_ref); void dev_relthread(struct cdev *_dev, int _ref); void dev_depends(struct cdev *_pdev, struct cdev *_cdev); void dev_ref(struct cdev *dev); void dev_refl(struct cdev *dev); void dev_rel(struct cdev *dev); void dev_strategy(struct cdev *dev, struct buf *bp); struct cdev *make_dev(struct cdevsw *_devsw, int _unit, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(6, 7); struct cdev *make_dev_cred(struct cdevsw *_devsw, int _unit, struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms, const char *_fmt, ...) __printflike(7, 8); #define MAKEDEV_REF 0x01 #define MAKEDEV_WHTOUT 0x02 #define MAKEDEV_NOWAIT 0x04 #define MAKEDEV_WAITOK 0x08 #define MAKEDEV_ETERNAL 0x10 #define MAKEDEV_CHECKNAME 0x20 struct cdev *make_dev_credf(int _flags, struct cdevsw *_devsw, int _unit, struct ucred *_cr, uid_t _uid, gid_t _gid, int _mode, const char *_fmt, ...) __printflike(8, 9); int make_dev_p(int _flags, struct cdev **_cdev, struct cdevsw *_devsw, struct ucred *_cr, uid_t _uid, gid_t _gid, int _mode, const char *_fmt, ...) __printflike(8, 9); struct cdev *make_dev_alias(struct cdev *_pdev, const char *_fmt, ...) __printflike(2, 3); void dev_lock(void); void dev_unlock(void); void setconf(void); #ifdef KLD_MODULE #define MAKEDEV_ETERNAL_KLD 0 #else #define MAKEDEV_ETERNAL_KLD MAKEDEV_ETERNAL #endif #define dev2unit(d) ((d)->si_drv0) typedef void (*cdevpriv_dtr_t)(void *data); int devfs_get_cdevpriv(void **datap); int devfs_set_cdevpriv(void *priv, cdevpriv_dtr_t dtr); void devfs_clear_cdevpriv(void); void devfs_fpdrop(struct file *fp); /* XXX This is not public KPI */ #define UID_ROOT 0 #define UID_BIN 3 #define UID_UUCP 66 #define UID_NOBODY 65534 #define GID_WHEEL 0 #define GID_KMEM 2 #define GID_TTY 4 #define GID_OPERATOR 5 #define GID_BIN 7 #define GID_GAMES 13 #define GID_DIALER 68 #define GID_NOBODY 65534 typedef void (*dev_clone_fn)(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **result); int dev_stdclone(char *_name, char **_namep, const char *_stem, int *_unit); EVENTHANDLER_DECLARE(dev_clone, dev_clone_fn); /* Stuff relating to kernel-dump */ struct dumperinfo { dumper_t *dumper; /* Dumping function. */ void *priv; /* Private parts. */ u_int blocksize; /* Size of block in bytes. */ u_int maxiosize; /* Max size allowed for an individual I/O */ off_t mediaoffset; /* Initial offset in bytes. */ off_t mediasize; /* Space available in bytes. */ }; int set_dumper(struct dumperinfo *); int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); void dumpsys(struct dumperinfo *); - -extern int dumping; -extern struct pcb dumppcb; -extern lwpid_t dumptid; +extern int dumping; /* system is dumping */ #endif /* _KERNEL */ #endif /* !_SYS_CONF_H_ */