diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c index 7a5fc4c..6f32361 100644 --- a/sbin/savecore/savecore.c +++ b/sbin/savecore/savecore.c @@ -137,7 +137,8 @@ printheader(FILE *f, const struct kerneldumpheader *h, const char *device, } static int -getbounds(void) { +getbounds(void) +{ FILE *fp; char buf[6]; int ret; @@ -168,7 +169,8 @@ getbounds(void) { } static void -writebounds(int bounds) { +writebounds(int bounds) +{ FILE *fp; if ((fp = fopen("bounds", "w")) == NULL) { @@ -288,12 +290,21 @@ check_space(const char *savedir, off_t dumpsize, int bounds) return (1); } +static int +compare_magic(struct kerneldumpheader *kdh, const char *magic) +{ + size_t len; + + len = strlen(magic); + return (strncmp(kdh->magic, magic, MIN(len, sizeof(kdh->magic) - 1))); +} + #define BLOCKSIZE (1<<12) #define BLOCKMASK (~(BLOCKSIZE-1)) static int -DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, - const char *filename, FILE *fp) +DoRegularFile(int fd, off_t dumpsize, u_int sectorsize, int sparse, char *buf, + const char *device, const char *filename, FILE *fp) { int he, hs, nr, nw, wl; off_t dmpcnt, origsize; @@ -301,12 +312,13 @@ DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, dmpcnt = 0; origsize = dumpsize; he = 0; + printf("dumpsize is %ju\n", dumpsize); while (dumpsize > 0) { wl = BUFFERSIZE; if (wl > dumpsize) wl = dumpsize; - nr = read(fd, buf, wl); - if (nr != wl) { + nr = read(fd, buf, roundup(wl, sectorsize)); + if (nr != (int)roundup(wl, sectorsize)) { if (nr == 0) syslog(LOG_WARNING, "WARNING: EOF on dump device"); @@ -315,7 +327,7 @@ DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, nerr++; return (-1); } - if (compress) { + if (!sparse) { nw = fwrite(buf, 1, wl, fp); } else { for (nw = 0; nw < nr; nw = he) { @@ -386,18 +398,18 @@ DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, * work to optimize/sparsify. */ static int -DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, - const char *device, const char *filename, FILE *fp) +DoTextdumpFile(int fd, off_t dumpsize, u_int sectorsize, off_t lasthd, + char *buf, const char *device, const char *filename, FILE *fp) { int nr, nw, wl; off_t dmpcnt, totsize; totsize = dumpsize; dmpcnt = 0; - wl = 512; + wl = sectorsize; if ((dumpsize % wl) != 0) { - syslog(LOG_ERR, "textdump uneven multiple of 512 on %s", - device); + syslog(LOG_ERR, "textdump uneven multiple of %u on %s", + sectorsize, device); nerr++; return (-1); } @@ -437,13 +449,13 @@ DoFile(const char *savedir, const char *device) static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; static char *buf = NULL; struct kerneldumpheader kdhf, kdhl; - off_t mediasize, dumpsize, firsthd, lasthd; + off_t mediasize, dumpextent, dumpsize, firsthd, lasthd; FILE *info, *fp; mode_t oumask; int fd, fdinfo, error; int bounds, status; u_int sectorsize; - int istextdump; + int istextdump, isgzipdump; bounds = getbounds(); mediasize = 0; @@ -492,8 +504,8 @@ DoFile(const char *savedir, const char *device) (long long)lasthd, device); goto closefd; } - istextdump = 0; - if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) { + istextdump = isgzipdump = 0; + if (compare_magic(&kdhl, TEXTDUMPMAGIC) == 0) { if (verbose) printf("textdump magic on last dump header on %s\n", device); @@ -507,8 +519,8 @@ DoFile(const char *savedir, const char *device) if (force == 0) goto closefd; } - } else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) == - 0) { + } else if (compare_magic(&kdhl, KERNELDUMPMAGIC) == 0 || + compare_magic(&kdhl, GZIPDUMPMAGIC) == 0) { if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { syslog(LOG_ERR, "unknown version (%d) in last dump header on %s", @@ -518,6 +530,11 @@ DoFile(const char *savedir, const char *device) if (force == 0) goto closefd; } + if (compare_magic(&kdhl, GZIPDUMPMAGIC) == 0) { + isgzipdump = 1; + if (compress && verbose) + printf("dump is already compressed\n"); + } } else { if (verbose) printf("magic mismatch on last dump header on %s\n", @@ -527,8 +544,7 @@ DoFile(const char *savedir, const char *device) if (force == 0) goto closefd; - if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED, - sizeof kdhl.magic) == 0) { + if (compare_magic(&kdhl, KERNELDUMPMAGIC_CLEARED) == 0) { if (verbose) printf("forcing magic on %s\n", device); memcpy(kdhl.magic, KERNELDUMPMAGIC, @@ -560,8 +576,9 @@ DoFile(const char *savedir, const char *device) if (force == 0) goto closefd; } + dumpextent = dtoh64(kdhl.dumpextent); dumpsize = dtoh64(kdhl.dumplength); - firsthd = lasthd - dumpsize - sizeof kdhf; + firsthd = lasthd - dumpextent - sizeof kdhf; lseek(fd, firsthd, SEEK_SET); error = read(fd, &kdhf, sizeof kdhf); if (error != sizeof kdhf) { @@ -571,6 +588,7 @@ DoFile(const char *savedir, const char *device) nerr++; goto closefd; } + printf("kdhf magic is '%s'\n", kdhf.magic); if (verbose >= 2) { printf("First dump headers:\n"); @@ -627,7 +645,10 @@ DoFile(const char *savedir, const char *device) goto closefd; } oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ - if (compress) { + if (isgzipdump) { + snprintf(corename, sizeof(corename), "vmcore.%d.gz", bounds); + fp = fopen(corename, "w"); + } else if (compress) { snprintf(corename, sizeof(corename), "%s.%d.gz", istextdump ? "textdump.tar" : "vmcore", bounds); fp = zopen(corename, "w"); @@ -662,12 +683,12 @@ DoFile(const char *savedir, const char *device) compress ? "compressed " : "", savedir, corename); if (istextdump) { - if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device, - corename, fp) < 0) + if (DoTextdumpFile(fd, dumpsize, sectorsize, lasthd, buf, + device, corename, fp) < 0) goto closeall; } else { - if (DoRegularFile(fd, dumpsize, buf, device, corename, fp) - < 0) + if (DoRegularFile(fd, dumpsize, sectorsize, + !(compress || isgzipdump), buf, device, corename, fp) < 0) goto closeall; } if (verbose) @@ -684,7 +705,7 @@ DoFile(const char *savedir, const char *device) syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", savedir, "info.last"); } - if (compress) { + if (compress || isgzipdump) { snprintf(linkname, sizeof(linkname), "%s.last.gz", istextdump ? "textdump.tar" : "vmcore"); } else { diff --git a/sys/amd64/amd64/minidump_machdep.c b/sys/amd64/amd64/minidump_machdep.c index 61b348e..53e4a6d 100644 --- a/sys/amd64/amd64/minidump_machdep.c +++ b/sys/amd64/amd64/minidump_machdep.c @@ -51,12 +51,6 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == 512); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) @@ -64,7 +58,6 @@ uint64_t *vm_page_dump; int vm_page_dump_size; static struct kerneldumpheader kdh; -static off_t dumplo; /* Handle chunked writes. */ static size_t fragsz; @@ -98,8 +91,7 @@ blk_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, dump_va, 0, dumplo, fragsz); - dumplo += fragsz; + error = dump_append(di, dump_va, 0, fragsz); fragsz = 0; return (error); } @@ -182,10 +174,9 @@ blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) wdog_kern_pat(WD_LASTVAL); if (ptr) { - error = dump_write(di, ptr, 0, dumplo, len); + error = dump_append(di, ptr, 0, len); if (error) return (error); - dumplo += len; ptr += len; sz -= len; } else { @@ -319,13 +310,6 @@ minidumpsys(struct dumperinfo *di) } dumpsize += PAGE_SIZE; - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = E2BIG; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ @@ -345,10 +329,9 @@ minidumpsys(struct dumperinfo *di) ptoa((uintmax_t)physmem) / 1048576); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump my header */ bzero(&fakepd, sizeof(fakepd)); @@ -432,14 +415,10 @@ minidumpsys(struct dumperinfo *di) if (error) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - dumplo += sizeof(kdh); - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/arm/arm/dump_machdep.c b/sys/arm/arm/dump_machdep.c index 19c19c90..6d2e8d6d 100644 --- a/sys/arm/arm/dump_machdep.c +++ b/sys/arm/arm/dump_machdep.c @@ -53,12 +53,6 @@ int do_minidump = 1; SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, "Enable mini crash dumps"); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) extern struct pcb dumppcb; @@ -71,7 +65,7 @@ struct md_pa { typedef int callback_t(struct md_pa *, int, void *); static struct kerneldumpheader kdh; -static off_t dumplo, fileofs; +static off_t fileofs; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; @@ -127,11 +121,9 @@ buf_write(struct dumperinfo *di, char *ptr, size_t sz) ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { - error = dump_write(di, buffer, 0, dumplo, - DEV_BSIZE); + error = dump_append(di, buffer, 0, DEV_BSIZE); if (error) return error; - dumplo += DEV_BSIZE; fragsz = 0; } } @@ -147,8 +139,7 @@ buf_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_append(di, buffer, 0, DEV_BSIZE); fragsz = 0; return (error); } @@ -202,11 +193,10 @@ cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif - error = dump_write(di, - (void *)(pa - (pa & L1_ADDR_BITS)),0, dumplo, sz); + error = dump_append(di, (void *)(pa - (pa & L1_ADDR_BITS)), + 0, sz); if (error) break; - dumplo += sz; pgs -= chunk; pa += sz; @@ -337,24 +327,15 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, di->blocksize); printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, ehdr.e_phnum - 1); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump ELF header */ error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); @@ -373,23 +354,21 @@ dumpsys(struct dumperinfo *di) * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignement * in the file to have the segment contents aligned at page - * boundary. We cannot use MD_ALIGN on dumplo, because we don't - * care and may very well be unaligned within the dump device. + * boundary. */ - dumplo += hdrgap; + error = dump_skip(di, hdrgap); + if (error != 0) + goto fail; - /* Dump memory chunks (updates dumplo) */ + /* Dump memory chunks. */ error = foreach_chunk(cb_dumpdata, di); if (error < 0) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/arm/arm/minidump_machdep.c b/sys/arm/arm/minidump_machdep.c index c12aefa..958d572 100644 --- a/sys/arm/arm/minidump_machdep.c +++ b/sys/arm/arm/minidump_machdep.c @@ -52,17 +52,10 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == 512); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - uint32_t *vm_page_dump; int vm_page_dump_size; static struct kerneldumpheader kdh; -static off_t dumplo; /* Handle chunked writes. */ static size_t fragsz, offset; @@ -93,8 +86,7 @@ blk_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, (char*)dump_va + offset, 0, dumplo, fragsz - offset); - dumplo += (fragsz - offset); + error = dump_append(di, (char *)dump_va + offset, 0, fragsz - offset); fragsz = 0; offset = 0; return (error); @@ -146,10 +138,9 @@ blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) wdog_kern_pat(WD_LASTVAL); #endif if (ptr) { - error = dump_write(di, ptr, 0, dumplo, len); + error = dump_append(di, ptr, 0, len); if (error) return (error); - dumplo += len; ptr += len; sz -= len; } else { @@ -291,14 +282,6 @@ minidumpsys(struct dumperinfo *di) dumpsize += PAGE_SIZE; - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ @@ -317,10 +300,9 @@ minidumpsys(struct dumperinfo *di) printf("Dumping %llu MB:", (long long)dumpsize >> 20); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump my header */ bzero(&fakept, sizeof(fakept)); @@ -451,14 +433,10 @@ minidumpsys(struct dumperinfo *di) prev_pa = 0; } - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - dumplo += sizeof(kdh); - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/conf/files b/sys/conf/files index f7a4310b..8d671b1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2938,6 +2938,7 @@ kern/kern_cpuset.c standard kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_dtrace.c optional kdtrace_hooks +kern/kern_dump.c standard kern/kern_environment.c standard kern/kern_et.c standard kern/kern_event.c standard diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c index 7bed8ae..aed2f79 100644 --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include diff --git a/sys/dev/null/null.c b/sys/dev/null/null.c index f836147..77b1c73 100644 --- a/sys/dev/null/null.c +++ b/sys/dev/null/null.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include diff --git a/sys/geom/geom.h b/sys/geom/geom.h index f313d02..48fb95f 100644 --- a/sys/geom/geom.h +++ b/sys/geom/geom.h @@ -45,6 +45,7 @@ #include #include #include +#include struct g_class; struct g_geom; diff --git a/sys/i386/i386/minidump_machdep.c b/sys/i386/i386/minidump_machdep.c index dd3490b..744ef84 100644 --- a/sys/i386/i386/minidump_machdep.c +++ b/sys/i386/i386/minidump_machdep.c @@ -47,12 +47,6 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == 512); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) @@ -60,7 +54,6 @@ uint32_t *vm_page_dump; int vm_page_dump_size; static struct kerneldumpheader kdh; -static off_t dumplo; /* Handle chunked writes. */ static size_t fragsz; @@ -96,8 +89,7 @@ blk_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, dump_va, 0, dumplo, fragsz); - dumplo += fragsz; + error = dump_append(di, dump_va, 0, fragsz); fragsz = 0; return (error); } @@ -145,10 +137,9 @@ blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) wdog_kern_pat(WD_LASTVAL); if (ptr) { - error = dump_write(di, ptr, 0, dumplo, len); + error = dump_append(di, ptr, 0, len); if (error) return (error); - dumplo += len; ptr += len; sz -= len; } else { @@ -247,14 +238,6 @@ minidumpsys(struct dumperinfo *di) } } dumpsize += PAGE_SIZE; - - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ @@ -274,11 +257,9 @@ minidumpsys(struct dumperinfo *di) printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576); printf("Dumping %llu MB:", (long long)dumpsize >> 20); - /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump my header */ bzero(&fakept, sizeof(fakept)); @@ -368,14 +349,10 @@ minidumpsys(struct dumperinfo *di) if (error) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - dumplo += sizeof(kdh); - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/kern/kern_dump.c b/sys/kern/kern_dump.c new file mode 100644 index 0000000..0912406 --- /dev/null +++ b/sys/kern/kern_dump.c @@ -0,0 +1,289 @@ +/*- + * Copyright (c) 2014 Mark Johnston + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_gzio.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Don't touch the first SIZEOF_METADATA bytes on the dump device. This + * is to protect us from metadata and to protect metadata from us. + */ +#define SIZEOF_METADATA (64 * 1024) + +static int compress_kernel_dumps = 0; +#ifdef GZIO +SYSCTL_INT(_kern, OID_AUTO, compress_kernel_dumps, CTLFLAG_RW | CTLFLAG_TUN, + &compress_kernel_dumps, 0, "Write compressed kernel crash dumps"); + +static int compress_kernel_dumps_gzlevel = 6; +SYSCTL_INT(_kern, OID_AUTO, compress_kernel_dumps_gzlevel, + CTLFLAG_RD | CTLFLAG_TUN, &compress_kernel_dumps, 0, + "Kernel core dump compression level"); + +static int dump_gz_write_cb(void *, size_t, off_t, void *); +#endif + +struct dump_stream { + void *buffer; + off_t offset; + struct gz_stream *gzs; +}; + +int +dump_init(struct dumperinfo *di, struct kerneldumpheader *kdh) +{ + struct dump_stream *s = (struct dump_stream *)di->dipriv; + uint64_t extent; + + extent = dtoh64(kdh->dumpextent); + if (di->mediasize < SIZEOF_METADATA + extent + sizeof(*kdh) * 2) { + if (compress_kernel_dumps) { + /* + * Try to use the whole swap partition (minus the first + * 64KB). If that's not enough, the bounds checking in + * dump_write() will catch us. + */ + extent = + di->mediasize - SIZEOF_METADATA - sizeof(*kdh) * 2; + kdh->dumpextent = htod64(extent); + } else + return (E2BIG); + } + + /* + * The initial offset at which we're going to write the dump itself + * (i.e. excluding the first header). + */ + s->offset = di->mediaoffset + di->mediasize - extent - sizeof(*kdh); + return (0); +} + +int +dump_end(struct dumperinfo *di, struct kerneldumpheader *kdh) +{ + struct dump_stream *s = di->dipriv; + uint64_t extent; + int error; + +#ifdef GZIO + if (compress_kernel_dumps) { + error = kern_gz_flush(s->gzs); + if (error != 0) + goto fail; + /* + * Now that we know the size of the compressed dump, update the + * dump header accordingly. + */ + extent = dtoh64(kdh->dumpextent); + kdh->dumplength = htod64(s->offset - + (di->mediaoffset + di->mediasize - sizeof(*kdh) - extent)); + } +#endif + kdh->parity = kerneldump_parity(kdh); + + /* + * Write out kernel dump headers at the beginning and end of the dump + * extent. + */ + error = dump_write(di, kdh, 0, + di->mediaoffset + di->mediasize - sizeof(*kdh), sizeof(*kdh)); + if (error != 0) + goto fail; + error = dump_write(di, kdh, 0, di->mediaoffset + di->mediasize - + dtoh64(kdh->dumpextent) - 2 * sizeof(*kdh), sizeof(*kdh)); + if (error != 0) + goto fail; + + /* Tell the dump media driver that we're done. */ + error = dump_write(di, NULL, 0, 0, 0); +fail: + return (error); +} + +int +dump_append(struct dumperinfo *di, void *virtual, vm_offset_t physical, + size_t length) +{ + struct dump_stream *s = (struct dump_stream *)di->dipriv; + int error; + +#ifdef GZIO + if (compress_kernel_dumps) { + KASSERT(length <= di->maxiosize, + ("invalid write size %zu", length)); + memcpy(s->buffer, virtual, length); + return (kern_gz_write(s->gzs, s->buffer, length)); + } +#endif + + error = dump_write(di, virtual, physical, s->offset, length); + s->offset += length; + return (error); +} + +int +dump_skip(struct dumperinfo *di, size_t gap) +{ + struct dump_stream *s = (struct dump_stream *)di->dipriv; + size_t bytes; + +#ifdef GZIO + if (compress_kernel_dumps) { + KASSERT(gap <= di->maxiosize, ("invalid write size %zu", gap)); + bytes = MIN(di->maxiosize, gap); + memset(s->buffer, 0, bytes); + return (kern_gz_write(s->gzs, s->buffer, bytes)); + } +#endif + + s->offset += gap; + return (0); +} + +#ifdef GZIO +static int +dump_gz_write_cb(void *base, size_t length, off_t offset __unused, void *arg) +{ + struct dumperinfo *di = (struct dumperinfo *)arg; + struct dump_stream *s = (struct dump_stream *)di->dipriv; + int error; + + KASSERT(length <= di->maxiosize, ("invalid write size %zu", length)); + KASSERT(s->offset % di->blocksize == 0, + ("writing to unaligned offset %ju", s->offset)); + if (length % di->blocksize != 0) { + memcpy(s->buffer, base, length); + base = s->buffer; + } + error = dump_write(di, base, 0, s->offset, + roundup(length, di->blocksize)); + if (error == 0) + s->offset += length; + return (error); +} +#endif + +/* Call dumper with bounds checking. */ +int +dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, + off_t offset, size_t length) +{ + + if (length != 0 && (offset < di->mediaoffset || + offset - di->mediaoffset + length > di->mediasize)) { + printf("Attempt to write outside dump device boundaries.\n" + "offset(%jd), mediaoffset(%jd), length(%ju), mediasize(%jd).\n", + (intmax_t)offset, (intmax_t)di->mediaoffset, + (uintmax_t)length, (intmax_t)di->mediasize); + return (ENOSPC); + } + return (di->dumper(di->priv, virtual, physical, offset, length)); +} + +char dumpdevname[sizeof(((struct cdev *)NULL)->si_name)]; + +/* Registration of dumpers */ +int +set_dumper(struct dumperinfo *di, const char *devname) +{ + struct dump_stream *s; + size_t wantcopy; + + if (di == NULL) { + if (dumper.dipriv != NULL) { + s = dumper.dipriv; +#ifdef GZIO + if (compress_kernel_dumps) { + kern_gz_fini(s->gzs); + free(s->buffer, M_TEMP); + } +#endif + } + bzero(&dumper, sizeof dumper); + dumpdevname[0] = '\0'; + return (0); + } + if (dumper.dumper != NULL) + return (EBUSY); + dumper = *di; + + s = malloc(sizeof(*s), M_TEMP, M_WAITOK | M_ZERO); +#ifdef GZIO + if (compress_kernel_dumps) { + s->gzs = kern_gz_init(dump_gz_write_cb, di->maxiosize, + compress_kernel_dumps_gzlevel, &dumper); + if (s->gzs == NULL) { + printf("set_dumper: failed to init dump compression\n"); + return (EINVAL); + } + s->buffer = malloc(di->maxiosize, M_TEMP, M_WAITOK); + } +#endif + dumper.dipriv = s; + + wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname)); + if (wantcopy >= sizeof(dumpdevname)) { + printf("set_dumper: device name truncated from '%s' -> '%s'\n", + devname, dumpdevname); + } + return (0); +} + +void +mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, + uint64_t dumplen, uint32_t blksz) +{ + + bzero(kdh, sizeof(*kdh)); + if (compress_kernel_dumps && strcmp(magic, KERNELDUMPMAGIC) == 0) + magic = GZIPDUMPMAGIC; + strncpy(kdh->magic, magic, sizeof(kdh->magic)); + strncpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture)); + kdh->version = htod32(KERNELDUMPVERSION); + kdh->architectureversion = htod32(archver); + kdh->dumpextent = htod64(dumplen); + if (compress_kernel_dumps) + kdh->dumplength = 0; /* We don't yet know the length. */ + else + kdh->dumplength = kdh->dumpextent; + 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)); +} diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 8135afb..7f367a9 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -334,6 +334,7 @@ fork_norfproc(struct thread *td, int flags) if (flags & RFCFDG) { struct filedesc *fdtmp; fdtmp = fdinit(td->td_proc->p_fd); + FILEDESC_SUNLOCK(td->td_proc->p_fd); fdescfree(td); p1->p_fd = fdtmp; } @@ -419,6 +420,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, */ if (flags & RFCFDG) { fd = fdinit(p1->p_fd); + FILEDESC_SUNLOCK(p1->p_fd); fdtol = NULL; } else if (flags & RFFDG) { fd = fdcopy(p1->p_fd); diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c index dfdca15..023d494 100644 --- a/sys/kern/kern_shutdown.c +++ b/sys/kern/kern_shutdown.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" +#include "opt_gzio.h" #include "opt_kdb.h" #include "opt_panic.h" #include "opt_sched.h" @@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -135,6 +137,9 @@ static int show_busybufs = 1; SYSCTL_INT(_kern_shutdown, OID_AUTO, show_busybufs, CTLFLAG_RW, &show_busybufs, 0, ""); +SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD, + dumpdevname, 0, "Device for kernel dumps"); + /* * Variable panicstr contains argument to first call to panic; used as flag * to indicate that the kernel has already called panic. @@ -143,7 +148,8 @@ const char *panicstr; int dumping; /* system is dumping */ int rebooting; /* system is rebooting */ -static struct dumperinfo dumper; /* our selected dumper */ + +struct dumperinfo dumper; /* our selected dumper */ /* Context information for dump-debuggers. */ static struct pcb dumppcb; /* Registers. */ @@ -245,36 +251,6 @@ print_uptime(void) printf("%lds\n", (long)ts.tv_sec); } -int -doadump(boolean_t textdump) -{ - boolean_t coredump; - int error; - - error = 0; - if (dumping) - return (EBUSY); - if (dumper.dumper == NULL) - return (ENXIO); - - savectx(&dumppcb); - dumptid = curthread->td_tid; - dumping++; - - coredump = TRUE; -#ifdef DDB - if (textdump && textdump_pending) { - coredump = FALSE; - textdump_dumpsys(&dumper); - } -#endif - if (coredump) - error = dumpsys(&dumper); - - dumping--; - return (error); -} - static int isbufbusy(struct buf *bp) { @@ -821,65 +797,32 @@ kthread_shutdown(void *arg, int howto) printf("done\n"); } -static char dumpdevname[sizeof(((struct cdev*)NULL)->si_name)]; -SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD, - dumpdevname, 0, "Device for kernel dumps"); - -/* Registration of dumpers */ int -set_dumper(struct dumperinfo *di, const char *devname) +doadump(boolean_t textdump) { - size_t wantcopy; + boolean_t coredump; + int error; - if (di == NULL) { - bzero(&dumper, sizeof dumper); - dumpdevname[0] = '\0'; - return (0); - } - if (dumper.dumper != NULL) + error = 0; + if (dumping) return (EBUSY); - dumper = *di; - wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname)); - if (wantcopy >= sizeof(dumpdevname)) { - printf("set_dumper: device name truncated from '%s' -> '%s'\n", - devname, dumpdevname); - } - return (0); -} + if (dumper.dumper == NULL) + return (ENXIO); -/* Call dumper with bounds checking. */ -int -dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, - off_t offset, size_t length) -{ + savectx(&dumppcb); + dumptid = curthread->td_tid; + dumping++; - if (length != 0 && (offset < di->mediaoffset || - offset - di->mediaoffset + length > di->mediasize)) { - printf("Attempt to write outside dump device boundaries.\n" - "offset(%jd), mediaoffset(%jd), length(%ju), mediasize(%jd).\n", - (intmax_t)offset, (intmax_t)di->mediaoffset, - (uintmax_t)length, (intmax_t)di->mediasize); - return (ENOSPC); + coredump = TRUE; +#ifdef DDB + if (textdump && textdump_pending) { + coredump = FALSE; + textdump_dumpsys(&dumper); } - 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) -{ +#endif + if (coredump) + error = dumpsys(&dumper); - 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); + dumping--; + return (error); } diff --git a/sys/mips/mips/dump_machdep.c b/sys/mips/mips/dump_machdep.c index fa96e79..318493e 100644 --- a/sys/mips/mips/dump_machdep.c +++ b/sys/mips/mips/dump_machdep.c @@ -53,12 +53,6 @@ int do_minidump = 1; SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, "Enable mini crash dumps"); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) extern struct pcb dumppcb; @@ -71,7 +65,7 @@ struct md_pa { typedef int callback_t(struct md_pa *, int, void *); static struct kerneldumpheader kdh; -static off_t dumplo, fileofs; +static off_t fileofs; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; @@ -127,11 +121,9 @@ buf_write(struct dumperinfo *di, char *ptr, size_t sz) ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { - error = dump_write(di, buffer, 0, dumplo, - DEV_BSIZE); + error = dump_append(di, buffer, 0, DEV_BSIZE); if (error) return error; - dumplo += DEV_BSIZE; fragsz = 0; } } @@ -147,8 +139,7 @@ buf_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_append(di, buffer, 0, DEV_BSIZE); fragsz = 0; return (error); } @@ -189,10 +180,9 @@ cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif - error = dump_write(di, (void *)(intptr_t)(pa),0, dumplo, sz); /* XXX fix PA */ + error = dump_append(di, (void *)(intptr_t)(pa), 0, sz); /* XXX fix PA */ if (error) break; - dumplo += sz; pgs -= chunk; pa += sz; @@ -300,24 +290,14 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize, di->blocksize); printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, ehdr.e_phnum); - /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump ELF header */ error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); @@ -334,23 +314,21 @@ dumpsys(struct dumperinfo *di) * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignement * in the file to have the segment contents aligned at page - * boundary. We cannot use MD_ALIGN on dumplo, because we don't - * care and may very well be unaligned within the dump device. + * boundary. */ - dumplo += hdrgap; + error = dump_skip(di, hdrgap); + if (error != 0) + goto fail; - /* Dump memory chunks (updates dumplo) */ + /* Dump memory chunks. */ error = foreach_chunk(cb_dumpdata, di); if (error < 0) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/mips/mips/minidump_machdep.c b/sys/mips/mips/minidump_machdep.c index 2122e00..6a6c434 100644 --- a/sys/mips/mips/minidump_machdep.c +++ b/sys/mips/mips/minidump_machdep.c @@ -49,18 +49,10 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == 512); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - uint32_t *vm_page_dump; int vm_page_dump_size; static struct kerneldumpheader kdh; -static off_t dumplo; -static off_t origdumplo; /* Handle chunked writes. */ static uint64_t counter, progress; @@ -132,10 +124,9 @@ write_buffer(struct dumperinfo *di, char *ptr, size_t sz) } if (ptr) { - error = dump_write(di, ptr, 0, dumplo, len); + error = dump_append(di, ptr, 0, len); if (error) return (error); - dumplo += len; ptr += len; sz -= len; } else { @@ -218,15 +209,6 @@ minidumpsys(struct dumperinfo *di) } dumpsize += PAGE_SIZE; - - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - - origdumplo = dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ @@ -246,10 +228,9 @@ minidumpsys(struct dumperinfo *di) printf("Dumping %llu MB:", (long long)dumpsize >> 20); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump my header */ bzero(tmpbuffer, sizeof(tmpbuffer)); @@ -316,14 +297,10 @@ minidumpsys(struct dumperinfo *di) } } - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - dumplo += sizeof(kdh); - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/powerpc/powerpc/dump_machdep.c b/sys/powerpc/powerpc/dump_machdep.c index 14e2f0f..1d944f9 100644 --- a/sys/powerpc/powerpc/dump_machdep.c +++ b/sys/powerpc/powerpc/dump_machdep.c @@ -46,19 +46,13 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == 512); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) typedef int callback_t(struct pmap_md *, int, void *); static struct kerneldumpheader kdh; -static off_t dumplo, fileofs; +static off_t fileofs; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; @@ -83,11 +77,9 @@ buf_write(struct dumperinfo *di, char *ptr, size_t sz) ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { - error = di->dumper(di->priv, buffer, 0, dumplo, - DEV_BSIZE); + error = dump_append(di->priv, buffer, 0, DEV_BSIZE); if (error) return error; - dumplo += DEV_BSIZE; fragsz = 0; } } @@ -103,8 +95,7 @@ buf_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = di->dumper(di->priv, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_append(di->priv, buffer, 0, DEV_BSIZE); fragsz = 0; return (error); } @@ -138,11 +129,10 @@ cb_dumpdata(struct pmap_md *md, int seqnr, void *arg) #ifdef SW_WATCHDOG wdog_kern_pat(WD_LASTVAL); #endif - error = di->dumper(di->priv, (void*)va, 0, dumplo, sz); + error = dump_append(di->priv, (void*)va, 0, sz); pmap_dumpsys_unmap(md, ofs, va); if (error) break; - dumplo += sz; resid -= sz; ofs += sz; @@ -242,18 +232,6 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* For block devices, determine the dump offset on the device. */ - if (di->mediasize > 0) { - if (di->mediasize < - SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; - } else - dumplo = 0; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_POWERPC_VERSION, dumpsize, di->blocksize); @@ -261,10 +239,9 @@ dumpsys(struct dumperinfo *di) ehdr.e_phnum); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump ELF header */ error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); @@ -281,23 +258,21 @@ dumpsys(struct dumperinfo *di) * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignement * in the file to have the segment contents aligned at page - * boundary. We cannot use MD_ALIGN on dumplo, because we don't - * care and may very well be unaligned within the dump device. + * boundary. */ - dumplo += hdrgap; + error = dump_skip(di, hdrgap); + if (error != 0) + goto fail; - /* Dump memory chunks (updates dumplo) */ + /* Dump memory chunks. */ error = foreach_chunk(cb_dumpdata, di); if (error < 0) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/sparc64/sparc64/dump_machdep.c b/sys/sparc64/sparc64/dump_machdep.c index 5af21cc..c57eb56 100644 --- a/sys/sparc64/sparc64/dump_machdep.c +++ b/sys/sparc64/sparc64/dump_machdep.c @@ -48,7 +48,7 @@ __FBSDID("$FreeBSD$"); CTASSERT(sizeof(struct kerneldumpheader) == DEV_BSIZE); static struct kerneldumpheader kdh; -static off_t dumplo, dumppos; +static off_t dumppos; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; @@ -71,11 +71,9 @@ buf_write(struct dumperinfo *di, char *ptr, size_t sz) ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { - error = dump_write(di, buffer, 0, dumplo, - DEV_BSIZE); + error = dump_append(di, buffer, 0, DEV_BSIZE); if (error) return error; - dumplo += DEV_BSIZE; fragsz = 0; } } @@ -91,8 +89,7 @@ buf_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_append(di, buffer, 0, DEV_BSIZE); fragsz = 0; return (error); } @@ -126,10 +123,9 @@ blk_dump(struct dumperinfo *di, vm_paddr_t pa, vm_size_t size) rsz = size - pos; rsz = (rsz > MAXDUMPSZ) ? MAXDUMPSZ : rsz; va = TLB_PHYS_TO_DIRECT(pa + pos); - error = dump_write(di, (void *)va, 0, dumplo, rsz); + error = dump_append(di, (void *)va, 0, rsz); if (error) break; - dumplo += rsz; /* Check for user abort. */ c = cncheckc(); @@ -168,19 +164,14 @@ dumpsys(struct dumperinfo *di) goto fail; } - /* Determine dump offset on device. */ - dumplo = di->mediaoffset + di->mediasize - totsize; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, size, di->blocksize); printf("Dumping %lu MB (%d chunks)\n", (u_long)(size >> 20), nreg); - /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump the private header. */ hdr.dh_hdr_size = hdrsize; @@ -210,13 +201,10 @@ dumpsys(struct dumperinfo *di) goto fail; } - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0); diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 8c50581..11e9da7 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -330,14 +330,13 @@ EVENTHANDLER_DECLARE(dev_clone, dev_clone_fn); struct dumperinfo { dumper_t *dumper; /* Dumping function. */ void *priv; /* Private parts. */ + void *dipriv; /* Device-independent private data. */ 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 *, const char *_devname); -int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); int dumpsys(struct dumperinfo *); int doadump(boolean_t); extern int dumping; /* system is dumping */ diff --git a/sys/sys/kerneldump.h b/sys/sys/kerneldump.h index a148736..3cae045 100644 --- a/sys/sys/kerneldump.h +++ b/sys/sys/kerneldump.h @@ -61,10 +61,11 @@ struct kerneldumpheader { char magic[20]; #define KERNELDUMPMAGIC "FreeBSD Kernel Dump" #define TEXTDUMPMAGIC "FreeBSD Text Dump" +#define GZIPDUMPMAGIC "FreeBSD GZIP Dump" #define KERNELDUMPMAGIC_CLEARED "Cleared Kernel Dump" char architecture[12]; uint32_t version; -#define KERNELDUMPVERSION 1 +#define KERNELDUMPVERSION 2 uint32_t architectureversion; #define KERNELDUMP_ALPHA_VERSION 1 #define KERNELDUMP_AMD64_VERSION 2 @@ -75,10 +76,11 @@ struct kerneldumpheader { #define KERNELDUMP_SPARC64_VERSION 1 #define KERNELDUMP_TEXT_VERSION 1 uint64_t dumplength; /* excl headers */ + uint64_t dumpextent; uint64_t dumptime; uint32_t blocksize; char hostname[64]; - char versionstring[192]; + char versionstring[184]; char panicstring[192]; uint32_t parity; }; @@ -100,8 +102,20 @@ kerneldump_parity(struct kerneldumpheader *kdhp) } #ifdef _KERNEL -void mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, - uint64_t dumplen, uint32_t blksz); +extern struct dumperinfo dumper; +extern char dumpdevname[]; + +__BEGIN_DECLS +int dump_init(struct dumperinfo *, struct kerneldumpheader *); +int dump_end(struct dumperinfo *, struct kerneldumpheader *); +int dump_append(struct dumperinfo *, void *, vm_offset_t, size_t); +int dump_skip(struct dumperinfo *, size_t); +int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); + +int set_dumper(struct dumperinfo *, const char *_devname); +void mkdumpheader(struct kerneldumpheader *kdh, char *magic, + uint32_t archver, uint64_t dumplen, uint32_t blksz); +__END_DECLS #endif #endif /* _SYS_KERNELDUMP_H */ diff --git a/sys/x86/x86/dump_machdep.c b/sys/x86/x86/dump_machdep.c index 4e048bf..a35629a 100644 --- a/sys/x86/x86/dump_machdep.c +++ b/sys/x86/x86/dump_machdep.c @@ -56,12 +56,6 @@ int do_minidump = 1; SYSCTL_INT(_debug, OID_AUTO, minidump, CTLFLAG_RWTUN, &do_minidump, 0, "Enable mini crash dumps"); -/* - * Don't touch the first SIZEOF_METADATA bytes on the dump device. This - * is to protect us from metadata and to protect metadata from us. - */ -#define SIZEOF_METADATA (64*1024) - #define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) #define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) @@ -73,7 +67,7 @@ struct md_pa { typedef int callback_t(struct md_pa *, int, void *); static struct kerneldumpheader kdh; -static off_t dumplo, fileofs; +static off_t fileofs; /* Handle buffered writes. */ static char buffer[DEV_BSIZE]; @@ -129,11 +123,9 @@ buf_write(struct dumperinfo *di, char *ptr, size_t sz) ptr += len; sz -= len; if (fragsz == DEV_BSIZE) { - error = dump_write(di, buffer, 0, dumplo, - DEV_BSIZE); + error = dump_append(di, buffer, 0, DEV_BSIZE); if (error) return error; - dumplo += DEV_BSIZE; fragsz = 0; } } @@ -149,8 +141,7 @@ buf_flush(struct dumperinfo *di) if (fragsz == 0) return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_append(di, buffer, 0, DEV_BSIZE); fragsz = 0; return (error); } @@ -198,10 +189,9 @@ cb_dumpdata(struct md_pa *mdp, int seqnr, void *arg) wdog_kern_pat(WD_LASTVAL); - error = dump_write(di, va, 0, dumplo, sz); + error = dump_append(di, va, 0, sz); if (error) break; - dumplo += sz; pgs -= chunk; pa += sz; @@ -309,25 +299,15 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; - } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_VERSION, dumpsize, di->blocksize); printf("Dumping %llu MB (%d chunks)\n", (long long)dumpsize >> 20, ehdr.e_phnum); - /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_init(di, &kdh); if (error) goto fail; - dumplo += sizeof(kdh); /* Dump ELF header */ error = buf_write(di, (char*)&ehdr, sizeof(ehdr)); @@ -344,23 +324,21 @@ dumpsys(struct dumperinfo *di) * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignement * in the file to have the segment contents aligned at page - * boundary. We cannot use MD_ALIGN on dumplo, because we don't - * care and may very well be unaligned within the dump device. + * boundary. */ - dumplo += hdrgap; + error = dump_skip(di, hdrgap); + if (error != 0) + goto fail; - /* Dump memory chunks (updates dumplo) */ + /* Dump memory chunks. */ error = foreach_chunk(cb_dumpdata, di); if (error < 0) goto fail; - /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); - if (error) - goto fail; - /* Signal completion, signoff and exit stage left. */ - dump_write(di, NULL, 0, 0, 0); + error = dump_end(di, &kdh); + if (error != 0) + goto fail; printf("\nDump complete\n"); return (0);