Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3836,6 +3836,7 @@ kern/kern_ubsan.c optional kubsan kern/kern_umtx.c standard kern/kern_uuid.c standard +kern/kern_vnodedumper.c standard kern/kern_xxx.c standard kern/link_elf.c standard kern/linker_if.m standard Index: sys/dev/mem/memdev.c =================================================================== --- sys/dev/mem/memdev.c +++ sys/dev/mem/memdev.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -97,7 +98,7 @@ vm_map_t map; vm_map_entry_t entry; struct mem_extract *me; - int error; + int fd, error; error = 0; switch (cmd) { @@ -120,6 +121,10 @@ } vm_map_unlock_read(map); break; + case MEM_INVOKE_DUMP: + fd = *(int *)data; + error = livedump_start(fd); + break; default: error = memioctl_md(dev, cmd, data, flags, td); break; Index: sys/kern/kern_shutdown.c =================================================================== --- sys/kern/kern_shutdown.c +++ sys/kern/kern_shutdown.c @@ -383,6 +383,17 @@ printf("%lds\n", (long)ts.tv_sec); } +/* + * Set up a context that can be extracted from the dump. + */ +void +dump_savectx(void) +{ + + savectx(&dumppcb); + dumptid = curthread->td_tid; +} + int doadump(boolean_t textdump) { @@ -395,8 +406,7 @@ if (TAILQ_EMPTY(&dumper_configs)) return (ENXIO); - savectx(&dumppcb); - dumptid = curthread->td_tid; + dump_savectx(); dumping++; coredump = TRUE; Index: sys/kern/kern_vnodedumper.c =================================================================== --- /dev/null +++ sys/kern/kern_vnodedumper.c @@ -0,0 +1,200 @@ +/*- + * Copyright (c) 2021 Juniper Networks + * + * This software was developed by Mitchell Horne + * under sponsorship from Juniper Networks and Klara Systems. + * + * 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 ``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 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static dumper_start_t vnode_dumper_start; +static dumper_t vnode_dump; +static dumper_hdr_t vnode_write_headers; + +static struct sx livedump_sx; +SX_SYSINIT(livedump, &livedump_sx, "Livedump sx"); + +struct dumperinfo *livedi = NULL; + +int +livedump_configure(struct diocskerneldump_arg *kda) +{ + struct dumperinfo di; + int error; + + sx_xlock(&livedump_sx); + + /* Free any existing dumper. */ + dumper_destroy(livedi); + livedi = NULL; + + /* Set up new dumper. */ + bzero(&di, sizeof(di)); + di.dumper_start = vnode_dumper_start; + di.dumper = vnode_dump; + di.dumper_hdr = vnode_write_headers; + di.blocksize = PAGE_SIZE; /* Arbitrary. */ + di.maxiosize = MAXDUMPPGS * PAGE_SIZE; + + error = dumper_create(&di, "livedump", kda, &livedi); + sx_xunlock(&livedump_sx); + return (error); +} + +/* + * Invoke a live minidump on the system. + */ +int +livedump_start(int fd) +{ +#if MINIDUMP_PAGE_TRACKING == 1 + struct diocskerneldump_arg kda; + struct vnode *vp; + struct file *fp; + void *rl_cookie; + int error; + + error = getvnode(curthread, fd, &cap_write_rights, &fp); + if (error != 0) + return (error); + vp = fp->f_vnode; + + if ((fp->f_flag & FWRITE) == 0) + return (EBADF); + + /* Set up dumper with default params for the first run. */ + if (livedi == NULL) { + bzero(&kda, sizeof(kda)); + error = livedump_configure(&kda); + if (error != 0) + return (error); + MPASS(livedi != NULL); + } + + /* Only allow one livedump to proceed at a time. */ + sx_xlock(&livedump_sx); + + /* To be used by the callback functions. */ + livedi->priv = vp; + + /* Lock the entire file range and vnode. */ + rl_cookie = vn_rangelock_wlock(vp, 0, OFF_MAX); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + dump_savectx(); + error = minidumpsys(livedi, true); + + VOP_UNLOCK(vp); + vn_rangelock_unlock(vp, rl_cookie); + livedi->priv = NULL; + sx_xunlock(&livedump_sx); + return (error); +#else + return (EOPNOTSUPP); +#endif /* MINIDUMP_PAGE_TRACKING == 1 */ +} + +int +vnode_dumper_start(struct dumperinfo *di, void *key, uint32_t keysize) +{ + + /* Stub implementation, required by dump_start(). */ + return (0); +} + +/* + * Callback from dumpsys() to dump a chunk of memory. + * + * Parameters: + * arg Opaque private pointer to vnode + * virtual Virtual address (where to read the data from) + * physical Physical memory address (unused) + * offset Offset from start of core file + * length Data length + * + * Return value: + * 0 on success + * errno on error + */ +int +vnode_dump(void *arg, void *virtual, vm_offset_t physical __unused, + off_t offset, size_t length) +{ + struct vnode *vp; + int error = 0; + + vp = arg; + MPASS(vp != NULL); + ASSERT_VOP_LOCKED(vp, __func__); + + /* Done? */ + if (virtual == NULL) + return (0); + + error = vn_rdwr(UIO_WRITE, vp, virtual, length, offset, UIO_SYSSPACE, + IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, curthread); + if (error != 0) + uprintf("%s: error writing livedump block at offset %lx: %d\n", + __func__, offset, error); + return (error); +} + +/* + * Callback from dumpsys() to write out the dump header, placed at the end. + */ +int +vnode_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh) +{ + struct vnode *vp; + int error; + + vp = di->priv; + MPASS(vp != NULL); + ASSERT_VOP_LOCKED(vp, __func__); + + /* Write the kernel dump header to the end of the file. */ + error = vn_rdwr(UIO_WRITE, vp, kdh, sizeof(*kdh), di->dumpoff, + UIO_SYSSPACE, IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, + curthread); + if (error != 0) + uprintf("%s: error writing livedump header: %d\n", __func__, + error); + return (error); +} Index: sys/sys/conf.h =================================================================== --- sys/sys/conf.h +++ sys/sys/conf.h @@ -362,6 +362,7 @@ extern int dumping; /* system is dumping */ +void dump_savectx(void); int doadump(boolean_t); struct diocskerneldump_arg; int dumper_create(const struct dumperinfo *di_template, const char *devname, Index: sys/sys/kerneldump.h =================================================================== --- sys/sys/kerneldump.h +++ sys/sys/kerneldump.h @@ -162,6 +162,9 @@ extern int do_minidump; +int livedump_start(int); +int livedump_configure(struct diocskerneldump_arg *); + #endif #endif /* _SYS_KERNELDUMP_H */ Index: sys/sys/memrange.h =================================================================== --- sys/sys/memrange.h +++ sys/sys/memrange.h @@ -58,6 +58,7 @@ }; #define MEM_EXTRACT_PADDR _IOWR('m', 52, struct mem_extract) +#define MEM_INVOKE_DUMP _IOW('m', 53, int) #ifdef _KERNEL