svn commit: r347192 - in head: sbin/dumpon sys/dev/null sys/geom sys/geom/raid sys/kern sys/netinet/netdump sys/sys

Conrad Meyer cem at FreeBSD.org
Mon May 6 18:24:10 UTC 2019


Author: cem
Date: Mon May  6 18:24:07 2019
New Revision: 347192
URL: https://svnweb.freebsd.org/changeset/base/347192

Log:
  List-ify kernel dump device configuration
  
  Allow users to specify multiple dump configurations in a prioritized list.
  This enables fallback to secondary device(s) if primary dump fails.  E.g.,
  one might configure a preference for netdump, but fallback to disk dump as a
  second choice if netdump is unavailable.
  
  This change does not list-ify netdump configuration, which is tracked
  separately from ordinary disk dumps internally; only one netdump
  configuration can be made at a time, for now.  It also does not implement
  IPv6 netdump.
  
  savecore(8) is already capable of scanning and iterating multiple devices
  from /etc/fstab or passed on the command line.
  
  This change doesn't update the rc or loader variables 'dumpdev' in any way;
  it can still be set to configure a single dump device, and rc.d/savecore
  still uses it as a single device.  Only dumpon(8) is updated to be able to
  configure the more complicated configurations for now.
  
  As part of revving the ABI, unify netdump and disk dump configuration ioctl
  / structure, and leave room for ipv6 netdump as a future possibility.
  Backwards-compatibility ioctls are added to smooth ABI transition,
  especially for developers who may not keep kernel and userspace perfectly
  synced.
  
  Reviewed by:	markj, scottl (earlier version)
  Relnotes:	maybe
  Sponsored by:	Dell EMC Isilon
  Differential Revision:	https://reviews.freebsd.org/D19996

Modified:
  head/sbin/dumpon/dumpon.8
  head/sbin/dumpon/dumpon.c
  head/sys/dev/null/null.c
  head/sys/geom/geom_dev.c
  head/sys/geom/raid/g_raid.h
  head/sys/kern/kern_shutdown.c
  head/sys/netinet/netdump/netdump.h
  head/sys/netinet/netdump/netdump_client.c
  head/sys/sys/conf.h
  head/sys/sys/disk.h
  head/sys/sys/param.h

Modified: head/sbin/dumpon/dumpon.8
==============================================================================
--- head/sbin/dumpon/dumpon.8	Mon May  6 16:54:35 2019	(r347191)
+++ head/sbin/dumpon/dumpon.8	Mon May  6 18:24:07 2019	(r347192)
@@ -28,7 +28,7 @@
 .\"     From: @(#)swapon.8	8.1 (Berkeley) 6/5/93
 .\" $FreeBSD$
 .\"
-.Dd November 17, 2018
+.Dd May 6, 2019
 .Dt DUMPON 8
 .Os
 .Sh NAME
@@ -36,12 +36,16 @@
 .Nd "specify a device for crash dumps"
 .Sh SYNOPSIS
 .Nm
+.Op Fl i Ar index
+.Op Fl r
 .Op Fl v
 .Op Fl k Ar pubkey
 .Op Fl Z
 .Op Fl z
 .Ar device
 .Nm
+.Op Fl i Ar index
+.Op Fl r
 .Op Fl v
 .Op Fl k Ar pubkey
 .Op Fl Z
@@ -72,8 +76,38 @@ and
 .Va dumpon_flags .
 For more information on this usage, see
 .Xr rc.conf 5 .
+.Pp
+Starting in
+.Fx 13.0 ,
+.Nm
+can configure a series of fallback dump devices.
+For example, an administrator may prefer
+.Xr netdump 4
+by default, but if the
+.Xr netdump 4
+service cannot be reached or some other failure occurs, they might choose a
+local disk dump as a second choice option.
 .Ss General options
 .Bl -tag -width _k_pubkey
+.It Fl i Ar index
+Insert the specified dump configuration into the prioritized fallback dump
+device list at the specified index, starting at zero.
+.Pp
+If
+.Fl i
+is not specified, the configured dump device is appended to the prioritized
+list.
+.It Fl r
+Remove the specified dump device configuration or configurations from the
+fallback dump device list rather than inserting or appending it.
+In contrast,
+.Do
+.Nm
+off
+.Dc
+removes all configured devices.
+Conflicts with
+.Fl i .
 .It Fl k Ar pubkey
 Configure encrypted kernel dumps.
 .Pp
@@ -96,7 +130,7 @@ The
 .Va pubkey
 file should be a PEM-formatted RSA key of at least 1024 bits.
 .It Fl l
-List the currently configured dump device, or /dev/null if no device is
+List the currently configured dump device(s), or /dev/null if no devices are
 configured.
 .It Fl v
 Enable verbose mode.

Modified: head/sbin/dumpon/dumpon.c
==============================================================================
--- head/sbin/dumpon/dumpon.c	Mon May  6 16:54:35 2019	(r347191)
+++ head/sbin/dumpon/dumpon.c	Mon May  6 18:24:07 2019	(r347192)
@@ -86,8 +86,8 @@ static void _Noreturn
 usage(void)
 {
 	fprintf(stderr,
-    "usage: dumpon [-v] [-k <pubkey>] [-Zz] <device>\n"
-    "       dumpon [-v] [-k <pubkey>] [-Zz]\n"
+    "usage: dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz] <device>\n"
+    "       dumpon [-i index] [-r] [-v] [-k <pubkey>] [-Zz]\n"
     "              [-g <gateway>] -s <server> -c <client> <iface>\n"
     "       dumpon [-v] off\n"
     "       dumpon [-v] -l\n");
@@ -290,8 +290,10 @@ genkey(const char *pubkeyfile, struct diocskerneldump_
 static void
 listdumpdev(void)
 {
+	static char ip[200];
+
 	char dumpdev[PATH_MAX];
-	struct netdump_conf ndconf;
+	struct diocskerneldump_arg ndconf;
 	size_t len;
 	const char *sysctlname = "kern.shutdown.dumpdevname";
 	int fd;
@@ -308,10 +310,18 @@ listdumpdev(void)
 	if (strlen(dumpdev) == 0)
 		(void)strlcpy(dumpdev, _PATH_DEVNULL, sizeof(dumpdev));
 
-	if (verbose)
-		printf("kernel dumps on ");
-	printf("%s\n", dumpdev);
+	if (verbose) {
+		char *ctx, *dd;
+		unsigned idx;
 
+		printf("kernel dumps on priority: device\n");
+		idx = 0;
+		ctx = dumpdev;
+		while ((dd = strsep(&ctx, ",")) != NULL)
+			printf("%u: %s\n", idx++, dd);
+	} else
+		printf("%s\n", dumpdev);
+
 	/* If netdump is enabled, print the configuration parameters. */
 	if (verbose) {
 		fd = open(_PATH_NETDUMP, O_RDONLY);
@@ -320,16 +330,22 @@ listdumpdev(void)
 				err(EX_OSERR, "opening %s", _PATH_NETDUMP);
 			return;
 		}
-		if (ioctl(fd, NETDUMPGCONF, &ndconf) != 0) {
+		if (ioctl(fd, DIOCGKERNELDUMP, &ndconf) != 0) {
 			if (errno != ENXIO)
-				err(EX_OSERR, "ioctl(NETDUMPGCONF)");
+				err(EX_OSERR, "ioctl(DIOCGKERNELDUMP)");
 			(void)close(fd);
 			return;
 		}
 
-		printf("server address: %s\n", inet_ntoa(ndconf.ndc_server));
-		printf("client address: %s\n", inet_ntoa(ndconf.ndc_client));
-		printf("gateway address: %s\n", inet_ntoa(ndconf.ndc_gateway));
+		printf("server address: %s\n",
+		    inet_ntop(ndconf.kda_af, &ndconf.kda_server, ip,
+			sizeof(ip)));
+		printf("client address: %s\n",
+		    inet_ntop(ndconf.kda_af, &ndconf.kda_client, ip,
+			sizeof(ip)));
+		printf("gateway address: %s\n",
+		    inet_ntop(ndconf.kda_af, &ndconf.kda_gateway, ip,
+			sizeof(ip)));
 		(void)close(fd);
 	}
 }
@@ -359,19 +375,20 @@ int
 main(int argc, char *argv[])
 {
 	char dumpdev[PATH_MAX];
-	struct diocskerneldump_arg _kda, *kdap;
-	struct netdump_conf ndconf;
+	struct diocskerneldump_arg ndconf, *kdap;
 	struct addrinfo hints, *res;
 	const char *dev, *pubkeyfile, *server, *client, *gateway;
 	int ch, error, fd;
-	bool enable, gzip, list, netdump, zstd;
+	bool gzip, list, netdump, zstd, insert, rflag;
+	uint8_t ins_idx;
 
-	gzip = list = netdump = zstd = false;
+	gzip = list = netdump = zstd = insert = rflag = false;
 	kdap = NULL;
 	pubkeyfile = NULL;
 	server = client = gateway = NULL;
+	ins_idx = KDA_APPEND;
 
-	while ((ch = getopt(argc, argv, "c:g:k:ls:vZz")) != -1)
+	while ((ch = getopt(argc, argv, "c:g:i:k:lrs:vZz")) != -1)
 		switch ((char)ch) {
 		case 'c':
 			client = optarg;
@@ -379,12 +396,28 @@ main(int argc, char *argv[])
 		case 'g':
 			gateway = optarg;
 			break;
+		case 'i':
+			{
+			int i;
+
+			i = atoi(optarg);
+			if (i < 0 || i >= KDA_APPEND - 1)
+				errx(EX_USAGE,
+				    "-i index must be between zero and %d.",
+				    (int)KDA_APPEND - 2);
+			insert = true;
+			ins_idx = i;
+			}
+			break;
 		case 'k':
 			pubkeyfile = optarg;
 			break;
 		case 'l':
 			list = true;
 			break;
+		case 'r':
+			rflag = true;
+			break;
 		case 's':
 			server = optarg;
 			break;
@@ -404,6 +437,9 @@ main(int argc, char *argv[])
 	if (gzip && zstd)
 		errx(EX_USAGE, "The -z and -Z options are mutually exclusive.");
 
+	if (insert && rflag)
+		errx(EX_USAGE, "The -i and -r options are mutually exclusive.");
+
 	argc -= optind;
 	argv += optind;
 
@@ -422,31 +458,30 @@ main(int argc, char *argv[])
 #endif
 
 	if (server != NULL && client != NULL) {
-		enable = true;
 		dev = _PATH_NETDUMP;
 		netdump = true;
-		kdap = &ndconf.ndc_kda;
 	} else if (server == NULL && client == NULL && argc > 0) {
-		enable = strcmp(argv[0], "off") != 0;
-		dev = enable ? argv[0] : _PATH_DEVNULL;
+		if (strcmp(argv[0], "off") == 0) {
+			rflag = true;
+			dev = _PATH_DEVNULL;
+		} else
+			dev = argv[0];
 		netdump = false;
-		kdap = &_kda;
 	} else
 		usage();
 
 	fd = opendumpdev(dev, dumpdev);
-	if (!netdump && !gzip)
+	if (!netdump && !gzip && !rflag)
 		check_size(fd, dumpdev);
 
+	kdap = &ndconf;
 	bzero(kdap, sizeof(*kdap));
-	kdap->kda_enable = 0;
-	if (ioctl(fd, DIOCSKERNELDUMP, kdap) != 0)
-		err(EX_OSERR, "ioctl(DIOCSKERNELDUMP)");
-	if (!enable)
-		exit(EX_OK);
 
-	explicit_bzero(kdap, sizeof(*kdap));
-	kdap->kda_enable = 1;
+	if (rflag)
+		kdap->kda_index = KDA_REMOVE;
+	else
+		kdap->kda_index = ins_idx;
+
 	kdap->kda_compression = KERNELDUMP_COMP_NONE;
 	if (zstd)
 		kdap->kda_compression = KERNELDUMP_COMP_ZSTD;
@@ -467,12 +502,12 @@ main(int argc, char *argv[])
 		    ((struct sockaddr_in *)(void *)res->ai_addr)->sin_addr);
 		freeaddrinfo(res);
 
-		if (strlcpy(ndconf.ndc_iface, argv[0],
-		    sizeof(ndconf.ndc_iface)) >= sizeof(ndconf.ndc_iface))
+		if (strlcpy(ndconf.kda_iface, argv[0],
+		    sizeof(ndconf.kda_iface)) >= sizeof(ndconf.kda_iface))
 			errx(EX_USAGE, "invalid interface name '%s'", argv[0]);
-		if (inet_aton(server, &ndconf.ndc_server) == 0)
+		if (inet_aton(server, &ndconf.kda_server.in4) == 0)
 			errx(EX_USAGE, "invalid server address '%s'", server);
-		if (inet_aton(client, &ndconf.ndc_client) == 0)
+		if (inet_aton(client, &ndconf.kda_client.in4) == 0)
 			errx(EX_USAGE, "invalid client address '%s'", client);
 
 		if (gateway == NULL) {
@@ -485,39 +520,41 @@ main(int argc, char *argv[])
 				gateway = server;
 			}
 		}
-		if (inet_aton(gateway, &ndconf.ndc_gateway) == 0)
+		if (inet_aton(gateway, &ndconf.kda_gateway.in4) == 0)
 			errx(EX_USAGE, "invalid gateway address '%s'", gateway);
+		ndconf.kda_af = AF_INET;
+	}
 
 #ifdef HAVE_CRYPTO
-		if (pubkeyfile != NULL)
-			genkey(pubkeyfile, kdap);
+	if (pubkeyfile != NULL)
+		genkey(pubkeyfile, kdap);
 #endif
-		error = ioctl(fd, NETDUMPSCONF, &ndconf);
-		if (error != 0)
-			error = errno;
-		explicit_bzero(kdap->kda_encryptedkey,
-		    kdap->kda_encryptedkeysize);
-		free(kdap->kda_encryptedkey);
-		explicit_bzero(kdap, sizeof(*kdap));
-		if (error != 0)
-			errc(EX_OSERR, error, "ioctl(NETDUMPSCONF)");
-	} else {
-#ifdef HAVE_CRYPTO
-		if (pubkeyfile != NULL)
-			genkey(pubkeyfile, kdap);
-#endif
-		error = ioctl(fd, DIOCSKERNELDUMP, kdap);
-		if (error != 0)
-			error = errno;
-		explicit_bzero(kdap->kda_encryptedkey,
-		    kdap->kda_encryptedkeysize);
-		free(kdap->kda_encryptedkey);
-		explicit_bzero(kdap, sizeof(*kdap));
-		if (error != 0)
-			errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
+	error = ioctl(fd, DIOCSKERNELDUMP, kdap);
+	if (error != 0)
+		error = errno;
+	explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
+	free(kdap->kda_encryptedkey);
+	explicit_bzero(kdap, sizeof(*kdap));
+	if (error != 0) {
+		if (netdump) {
+			/*
+			 * Be slightly less user-hostile for some common
+			 * errors, especially as users don't have any great
+			 * discoverability into which NICs support netdump.
+			 */
+			if (error == ENXIO)
+				errx(EX_OSERR, "Unable to configure netdump "
+				    "because the interface's link is down.");
+			else if (error == ENODEV)
+				errx(EX_OSERR, "Unable to configure netdump "
+				    "because the interface driver does not yet "
+				    "support netdump.");
+		}
+		errc(EX_OSERR, error, "ioctl(DIOCSKERNELDUMP)");
 	}
+
 	if (verbose)
-		printf("kernel dumps on %s\n", dumpdev);
+		listdumpdev();
 
 	exit(EX_OK);
 }

Modified: head/sys/dev/null/null.c
==============================================================================
--- head/sys/dev/null/null.c	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/dev/null/null.c	Mon May  6 18:24:07 2019	(r347192)
@@ -106,15 +106,26 @@ static int
 null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
     int flags __unused, struct thread *td)
 {
+	struct diocskerneldump_arg kda;
 	int error;
 
 	error = 0;
 	switch (cmd) {
 #ifdef COMPAT_FREEBSD11
 	case DIOCSKERNELDUMP_FREEBSD11:
+		gone_in(13, "FreeBSD 11.x ABI compat");
+		/* FALLTHROUGH */
 #endif
+#ifdef COMPAT_FREEBSD12
+	case DIOCSKERNELDUMP_FREEBSD12:
+		if (cmd == DIOCSKERNELDUMP_FREEBSD12)
+			gone_in(14, "FreeBSD 12.x ABI compat");
+		/* FALLTHROUGH */
+#endif
 	case DIOCSKERNELDUMP:
-		error = clear_dumper(td);
+		bzero(&kda, sizeof(kda));
+		kda.kda_index = KDA_REMOVE_ALL;
+		error = dumper_remove(NULL, &kda);
 		break;
 	case FIONBIO:
 		break;

Modified: head/sys/geom/geom_dev.c
==============================================================================
--- head/sys/geom/geom_dev.c	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/geom/geom_dev.c	Mon May  6 18:24:07 2019	(r347192)
@@ -135,15 +135,14 @@ g_dev_fini(struct g_class *mp)
 }
 
 static int
-g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda,
-    struct thread *td)
+g_dev_setdumpdev(struct cdev *dev, struct diocskerneldump_arg *kda)
 {
 	struct g_kerneldump kd;
 	struct g_consumer *cp;
 	int error, len;
 
-	if (dev == NULL || kda == NULL)
-		return (clear_dumper(td));
+	MPASS(dev != NULL && kda != NULL);
+	MPASS(kda->kda_index != KDA_REMOVE);
 
 	cp = dev->si_drv2;
 	len = sizeof(kd);
@@ -154,9 +153,7 @@ g_dev_setdumpdev(struct cdev *dev, struct diocskerneld
 	if (error != 0)
 		return (error);
 
-	error = set_dumper(&kd.di, devtoname(dev), td, kda->kda_compression,
-	    kda->kda_encryption, kda->kda_key, kda->kda_encryptedkeysize,
-	    kda->kda_encryptedkey);
+	error = dumper_insert(&kd.di, devtoname(dev), kda);
 	if (error == 0)
 		dev->si_flags |= SI_DUMPDEV;
 
@@ -173,7 +170,7 @@ init_dumpdev(struct cdev *dev)
 	size_t len;
 
 	bzero(&kda, sizeof(kda));
-	kda.kda_enable = 1;
+	kda.kda_index = KDA_APPEND;
 
 	if (dumpdev == NULL)
 		return (0);
@@ -190,7 +187,7 @@ init_dumpdev(struct cdev *dev)
 	if (error != 0)
 		return (error);
 
-	error = g_dev_setdumpdev(dev, &kda, curthread);
+	error = g_dev_setdumpdev(dev, &kda);
 	if (error == 0) {
 		freeenv(dumpdev);
 		dumpdev = NULL;
@@ -509,6 +506,9 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data
 	struct g_provider *pp;
 	off_t offset, length, chunk, odd;
 	int i, error;
+#ifdef COMPAT_FREEBSD12
+	struct diocskerneldump_arg kda_copy;
+#endif
 
 	cp = dev->si_drv2;
 	pp = cp->provider;
@@ -547,31 +547,55 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data
 	    {
 		struct diocskerneldump_arg kda;
 
+		gone_in(13, "FreeBSD 11.x ABI compat");
+
 		bzero(&kda, sizeof(kda));
 		kda.kda_encryption = KERNELDUMP_ENC_NONE;
-		kda.kda_enable = (uint8_t)*(u_int *)data;
-		if (kda.kda_enable == 0)
-			error = g_dev_setdumpdev(NULL, NULL, td);
+		kda.kda_index = (*(u_int *)data ? 0 : KDA_REMOVE_ALL);
+		if (kda.kda_index == KDA_REMOVE_ALL)
+			error = dumper_remove(devtoname(dev), &kda);
 		else
-			error = g_dev_setdumpdev(dev, &kda, td);
+			error = g_dev_setdumpdev(dev, &kda);
 		break;
 	    }
 #endif
+#ifdef COMPAT_FREEBSD12
+	case DIOCSKERNELDUMP_FREEBSD12:
+	    {
+		struct diocskerneldump_arg_freebsd12 *kda12;
+
+		gone_in(14, "FreeBSD 12.x ABI compat");
+
+		kda12 = (void *)data;
+		memcpy(&kda_copy, kda12, sizeof(kda_copy));
+		kda_copy.kda_index = (kda12->kda12_enable ?
+		    0 : KDA_REMOVE_ALL);
+
+		explicit_bzero(kda12, sizeof(*kda12));
+		/* Kludge to pass kda_copy to kda in fallthrough. */
+		data = (void *)&kda_copy;
+	    }
+	    /* FALLTHROUGH */
+#endif
 	case DIOCSKERNELDUMP:
 	    {
 		struct diocskerneldump_arg *kda;
 		uint8_t *encryptedkey;
 
 		kda = (struct diocskerneldump_arg *)data;
-		if (kda->kda_enable == 0) {
-			error = g_dev_setdumpdev(NULL, NULL, td);
+		if (kda->kda_index == KDA_REMOVE_ALL ||
+		    kda->kda_index == KDA_REMOVE_DEV ||
+		    kda->kda_index == KDA_REMOVE) {
+			error = dumper_remove(devtoname(dev), kda);
+			explicit_bzero(kda, sizeof(*kda));
 			break;
 		}
 
 		if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
-			if (kda->kda_encryptedkeysize <= 0 ||
+			if (kda->kda_encryptedkeysize == 0 ||
 			    kda->kda_encryptedkeysize >
 			    KERNELDUMP_ENCKEY_MAX_SIZE) {
+				explicit_bzero(kda, sizeof(*kda));
 				return (EINVAL);
 			}
 			encryptedkey = malloc(kda->kda_encryptedkeysize, M_TEMP,
@@ -583,7 +607,7 @@ g_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data
 		}
 		if (error == 0) {
 			kda->kda_encryptedkey = encryptedkey;
-			error = g_dev_setdumpdev(dev, kda, td);
+			error = g_dev_setdumpdev(dev, kda);
 		}
 		if (encryptedkey != NULL) {
 			explicit_bzero(encryptedkey, kda->kda_encryptedkeysize);
@@ -859,8 +883,13 @@ g_dev_orphan(struct g_consumer *cp)
 	g_trace(G_T_TOPOLOGY, "g_dev_orphan(%p(%s))", cp, cp->geom->name);
 
 	/* Reset any dump-area set on this device */
-	if (dev->si_flags & SI_DUMPDEV)
-		(void)clear_dumper(curthread);
+	if (dev->si_flags & SI_DUMPDEV) {
+		struct diocskerneldump_arg kda;
+
+		bzero(&kda, sizeof(kda));
+		kda.kda_index = KDA_REMOVE_DEV;
+		(void)dumper_remove(devtoname(dev), &kda);
+	}
 
 	/* Destroy the struct cdev *so we get no more requests */
 	delist_dev(dev);

Modified: head/sys/geom/raid/g_raid.h
==============================================================================
--- head/sys/geom/raid/g_raid.h	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/geom/raid/g_raid.h	Mon May  6 18:24:07 2019	(r347192)
@@ -155,7 +155,6 @@ struct g_raid_disk {
 	struct g_raid_softc	*d_softc;	/* Back-pointer to softc. */
 	struct g_consumer	*d_consumer;	/* GEOM disk consumer. */
 	void			*d_md_data;	/* Disk's metadata storage. */
-	struct g_kerneldump	 d_kd;		/* Kernel dumping method/args. */
 	int			 d_candelete;	/* BIO_DELETE supported. */
 	uint64_t		 d_flags;	/* Additional flags. */
 	u_int			 d_state;	/* Disk state. */
@@ -164,6 +163,7 @@ struct g_raid_disk {
 	int			 d_read_errs;	/* Count of the read errors */
 	TAILQ_HEAD(, g_raid_subdisk)	 d_subdisks; /* List of subdisks. */
 	TAILQ_ENTRY(g_raid_disk)	 d_next;	/* Next disk in the node. */
+	struct g_kerneldump	 d_kd;		/* Kernel dumping method/args. */
 };
 
 #define G_RAID_SUBDISK_S_NONE		0x00	/* Absent. */

Modified: head/sys/kern/kern_shutdown.c
==============================================================================
--- head/sys/kern/kern_shutdown.c	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/kern/kern_shutdown.c	Mon May  6 18:24:07 2019	(r347192)
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_ekcd.h"
 #include "opt_kdb.h"
 #include "opt_panic.h"
+#include "opt_printf.h"
 #include "opt_sched.h"
 #include "opt_watchdog.h"
 
@@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/conf.h>
 #include <sys/compressor.h>
 #include <sys/cons.h>
+#include <sys/disk.h>
 #include <sys/eventhandler.h>
 #include <sys/filedesc.h>
 #include <sys/jail.h>
@@ -69,6 +71,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/reboot.h>
 #include <sys/resourcevar.h>
 #include <sys/rwlock.h>
+#include <sys/sbuf.h>
 #include <sys/sched.h>
 #include <sys/smp.h>
 #include <sys/sysctl.h>
@@ -209,8 +212,17 @@ const char *panicstr;
 
 int dumping;				/* system is dumping */
 int rebooting;				/* system is rebooting */
-static struct dumperinfo dumper;	/* our selected dumper */
+/*
+ * Used to serialize between sysctl kern.shutdown.dumpdevname and list
+ * modifications via ioctl.
+ */
+static struct mtx dumpconf_list_lk;
+MTX_SYSINIT(dumper_configs, &dumpconf_list_lk, "dumper config list", MTX_DEF);
 
+/* Our selected dumper(s). */
+static TAILQ_HEAD(dumpconflist, dumperinfo) dumper_configs =
+    TAILQ_HEAD_INITIALIZER(dumper_configs);
+
 /* Context information for dump-debuggers. */
 static struct pcb dumppcb;		/* Registers. */
 lwpid_t dumptid;			/* Thread ID. */
@@ -364,7 +376,7 @@ doadump(boolean_t textdump)
 	error = 0;
 	if (dumping)
 		return (EBUSY);
-	if (dumper.dumper == NULL)
+	if (TAILQ_EMPTY(&dumper_configs))
 		return (ENXIO);
 
 	savectx(&dumppcb);
@@ -375,12 +387,19 @@ doadump(boolean_t textdump)
 #ifdef DDB
 	if (textdump && textdump_pending) {
 		coredump = FALSE;
-		textdump_dumpsys(&dumper);
+		textdump_dumpsys(TAILQ_FIRST(&dumper_configs));
 	}
 #endif
-	if (coredump)
-		error = dumpsys(&dumper);
+	if (coredump) {
+		struct dumperinfo *di;
 
+		TAILQ_FOREACH(di, &dumper_configs, di_next) {
+			error = dumpsys(di);
+			if (error == 0)
+				break;
+		}
+	}
+
 	dumping--;
 	return (error);
 }
@@ -952,10 +971,36 @@ 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");
+static int
+dumpdevname_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+	char buf[256];
+	struct dumperinfo *di;
+	struct sbuf sb;
+	int error;
 
+	error = sysctl_wire_old_buffer(req, 0);
+	if (error != 0)
+		return (error);
+
+	sbuf_new_for_sysctl(&sb, buf, sizeof(buf), req);
+
+	mtx_lock(&dumpconf_list_lk);
+	TAILQ_FOREACH(di, &dumper_configs, di_next) {
+		if (di != TAILQ_FIRST(&dumper_configs))
+			sbuf_putc(&sb, ',');
+		sbuf_cat(&sb, di->di_devname);
+	}
+	mtx_unlock(&dumpconf_list_lk);
+
+	error = sbuf_finish(&sb);
+	sbuf_delete(&sb);
+	return (error);
+}
+SYSCTL_PROC(_kern_shutdown, OID_AUTO, dumpdevname, CTLTYPE_STRING | CTLFLAG_RD,
+    &dumper_configs, 0, dumpdevname_sysctl_handler, "A",
+    "Device(s) for kernel dumps");
+
 static int	_dump_append(struct dumperinfo *di, void *virtual,
 		    vm_offset_t physical, size_t length);
 
@@ -1092,31 +1137,67 @@ kerneldumpcomp_destroy(struct dumperinfo *di)
 	free(kdcomp, M_DUMPER);
 }
 
+/*
+ * Must not be present on global list.
+ */
+static void
+free_single_dumper(struct dumperinfo *di)
+{
+
+	if (di == NULL)
+		return;
+
+	if (di->blockbuf != NULL) {
+		explicit_bzero(di->blockbuf, di->blocksize);
+		free(di->blockbuf, M_DUMPER);
+	}
+
+	kerneldumpcomp_destroy(di);
+
+#ifdef EKCD
+	if (di->kdcrypto != NULL) {
+		explicit_bzero(di->kdcrypto, sizeof(*di->kdcrypto) +
+		    di->kdcrypto->kdc_dumpkeysize);
+		free(di->kdcrypto, M_EKCD);
+	}
+#endif
+
+	explicit_bzero(di, sizeof(*di));
+	free(di, M_DUMPER);
+}
+
 /* Registration of dumpers */
 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)
+dumper_insert(const struct dumperinfo *di_template, const char *devname,
+    const struct diocskerneldump_arg *kda)
 {
-	size_t wantcopy;
+	struct dumperinfo *newdi, *listdi;
+	bool inserted;
+	uint8_t index;
 	int error;
 
-	error = priv_check(td, PRIV_SETDUMPER);
+	index = kda->kda_index;
+	MPASS(index != KDA_REMOVE && index != KDA_REMOVE_DEV &&
+	    index != KDA_REMOVE_ALL);
+
+	error = priv_check(curthread, PRIV_SETDUMPER);
 	if (error != 0)
 		return (error);
 
-	if (dumper.dumper != NULL)
-		return (EBUSY);
-	dumper = *di;
-	dumper.blockbuf = NULL;
-	dumper.kdcrypto = NULL;
-	dumper.kdcomp = NULL;
+	newdi = malloc(sizeof(*newdi) + strlen(devname) + 1, M_DUMPER, M_WAITOK
+	    | M_ZERO);
+	memcpy(newdi, di_template, sizeof(*newdi));
+	newdi->blockbuf = NULL;
+	newdi->kdcrypto = NULL;
+	newdi->kdcomp = NULL;
+	strcpy(newdi->di_devname, devname);
 
-	if (encryption != KERNELDUMP_ENC_NONE) {
+	if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
 #ifdef EKCD
-		dumper.kdcrypto = kerneldumpcrypto_create(di->blocksize,
-		    encryption, key, encryptedkeysize, encryptedkey);
-		if (dumper.kdcrypto == NULL) {
+		newdi->kdcrypto = kerneldumpcrypto_create(di_template->blocksize,
+		    kda->kda_encryption, kda->kda_key,
+		    kda->kda_encryptedkeysize, kda->kda_encryptedkey);
+		if (newdi->kdcrypto == NULL) {
 			error = EINVAL;
 			goto cleanup;
 		}
@@ -1125,66 +1206,117 @@ set_dumper(struct dumperinfo *di, const char *devname,
 		goto cleanup;
 #endif
 	}
-
-	wantcopy = strlcpy(dumpdevname, devname, sizeof(dumpdevname));
-	if (wantcopy >= sizeof(dumpdevname)) {
-		printf("set_dumper: device name truncated from '%s' -> '%s'\n",
-		    devname, dumpdevname);
-	}
-
-	if (compression != KERNELDUMP_COMP_NONE) {
+	if (kda->kda_compression != KERNELDUMP_COMP_NONE) {
 		/*
 		 * We currently can't support simultaneous encryption and
-		 * compression.
+		 * compression because our only encryption mode is an unpadded
+		 * block cipher, go figure.  This is low hanging fruit to fix.
 		 */
-		if (encryption != KERNELDUMP_ENC_NONE) {
+		if (kda->kda_encryption != KERNELDUMP_ENC_NONE) {
 			error = EOPNOTSUPP;
 			goto cleanup;
 		}
-		dumper.kdcomp = kerneldumpcomp_create(&dumper, compression);
-		if (dumper.kdcomp == NULL) {
+		newdi->kdcomp = kerneldumpcomp_create(newdi,
+		    kda->kda_compression);
+		if (newdi->kdcomp == NULL) {
 			error = EINVAL;
 			goto cleanup;
 		}
 	}
 
-	dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
+	newdi->blockbuf = malloc(newdi->blocksize, M_DUMPER, M_WAITOK | M_ZERO);
+
+	/* Add the new configuration to the queue */
+	mtx_lock(&dumpconf_list_lk);
+	inserted = false;
+	TAILQ_FOREACH(listdi, &dumper_configs, di_next) {
+		if (index == 0) {
+			TAILQ_INSERT_BEFORE(listdi, newdi, di_next);
+			inserted = true;
+			break;
+		}
+		index--;
+	}
+	if (!inserted)
+		TAILQ_INSERT_TAIL(&dumper_configs, newdi, di_next);
+	mtx_unlock(&dumpconf_list_lk);
+
 	return (0);
 
 cleanup:
-	(void)clear_dumper(td);
+	free_single_dumper(newdi);
 	return (error);
 }
 
-int
-clear_dumper(struct thread *td)
+static bool
+dumper_config_match(const struct dumperinfo *di, const char *devname,
+    const struct diocskerneldump_arg *kda)
 {
-	int error;
+	if (kda->kda_index == KDA_REMOVE_ALL)
+		return (true);
 
-	error = priv_check(td, PRIV_SETDUMPER);
-	if (error != 0)
-		return (error);
+	if (strcmp(di->di_devname, devname) != 0)
+		return (false);
 
-#ifdef NETDUMP
-	netdump_mbuf_drain();
-#endif
+	/*
+	 * Allow wildcard removal of configs matching a device on g_dev_orphan.
+	 */
+	if (kda->kda_index == KDA_REMOVE_DEV)
+		return (true);
 
+	if (di->kdcomp != NULL) {
+		if (di->kdcomp->kdc_format != kda->kda_compression)
+			return (false);
+	} else if (kda->kda_compression != KERNELDUMP_COMP_NONE)
+		return (false);
 #ifdef EKCD
-	if (dumper.kdcrypto != NULL) {
-		explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) +
-		    dumper.kdcrypto->kdc_dumpkeysize);
-		free(dumper.kdcrypto, M_EKCD);
-	}
+	if (di->kdcrypto != NULL) {
+		if (di->kdcrypto->kdc_encryption != kda->kda_encryption)
+			return (false);
+		/*
+		 * Do we care to verify keys match to delete?  It seems weird
+		 * to expect multiple fallback dump configurations on the same
+		 * device that only differ in crypto key.
+		 */
+	} else
 #endif
+		if (kda->kda_encryption != KERNELDUMP_ENC_NONE)
+			return (false);
 
-	kerneldumpcomp_destroy(&dumper);
+	return (true);
+}
 
-	if (dumper.blockbuf != NULL) {
-		explicit_bzero(dumper.blockbuf, dumper.blocksize);
-		free(dumper.blockbuf, M_DUMPER);
+int
+dumper_remove(const char *devname, const struct diocskerneldump_arg *kda)
+{
+	struct dumperinfo *di, *sdi;
+	bool found;
+	int error;
+
+	error = priv_check(curthread, PRIV_SETDUMPER);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Try to find a matching configuration, and kill it.
+	 *
+	 * NULL 'kda' indicates remove any configuration matching 'devname',
+	 * which may remove multiple configurations in atypical configurations.
+	 */
+	found = false;
+	mtx_lock(&dumpconf_list_lk);
+	TAILQ_FOREACH_SAFE(di, &dumper_configs, di_next, sdi) {
+		if (dumper_config_match(di, devname, kda)) {
+			found = true;
+			TAILQ_REMOVE(&dumper_configs, di, di_next);
+			free_single_dumper(di);
+		}
 	}
-	explicit_bzero(&dumper, sizeof(dumper));
-	dumpdevname[0] = '\0';
+	mtx_unlock(&dumpconf_list_lk);
+
+	/* Only produce ENOENT if a more targeted match didn't match. */
+	if (!found && kda->kda_index == KDA_REMOVE)
+		return (ENOENT);
 	return (0);
 }
 

Modified: head/sys/netinet/netdump/netdump.h
==============================================================================
--- head/sys/netinet/netdump/netdump.h	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/netinet/netdump/netdump.h	Mon May  6 18:24:07 2019	(r347192)
@@ -60,18 +60,18 @@ struct netdump_ack {
 	uint32_t	na_seqno;	/* Match acks with msgs. */
 } __packed;
 
-struct netdump_conf {
-	struct diocskerneldump_arg ndc_kda;
-	char		ndc_iface[IFNAMSIZ];
-	struct in_addr	ndc_server;
-	struct in_addr	ndc_client;
-	struct in_addr	ndc_gateway;
+struct netdump_conf_freebsd12 {
+	struct diocskerneldump_arg_freebsd12 ndc12_kda;
+	char		ndc12_iface[IFNAMSIZ];
+	struct in_addr	ndc12_server;
+	struct in_addr	ndc12_client;
+	struct in_addr	ndc12_gateway;
 };
 
-#define	_PATH_NETDUMP	"/dev/netdump"
+#define	NETDUMPGCONF_FREEBSD12	_IOR('n', 1, struct netdump_conf_freebsd12)
+#define	NETDUMPSCONF_FREEBSD12	_IOW('n', 2, struct netdump_conf_freebsd12)
 
-#define	NETDUMPGCONF	_IOR('n', 1, struct netdump_conf)
-#define	NETDUMPSCONF	_IOW('n', 2, struct netdump_conf)
+#define	_PATH_NETDUMP	"/dev/netdump"
 
 #ifdef _KERNEL
 #ifdef NETDUMP

Modified: head/sys/netinet/netdump/netdump_client.c
==============================================================================
--- head/sys/netinet/netdump/netdump_client.c	Mon May  6 16:54:35 2019	(r347191)
+++ head/sys/netinet/netdump/netdump_client.c	Mon May  6 18:24:07 2019	(r347192)
@@ -89,7 +89,8 @@ __FBSDID("$FreeBSD$");
 
 static int	 netdump_arp_gw(void);
 static void	 netdump_cleanup(void);
-static int	 netdump_configure(struct netdump_conf *, struct thread *);
+static int	 netdump_configure(struct diocskerneldump_arg *,
+		    struct thread *);
 static int	 netdump_dumper(void *priv __unused, void *virtual,
 		    vm_offset_t physical __unused, off_t offset, size_t length);
 static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp,
@@ -118,10 +119,10 @@ static uint64_t rcvd_acks;
 CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT);
 
 /* Configuration parameters. */
-static struct netdump_conf nd_conf;
-#define	nd_server	nd_conf.ndc_server
-#define	nd_client	nd_conf.ndc_client
-#define	nd_gateway	nd_conf.ndc_gateway
+static struct diocskerneldump_arg nd_conf;
+#define	nd_server	nd_conf.kda_server.in4
+#define	nd_client	nd_conf.kda_client.in4
+#define	nd_gateway	nd_conf.kda_gateway.in4
 
 /* General dynamic settings. */
 static struct ether_addr nd_gw_mac;
@@ -1059,7 +1060,7 @@ static struct cdevsw netdump_cdevsw = {
 static struct cdev *netdump_cdev;
 
 static int
-netdump_configure(struct netdump_conf *conf, struct thread *td)
+netdump_configure(struct diocskerneldump_arg *conf, struct thread *td)
 {
 	struct epoch_tracker et;
 	struct ifnet *ifp;
@@ -1071,7 +1072,7 @@ netdump_configure(struct netdump_conf *conf, struct th
 	}
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
-		if (strcmp(ifp->if_xname, conf->ndc_iface) == 0)
+		if (strcmp(ifp->if_xname, conf->kda_iface) == 0)
 			break;
 	}
 	/* XXX ref */
@@ -1083,7 +1084,7 @@ netdump_configure(struct netdump_conf *conf, struct th
 	if ((if_getflags(ifp) & IFF_UP) == 0)
 		return (ENXIO);
 	if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER)
-		return (EINVAL);
+		return (ENODEV);
 
 	nd_ifp = ifp;
 	netdump_reinit(ifp);
@@ -1135,19 +1136,24 @@ static int
 netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
     int flags __unused, struct thread *td)
 {
-	struct diocskerneldump_arg *kda;
+	struct diocskerneldump_arg kda_copy, *conf;
 	struct dumperinfo dumper;
-	struct netdump_conf *conf;
 	uint8_t *encryptedkey;
 	int error;
 #ifdef COMPAT_FREEBSD11
 	u_int u;
 #endif
+#ifdef COMPAT_FREEBSD12
+	struct diocskerneldump_arg_freebsd12 *kda12;
+	struct netdump_conf_freebsd12 *conf12;
+#endif
 
+	conf = NULL;
 	error = 0;
 	switch (cmd) {
 #ifdef COMPAT_FREEBSD11

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list