Refactor some of the MI kernel dump code in preparation for netdump.
- Add clear_dumper() to complement set_dumper(). - Drain netdump's preallocated mbuf pool when clearing the dumper. - Don't do bounds checking for dumpers with mediasize 0. - Add dumper callbacks for initialization for writing out headers. Reviewed by: sbruno MFC after: 1 month Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D15252
This commit is contained in:
parent
5475ca5aca
commit
bd92e6b6f5
@ -107,14 +107,14 @@ null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
|
||||
int flags __unused, struct thread *td)
|
||||
{
|
||||
int error;
|
||||
error = 0;
|
||||
|
||||
error = 0;
|
||||
switch (cmd) {
|
||||
#ifdef COMPAT_FREEBSD11
|
||||
case DIOCSKERNELDUMP_FREEBSD11:
|
||||
#endif
|
||||
case DIOCSKERNELDUMP:
|
||||
error = set_dumper(NULL, NULL, td, 0, 0, NULL, 0, NULL);
|
||||
error = clear_dumper(td);
|
||||
break;
|
||||
case FIONBIO:
|
||||
break;
|
||||
|
@ -138,10 +138,11 @@ g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda,
|
||||
int error, len;
|
||||
|
||||
if (dev == NULL || kda == NULL)
|
||||
return (set_dumper(NULL, NULL, td, 0, 0, NULL, 0, NULL));
|
||||
return (clear_dumper(td));
|
||||
|
||||
cp = dev->si_drv2;
|
||||
len = sizeof(kd);
|
||||
memset(&kd, 0, len);
|
||||
kd.offset = 0;
|
||||
kd.length = OFF_MAX;
|
||||
error = g_io_getattr("GEOM::kerneldump", cp, &len, &kd);
|
||||
@ -833,7 +834,7 @@ g_dev_orphan(struct g_consumer *cp)
|
||||
|
||||
/* Reset any dump-area set on this device */
|
||||
if (dev->si_flags & SI_DUMPDEV)
|
||||
(void)set_dumper(NULL, NULL, curthread, 0, 0, NULL, 0, NULL);
|
||||
(void)clear_dumper(curthread);
|
||||
|
||||
/* Destroy the struct cdev *so we get no more requests */
|
||||
destroy_dev_sched_cb(dev, g_dev_callback, cp);
|
||||
|
@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/proc.h>
|
||||
@ -1090,10 +1091,6 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
if (di == NULL) {
|
||||
error = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (dumper.dumper != NULL)
|
||||
return (EBUSY);
|
||||
dumper = *di;
|
||||
@ -1139,7 +1136,25 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
|
||||
|
||||
dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
|
||||
return (0);
|
||||
|
||||
cleanup:
|
||||
(void)clear_dumper(td);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
clear_dumper(struct thread *td)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = priv_check(td, PRIV_SETDUMPER);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
#ifdef NETDUMP
|
||||
netdump_mbuf_drain();
|
||||
#endif
|
||||
|
||||
#ifdef EKCD
|
||||
if (dumper.kdcrypto != NULL) {
|
||||
explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) +
|
||||
@ -1156,14 +1171,14 @@ cleanup:
|
||||
}
|
||||
explicit_bzero(&dumper, sizeof(dumper));
|
||||
dumpdevname[0] = '\0';
|
||||
return (error);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
dump_check_bounds(struct dumperinfo *di, off_t offset, size_t length)
|
||||
{
|
||||
|
||||
if (length != 0 && (offset < di->mediaoffset ||
|
||||
if (di->mediasize > 0 && length != 0 && (offset < di->mediaoffset ||
|
||||
offset - di->mediaoffset + length > di->mediasize)) {
|
||||
if (di->kdcomp != NULL && offset >= di->mediaoffset) {
|
||||
printf(
|
||||
@ -1244,18 +1259,6 @@ dump_encrypted_write(struct dumperinfo *di, void *virtual,
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
dump_write_key(struct dumperinfo *di, off_t offset)
|
||||
{
|
||||
struct kerneldumpcrypto *kdc;
|
||||
|
||||
kdc = di->kdcrypto;
|
||||
if (kdc == NULL)
|
||||
return (0);
|
||||
return (dump_write(di, kdc->kdc_dumpkey, 0, offset,
|
||||
kdc->kdc_dumpkeysize));
|
||||
}
|
||||
#endif /* EKCD */
|
||||
|
||||
static int
|
||||
@ -1289,20 +1292,42 @@ kerneldumpcomp_write_cb(void *base, size_t length, off_t offset, void *arg)
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a kerneldumpheader at the specified offset. The header structure is 512
|
||||
* bytes in size, but we must pad to the device sector size.
|
||||
* Write kernel dump headers at the beginning and end of the dump extent.
|
||||
* Write the kernel dump encryption key after the leading header if we were
|
||||
* configured to do so.
|
||||
*/
|
||||
static int
|
||||
dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh,
|
||||
off_t offset)
|
||||
dump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh)
|
||||
{
|
||||
void *buf;
|
||||
#ifdef EKCD
|
||||
struct kerneldumpcrypto *kdc;
|
||||
#endif
|
||||
void *buf, *key;
|
||||
size_t hdrsz;
|
||||
uint64_t extent;
|
||||
uint32_t keysize;
|
||||
int error;
|
||||
|
||||
hdrsz = sizeof(*kdh);
|
||||
if (hdrsz > di->blocksize)
|
||||
return (ENOMEM);
|
||||
|
||||
#ifdef EKCD
|
||||
kdc = di->kdcrypto;
|
||||
key = kdc->kdc_dumpkey;
|
||||
keysize = kerneldumpcrypto_dumpkeysize(kdc);
|
||||
#else
|
||||
key = NULL;
|
||||
keysize = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the dump device has special handling for headers, let it take care
|
||||
* of writing them out.
|
||||
*/
|
||||
if (di->dumper_hdr != NULL)
|
||||
return (di->dumper_hdr(di, kdh, key, keysize));
|
||||
|
||||
if (hdrsz == di->blocksize)
|
||||
buf = kdh;
|
||||
else {
|
||||
@ -1311,7 +1336,24 @@ dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh,
|
||||
memcpy(buf, kdh, hdrsz);
|
||||
}
|
||||
|
||||
return (dump_write(di, buf, 0, offset, di->blocksize));
|
||||
extent = dtoh64(kdh->dumpextent);
|
||||
#ifdef EKCD
|
||||
if (kdc != NULL) {
|
||||
error = dump_write(di, kdc->kdc_dumpkey, 0,
|
||||
di->mediaoffset + di->mediasize - di->blocksize - extent -
|
||||
keysize, keysize);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
#endif
|
||||
|
||||
error = dump_write(di, buf, 0,
|
||||
di->mediaoffset + di->mediasize - 2 * di->blocksize - extent -
|
||||
keysize, di->blocksize);
|
||||
if (error == 0)
|
||||
error = dump_write(di, buf, 0, di->mediaoffset + di->mediasize -
|
||||
di->blocksize, di->blocksize);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1336,26 +1378,37 @@ dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh,
|
||||
* Uncompressed dumps will use the entire extent, but compressed dumps typically
|
||||
* will not. The true length of the dump is recorded in the leading and trailing
|
||||
* headers once the dump has been completed.
|
||||
*
|
||||
* The dump device may provide a callback, in which case it will initialize
|
||||
* dumpoff and take care of laying out the headers.
|
||||
*/
|
||||
int
|
||||
dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh)
|
||||
{
|
||||
uint64_t dumpextent;
|
||||
uint64_t dumpextent, span;
|
||||
uint32_t keysize;
|
||||
int error;
|
||||
|
||||
#ifdef EKCD
|
||||
int error = kerneldumpcrypto_init(di->kdcrypto);
|
||||
error = kerneldumpcrypto_init(di->kdcrypto);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto);
|
||||
#else
|
||||
error = 0;
|
||||
keysize = 0;
|
||||
#endif
|
||||
|
||||
dumpextent = dtoh64(kdh->dumpextent);
|
||||
if (di->mediasize < SIZEOF_METADATA + dumpextent + 2 * di->blocksize +
|
||||
keysize) {
|
||||
if (di->kdcomp != NULL) {
|
||||
if (di->dumper_start != NULL) {
|
||||
error = di->dumper_start(di);
|
||||
} else {
|
||||
dumpextent = dtoh64(kdh->dumpextent);
|
||||
span = SIZEOF_METADATA + dumpextent + 2 * di->blocksize +
|
||||
keysize;
|
||||
if (di->mediasize < span) {
|
||||
if (di->kdcomp == NULL)
|
||||
return (E2BIG);
|
||||
|
||||
/*
|
||||
* We don't yet know how much space the compressed dump
|
||||
* will occupy, so try to use the whole swap partition
|
||||
@ -1364,18 +1417,18 @@ dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh)
|
||||
* be enough, the bounds checking in dump_write()
|
||||
* will catch us and cause the dump to fail.
|
||||
*/
|
||||
dumpextent = di->mediasize - SIZEOF_METADATA -
|
||||
2 * di->blocksize - keysize;
|
||||
dumpextent = di->mediasize - span + dumpextent;
|
||||
kdh->dumpextent = htod64(dumpextent);
|
||||
} else
|
||||
return (E2BIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* The offset at which to begin writing the dump.
|
||||
*/
|
||||
di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize -
|
||||
dumpextent;
|
||||
}
|
||||
|
||||
/* The offset at which to begin writing the dump. */
|
||||
di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize -
|
||||
dumpextent;
|
||||
|
||||
return (0);
|
||||
di->origdumpoff = di->dumpoff;
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1443,17 +1496,10 @@ int
|
||||
dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh)
|
||||
{
|
||||
uint64_t extent;
|
||||
uint32_t keysize;
|
||||
int error;
|
||||
|
||||
extent = dtoh64(kdh->dumpextent);
|
||||
|
||||
#ifdef EKCD
|
||||
keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto);
|
||||
#else
|
||||
keysize = 0;
|
||||
#endif
|
||||
|
||||
if (di->kdcomp != NULL) {
|
||||
error = compressor_flush(di->kdcomp->kdc_stream);
|
||||
if (error == EAGAIN) {
|
||||
@ -1470,33 +1516,14 @@ dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh)
|
||||
* We now know the size of the compressed dump, so update the
|
||||
* header accordingly and recompute parity.
|
||||
*/
|
||||
kdh->dumplength = htod64(di->dumpoff -
|
||||
(di->mediaoffset + di->mediasize - di->blocksize - extent));
|
||||
kdh->dumplength = htod64(di->dumpoff - di->origdumpoff);
|
||||
kdh->parity = 0;
|
||||
kdh->parity = kerneldump_parity(kdh);
|
||||
|
||||
compressor_reset(di->kdcomp->kdc_stream);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write kerneldump headers at the beginning and end of the dump extent.
|
||||
* Write the key after the leading header.
|
||||
*/
|
||||
error = dump_write_header(di, kdh,
|
||||
di->mediaoffset + di->mediasize - 2 * di->blocksize - extent -
|
||||
keysize);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
#ifdef EKCD
|
||||
error = dump_write_key(di,
|
||||
di->mediaoffset + di->mediasize - di->blocksize - extent - keysize);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
#endif
|
||||
|
||||
error = dump_write_header(di, kdh,
|
||||
di->mediaoffset + di->mediasize - di->blocksize);
|
||||
error = dump_write_headers(di, kdh);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
|
@ -101,6 +101,8 @@ struct cdev {
|
||||
|
||||
struct bio;
|
||||
struct buf;
|
||||
struct dumperinfo;
|
||||
struct kerneldumpheader;
|
||||
struct thread;
|
||||
struct uio;
|
||||
struct knote;
|
||||
@ -131,6 +133,9 @@ typedef int dumper_t(
|
||||
vm_offset_t _physical, /* Physical address of virtual. */
|
||||
off_t _offset, /* Byte-offset to write at. */
|
||||
size_t _length); /* Number of bytes to dump. */
|
||||
typedef int dumper_start_t(struct dumperinfo *di);
|
||||
typedef int dumper_hdr_t(struct dumperinfo *di, struct kerneldumpheader *kdh,
|
||||
void *key, uint32_t keylen);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
@ -332,13 +337,18 @@ struct kerneldumpheader;
|
||||
|
||||
struct dumperinfo {
|
||||
dumper_t *dumper; /* Dumping function. */
|
||||
dumper_start_t *dumper_start; /* Dumper callback for dump_start(). */
|
||||
dumper_hdr_t *dumper_hdr; /* Dumper callback for writing headers. */
|
||||
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. */
|
||||
|
||||
/* MI kernel dump state. */
|
||||
void *blockbuf; /* Buffer for padding shorter dump blocks */
|
||||
off_t dumpoff; /* Offset of ongoing kernel dump. */
|
||||
off_t origdumpoff; /* Starting dump offset. */
|
||||
struct kerneldumpcrypto *kdcrypto; /* Kernel dump crypto. */
|
||||
struct kerneldumpcomp *kdcomp; /* Kernel dump compression. */
|
||||
};
|
||||
@ -349,6 +359,7 @@ int doadump(boolean_t);
|
||||
int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
|
||||
uint8_t compression, uint8_t encryption, const uint8_t *key,
|
||||
uint32_t encryptedkeysize, const uint8_t *encryptedkey);
|
||||
int clear_dumper(struct thread *td);
|
||||
|
||||
int dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh);
|
||||
int dump_append(struct dumperinfo *, void *, vm_offset_t, size_t);
|
||||
|
Loading…
x
Reference in New Issue
Block a user