Page MenuHomeFreeBSD

D3057.id.diff
No OneTemporary

D3057.id.diff

Index: sys/kern/kern_dump.c
===================================================================
--- sys/kern/kern_dump.c
+++ sys/kern/kern_dump.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2002 Marcel Moolenaar
+ * Copyright (c) 2015 EMC Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,15 +29,18 @@
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
+#include "opt_gzio.h"
#include "opt_watchdog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/cons.h>
+#include <sys/gzio.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/kerneldump.h>
+#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
@@ -79,12 +83,76 @@
static off_t dumpoff;
static char buffer[DEV_BSIZE];
static size_t fragsz;
+#ifdef GZIO
+static struct gzio_stream *gzs;
+static uint8_t *gzbuffer;
+#endif
static char dumpdevname[sizeof(((struct cdev *)NULL)->si_name)];
SYSCTL_DECL(_kern_shutdown);
SYSCTL_STRING(_kern_shutdown, OID_AUTO, dumpdevname, CTLFLAG_RD, dumpdevname, 0,
"Device for kernel dumps");
+static int compress_kernel_dumps = 0;
+
+#ifdef GZIO
+static int compress_kernel_dumps_gzlevel = 6;
+SYSCTL_INT(_kern, OID_AUTO, compress_kernel_dumps_gzlevel, CTLFLAG_RW,
+ &compress_kernel_dumps_gzlevel, 0,
+ "Kernel crash dump compression level");
+
+static int sysctl_dump_gz_toggle(SYSCTL_HANDLER_ARGS);
+SYSCTL_PROC(_kern, OID_AUTO, compress_kernel_dumps, CTLFLAG_RW | CTLTYPE_INT,
+ &compress_kernel_dumps, 0, sysctl_dump_gz_toggle, "I",
+ "Enable compressed kernel crash dumps");
+
+static int dump_gz_configure(struct dumperinfo *);
+static void dump_gz_disable(void);
+static int dump_gz_write_cb(void *, size_t, off_t, void *);
+
+static int
+sysctl_dump_gz_toggle(SYSCTL_HANDLER_ARGS)
+{
+ int error, value;
+
+ value = *(int *)arg1;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (value == 0) {
+ compress_kernel_dumps = 0;
+ dump_gz_disable();
+ } else if (compress_kernel_dumps == 0) {
+ if (strlen(dumpdevname) > 0)
+ error = dump_gz_configure(&dumper);
+ if (error == 0)
+ compress_kernel_dumps = 1;
+ }
+ return (error);
+}
+#endif /* GZIO */
+
+/*
+ * When writing a kernel dump to disk, we also include dump metadata that is
+ * used by savecore(8) to locate and recover the dump. This metadata is
+ * represented by the struct kerneldumpheader type. When a kernel dump is
+ * complete, two copies of the header are written: one to the last sector of
+ * the dump medium, and one immediately before the beginning of the dump. The
+ * last header is used to locate the first header, and thus, the dump itself.
+ *
+ * When the dump is written without compression, things are simple: we know
+ * exactly how long the dump will be, so the initial offset in the medium can be
+ * chosen such that the end of the dump is flush with the terminating header.
+ * In this case, the "extent" of the dump (the space between the two headers) is
+ * equal to its length. In the compressed case we don't know the dump length
+ * a priori, so we write the dump starting at the same offset as we would in the
+ * uncompressed case. Once the dump is complete, we know its compressed length,
+ * so the dump headers are updated and written to the medium. In this case, the
+ * extent tells savecore(8) where to find the beginning of the dump, and the
+ * length tells it how far into the extent it must read to recover the dump.
+ */
+
int
doadump(boolean_t textdump)
{
@@ -122,10 +190,27 @@
uint64_t length;
length = dtoh64(kdh->dumplength);
- if (di->mediasize < SIZEOF_METADATA + length + sizeof(*kdh) * 2)
- return (E2BIG);
+ if (di->mediasize < SIZEOF_METADATA + length + 2 * sizeof(*kdh)) {
+ if (compress_kernel_dumps)
+ /*
+ * We don't yet know how much space the compressed dump
+ * will occupy, so try to use the whole swap partition
+ * (minus the first 64KB). If that doesn't turn out to
+ * be enough, the bounds checking in dump_write_raw()
+ * will catch us.
+ */
+ length = di->mediasize - SIZEOF_METADATA -
+ 2 * sizeof(*kdh);
+ else
+ return (ENOSPC);
+ }
+ /*
+ * The initial offset at which we're going to write the dump (excluding
+ * the leading kernel dump header).
+ */
dumpoff = di->mediaoffset + di->mediasize - length - sizeof(*kdh);
+ kdh->dumpextent = htod64(length);
return (0);
}
@@ -133,12 +218,27 @@
int
dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh)
{
- uint64_t length;
+ uint64_t extent;
int error;
- dumpoff = 0;
-
- length = dtoh64(kdh->dumplength);
+ extent = dtoh64(kdh->dumpextent);
+
+#ifdef GZIO
+ if (compress_kernel_dumps) {
+ error = gzio_flush(gzs);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Now that we've completed the compressed dump, we know its
+ * size, so update the header accordingly and recompute parity.
+ */
+ kdh->dumplength = htod64(dumpoff -
+ (di->mediaoffset + di->mediasize - extent - sizeof(*kdh)));
+ kdh->parity = 0;
+ kdh->parity = kerneldump_parity(kdh);
+ }
+#endif
/* Write dump headers at the beginning and end of the dump extent. */
error = dump_write_raw(di, kdh, 0,
@@ -146,11 +246,18 @@
if (error != 0)
return (error);
error = dump_write_raw(di, kdh, 0,
- di->mediaoffset + di->mediasize - length - 2 * sizeof(*kdh),
- sizeof(*kdh));
+ di->mediaoffset + di->mediasize - extent - 2 * sizeof(*kdh),
+ sizeof(*kdh));
if (error != 0)
return (error);
+ /* Reset dump state. */
+#ifdef GZIO
+ if (compress_kernel_dumps)
+ gzio_reset(gzs);
+#endif
+ dumpoff = 0;
+
/* Tell the dump media driver that we're done. */
return (dump_write_raw(di, NULL, 0, 0, 0));
}
@@ -162,6 +269,14 @@
{
int error;
+#ifdef GZIO
+ if (compress_kernel_dumps) {
+ /* Bounce through a buffer to avoid gzip CRC errors. */
+ memmove(gzbuffer, virtual, length);
+ return (gzio_write(gzs, gzbuffer, length));
+ }
+#endif
+
error = dump_write_raw(di, virtual, physical, dumpoff, length);
if (error == 0)
dumpoff += length;
@@ -173,6 +288,16 @@
dump_skip(struct dumperinfo *di, size_t gap)
{
+ if (gap > di->maxiosize)
+ return (EINVAL);
+
+#ifdef GZIO
+ if (compress_kernel_dumps) {
+ memset(gzbuffer, 0, di->maxiosize);
+ return (gzio_write(gzs, gzbuffer, gap));
+ }
+#endif
+
dumpoff += gap;
return (0);
}
@@ -194,6 +319,57 @@
return (di->dumper(di->priv, virtual, physical, offset, length));
}
+#ifdef GZIO
+static int
+dump_gz_configure(struct dumperinfo *di)
+{
+
+ MPASS(gzs == NULL);
+ gzs = gzio_init(dump_gz_write_cb, GZIO_DEFLATE, di->maxiosize,
+ compress_kernel_dumps_gzlevel, di);
+ if (gzs == NULL)
+ return (EINVAL);
+ gzbuffer = malloc(di->maxiosize, M_TEMP, M_WAITOK | M_NODUMP);
+ return (0);
+}
+
+static void
+dump_gz_disable(void)
+{
+
+ if (gzs != NULL) {
+ gzio_fini(gzs);
+ gzs = NULL;
+ }
+ free(gzbuffer, M_TEMP);
+ gzbuffer = NULL;
+}
+
+/* Write compressed data to the dump medium. */
+static int
+dump_gz_write_cb(void *base, size_t length, off_t offset __unused, void *arg)
+{
+ struct dumperinfo *di;
+ int error;
+
+ di = (struct dumperinfo *)arg;
+
+ if (dumpoff % di->blocksize != 0)
+ /*
+ * A previous write caused us to end up at an unaligned offset.
+ * Only the final gzio flush should cause that, in which case we
+ * shouldn't be here.
+ */
+ return (EINVAL);
+
+ error = dump_write_raw(di, base, 0, dumpoff,
+ roundup(length, di->blocksize));
+ if (error == 0)
+ dumpoff += length;
+ return (error);
+}
+#endif /* GZIO */
+
/* Register a dumper. */
int
set_dumper(struct dumperinfo *di, const char *devname, struct thread *td)
@@ -208,6 +384,10 @@
if (di == NULL) {
bzero(&dumper, sizeof dumper);
dumpdevname[0] = '\0';
+#ifdef GZIO
+ if (compress_kernel_dumps)
+ dump_gz_disable();
+#endif
return (0);
}
if (dumper.dumper != NULL)
@@ -217,7 +397,11 @@
if (wantcopy >= sizeof(dumpdevname))
printf("set_dumper: device name truncated from '%s' -> '%s'\n",
devname, dumpdevname);
- return (0);
+#ifdef GZIO
+ if (compress_kernel_dumps)
+ error = dump_gz_configure(di);
+#endif
+ return (error);
}
void
@@ -228,9 +412,14 @@
bzero(kdh, sizeof(*kdh));
strlcpy(kdh->magic, magic, sizeof(kdh->magic));
strlcpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture));
+ if (compress_kernel_dumps && strcmp(magic, KERNELDUMPMAGIC) == 0)
+ strlcpy(kdh->magic, GZDUMPMAGIC, sizeof(kdh->magic));
+ else
+ strlcpy(kdh->magic, magic, sizeof(kdh->magic));
kdh->version = htod32(KERNELDUMPVERSION);
kdh->architectureversion = htod32(archver);
kdh->dumplength = htod64(dumplen);
+ kdh->dumpextent = kdh->dumplength;
kdh->dumptime = htod64(time_second);
kdh->blocksize = htod32(blksz);
strlcpy(kdh->hostname, prison0.pr_hostname, sizeof(kdh->hostname));
Index: sys/sys/kerneldump.h
===================================================================
--- sys/sys/kerneldump.h
+++ sys/sys/kerneldump.h
@@ -61,10 +61,11 @@
char magic[20];
#define KERNELDUMPMAGIC "FreeBSD Kernel Dump"
#define TEXTDUMPMAGIC "FreeBSD Text Dump"
+#define GZDUMPMAGIC "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_AARCH64_VERSION 1
#define KERNELDUMP_AMD64_VERSION 2
@@ -75,10 +76,11 @@
#define KERNELDUMP_SPARC64_VERSION 1
#define KERNELDUMP_TEXT_VERSION 1
uint64_t dumplength; /* excl headers */
+ uint64_t dumpextent; /* space between headers */
uint64_t dumptime;
uint32_t blocksize;
char hostname[64];
- char versionstring[192];
+ char versionstring[184];
char panicstring[192];
uint32_t parity;
};
@@ -105,20 +107,20 @@
vm_paddr_t pa_size;
};
-int dumpsys_generic(struct dumperinfo *);
+int dumpsys_generic(struct dumperinfo *);
-void dumpsys_map_chunk(vm_paddr_t, size_t, void **);
+void dumpsys_map_chunk(vm_paddr_t, size_t, void **);
typedef int dumpsys_callback_t(struct dump_pa *, int, void *);
-int dumpsys_foreach_chunk(dumpsys_callback_t, void *);
-int dumpsys_cb_dumpdata(struct dump_pa *, int, void *);
-int dumpsys_buf_write(struct dumperinfo *, char *, size_t);
-int dumpsys_buf_flush(struct dumperinfo *);
+int dumpsys_foreach_chunk(dumpsys_callback_t, void *);
+int dumpsys_cb_dumpdata(struct dump_pa *, int, void *);
+int dumpsys_buf_write(struct dumperinfo *, char *, size_t);
+int dumpsys_buf_flush(struct dumperinfo *);
-void dumpsys_gen_pa_init(void);
+void dumpsys_gen_pa_init(void);
struct dump_pa *dumpsys_gen_pa_next(struct dump_pa *);
-void dumpsys_gen_wbinv_all(void);
-void dumpsys_gen_unmap_chunk(vm_paddr_t, size_t, void *);
-int dumpsys_gen_write_aux_headers(struct dumperinfo *);
+void dumpsys_gen_wbinv_all(void);
+void dumpsys_gen_unmap_chunk(vm_paddr_t, size_t, void *);
+int dumpsys_gen_write_aux_headers(struct dumperinfo *);
int dump_start(struct dumperinfo *, struct kerneldumpheader *);
int dump_finish(struct dumperinfo *, struct kerneldumpheader *);

File Metadata

Mime Type
text/plain
Expires
Wed, May 27, 8:47 PM (8 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33564674
Default Alt Text
D3057.id.diff (10 KB)

Event Timeline