Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157961149
D3057.id6867.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D3057.id6867.diff
View Options
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 + sizeof(*kdh) * 2) {
+ 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 (ENXIO);
+
+#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,48 @@
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;
+ 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 +375,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 +388,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 +403,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
Details
Attached
Mime Type
text/plain
Expires
Wed, May 27, 11:00 PM (11 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33567927
Default Alt Text
D3057.id6867.diff (10 KB)
Attached To
Mode
D3057: kern_dump.c add kernel support for compressed kernel dumps
Attached
Detach File
Event Timeline
Log In to Comment