svn commit: r333282 - in head/sys: dev/null geom kern sys

Mark Johnston markj at FreeBSD.org
Sun May 6 00:22:39 UTC 2018


Author: markj
Date: Sun May  6 00:22:38 2018
New Revision: 333282
URL: https://svnweb.freebsd.org/changeset/base/333282

Log:
  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

Modified:
  head/sys/dev/null/null.c
  head/sys/geom/geom_dev.c
  head/sys/kern/kern_shutdown.c
  head/sys/sys/conf.h

Modified: head/sys/dev/null/null.c
==============================================================================
--- head/sys/dev/null/null.c	Sun May  6 00:19:48 2018	(r333281)
+++ head/sys/dev/null/null.c	Sun May  6 00:22:38 2018	(r333282)
@@ -107,14 +107,14 @@ null_ioctl(struct cdev *dev __unused, u_long cmd, cadd
     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;

Modified: head/sys/geom/geom_dev.c
==============================================================================
--- head/sys/geom/geom_dev.c	Sun May  6 00:19:48 2018	(r333281)
+++ head/sys/geom/geom_dev.c	Sun May  6 00:22:38 2018	(r333282)
@@ -138,10 +138,11 @@ g_dev_setdumpdev(struct cdev *dev, struct diocskerneld
 	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);

Modified: head/sys/kern/kern_shutdown.c
==============================================================================
--- head/sys/kern/kern_shutdown.c	Sun May  6 00:19:48 2018	(r333281)
+++ head/sys/kern/kern_shutdown.c	Sun May  6 00:22:38 2018	(r333282)
@@ -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,
 	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,
 
 	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 *virt
 
 	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
 }
 
 /*
- * 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 kernel
 		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 kernel
  * 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 kerneldumphea
 			 * 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;
-
-	return (0);
+		/*
+		 * The offset at which to begin writing the dump.
+		 */
+		di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize -
+		    dumpextent;
+	}
+	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 kerneldumphe
 		 * 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);
 

Modified: head/sys/sys/conf.h
==============================================================================
--- head/sys/sys/conf.h	Sun May  6 00:19:48 2018	(r333281)
+++ head/sys/sys/conf.h	Sun May  6 00:22:38 2018	(r333282)
@@ -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);


More information about the svn-src-head mailing list