changeset 22431:3e6640275fba

Merge pull request #611 from hadfl/upstream_merge/2019112701 Upstream merge/2019112701
author Tobias Oetiker <tobi@oetiker.ch>
date Thu, 28 Nov 2019 09:20:11 +0100
parents f742c788cb10 (current diff) 6c724155cbda (diff)
children a2991c51f814 f3cf19817391
files usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup.ksh usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/setup.ksh
diffstat 73 files changed, 1413 insertions(+), 380 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/boot/sys/boot/efi/libefi/efipart.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/boot/sys/boot/efi/libefi/efipart.c	Thu Nov 28 09:20:11 2019 +0100
@@ -299,11 +299,14 @@
 static pdinfo_t *
 efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
 {
-	pdinfo_t *pd;
+	pdinfo_t *pd, *part;
 
 	STAILQ_FOREACH(pd, pdi, pd_link) {
 		if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
 			return (pd);
+		part = efipart_find_parent(&pd->pd_part, devpath);
+		if (part != NULL)
+			return (part);
 	}
 	return (NULL);
 }
@@ -486,22 +489,65 @@
 	return (0);
 }
 
-static void
-efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node)
+static bool
+efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node)
 {
-	pdinfo_t *pd, *last;
+	pdinfo_t *pd, *ptr;
 
+	if (node == NULL)
+		return (false);
+
+	/* Find our disk device. */
 	STAILQ_FOREACH(pd, &hdinfo, pd_link) {
-		if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) {
-			/* Add the partition. */
-			hd->pd_unit = node->PartitionNumber;
-			hd->pd_parent = pd;
-			hd->pd_devsw = &efipart_hddev;
-			STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
-			return;
+		if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath))
+			break;
+	}
+	if (pd == NULL)
+		return (false);
+
+	/* If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition. */
+	if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) {
+		STAILQ_FOREACH(ptr, &pd->pd_part, pd_link) {
+			if (efi_devpath_is_prefix(ptr->pd_devpath,
+			    hd->pd_devpath))
+				break;
 		}
+		/*
+		 * If we don't find a pdinfo_t for this node, then that
+		 * means that the partition list we got from firmware isn't
+		 * properly ordered. We assume that firmware has ordered it.
+		 * In case we don't find anything, rather than blow up, we
+		 * add this node as another partition.
+		 */
+		if (ptr != NULL)
+			pd = ptr;
+
+		/* Add the partition. */
+		ptr = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
+		if (ptr != NULL)
+			hd->pd_unit = ptr->pd_unit + 1;
+		else
+			hd->pd_unit = 0;
+	} else {
+		/* Add the partition. */
+		hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
 	}
 
+	hd->pd_parent = pd;
+	hd->pd_devsw = &efipart_hddev;
+
+	STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
+	return (true);
+}
+
+static void
+efipart_hdinfo_add(pdinfo_t *hd, EFI_DEVICE_PATH *node)
+{
+	pdinfo_t *last;
+
+	if (efipart_hdinfo_add_node(hd, node))
+		return;
+
 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
 	if (last != NULL)
 		hd->pd_unit = last->pd_unit + 1;
@@ -663,7 +709,7 @@
 			efipart_hdinfo_add(parent, NULL);
 		}
 
-		efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node);
+		efipart_hdinfo_add(hd, node);
 		goto restart;
 	}
 }
--- a/usr/src/cmd/format/auto_sense.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/auto_sense.c	Thu Nov 28 09:20:11 2019 +0100
@@ -226,7 +226,7 @@
 				int	n);
 static	int		adjust_disk_geometry(diskaddr_t capacity, uint_t *cyl,
 				uint_t *nsect, uint_t *nhead);
-static void 		compute_chs_values(diskaddr_t total_capacity,
+static void		compute_chs_values(diskaddr_t total_capacity,
 				diskaddr_t usable_capacity, uint_t *pcylp,
 				uint_t *nheadp, uint_t *nsectp);
 #if defined(_SUNOS_VTOC_8)
@@ -254,6 +254,7 @@
 	struct ctlr_info *ctlr;
 	struct dk_cinfo dkinfo;
 	struct partition_info *part;
+	uint64_t reserved;
 
 	if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) {
 		if (option_msg && diag_msg) {
@@ -286,6 +287,7 @@
 	}
 
 	label->e_parts = vtoc;
+	reserved = efi_reserved_sectors(vtoc);
 
 	/*
 	 * Create a whole hog EFI partition table:
@@ -295,7 +297,7 @@
 	vtoc->efi_parts[0].p_tag = V_USR;
 	vtoc->efi_parts[0].p_start = vtoc->efi_first_u_lba;
 	vtoc->efi_parts[0].p_size = vtoc->efi_last_u_lba - vtoc->efi_first_u_lba
-	    - EFI_MIN_RESV_SIZE + 1;
+	    - reserved + 1;
 
 	/*
 	 * S1-S6 are unassigned slices.
@@ -311,8 +313,8 @@
 	 */
 	vtoc->efi_parts[vtoc->efi_nparts - 1].p_tag = V_RESERVED;
 	vtoc->efi_parts[vtoc->efi_nparts - 1].p_start =
-	    vtoc->efi_last_u_lba - EFI_MIN_RESV_SIZE + 1;
-	vtoc->efi_parts[vtoc->efi_nparts - 1].p_size = EFI_MIN_RESV_SIZE;
+	    vtoc->efi_last_u_lba - reserved + 1;
+	vtoc->efi_parts[vtoc->efi_nparts - 1].p_size = reserved;
 
 	/*
 	 * Now stick all of it into the disk_type struct
@@ -462,7 +464,7 @@
 	efi_gpt_t	*databack = NULL;
 	struct dk_geom	disk_geom;
 	struct dk_minfo	disk_info;
-	efi_gpt_t 	*backsigp;
+	efi_gpt_t	*backsigp;
 	int		fd = cur_file;
 	int		rval = -1;
 	int		efisize = EFI_LABEL_SIZE * 2;
@@ -2190,7 +2192,7 @@
 	}
 
 	if (((*dim1) > lim1) || ((*dim2) > lim2) || ((*dim3) > lim3)) {
-		double 	d[4];
+		double	d[4];
 
 		/*
 		 * Second:
@@ -2269,15 +2271,15 @@
 	 * The following table (in order) illustrates some end result
 	 * calculations:
 	 *
-	 * Maximum number of blocks 		nhead	nsect
+	 * Maximum number of blocks		nhead	nsect
 	 *
 	 * 2097152 (1GB)			64	32
 	 * 16777216 (8GB)			128	32
-	 * 1052819775 (502.02GB)		255  	63
+	 * 1052819775 (502.02GB)		255	63
 	 * 2105639550 (0.98TB)			255	126
-	 * 3158459325 (1.47TB)			255  	189
-	 * 4211279100 (1.96TB)			255  	252
-	 * 5264098875 (2.45TB)			255  	315
+	 * 3158459325 (1.47TB)			255	189
+	 * 4211279100 (1.96TB)			255	252
+	 * 5264098875 (2.45TB)			255	315
 	 * ...
 	 */
 
--- a/usr/src/cmd/format/io.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/io.c	Thu Nov 28 09:20:11 2019 +0100
@@ -38,6 +38,7 @@
 #include <sys/tty.h>
 #include <sys/termio.h>
 #include <sys/termios.h>
+#include <sys/efi_partition.h>
 
 #include "startup.h"
 #include "misc.h"
@@ -1556,8 +1557,11 @@
 		 * either blocks/cyls/megabytes - a convenient fiction.
 		 */
 		if (strcmp(cleantoken, WILD_STRING) == 0) {
-			return (bounds->upper - EFI_MIN_RESV_SIZE -
-			    efi_deflt->start_sector);
+			uint64_t reserved;
+
+			reserved = efi_reserved_sectors(cur_parts->etoc);
+			return (bounds->upper - reserved -
+			    efi_deflt->start_sector + 1);
 		}
 
 		/*
--- a/usr/src/cmd/format/label.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/label.c	Thu Nov 28 09:20:11 2019 +0100
@@ -55,40 +55,22 @@
 #define	WD_NODE		7
 #endif
 
-#ifdef	__STDC__
-/*
- * Prototypes for ANSI C compilers
- */
 static int	do_geometry_sanity_check(void);
-static int	vtoc_to_label(struct dk_label *label, struct extvtoc *vtoc,
-		struct dk_geom *geom, struct dk_cinfo *cinfo);
+static int	vtoc_to_label(struct dk_label *, struct extvtoc *,
+		struct dk_geom *, struct dk_cinfo *);
 extern int	read_extvtoc(int, struct extvtoc *);
 extern int	write_extvtoc(int, struct extvtoc *);
 static int	vtoc64_to_label(struct efi_info *, struct dk_gpt *);
 
-#else	/* __STDC__ */
-
-/*
- * Prototypes for non-ANSI C compilers
- */
-static int	do_geometry_sanity_check();
-static int	vtoc_to_label();
-extern int	read_extvtoc();
-extern int	write_extvtoc();
-static int	vtoc64_to_label();
-
-#endif	/* __STDC__ */
-
 #ifdef	DEBUG
-static void dump_label(struct dk_label *label);
+static void dump_label(struct dk_label *);
 #endif
 
 /*
  * This routine checks the given label to see if it is valid.
  */
 int
-checklabel(label)
-	register struct dk_label *label;
+checklabel(struct dk_label *label)
 {
 
 	/*
@@ -109,12 +91,10 @@
  * the mode it is called in.
  */
 int
-checksum(label, mode)
-	struct	dk_label *label;
-	int	mode;
+checksum(struct	dk_label *label, int mode)
 {
-	register short *sp, sum = 0;
-	register short count = (sizeof (struct dk_label)) / (sizeof (short));
+	short *sp, sum = 0;
+	short count = (sizeof (struct dk_label)) / (sizeof (short));
 
 	/*
 	 * If we are generating a checksum, don't include the checksum
@@ -152,10 +132,9 @@
  * and truncate it there.
  */
 int
-trim_id(id)
-	char	*id;
+trim_id(char *id)
 {
-	register char *c;
+	char *c;
 
 	/*
 	 * Start at the end of the string.  When we match the word ' cyl',
@@ -167,7 +146,8 @@
 			 * Remove any white space.
 			 */
 			for (; (((*(c - 1) == ' ') || (*(c - 1) == '\t')) &&
-				(c >= id)); c--);
+			    (c >= id)); c--)
+				;
 			break;
 		}
 	}
@@ -222,13 +202,15 @@
 SMI_vtoc_to_EFI(int fd, struct dk_gpt **new_vtoc)
 {
 	int i;
-	struct dk_gpt	*efi;
+	struct dk_gpt *efi;
+	uint64_t reserved;
 
 	if (efi_alloc_and_init(fd, EFI_NUMPAR, new_vtoc) != 0) {
 		err_print("SMI vtoc to EFI failed\n");
 		return (-1);
 	}
 	efi = *new_vtoc;
+	reserved = efi_reserved_sectors(efi);
 
 	/*
 	 * create a clear EFI partition table:
@@ -239,7 +221,7 @@
 	efi->efi_parts[0].p_tag = V_USR;
 	efi->efi_parts[0].p_start = efi->efi_first_u_lba;
 	efi->efi_parts[0].p_size = efi->efi_last_u_lba - efi->efi_first_u_lba
-	    - EFI_MIN_RESV_SIZE + 1;
+	    - reserved + 1;
 
 	/*
 	 * s1-s6 are unassigned slices
@@ -255,8 +237,8 @@
 	 */
 	efi->efi_parts[efi->efi_nparts - 1].p_tag = V_RESERVED;
 	efi->efi_parts[efi->efi_nparts - 1].p_start =
-	    efi->efi_last_u_lba - EFI_MIN_RESV_SIZE + 1;
-	efi->efi_parts[efi->efi_nparts - 1].p_size = EFI_MIN_RESV_SIZE;
+	    efi->efi_last_u_lba - reserved + 1;
+	efi->efi_parts[efi->efi_nparts - 1].p_size = reserved;
 
 	return (0);
 }
@@ -1001,14 +983,16 @@
 void
 err_check(struct dk_gpt *vtoc)
 {
-	int			resv_part = -1;
-	int			i, j;
-	diskaddr_t		istart, jstart, isize, jsize, endsect;
-	int			overlap = 0;
+	int		resv_part = -1;
+	int		i, j;
+	diskaddr_t	istart, jstart, isize, jsize, endsect;
+	int		overlap = 0;
+	uint_t		reserved;
 
 	/*
 	 * make sure no partitions overlap
 	 */
+	reserved = efi_reserved_sectors(vtoc);
 	for (i = 0; i < vtoc->efi_nparts; i++) {
 		/* It can't be unassigned and have an actual size */
 		if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) &&
@@ -1026,10 +1010,10 @@
 "found duplicate reserved partition at %d\n", i);
 			}
 			resv_part = i;
-			if (vtoc->efi_parts[i].p_size != EFI_MIN_RESV_SIZE)
+			if (vtoc->efi_parts[i].p_size != reserved)
 				(void) fprintf(stderr,
-"Warning: reserved partition size must be %d sectors\n",
-				    EFI_MIN_RESV_SIZE);
+"Warning: reserved partition size must be %u sectors\n",
+				    reserved);
 		}
 		if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) ||
 		    (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) {
@@ -1088,8 +1072,7 @@
 
 #ifdef	DEBUG
 static void
-dump_label(label)
-	struct dk_label	*label;
+dump_label(struct dk_label *label)
 {
 	int		i;
 
@@ -1141,8 +1124,8 @@
 
 #if defined(_SUNOS_VTOC_8)
 		fmt_print("%c:        cyl=%d, blocks=%d", i+'a',
-			label->dkl_map[i].dkl_cylno,
-			label->dkl_map[i].dkl_nblk);
+		    label->dkl_map[i].dkl_cylno,
+		    label->dkl_map[i].dkl_nblk);
 
 #elif defined(_SUNOS_VTOC_16)
 		fmt_print("%c:        start=%u, blocks=%u", i+'a',
@@ -1153,8 +1136,8 @@
 #endif				/* defined(_SUNOS_VTOC_8) */
 
 		fmt_print(",  tag=%d,  flag=%d",
-			label->dkl_vtoc.v_part[i].p_tag,
-			label->dkl_vtoc.v_part[i].p_flag);
+		    label->dkl_vtoc.v_part[i].p_tag,
+		    label->dkl_vtoc.v_part[i].p_flag);
 		fmt_print("\n");
 	}
 
--- a/usr/src/cmd/format/menu_command.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/menu_command.c	Thu Nov 28 09:20:11 2019 +0100
@@ -575,7 +575,9 @@
 		new_partitiontable(tptr, oldtype);
 	} else if ((index == other_choice) && (cur_label == L_TYPE_EFI)) {
 		uint64_t start_lba = cur_parts->etoc->efi_first_u_lba;
+		uint64_t reserved;
 
+		reserved = efi_reserved_sectors(cur_parts->etoc);
 		maxLBA = get_mlba();
 		cur_parts->etoc->efi_last_lba = maxLBA;
 		cur_parts->etoc->efi_last_u_lba = maxLBA - start_lba;
@@ -585,8 +587,8 @@
 			cur_parts->etoc->efi_parts[i].p_tag = V_UNASSIGNED;
 		}
 		cur_parts->etoc->efi_parts[8].p_start =
-		    maxLBA - start_lba - (1024 * 16);
-		cur_parts->etoc->efi_parts[8].p_size = (1024 * 16);
+		    maxLBA - start_lba - reserved;
+		cur_parts->etoc->efi_parts[8].p_size = reserved;
 		cur_parts->etoc->efi_parts[8].p_tag = V_RESERVED;
 		if (write_label()) {
 			err_print("Write label failed\n");
--- a/usr/src/cmd/format/menu_partition.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/menu_partition.c	Thu Nov 28 09:20:11 2019 +0100
@@ -380,7 +380,7 @@
  * for all the partitions in the current partition map.
  */
 int
-p_print()
+p_print(void)
 {
 	/*
 	 * check if there exists a partition table for the disk.
@@ -411,13 +411,16 @@
 		fmt_print("Total disk cylinders available: %d + %d "
 		    "(reserved cylinders)\n\n", ncyl, acyl);
 	} else if (cur_label == L_TYPE_EFI) {
+		unsigned reserved;
+
+		reserved = efi_reserved_sectors(cur_parts->etoc);
 		fmt_print("Current partition table (%s):\n",
 		    cur_parts->pinfo_name != NULL ?
 		    cur_parts->pinfo_name : "unnamed");
-		fmt_print("Total disk sectors available: %llu + %d "
+		fmt_print("Total disk sectors available: %llu + %u "
 		    "(reserved sectors)\n\n",
-		    cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE -
-		    cur_parts->etoc->efi_first_u_lba + 1, EFI_MIN_RESV_SIZE);
+		    cur_parts->etoc->efi_last_u_lba - reserved -
+		    cur_parts->etoc->efi_first_u_lba + 1, reserved);
 	}
 
 
--- a/usr/src/cmd/format/modify_partition.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/format/modify_partition.c	Thu Nov 28 09:20:11 2019 +0100
@@ -560,53 +560,54 @@
 	char		tmpstr[80];
 	uint64_t	i64;
 	uint64_t	start_lba = map->efi_first_u_lba;
+	uint64_t	reserved;
 
+	reserved = efi_reserved_sectors(map);
 	for (i = 0; i < map->efi_nparts - 1; i++) {
-		if (i == float_part)
+		/* GPT partition 7 is whole disk device, minor node "wd" */
+		if (i == float_part || i == 7)
 			continue;
-		else {
-			ioparam.io_bounds.lower = start_lba;
-			ioparam.io_bounds.upper = map->efi_last_u_lba;
-			efi_deflt.start_sector = ioparam.io_bounds.lower;
-			efi_deflt.end_sector = map->efi_parts[i].p_size;
-			(void) sprintf(tmpstr,
-			    "Enter size of partition %d ", i);
-			i64 = input(FIO_EFI, tmpstr, ':',
-			    &ioparam, (int *)&efi_deflt, DATA_INPUT);
-			if (i64 == 0) {
-			    map->efi_parts[i].p_tag = V_UNASSIGNED;
-			} else if ((i64 != 0) && (map->efi_parts[i].p_tag ==
-				V_UNASSIGNED)) {
-			    map->efi_parts[i].p_tag = V_USR;
-			}
-			if (i64 == 0) {
-			    map->efi_parts[i].p_start = 0;
-			} else {
-			    map->efi_parts[i].p_start = start_lba;
-			}
-			map->efi_parts[i].p_size = i64;
-			start_lba += i64;
+
+		ioparam.io_bounds.lower = start_lba;
+		ioparam.io_bounds.upper = map->efi_last_u_lba;
+		efi_deflt.start_sector = ioparam.io_bounds.lower;
+		efi_deflt.end_sector = map->efi_parts[i].p_size;
+		(void) sprintf(tmpstr, "Enter size of partition %d ", i);
+		i64 = input(FIO_EFI, tmpstr, ':',
+		    &ioparam, (int *)&efi_deflt, DATA_INPUT);
+		if (i64 == 0) {
+			map->efi_parts[i].p_tag = V_UNASSIGNED;
+		} else if ((i64 != 0) && (map->efi_parts[i].p_tag ==
+		    V_UNASSIGNED)) {
+			map->efi_parts[i].p_tag = V_USR;
+		}
+		if (i64 == 0) {
+			map->efi_parts[i].p_start = 0;
+		} else {
+			map->efi_parts[i].p_start = start_lba;
+		}
+		map->efi_parts[i].p_size = i64;
+		start_lba += i64;
+	}
+	map->efi_parts[float_part].p_start = start_lba;
+	map->efi_parts[float_part].p_size = map->efi_last_u_lba + 1 -
+		start_lba - reserved;
+	map->efi_parts[float_part].p_tag = V_USR;
+	if (map->efi_parts[float_part].p_size == 0) {
+		map->efi_parts[float_part].p_size = 0;
+		map->efi_parts[float_part].p_start = 0;
+		map->efi_parts[float_part].p_tag = V_UNASSIGNED;
+		fmt_print("Warning: No space left for HOG\n");
+	}
+
+	for (i = 0; i < map->efi_nparts; i++) {
+		if (map->efi_parts[i].p_tag == V_RESERVED) {
+			map->efi_parts[i].p_start = map->efi_last_u_lba -
+			    reserved + 1;
+			map->efi_parts[i].p_size = reserved;
+			break;
 		}
 	}
-		map->efi_parts[float_part].p_start = start_lba;
-		map->efi_parts[float_part].p_size = map->efi_last_u_lba -
-			start_lba - (1024 * 16);
-		map->efi_parts[float_part].p_tag = V_USR;
-		if (map->efi_parts[float_part].p_size == UINT_MAX64) {
-			map->efi_parts[float_part].p_size = 0;
-			map->efi_parts[float_part].p_start = 0;
-			map->efi_parts[float_part].p_tag = V_UNASSIGNED;
-			fmt_print("Warning: No space left for HOG\n");
-		}
-
-		for (i = 0; i < map->efi_nparts; i++) {
-		    if (map->efi_parts[i].p_tag == V_RESERVED) {
-			map->efi_parts[i].p_start = map->efi_last_u_lba -
-			    (1024 * 16);
-			map->efi_parts[i].p_size = (1024 * 16);
-			break;
-		    }
-		}
 }
 
 
--- a/usr/src/cmd/zdb/zdb.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/cmd/zdb/zdb.c	Thu Nov 28 09:20:11 2019 +0100
@@ -448,7 +448,8 @@
 			    strcmp(attr.za_name,
 			    DSL_CRYPTO_KEY_HMAC_KEY) == 0 ||
 			    strcmp(attr.za_name, DSL_CRYPTO_KEY_IV) == 0 ||
-			    strcmp(attr.za_name, DSL_CRYPTO_KEY_MAC) == 0) {
+			    strcmp(attr.za_name, DSL_CRYPTO_KEY_MAC) == 0 ||
+			    strcmp(attr.za_name, DMU_POOL_CHECKSUM_SALT) == 0) {
 				uint8_t *u8 = prop;
 
 				for (i = 0; i < attr.za_num_integers; i++) {
--- a/usr/src/lib/fm/topo/libtopo/common/libtopo.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/libtopo.h	Thu Nov 28 09:20:11 2019 +0100
@@ -23,7 +23,7 @@
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright (c) 2019, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #ifndef _LIBTOPO_H
@@ -293,6 +293,7 @@
 
 extern const char *topo_strerror(int);
 extern void topo_hdl_strfree(topo_hdl_t *, char *);
+extern void topo_hdl_strfreev(topo_hdl_t *, char **, uint_t);
 extern void topo_debug_set(topo_hdl_t *, const char *, const char *);
 
 /*
--- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers	Thu Nov 28 09:20:11 2019 +0100
@@ -20,7 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 #
 
 #
@@ -77,6 +77,7 @@
 	topo_hdl_prominfo;
 	topo_hdl_strdup;
 	topo_hdl_strfree;
+	topo_hdl_strfreev;
 	topo_hdl_zalloc;
 	topo_led_state_name;
 	topo_led_type_name;
@@ -125,6 +126,7 @@
 	topo_mod_str2nvl;
 	topo_mod_strdup;
 	topo_mod_strfree;
+	topo_mod_strfreev;
 	topo_mod_unload;
 	topo_mod_unregister;
 	topo_mod_walk_init;
--- a/usr/src/lib/fm/topo/libtopo/common/topo_2xml.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_2xml.c	Thu Nov 28 09:20:11 2019 +0100
@@ -318,10 +318,7 @@
 				begin_end_element(fp, Propitem, Value, val[i],
 				    NULL);
 			}
-			for (uint_t i = 0; i < nelem; i++) {
-				topo_hdl_strfree(thp, val[i]);
-			}
-			topo_hdl_free(thp, val, nelem * sizeof (char *));
+			topo_hdl_strfreev(thp, val, nelem);
 
 			end_element(fp, Propval);
 			break;
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h	Thu Nov 28 09:20:11 2019 +0100
@@ -23,7 +23,7 @@
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright (c) 2019, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #ifndef _TOPO_MOD_H
@@ -220,6 +220,7 @@
 extern void topo_mod_free(topo_mod_t *, void *, size_t);
 extern char *topo_mod_strdup(topo_mod_t *, const char *);
 extern void topo_mod_strfree(topo_mod_t *, char *);
+extern void topo_mod_strfreev(topo_mod_t *, char **, uint_t);
 extern int topo_mod_nvalloc(topo_mod_t *, nvlist_t **, uint_t);
 extern int topo_mod_nvdup(topo_mod_t *, nvlist_t *, nvlist_t **);
 
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.map	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.map	Thu Nov 28 09:20:11 2019 +0100
@@ -1,6 +1,6 @@
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 #
 # CDDL HEADER START
 #
@@ -40,6 +40,7 @@
 	topo_mod_nvalloc		{ TYPE = FUNCTION; FLAGS = extern };
 	topo_mod_nvdup			{ TYPE = FUNCTION; FLAGS = extern };
 	topo_mod_strfree		{ TYPE = FUNCTION; FLAGS = extern };
+	topo_mod_strfreev		{ TYPE = FUNCTION; FLAGS = extern };
 	topo_mod_strdup			{ TYPE = FUNCTION; FLAGS = extern };
 
 	topo_mod_clrdebug		{ TYPE = FUNCTION; FLAGS = extern };
--- a/usr/src/lib/fm/topo/libtopo/common/topo_string.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_string.c	Thu Nov 28 09:20:11 2019 +0100
@@ -23,8 +23,9 @@
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
 
 #include <strings.h>
 #include <ctype.h>
@@ -55,6 +56,15 @@
 		topo_hdl_free(thp, s, strlen(s) + 1);
 }
 
+void
+topo_hdl_strfreev(topo_hdl_t *thp, char **strarr, uint_t nelem)
+{
+	for (uint_t i = 0; i < nelem; i++)
+		topo_hdl_strfree(thp, strarr[i]);
+
+	topo_hdl_free(thp, strarr, (nelem * sizeof (char *)));
+}
+
 char *
 topo_mod_strdup(topo_mod_t *mod, const char *s)
 {
@@ -67,6 +77,12 @@
 	topo_hdl_strfree(mod->tm_hdl, s);
 }
 
+void
+topo_mod_strfreev(topo_mod_t *mod, char **strarr, uint_t nelem)
+{
+	topo_hdl_strfreev(mod->tm_hdl, strarr, nelem);
+}
+
 const char *
 topo_strbasename(const char *s)
 {
--- a/usr/src/lib/fm/topo/libtopo/common/topo_xml.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_xml.c	Thu Nov 28 09:20:11 2019 +0100
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #include <libxml/parser.h>
@@ -55,16 +55,6 @@
     tf_pad_t **);
 
 
-static void
-strarr_free(topo_mod_t *mod, char **arr, uint_t nelems)
-{
-	int i;
-
-	for (i = 0; i < nelems; i++)
-		topo_mod_strfree(mod, arr[i]);
-	topo_mod_free(mod, arr, (nelems * sizeof (char *)));
-}
-
 int
 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
     topo_stability_t *rs)
@@ -388,7 +378,7 @@
 		}
 
 		rv = nvlist_add_string_array(nvl, name, strarrbuf, nelems);
-		strarr_free(mp, strarrbuf, nelems);
+		topo_mod_strfreev(mp, strarrbuf, nelems);
 		break;
 	case TOPO_TYPE_FMRI_ARRAY:
 		if ((nvlarrbuf = topo_mod_alloc(mp, (nelems *
--- a/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c	Thu Nov 28 09:20:11 2019 +0100
@@ -23,7 +23,7 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  * Copyright 2019 by Western Digital Corporation
  */
 #include <unistd.h>
@@ -194,14 +194,6 @@
 	topo_mod_unregister(mod);
 }
 
-static void
-strarr_free(topo_mod_t *mod, char **arr, uint_t nelems)
-{
-	for (int i = 0; i < nelems; i++)
-		topo_mod_strfree(mod, arr[i]);
-	topo_mod_free(mod, arr, (nelems * sizeof (char *)));
-}
-
 /*
  * Some platforms (most notably G1/2N) use the 'platform event message' command
  * to manipulate disk fault LEDs over IPMI, but uses the standard sensor
@@ -374,7 +366,7 @@
 
 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		return (-1);
 	}
 
@@ -398,7 +390,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -416,7 +408,7 @@
 			topo_mod_dprintf(mod, "%s does not refer to a full or "
 			    "compact SDR\n", entity_refs[i]);
 			topo_mod_ipmi_rele(mod);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			return (-1);
 	}
 	if ((reading = ipmi_get_sensor_reading(hdl, sensor_num))
@@ -424,18 +416,18 @@
 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
 		    "%s, sensor_num=%d (%s)\n", entity_refs[i], sensor_num,
 		    ipmi_errmsg(hdl));
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
 	if (reading->isr_state_unavailable) {
 		topo_mod_dprintf(mod, "Unavailable sensor %s, sensor_num=%d\n",
 		    entity_refs[i], sensor_num);
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
@@ -503,7 +495,7 @@
 
 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		return (-1);
 	}
 
@@ -527,7 +519,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -540,7 +532,7 @@
 			topo_mod_dprintf(mod, "%s does not refer to a full "
 			    "sensor SDR\n", entity_refs[i]);
 			topo_mod_ipmi_rele(mod);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			return (-1);
 	}
 
@@ -548,7 +540,7 @@
 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
 		    "%s, sensor_num=%d (%s)\n", entity_refs[i],
 		    sensor_num, ipmi_errmsg(hdl));
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -558,10 +550,10 @@
 	    != 0) {
 		topo_mod_dprintf(mod, "Failed to convert sensor reading for "
 		    "sensor %s (%s)\n", entity_refs[i], ipmi_errmsg(hdl));
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		return (-1);
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 
 	(void) snprintf(reading_str, BUFSZ, "%f", conv_reading);
 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
@@ -623,7 +615,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -641,7 +633,7 @@
 		    &mode_in)) != 0) {
 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -649,7 +641,7 @@
 		    mode_in != TOPO_LED_STATE_ON) {
 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
 			    mode_in);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -658,7 +650,7 @@
 			topo_mod_dprintf(mod, "%s: Failed to set LED mode for "
 			    "%s (%s) to %s\n", __func__, entity_refs[i],
 			    ipmi_errmsg(hdl), ledmode ? "ON" : "OFF");
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -670,12 +662,12 @@
 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
 			    "%s (%s)\n", __func__, entity_refs[i],
 			    ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
@@ -744,7 +736,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -762,7 +754,7 @@
 		    &mode_in)) != 0) {
 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -770,7 +762,7 @@
 		    mode_in != TOPO_LED_STATE_ON) {
 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
 			    mode_in);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -781,7 +773,7 @@
 		if (ipmi_sunoem_led_set(hdl, gdl, ledmode) < 0) {
 			topo_mod_dprintf(mod, "Failed to set LED mode for %s "
 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -792,12 +784,12 @@
 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
 			topo_mod_dprintf(mod, "Failed to get LED mode for %s "
 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (ledmode == IPMI_SUNOEM_LED_MODE_SLOW ||
@@ -883,14 +875,14 @@
 	 */
 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		return (-1);
 	}
 
 	if ((sp_devid = ipmi_get_deviceid(hdl)) == NULL) {
 		topo_mod_dprintf(mod, "%s: GET DEVICEID command failed (%s)\n",
 		    __func__, ipmi_errmsg(hdl));
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -908,7 +900,7 @@
 		    &ledmode)) != 0) {
 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -929,7 +921,7 @@
 			}
 
 			if (! found_sdr) {
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -939,7 +931,7 @@
 				topo_mod_dprintf(mod,
 				    "Failed to set LED mode for %s (%s)\n",
 				    entity_refs[i], ipmi_errmsg(hdl));
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -956,7 +948,7 @@
 			}
 
 			if (! found_sdr) {
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -979,7 +971,7 @@
 				topo_mod_dprintf(mod, "%s: Failed to send "
 				    "platform event mesg for %s (%s)\n",
 				    __func__, entity_refs[i], ipmi_errmsg(hdl));
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -1000,7 +992,7 @@
 		}
 
 		if (! found_sdr) {
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -1008,13 +1000,13 @@
 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
 			    "%s (%s)\n", __func__, entity_refs[i],
 			    ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
 		ledmode = mode_in;
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
@@ -1059,7 +1051,7 @@
 
 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		return (-1);
 	}
 	for (i = 0; i < nelems; i++) {
@@ -1074,7 +1066,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -1092,7 +1084,7 @@
 		    &ledmode)) != 0) {
 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -1109,7 +1101,7 @@
 		} else {
 			topo_mod_dprintf(mod, "%s: Invalid LED mode: "
 			    "%d\n", __func__, ledmode);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -1120,7 +1112,7 @@
 			topo_mod_dprintf(mod, "%s: Failed to set "
 			    "sensor reading for %s (%s)\n", __func__,
 			    entity_refs[i], ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -1136,7 +1128,7 @@
 			topo_mod_dprintf(mod, "Failed to get sensor reading "
 			    "for sensor %s (sensor num: %d) (error: %s)\n",
 			    entity_refs[i], cs->is_cs_number, ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -1145,7 +1137,7 @@
 		else
 			ledmode = TOPO_LED_STATE_OFF;
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
@@ -1211,7 +1203,7 @@
 	}
 
 	if (! found_sdr) {
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		topo_mod_ipmi_rele(mod);
 		return (-1);
 	}
@@ -1229,7 +1221,7 @@
 		    &mode_in)) != 0) {
 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
@@ -1241,7 +1233,7 @@
 		if ((sp_devid = ipmi_get_deviceid(hdl)) == NULL) {
 			topo_mod_dprintf(mod, "%s: GET DEVICEID command failed "
 			"(%s)\n", __func__, ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
@@ -1254,7 +1246,7 @@
 			    mode_in != TOPO_LED_STATE_ON) {
 				topo_mod_dprintf(mod, "Invalid property value: "
 				    "%d\n", mode_in);
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 			}
@@ -1263,7 +1255,7 @@
 				topo_mod_dprintf(mod, "Failed to set LED mode "
 				    "for %s (%s)\n", entity_refs[i],
 				    ipmi_errmsg(hdl));
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -1287,7 +1279,7 @@
 				topo_mod_dprintf(mod, "%s: Failed to send "
 				    "platform event mesg for sensor 0 (%s)\n",
 				    __func__, ipmi_errmsg(hdl));
-				strarr_free(mod, entity_refs, nelems);
+				topo_mod_strfreev(mod, entity_refs, nelems);
 				topo_mod_ipmi_rele(mod);
 				return (-1);
 			}
@@ -1300,12 +1292,12 @@
 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
 			    "%s (%s)\n", __func__, entity_refs[i],
 			    ipmi_errmsg(hdl));
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			topo_mod_ipmi_rele(mod);
 			return (-1);
 		}
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	topo_mod_ipmi_rele(mod);
 
 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
@@ -1529,10 +1521,10 @@
 		topo_mod_dprintf(mod, "%s: Failed to set entity_ref property "
 		    "on node: %s=%d (%s)\n", __func__, topo_node_name(fnode),
 		    topo_node_instance(fnode), topo_strerror(err));
-		strarr_free(mod, entity_refs, 1);
+		topo_mod_strfreev(mod, entity_refs, 1);
 		return (topo_mod_seterrno(mod, err));
 	}
-	strarr_free(mod, entity_refs, 1);
+	topo_mod_strfreev(mod, entity_refs, 1);
 
 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
 	    TOPO_PROP_IMMUTABLE, sd->sd_class, &err) != 0) {
@@ -1755,7 +1747,7 @@
 			    "(%s)\n", __func__, entity_refs[i],
 			    ipmi_errmsg(hdl));
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	if (! found_sdr) {
 		topo_mod_ipmi_rele(mod);
 		return (-1);
@@ -1838,7 +1830,7 @@
 out:
 	topo_mod_ipmi_rele(mod);
 	if (ei.ei_list != NULL)
-		strarr_free(mod, ei.ei_list, ei.ei_listsz);
+		topo_mod_strfreev(mod, ei.ei_list, ei.ei_listsz);
 
 	return (ret);
 }
@@ -1904,7 +1896,7 @@
 		default:
 			topo_mod_dprintf(mod, "Invalid 'nparams' argval (%d)\n",
 			    nparams);
-			strarr_free(mod, entity_refs, nelems);
+			topo_mod_strfreev(mod, entity_refs, nelems);
 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
 		}
 		entity_refs[i] = topo_mod_strdup(mod, buf);
@@ -1917,11 +1909,11 @@
 	    nelems) != 0) {
 
 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		nvlist_free(nvl);
 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	*out = nvl;
 
 	return (0);
@@ -1980,11 +1972,11 @@
 	    nvlist_add_string_array(nvl, TOPO_PROP_VAL_VAL, entity_refs, nelems)
 	    != 0) {
 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		nvlist_free(nvl);
 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	*out = nvl;
 
 	return (0);
@@ -2052,11 +2044,11 @@
 	    nvlist_add_string_array(nvl, TOPO_PROP_VAL_VAL, entity_refs, nelems)
 	    != 0) {
 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
-		strarr_free(mod, entity_refs, nelems);
+		topo_mod_strfreev(mod, entity_refs, nelems);
 		nvlist_free(nvl);
 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
 	}
-	strarr_free(mod, entity_refs, nelems);
+	topo_mod_strfreev(mod, entity_refs, nelems);
 	*out = nvl;
 
 	return (0);
--- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_serial.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_serial.c	Thu Nov 28 09:20:11 2019 +0100
@@ -23,6 +23,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -144,7 +147,7 @@
 	 * The components are:
 	 *
 	 * yyyy: JEDEC ID in hex (2 byte manufacture ID, 2 byte continuation
-	 * 		code).
+	 *	code).
 	 *
 	 * ll:   The memory module's manufacturing location.
 	 *
@@ -233,9 +236,7 @@
 		/* topo errno already set */
 		rv = -1;
 	}
-	for (i = 0; i < nelems; i++)
-		topo_mod_strfree(mod, entity_refs[i]);
-	topo_mod_free(mod, entity_refs, (nelems * sizeof (char *)));
+	topo_mod_strfreev(mod, entity_refs, nelems);
 
 	return (rv);
 }
--- a/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem.c	Thu Nov 28 09:20:11 2019 +0100
@@ -24,6 +24,9 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
 
 #include <strings.h>
 #include <umem.h>
@@ -209,18 +212,6 @@
 	return (0);
 }
 
-void
-mem_strarray_free(topo_mod_t *mod, char **arr, size_t dim)
-{
-	int i;
-
-	for (i = 0; i < dim; i++) {
-		if (arr[i] != NULL)
-			topo_mod_strfree(mod, arr[i]);
-	}
-	topo_mod_free(mod, arr, sizeof (char *) * dim);
-}
-
 /*
  * Niagara-1, Niagara-2, and Victoria Falls all have physical address
  * spaces of 40 bits.
--- a/usr/src/man/man2/mmap.2	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/man/man2/mmap.2	Thu Nov 28 09:20:11 2019 +0100
@@ -45,12 +45,12 @@
 .\" Copyright (c) 2009, Sun Microsystems, Inc.  All Rights Reserved.
 .\" Copyright 2013 OmniTI Computer Consulting, Inc. All Rights Reserved.
 .\" Copyright 2016 James S Blachly, MD. All Rights Reserved.
+.\" Copyright 2019 Joyent, Inc.
 .\"
-.TH MMAP 2 "August 29, 2016"
+.TH MMAP 2 "Nov 19, 2019"
 .SH NAME
 mmap \- map pages of memory
 .SH SYNOPSIS
-.LP
 .nf
 #include <sys/mman.h>
 
@@ -59,7 +59,6 @@
 .fi
 
 .SH DESCRIPTION
-.LP
 The \fBmmap()\fR function establishes a mapping between a process's address
 space and a file or shared memory object. The format of the call is as follows:
 .sp
@@ -71,7 +70,7 @@
 The \fBmmap()\fR function establishes a mapping between the address space of
 the process at an address \fIpa\fR for \fIlen\fR bytes to the memory object
 represented by the file descriptor \fIfildes\fR at offset \fIoff\fR for
-\fIlen\fR bytes. The value of \fIpa\fR is a function of the  \fIaddr\fR
+\fIlen\fR bytes. The value of \fIpa\fR is a function of the \fIaddr\fR
 argument and values of \fIflags\fR, further described below. A successful
 \fBmmap()\fR call returns \fIpa\fR as its result. The address range starting at
 \fIpa\fR and continuing for \fIlen\fR bytes will be legitimate for the possible
@@ -105,7 +104,7 @@
 objects. Support for any other type of file is unspecified.
 .sp
 .LP
-The  \fIprot\fR argument determines whether read, write, execute, or some
+The \fIprot\fR argument determines whether read, write, execute, or some
 combination of accesses are permitted to the data being mapped. The \fIprot\fR
 argument should be either \fBPROT_NONE\fR or the bitwise inclusive \fBOR\fR of
 one or more of the other flags in the following table, defined in the header
@@ -165,7 +164,7 @@
 specified in the \fIflags\fR argument as described below.
 .sp
 .LP
-The  \fIflags\fR argument provides other information about the handling of the
+The \fIflags\fR argument provides other information about the handling of the
 mapped data. The value of \fIflags\fR is the bitwise inclusive \fBOR\fR of
 these options, defined in <\fBsys/mman.h\fR>:
 .sp
@@ -304,7 +303,7 @@
 When \fBMAP_ALIGN\fR is set, the system is informed that the alignment of
 \fIpa\fR must be the same as \fIaddr\fR. The alignment value in \fIaddr\fR must
 be 0 or some power of two multiple of page size as returned by
-\fBsysconf\fR(3C). If addr is 0, the system will choose a suitable  alignment.
+\fBsysconf\fR(3C). If addr is 0, the system will choose a suitable alignment.
 .sp
 .LP
 The \fBMAP_NORESERVE\fR option specifies that no swap space be reserved for a
@@ -312,11 +311,11 @@
 mapping reserves swap space equal to the size of the mapping; when the mapping
 is written into, the reserved space is employed to hold private copies of the
 data. A write into a \fBMAP_NORESERVE\fR mapping produces results which depend
-on the current availability of swap  space in the system.  If space is
-available, the write succeeds and a  private copy of the written page is
+on the current availability of swap space in the system.  If space is
+available, the write succeeds and a private copy of the written page is
 created; if space is not available, the write fails and a \fBSIGBUS\fR or
 \fBSIGSEGV\fR signal is delivered to the writing process.  \fBMAP_NORESERVE\fR
-mappings are inherited across  \fBfork()\fR; at the time of the \fBfork()\fR,
+mappings are inherited across \fBfork()\fR; at the time of the \fBfork()\fR,
 swap space is reserved in the child for all private pages that currently exist
 in the parent; thereafter the child's mapping behaves as described above.
 .sp
@@ -358,7 +357,7 @@
 value returned by \fBsysconf()\fR when passed \fB_SC_PAGESIZE\fR or
 \fB_SC_PAGE_SIZE\fR. When \fBMAP_FIXED\fR is specified, the \fIaddr\fR argument
 must also meet these constraints. The system performs mapping operations over
-whole pages. Thus, while the  \fIlen\fR argument need not meet a size or
+whole pages. Thus, while the \fIlen\fR argument need not meet a size or
 alignment constraint, the system will include, in any mapping operation, any
 partial page specified by the range [\fIpa, pa + len\fR).
 .sp
@@ -712,7 +711,6 @@
 .RE
 
 .SH RETURN VALUES
-.LP
 Upon successful completion, the \fBmmap()\fR function returns the address at
 which the mapping was placed (\fIpa\fR); otherwise, it returns a value of
 \fBMAP_FAILED\fR and sets \fBerrno\fR to indicate the error. The symbol
@@ -724,7 +722,6 @@
 \fBENOTSUP\fR, some of the mappings in the address range starting at \fIaddr\fR
 and continuing for \fIlen\fR bytes may have been unmapped.
 .SH ERRORS
-.LP
 The \fBmmap()\fR function will fail if:
 .sp
 .ne 2
@@ -820,7 +817,7 @@
 because it would require more space than the system is able to supply.
 .sp
 The composite size of \fIlen\fR plus the lengths obtained from all previous
-calls to \fBmmap()\fR exceeds \fBRLIMIT_VMEM\fR (see  \fBgetrlimit\fR(2)).
+calls to \fBmmap()\fR exceeds \fBRLIMIT_VMEM\fR (see \fBgetrlimit\fR(2)).
 .RE
 
 .sp
@@ -872,7 +869,6 @@
 .RE
 
 .SH USAGE
-.LP
 Use of \fBmmap()\fR may reduce the amount of memory available to other memory
 allocation functions.
 .sp
@@ -917,7 +913,7 @@
 
 .sp
 .LP
-The following is a rewrite using  \fBmmap()\fR:
+The following is a rewrite using \fBmmap()\fR:
 .sp
 .in +2
 .nf
@@ -929,7 +925,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
 
@@ -946,7 +941,6 @@
 .TE
 
 .SH SEE ALSO
-.LP
 \fBclose\fR(2), \fBexec\fR(2), \fBfcntl\fR(2), \fBfork\fR(2),
 \fBgetrlimit\fR(2), \fBmemcntl\fR(2), \fBmmapobj\fR(2), \fBmprotect\fR(2),
 \fBmunmap\fR(2), \fBshmat\fR(2), \fBlockf\fR(3C), \fBmlockall\fR(3C),
--- a/usr/src/pkg/manifests/system-test-zfstest.mf	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/pkg/manifests/system-test-zfstest.mf	Thu Nov 28 09:20:11 2019 +0100
@@ -53,6 +53,7 @@
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_copies
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_create
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_destroy
+dir path=opt/zfs-tests/tests/functional/cli_root/zfs_diff
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_get
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_inherit
 dir path=opt/zfs-tests/tests/functional/cli_root/zfs_load-key
@@ -891,6 +892,19 @@
 file \
     path=opt/zfs-tests/tests/functional/cli_root/zfs_destroy/zfs_destroy_common.kshlib \
     mode=0444
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/setup mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/socket mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_changes \
+    mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_cliargs \
+    mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_encrypted \
+    mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_timestamp \
+    mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_types \
+    mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/cleanup mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/setup mode=0555
 file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/zfs_get_001_pos \
@@ -2603,6 +2617,8 @@
 file path=opt/zfs-tests/tests/functional/mount/cleanup mode=0555
 file path=opt/zfs-tests/tests/functional/mount/setup mode=0555
 file path=opt/zfs-tests/tests/functional/mount/umount_001 mode=0555
+file path=opt/zfs-tests/tests/functional/mount/umount_002 mode=0555
+file path=opt/zfs-tests/tests/functional/mount/umount_unlinked_drain mode=0555
 file path=opt/zfs-tests/tests/functional/mount/umountall_001 mode=0555
 file path=opt/zfs-tests/tests/functional/mv_files/cleanup mode=0555
 file path=opt/zfs-tests/tests/functional/mv_files/mv_files.cfg mode=0444
--- a/usr/src/test/zfs-tests/runfiles/delphix.run	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/test/zfs-tests/runfiles/delphix.run	Thu Nov 28 09:20:11 2019 +0100
@@ -144,6 +144,11 @@
     'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
     'zfs_destroy_016_pos']
 
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+    'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
 [/opt/zfs-tests/tests/functional/cli_root/zfs_get]
 tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
     'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
@@ -516,7 +521,7 @@
     'mmp_on_zdb']
 
 [/opt/zfs-tests/tests/functional/mount]
-tests = ['umount_001', 'umountall_001']
+tests = ['umount_001', 'umount_002', 'umountall_001']
 
 [/opt/zfs-tests/tests/functional/mv_files]
 tests = ['mv_files_001_pos', 'mv_files_002_pos']
--- a/usr/src/test/zfs-tests/runfiles/omnios.run	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/test/zfs-tests/runfiles/omnios.run	Thu Nov 28 09:20:11 2019 +0100
@@ -144,6 +144,11 @@
     'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
     'zfs_destroy_016_pos']
 
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+    'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
 [/opt/zfs-tests/tests/functional/cli_root/zfs_get]
 tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
     'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
@@ -515,7 +520,7 @@
     'mmp_on_zdb']
 
 [/opt/zfs-tests/tests/functional/mount]
-tests = ['umount_001', 'umountall_001']
+tests = ['umount_001', 'umount_002', 'umountall_001']
 
 [/opt/zfs-tests/tests/functional/mv_files]
 tests = ['mv_files_001_pos', 'mv_files_002_pos']
--- a/usr/src/test/zfs-tests/runfiles/openindiana.run	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/test/zfs-tests/runfiles/openindiana.run	Thu Nov 28 09:20:11 2019 +0100
@@ -144,6 +144,11 @@
     'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
     'zfs_destroy_016_pos']
 
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+    'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
 [/opt/zfs-tests/tests/functional/cli_root/zfs_get]
 tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
     'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
@@ -515,7 +520,7 @@
     'mmp_on_zdb']
 
 [/opt/zfs-tests/tests/functional/mount]
-tests = ['umount_001', 'umountall_001']
+tests = ['umount_001', 'umount_002', 'umountall_001']
 
 [/opt/zfs-tests/tests/functional/mv_files]
 tests = ['mv_files_001_pos', 'mv_files_002_pos']
--- a/usr/src/test/zfs-tests/runfiles/smartos.run	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/test/zfs-tests/runfiles/smartos.run	Thu Nov 28 09:20:11 2019 +0100
@@ -106,6 +106,11 @@
     'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
     'zfs_destroy_016_pos']
 
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+    'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
 [/opt/zfs-tests/tests/functional/cli_root/zfs_get]
 tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
     'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
@@ -447,7 +452,7 @@
     'mmp_on_zdb']
 
 [/opt/zfs-tests/tests/functional/mount]
-tests = ['umount_001', 'umountall_001']
+tests = ['umount_001', 'umount_002', 'umountall_001']
 
 [/opt/zfs-tests/tests/functional/mv_files]
 tests = ['mv_files_001_pos', 'mv_files_002_pos']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/Makefile	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/zfs-tests
+TARGETDIR = $(ROOTOPTPKG)/tests/functional/cli_root/zfs_diff
+
+PROG = socket
+OBJS = $(PROG:%=%.o)
+SRCS = $(OBJS:%.o=%.c)
+
+include $(SRC)/cmd/Makefile.cmd
+
+$(TARGETDIR)/$(PROG) :=	FILEMODE = 0555
+
+CPPFLAGS = -D__EXTENSIONS__
+LDLIBS = -lsocket
+
+$(PROG): $(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(POST_PROCESS)
+
+install: $(TARGETDIR)/$(PROG)
+
+clobber: clean
+	-$(RM) $(PROG)
+
+clean:
+	-$(RM) $(OBJS)
+
+$(TARGETDIR)/$(PROG): $(TARGETDIR)
+
+include $(SRC)/test/zfs-tests/Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/setup.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,21 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+DISK=${DISKS%% *}
+
+default_volume_setup $DISK
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/socket.c	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,59 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* ARGSUSED */
+int
+main(int argc, char *argv[])
+{
+	struct sockaddr_un sock;
+	int fd;
+	char *path;
+	size_t size;
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s /path/to/socket\n", argv[0]);
+		exit(1);
+	}
+	path = argv[1];
+	size =  sizeof (sock.sun_path);
+	(void) strncpy(sock.sun_path, (char *)path, size - 1);
+	sock.sun_path[size - 1] = '\0';
+
+	sock.sun_family = AF_UNIX;
+	if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
+		perror("socket");
+		return (1);
+	}
+	if (bind(fd, (struct sockaddr *)&sock, sizeof (struct sockaddr_un))) {
+		perror("bind");
+		return (1);
+	}
+	if (close(fd)) {
+		perror("close");
+		return (1);
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_changes.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,96 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should display changes correctly.
+#
+# STRATEGY:
+# 1. Create a filesystem with both files and directories, then snapshot it
+# 2. Generate different types of changes and verify 'zfs diff' displays them
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+	log_must zfs destroy -r "$DATASET"
+	rm -f "$FILEDIFF"
+}
+
+#
+# Verify object $path has $change type
+# Valid types are:
+# * - (The path has been removed)
+# * + (The path has been created)
+# * M (The path has been modified)
+# * R (The path has been renamed)
+#
+function verify_object_change # <path> <change>
+{
+	path="$1"
+	change="$2"
+
+	log_must eval "zfs diff -F $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+	diffchg="$(nawk -v path="$path" '$NF == path { print $1 }' < $FILEDIFF)"
+	if [[ "$diffchg" != "$change" ]]; then
+		log_fail "Unexpected change for $path ('$diffchg' != '$change')"
+	else
+		log_note "Object $path change is displayed correctly: '$change'"
+	fi
+}
+
+log_assert "'zfs diff' should display changes correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS/fs"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+
+# 1. Create a filesystem with both files and directories, then snapshot it
+log_must zfs create $DATASET
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+log_must touch "$MNTPOINT/fremoved"
+log_must touch "$MNTPOINT/frenamed"
+log_must touch "$MNTPOINT/fmodified"
+log_must mkdir "$MNTPOINT/dremoved"
+log_must mkdir "$MNTPOINT/drenamed"
+log_must mkdir "$MNTPOINT/dmodified"
+log_must zfs snapshot "$TESTSNAP1"
+
+# 2. Generate different types of changes and verify 'zfs diff' displays them
+log_must rm -f "$MNTPOINT/fremoved"
+log_must mv "$MNTPOINT/frenamed" "$MNTPOINT/frenamed.new"
+log_must touch "$MNTPOINT/fmodified"
+log_must rmdir "$MNTPOINT/dremoved"
+log_must mv "$MNTPOINT/drenamed" "$MNTPOINT/drenamed.new"
+log_must touch "$MNTPOINT/dmodified/file"
+log_must touch "$MNTPOINT/fcreated"
+log_must mkdir "$MNTPOINT/dcreated"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_change "$MNTPOINT/fremoved" "-"
+verify_object_change "$MNTPOINT/frenamed.new" "R"
+verify_object_change "$MNTPOINT/fmodified" "M"
+verify_object_change "$MNTPOINT/fcreated" "+"
+verify_object_change "$MNTPOINT/dremoved" "-"
+verify_object_change "$MNTPOINT/drenamed.new" "R"
+verify_object_change "$MNTPOINT/dmodified" "M"
+verify_object_change "$MNTPOINT/dcreated" "+"
+
+log_pass "'zfs diff' displays changes correctly."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_cliargs.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,80 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should only work with supported options.
+#
+# STRATEGY:
+# 1. Create two snapshots
+# 2. Verify every supported option is accepted
+# 3. Verify supported options raise an error with unsupported arguments
+# 4. Verify other unsupported options raise an error
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+	for snap in $TESTSNAP1 $TESTSNAP2; do
+		if snapexists "$snap"; then
+			log_must zfs destroy "$snap"
+		fi
+	done
+}
+
+log_assert "'zfs diff' should only work with supported options."
+log_onexit cleanup
+
+typeset goodopts=("" "-F" "-H" "-t" "-FH" "-Ft" "-Ht" "-FHt")
+typeset badopts=("-f" "-h" "-h" "-T" "-Fx" "-Ho" "-tT" "-")
+
+DATASET="$TESTPOOL/$TESTFS"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+
+# 1. Create two snapshots
+log_must zfs snapshot "$TESTSNAP1"
+log_must zfs snapshot "$TESTSNAP2"
+
+# 2. Verify every supported option is accepted
+for opt in ${goodopts[@]}
+do
+	log_must zfs diff $opt "$TESTSNAP1"
+	log_must zfs diff $opt "$TESTSNAP1" "$DATASET"
+	log_must zfs diff $opt "$TESTSNAP1" "$TESTSNAP2"
+done
+
+# 3. Verify supported options raise an error with unsupported arguments
+for opt in ${goodopts[@]}
+do
+	log_mustnot zfs diff $opt
+	log_mustnot zfs diff $opt "$DATASET"
+	log_mustnot zfs diff $opt "$DATASET@noexists"
+	log_mustnot zfs diff $opt "$DATASET" "$TESTSNAP1"
+	log_mustnot zfs diff $opt "$TESTSNAP2" "$TESTSNAP1"
+done
+
+# 4. Verify other unsupported options raise an error
+for opt in ${badopts[@]}
+do
+	log_mustnot zfs diff $opt "$TESTSNAP1" "$DATASET"
+	log_mustnot zfs diff $opt "$TESTSNAP1" "$TESTSNAP2"
+done
+
+log_pass "'zfs diff' only works with supported options."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_encrypted.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,54 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018, Datto Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should work with encrypted datasets
+#
+# STRATEGY:
+# 1. Create an encrypted dataset
+# 2. Create two snapshots of the dataset
+# 3. Perform 'zfs diff -Ft' and verify no errors occur
+# 4. Perform the same test on a dataset with large dnodes
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+	datasetexists $TESTPOOL/$TESTFS1 && \
+		log_must zfs destroy -r $TESTPOOL/$TESTFS1
+}
+
+log_assert "'zfs diff' should work with encrypted datasets"
+log_onexit cleanup
+
+# 1. Create an encrypted dataset
+log_must eval "echo 'password' | zfs create -o encryption=on \
+	-o keyformat=passphrase $TESTPOOL/$TESTFS1"
+MNTPOINT="$(get_prop mountpoint $TESTPOOL/$TESTFS1)"
+
+# 2. Create two snapshots of the dataset
+log_must zfs snapshot $TESTPOOL/$TESTFS1@snap1
+log_must touch "$MNTPOINT/file"
+log_must zfs snapshot $TESTPOOL/$TESTFS1@snap2
+
+# 3. Perform 'zfs diff' and verify no errors occur
+log_must zfs diff -Ft $TESTPOOL/$TESTFS1@snap1 $TESTPOOL/$TESTFS1@snap2
+
+log_pass "'zfs diff' works with encrypted datasets"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_timestamp.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,100 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff -t' should display inode change time correctly.
+#
+# STRATEGY:
+# 1. Create a snapshot
+# 2. Create some files with a random delay and snapshot the filesystem again
+# 3. Verify 'zfs diff -t' correctly display timestamps
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+	for snap in $TESTSNAP1 $TESTSNAP2; do
+		if snapexists "$snap"; then
+			log_must zfs destroy "$snap"
+		fi
+	done
+	find "$MNTPOINT" -type f -delete
+	rm -f "$FILEDIFF"
+}
+
+#
+# Creates $count files in $fspath. Waits a random delay between each file.
+#
+function create_random # <fspath> <count>
+{
+	fspath="$1"
+	typeset -i count="$2"
+	typeset -i i=0
+
+	while (( i < count )); do
+		log_must touch "$fspath/file$i"
+		sleep $(random 3)
+		(( i = i + 1 ))
+	done
+}
+
+log_assert "'zfs diff -t' should display inode change time correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+FILENUM=5
+
+# 1. Create a snapshot
+log_must zfs snapshot "$TESTSNAP1"
+
+# 2. Create some files with a random delay and snapshot the filesystem again
+create_random "$MNTPOINT" $FILENUM
+log_must zfs snapshot "$TESTSNAP2"
+
+# 3. Verify 'zfs diff -t' correctly display timestamps
+typeset -i count=0
+log_must eval "zfs diff -t $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+nawk '{print substr($1,1,index($1,".")-1)" "$NF}' < "$FILEDIFF" | while read line
+do
+	read ctime file <<< "$line"
+
+	# If path from 'zfs diff' is not a file (could be xattr object) skip it
+	if [[ ! -f "$file" ]]; then
+		continue;
+	fi
+
+	filetime="$(stat -c '%Z' $file)"
+	if [[ "$filetime" != "$ctime" ]]; then
+		log_fail "Unexpected ctime for file $file ($filetime != $ctime)"
+	else
+		log_note "Correct ctime read on $file: $ctime"
+	fi
+
+	(( i = i + 1 ))
+done
+if [[ $i != $FILENUM ]]; then
+	log_fail "Wrong number of files verified ($i != $FILENUM)"
+fi
+
+log_pass "'zfs diff -t' displays inode change time correctly."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_types.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,126 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff -F' shows different object types correctly.
+#
+# STRATEGY:
+# 1. Prepare a dataset
+# 2. Create different objects and verify 'zfs diff -F' shows the correct type
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+	log_must zfs destroy -r "$DATASET"
+	rm -f "$FILEDIFF"
+}
+
+#
+# Verify object at $path is of type $symbol using 'zfs diff -F'
+# Valid types are:
+# * B (Block device)
+# * C (Character device)
+# * / (Directory)
+# * > (Door)
+# * | (Named pipe)
+# * @ (Symbolic link)
+# * P (Event port)
+# * = (Socket)
+# * F (Regular file)
+#
+function verify_object_class # <path> <symbol>
+{
+	path="$1"
+	symbol="$2"
+
+	log_must eval "zfs diff -F $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+	diffsym="$(nawk -v path="$path" '$NF == path { print $2 }' < $FILEDIFF)"
+	if [[ "$diffsym" != "$symbol" ]]; then
+		log_fail "Unexpected type for $path ('$diffsym' != '$symbol')"
+	else
+		log_note "Object $path type is correctly displayed as '$symbol'"
+	fi
+
+	log_must zfs destroy "$TESTSNAP1"
+	log_must zfs destroy "$TESTSNAP2"
+}
+
+log_assert "'zfs diff -F' should show different object types correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS/fs"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+MAJOR=$(stat -c %t /dev/null)
+MINOR=$(stat -c %T /dev/null)
+
+# 1. Prepare a dataset
+log_must zfs create $DATASET
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+log_must zfs set devices=on $DATASET
+log_must zfs set xattr=on $DATASET
+
+# 2. Create different objects and verify 'zfs diff -F' shows the correct type
+# 2. F (Regular file)
+log_must zfs snapshot "$TESTSNAP1"
+log_must touch "$MNTPOINT/file"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/file" "F"
+
+# 2. @ (Symbolic link)
+log_must zfs snapshot "$TESTSNAP1"
+log_must ln -s "$MNTPOINT/file" "$MNTPOINT/link"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/link" "@"
+
+# 2. B (Block device)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/bdev" b $MAJOR $MINOR
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/bdev" "B"
+
+# 2. C (Character device)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/cdev" c $MAJOR $MINOR
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/cdev" "C"
+
+# 2. | (Named pipe)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/fifo" p
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/fifo" "|"
+
+# 2. / (Directory)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mkdir "$MNTPOINT/dir"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/dir" "/"
+
+# 2. = (Socket)
+log_must zfs snapshot "$TESTSNAP1"
+log_must $STF_SUITE/tests/functional/cli_root/zfs_diff/socket "$MNTPOINT/sock"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/sock" "="
+
+log_pass "'zfs diff -F' shows different object types correctly."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/mount/umount_002.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,52 @@
+#! /usr/bin/ksh -p
+#
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Nexenta Systems, Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# zfs umount should not fail because flushing of DNLC
+# uses async implementation of zfs_inactive
+#
+# STRATEGY:
+# 1. Call zfs unmount/mount to be sure DNLC is empty
+# 2. Create a lot of files
+# 3. Call zfs unmount command
+# 4. Make sure the file systems were unmounted
+# 5. Mount them back
+#
+
+for fs in 1 2 3; do
+	log_must mounted $TESTDIR.$fs
+	log_must zfs umount $TESTPOOL/$TESTFS.$fs
+	log_must unmounted $TESTDIR.$fs
+	log_must zfs mount $TESTPOOL/$TESTFS.$fs
+	log_must mounted $TESTDIR.$fs
+
+	for fn in $(seq 1 8096); do
+		log_must dd if=/dev/urandom of=$TESTDIR.$fs/file$fn bs=1024 \
+		    count=1
+	done
+
+	log_must zfs umount $TESTPOOL/$TESTFS.$fs
+	log_must unmounted $TESTDIR.$fs
+	log_must zfs mount $TESTPOOL/$TESTFS.$fs
+	log_must mounted $TESTDIR.$fs
+	log_must rm -f $TESTDIR.$fs/file*
+done
+
+log_pass "All file systems are unmounted"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/zfs-tests/tests/functional/mount/umount_unlinked_drain.ksh	Thu Nov 28 09:20:11 2019 +0100
@@ -0,0 +1,119 @@
+#!/bin/ksh -p
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Datto Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# Test async unlinked drain to ensure mounting is not held up when there are
+# entries in the unlinked set. We also try to test that the list is able to be
+# filled up and drained at the same time.
+#
+# STRATEGY:
+# 1. Use zfs_unlink_suspend_progress tunable to disable freeing to build up
+#    the unlinked set
+# 2. Make sure mount happens even when there are entries in the unlinked set
+# 3. Drain and build up the unlinked list at the same time to test for races
+#
+
+function cleanup
+{
+	log_must set_tunable32 zfs_unlink_suspend_progress $default_unlink_sp
+	for fs in $(seq 1 3); do
+		mounted $TESTDIR.$fs || zfs mount $TESTPOOL/$TESTFS.$fs
+		rm -f $TESTDIR.$fs/file-*
+		zfs set xattr=on $TESTPOOL/$TESTFS.$fs
+	done
+}
+
+function unlinked_size_is
+{
+	MAX_ITERS=5 # iteration to do before we consider reported number stable
+	iters=0
+	last_usize=0
+	while [[ $iters -le $MAX_ITERS ]]; do
+		kstat_file=$(grep -nrwl /proc/spl/kstat/zfs/$2/objset-0x* -e $3)
+		nunlinks=`cat $kstat_file | grep nunlinks | awk '{print $3}'`
+		nunlinked=`cat $kstat_file | grep nunlinked | awk '{print $3}'`
+		usize=$(($nunlinks - $nunlinked))
+		if [[ $iters == $MAX_ITERS && $usize == $1 ]]; then
+			return 0
+		fi
+		if [[ $usize == $last_usize ]]; then
+			(( iters++ ))
+		else
+			iters=0
+		fi
+		last_usize=$usize
+	done
+
+	log_note "Unexpected unlinked set size: $last_usize, expected $1"
+	return 1
+}
+
+
+UNLINK_SP_PARAM=/sys/module/zfs/parameters/zfs_unlink_suspend_progress
+default_unlink_sp=$(get_tunable zfs_unlink_suspend_progress)
+
+log_onexit cleanup
+
+log_assert "Unlinked list drain does not hold up mounting of fs"
+
+for fs in 1 2 3; do
+	set -A xattrs on sa off
+	for xa in ${xattrs[@]}; do
+		# setup fs and ensure all deleted files got into unliked set
+		log_must mounted $TESTDIR.$fs
+
+		log_must zfs set xattr=$xa $TESTPOOL/$TESTFS.$fs
+
+		if [[ $xa == off ]]; then
+			for fn in $(seq 1 175); do
+				log_must mkfile 128k $TESTDIR.$fs/file-$fn
+			done
+		else
+			log_must xattrtest -f 175 -x 3 -r -k -p $TESTDIR.$fs
+		fi
+
+		log_must set_tunable32 zfs_unlink_suspend_progress 1
+		log_must unlinked_size_is 0 $TESTPOOL $TESTPOOL/$TESTFS.$fs
+
+		# build up unlinked set
+		for fn in $(seq 1 100); do
+			log_must eval "rm $TESTDIR.$fs/file-$fn &"
+		done
+		log_must unlinked_size_is 100 $TESTPOOL $TESTPOOL/$TESTFS.$fs
+
+		# test that we can mount fs without emptying the unlinked list
+		log_must zfs umount $TESTPOOL/$TESTFS.$fs
+		log_must unmounted $TESTDIR.$fs
+		log_must zfs mount $TESTPOOL/$TESTFS.$fs
+		log_must mounted $TESTDIR.$fs
+		log_must unlinked_size_is 100 $TESTPOOL $TESTPOOL/$TESTFS.$fs
+
+		# confirm we can drain and add to unlinked set at the same time
+		log_must set_tunable32 zfs_unlink_suspend_progress 0
+		log_must zfs umount $TESTPOOL/$TESTFS.$fs
+		log_must zfs mount $TESTPOOL/$TESTFS.$fs
+		for fn in $(seq 101 175); do
+			log_must eval "rm $TESTDIR.$fs/file-$fn &"
+		done
+		log_must unlinked_size_is 0 $TESTPOOL $TESTPOOL/$TESTFS.$fs
+	done
+done
+
+log_pass "Confirmed unlinked list drain does not hold up mounting of fs"
--- a/usr/src/tools/smatch/Makefile	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/Makefile	Thu Nov 28 09:20:11 2019 +0100
@@ -20,7 +20,7 @@
 #
 
 PROG = smatch
-SPARSE_VERSION = 0.6.1-rc1-il-1
+SPARSE_VERSION = 0.6.1-rc1-il-2
 
 include ../Makefile.tools
 
--- a/usr/src/tools/smatch/src/Makefile	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/Makefile	Thu Nov 28 09:20:11 2019 +0100
@@ -1,4 +1,4 @@
-VERSION=0.6.1-rc1-il-1
+VERSION=0.6.1-rc1-il-2
 
 ########################################################################
 # The following variables can be overwritten from the command line
@@ -9,7 +9,7 @@
 LD = $(CC)
 AR = ar
 
-CFLAGS ?= -O2 -g
+CFLAGS ?= -g
 
 DESTDIR ?=
 PREFIX ?= $(HOME)
--- a/usr/src/tools/smatch/src/check_arm64_tagged.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/check_arm64_tagged.c	Thu Nov 28 09:20:11 2019 +0100
@@ -154,8 +154,13 @@
 
 static bool rl_is_tagged(struct range_list *rl)
 {
-	sval_t invalid = { .type = &ullong_ctype, .value = (1ULL << 56) };
-	sval_t invalid_kernel = { .type = &ullong_ctype, .value = (0xff8ULL << 52) };
+	sval_t invalid;
+	sval_t invalid_kernel;
+
+	invalid.type = &ullong_ctype;
+	invalid.value = 1ULL << 56;
+	invalid_kernel.type = &ullong_ctype;
+	invalid_kernel.value = 0xff8ULL << 52;
 
 	/*
 	 * We only care for tagged addresses, thus ignore anything where the
--- a/usr/src/tools/smatch/src/ident-list.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/ident-list.h	Thu Nov 28 09:20:11 2019 +0100
@@ -31,7 +31,7 @@
 /* C99 keywords */
 IDENT(restrict); IDENT(__restrict); IDENT(__restrict__);
 IDENT(_Bool);
-IDENT(_Complex);
+IDENT_RESERVED(_Complex);
 IDENT_RESERVED(_Imaginary);
 
 /* C11 keywords */
--- a/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Thu Nov 28 09:20:11 2019 +0100
@@ -3,6 +3,7 @@
 dtrace_disx86
 elf32exec
 elfexec
+emlxs_sli4_process_unsol_rcv
 iscsi_ioctl
 lm_idle_chk
 ld64_sym_validate
--- a/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions	Thu Nov 28 09:20:11 2019 +0100
@@ -1,8 +1,6 @@
 /*
- * The below functions cause smatch to fail with "turning off implications after
- * 60 seconds" or similar, generally because they're too large for it to handle.
- *
- * This will disable analysis altogether.
+ * These are specific functions that are generally too complex for smatch to
+ * reasonably handle.
  */
 
 /* libast */
--- a/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Thu Nov 28 09:20:11 2019 +0100
@@ -681,10 +681,15 @@
 static struct range_list *strip_negatives(struct range_list *rl)
 {
 	sval_t min = rl_min(rl);
-	sval_t minus_one = { .type = rl_type(rl), .value = -1 };
-	sval_t over = { .type = rl_type(rl), .value = INT_MAX + 1ULL };
+	sval_t minus_one;
+	sval_t over;
 	sval_t max = sval_type_max(rl_type(rl));
 
+	minus_one.type = rl_type(rl);
+	minus_one.value = -1;
+	over.type = rl_type(rl);
+	over.value = INT_MAX + 1ULL;
+
 	if (!rl)
 		return NULL;
 
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c	Thu Nov 28 09:20:11 2019 +0100
@@ -221,6 +221,9 @@
 
 	dp->dp_vnrele_taskq = taskq_create("zfs_vn_rele_taskq", 1, minclsyspri,
 	    1, 4, 0);
+	dp->dp_unlinked_drain_taskq = taskq_create("z_unlinked_drain",
+	    max_ncpus, minclsyspri, max_ncpus, INT_MAX,
+	    TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
 
 	return (dp);
 }
@@ -402,6 +405,7 @@
 
 	rrw_destroy(&dp->dp_config_rwlock);
 	mutex_destroy(&dp->dp_lock);
+	taskq_destroy(dp->dp_unlinked_drain_taskq);
 	taskq_destroy(dp->dp_vnrele_taskq);
 	if (dp->dp_blkstats != NULL)
 		kmem_free(dp->dp_blkstats, sizeof (zfs_all_blkstats_t));
@@ -1077,6 +1081,12 @@
 	return (dp->dp_vnrele_taskq);
 }
 
+taskq_t *
+dsl_pool_unlinked_drain_taskq(dsl_pool_t *dp)
+{
+	return (dp->dp_unlinked_drain_taskq);
+}
+
 /*
  * Walk through the pool-wide zap object of temporary snapshot user holds
  * and release them.
--- a/usr/src/uts/common/fs/zfs/sys/dsl_pool.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_pool.h	Thu Nov 28 09:20:11 2019 +0100
@@ -93,6 +93,7 @@
 	struct dsl_dataset *dp_origin_snap;
 	uint64_t dp_root_dir_obj;
 	taskq_t *dp_vnrele_taskq;
+	struct taskq *dp_unlinked_drain_taskq;
 
 	/* No lock needed - sync context only */
 	blkptr_t dp_meta_rootbp;
@@ -173,6 +174,7 @@
 boolean_t dsl_pool_need_dirty_delay(dsl_pool_t *dp);
 
 taskq_t *dsl_pool_vnrele_taskq(dsl_pool_t *dp);
+taskq_t *dsl_pool_unlinked_drain_taskq(dsl_pool_t *dp);
 
 int dsl_pool_user_hold(dsl_pool_t *dp, uint64_t dsobj,
     const char *tag, uint64_t now, dmu_tx_t *tx);
--- a/usr/src/uts/common/fs/zfs/sys/zfs_dir.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_dir.h	Thu Nov 28 09:20:11 2019 +0100
@@ -63,6 +63,7 @@
 extern boolean_t zfs_dirempty(znode_t *);
 extern void zfs_unlinked_add(znode_t *, dmu_tx_t *);
 extern void zfs_unlinked_drain(zfsvfs_t *zfsvfs);
+extern void zfs_unlinked_drain_stop_wait(zfsvfs_t *zfsvfs);
 extern int zfs_sticky_remove_access(znode_t *, znode_t *, cred_t *cr);
 extern int zfs_get_xattrdir(znode_t *, vnode_t **, cred_t *, int);
 extern int zfs_make_xattrdir(znode_t *, vattr_t *, vnode_t **, cred_t *);
--- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Thu Nov 28 09:20:11 2019 +0100
@@ -75,6 +75,8 @@
 	boolean_t	z_use_fuids;	/* version allows fuids */
 	boolean_t	z_replay;	/* set during ZIL replay */
 	boolean_t	z_use_sa;	/* version allow system attributes */
+	boolean_t	z_draining;	/* is true when drain is active */
+	boolean_t	z_drain_cancel; /* signal the unlinked drain to stop */
 	uint64_t	z_version;	/* ZPL version */
 	uint64_t	z_shares_dir;	/* hidden shares dir */
 	kmutex_t	z_lock;
@@ -88,6 +90,7 @@
 	sa_attr_type_t	*z_attr_table;	/* SA attr mapping->id */
 #define	ZFS_OBJ_MTX_SZ	64
 	kmutex_t	z_hold_mtx[ZFS_OBJ_MTX_SZ];	/* znode hold locks */
+	taskqid_t	z_drain_task;	/* task id for the unlink drain task */
 };
 
 /*
--- a/usr/src/uts/common/fs/zfs/zfs_dir.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/zfs_dir.c	Thu Nov 28 09:20:11 2019 +0100
@@ -480,20 +480,23 @@
  * Clean up any znodes that had no links when we either crashed or
  * (force) umounted the file system.
  */
-void
-zfs_unlinked_drain(zfsvfs_t *zfsvfs)
+static void
+zfs_unlinked_drain_task(void *arg)
 {
+	zfsvfs_t *zfsvfs = arg;
 	zap_cursor_t	zc;
 	zap_attribute_t zap;
 	dmu_object_info_t doi;
 	znode_t		*zp;
 	int		error;
 
+	ASSERT3B(zfsvfs->z_draining, ==, B_TRUE);
+
 	/*
 	 * Interate over the contents of the unlinked set.
 	 */
 	for (zap_cursor_init(&zc, zfsvfs->z_os, zfsvfs->z_unlinkedobj);
-	    zap_cursor_retrieve(&zc, &zap) == 0;
+	    zap_cursor_retrieve(&zc, &zap) == 0 && !zfsvfs->z_drain_cancel;
 	    zap_cursor_advance(&zc)) {
 
 		/*
@@ -523,9 +526,52 @@
 			continue;
 
 		zp->z_unlinked = B_TRUE;
+
 		VN_RELE(ZTOV(zp));
+		ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE);
 	}
 	zap_cursor_fini(&zc);
+
+	zfsvfs->z_draining = B_FALSE;
+	zfsvfs->z_drain_task = TASKQID_INVALID;
+}
+
+/*
+ * Sets z_draining then tries to dispatch async unlinked drain.
+ * If that fails executes synchronous unlinked drain.
+ */
+void
+zfs_unlinked_drain(zfsvfs_t *zfsvfs)
+{
+	ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE);
+	ASSERT3B(zfsvfs->z_draining, ==, B_FALSE);
+
+	zfsvfs->z_draining = B_TRUE;
+	zfsvfs->z_drain_cancel = B_FALSE;
+
+	zfsvfs->z_drain_task = taskq_dispatch(
+	    dsl_pool_unlinked_drain_taskq(dmu_objset_pool(zfsvfs->z_os)),
+	    zfs_unlinked_drain_task, zfsvfs, TQ_SLEEP);
+	if (zfsvfs->z_drain_task == TASKQID_INVALID) {
+		zfs_dbgmsg("async zfs_unlinked_drain dispatch failed");
+		zfs_unlinked_drain_task(zfsvfs);
+	}
+}
+
+/*
+ * Wait for the unlinked drain taskq task to stop. This will interrupt the
+ * unlinked set processing if it is in progress.
+ */
+void
+zfs_unlinked_drain_stop_wait(zfsvfs_t *zfsvfs)
+{
+	ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE);
+
+	while (zfsvfs->z_draining) {
+		zfsvfs->z_drain_cancel = B_TRUE;
+		taskq_wait(dsl_pool_unlinked_drain_taskq(
+		    dmu_objset_pool(zfsvfs->z_os)));
+	}
 }
 
 /*
@@ -1109,7 +1155,7 @@
 int
 zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
 {
-	uid_t  		uid;
+	uid_t		uid;
 	uid_t		downer;
 	uid_t		fowner;
 	zfsvfs_t	*zfsvfs = zdp->z_zfsvfs;
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Thu Nov 28 09:20:11 2019 +0100
@@ -1200,6 +1200,10 @@
 		return (error);
 	}
 
+	zfsvfs->z_drain_task = TASKQID_INVALID;
+	zfsvfs->z_draining = B_FALSE;
+	zfsvfs->z_drain_cancel = B_TRUE;
+
 	*zfvp = zfsvfs;
 	return (0);
 }
@@ -1228,10 +1232,11 @@
 		 * allow replays to succeed.
 		 */
 		readonly = zfsvfs->z_vfs->vfs_flag & VFS_RDONLY;
-		if (readonly != 0)
+		if (readonly != 0) {
 			zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY;
-		else
+		} else {
 			zfs_unlinked_drain(zfsvfs);
+		}
 
 		/*
 		 * Parse and replay the intent log.
@@ -2038,6 +2043,8 @@
 {
 	znode_t	*zp;
 
+	zfs_unlinked_drain_stop_wait(zfsvfs);
+
 	rrm_enter(&zfsvfs->z_teardown_lock, RW_WRITER, FTAG);
 
 	if (!unmounting) {
@@ -2359,6 +2366,16 @@
 	}
 	mutex_exit(&zfsvfs->z_znodes_lock);
 
+	if (((zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) == 0) &&
+	    !zfsvfs->z_unmounted) {
+		/*
+		 * zfs_suspend_fs() could have interrupted freeing
+		 * of dnodes. We need to restart this freeing so
+		 * that we don't "leak" the space.
+		 */
+		zfs_unlinked_drain(zfsvfs);
+	}
+
 bail:
 	/* release the VOPs */
 	rw_exit(&zfsvfs->z_teardown_inactive_lock);
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c	Thu Nov 28 09:20:11 2019 +0100
@@ -100,6 +100,12 @@
 
 static kmem_cache_t *znode_cache = NULL;
 
+/*
+ * This is used by the test suite so that it can delay znodes from being
+ * freed in order to inspect the unlinked set.
+ */
+int zfs_unlink_suspend_progress = 0;
+
 /*ARGSUSED*/
 static void
 znode_evict_error(dmu_buf_t *dbuf, void *user_ptr)
@@ -1416,7 +1422,8 @@
 	 */
 	if (zp->z_unlinked) {
 		ASSERT(!zfsvfs->z_issnap);
-		if ((zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) == 0) {
+		if ((zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) == 0 &&
+		    !zfs_unlink_suspend_progress) {
 			mutex_exit(&zp->z_lock);
 			ZFS_OBJ_HOLD_EXIT(zfsvfs, z_id);
 			zfs_rmnode(zp);
--- a/usr/src/uts/common/io/blkdev/blkdev.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/io/blkdev/blkdev.c	Thu Nov 28 09:20:11 2019 +0100
@@ -122,9 +122,10 @@
  * There are 4 instance global locks d_ocmutex, d_ksmutex, d_errmutex and
  * d_statemutex. As well a q_iomutex per waitq/runq pair.
  *
- * Currently, there is no lock hierarchy. Nowhere do we ever own more than
- * one lock, any change needs to be documented here with a defined
- * hierarchy.
+ * Lock Hierarchy
+ * --------------
+ * The only two locks which may be held simultaneously are q_iomutex and
+ * d_ksmutex. In all cases q_iomutex must be acquired before d_ksmutex.
  */
 
 #define	BD_MAXPART	64
@@ -495,20 +496,35 @@
 bd_errstats_setstr(kstat_named_t *k, char *str, size_t len, char *alt)
 {
 	char	*tmp;
+	size_t	km_len;
 
 	if (KSTAT_NAMED_STR_PTR(k) == NULL) {
-		if (len > 0) {
-			tmp = kmem_alloc(len + 1, KM_SLEEP);
-			(void) strlcpy(tmp, str, len + 1);
-		} else {
-			tmp = alt;
-		}
+		if (len > 0)
+			km_len = strnlen(str, len);
+		else if (alt != NULL)
+			km_len = strlen(alt);
+		else
+			return;
+
+		tmp = kmem_alloc(km_len + 1, KM_SLEEP);
+		bcopy(len > 0 ? str : alt, tmp, km_len);
+		tmp[km_len] = '\0';
 
 		kstat_named_setstr(k, tmp);
 	}
 }
 
 static void
+bd_errstats_clrstr(kstat_named_t *k)
+{
+	if (KSTAT_NAMED_STR_PTR(k) == NULL)
+		return;
+
+	kmem_free(KSTAT_NAMED_STR_PTR(k), KSTAT_NAMED_STR_BUFLEN(k));
+	kstat_named_setstr(k, NULL);
+}
+
+static void
 bd_init_errstats(bd_t *bd, bd_drive_t *drive)
 {
 	struct bd_errstats	*est = bd->d_kerr;
@@ -535,6 +551,22 @@
 }
 
 static void
+bd_fini_errstats(bd_t *bd)
+{
+	struct bd_errstats	*est = bd->d_kerr;
+
+	mutex_enter(&bd->d_errmutex);
+
+	bd_errstats_clrstr(&est->bd_model);
+	bd_errstats_clrstr(&est->bd_vid);
+	bd_errstats_clrstr(&est->bd_pid);
+	bd_errstats_clrstr(&est->bd_revision);
+	bd_errstats_clrstr(&est->bd_serial);
+
+	mutex_exit(&bd->d_errmutex);
+}
+
+static void
 bd_queues_free(bd_t *bd)
 {
 	uint32_t i;
@@ -773,6 +805,7 @@
 	}
 
 	if (bd->d_errstats != NULL) {
+		bd_fini_errstats(bd);
 		kstat_delete(bd->d_errstats);
 		bd->d_errstats = NULL;
 	} else {
@@ -1636,6 +1669,10 @@
 
 	while ((bq->q_qactive < bq->q_qsize) &&
 	    ((xi = list_remove_head(&bq->q_waitq)) != NULL)) {
+		mutex_enter(&bd->d_ksmutex);
+		kstat_waitq_to_runq(bd->d_kiop);
+		mutex_exit(&bd->d_ksmutex);
+
 		bq->q_qactive++;
 		list_insert_tail(&bq->q_runq, xi);
 
@@ -1647,10 +1684,6 @@
 
 		mutex_exit(&bq->q_iomutex);
 
-		mutex_enter(&bd->d_ksmutex);
-		kstat_waitq_to_runq(bd->d_kiop);
-		mutex_exit(&bd->d_ksmutex);
-
 		rv = xi->i_func(bd->d_private, &xi->i_public);
 		if (rv != 0) {
 			bp = xi->i_bp;
@@ -1658,11 +1691,13 @@
 			biodone(bp);
 
 			atomic_inc_32(&bd->d_kerr->bd_transerrs.value.ui32);
+
+			mutex_enter(&bq->q_iomutex);
+
 			mutex_enter(&bd->d_ksmutex);
 			kstat_runq_exit(bd->d_kiop);
 			mutex_exit(&bd->d_ksmutex);
 
-			mutex_enter(&bq->q_iomutex);
 			bq->q_qactive--;
 			list_remove(&bq->q_runq, xi);
 			bd_xfer_free(xi);
@@ -1685,13 +1720,15 @@
 	xi->i_qnum = q;
 
 	mutex_enter(&bq->q_iomutex);
+
 	list_insert_tail(&bq->q_waitq, xi);
-	mutex_exit(&bq->q_iomutex);
 
 	mutex_enter(&bd->d_ksmutex);
 	kstat_waitq_enter(bd->d_kiop);
 	mutex_exit(&bd->d_ksmutex);
 
+	mutex_exit(&bq->q_iomutex);
+
 	bd_sched(bd, bq);
 }
 
@@ -1704,13 +1741,14 @@
 
 	mutex_enter(&bq->q_iomutex);
 	bq->q_qactive--;
-	list_remove(&bq->q_runq, xi);
-	mutex_exit(&bq->q_iomutex);
 
 	mutex_enter(&bd->d_ksmutex);
 	kstat_runq_exit(bd->d_kiop);
 	mutex_exit(&bd->d_ksmutex);
 
+	list_remove(&bq->q_runq, xi);
+	mutex_exit(&bq->q_iomutex);
+
 	if (err == 0) {
 		if (bp->b_flags & B_READ) {
 			atomic_inc_uint(&bd->d_kiop->reads);
--- a/usr/src/uts/common/io/vioif/vioif.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/io/vioif/vioif.c	Thu Nov 28 09:20:11 2019 +0100
@@ -13,6 +13,7 @@
  * Copyright 2013 Nexenta Inc.  All rights reserved.
  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
  * Copyright 2019 Joyent, Inc.
+ * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org>
  */
 
 /* Based on the NetBSD virtio driver by Minoura Makoto. */
@@ -62,6 +63,7 @@
 #include <sys/ethernet.h>
 #include <sys/vlan.h>
 #include <sys/sysmacros.h>
+#include <sys/smbios.h>
 
 #include <sys/dlpi.h>
 #include <sys/taskq.h>
@@ -180,6 +182,13 @@
 uint_t vioif_reclaim_ms = 200;
 
 /*
+ * Allow the operator to override the kinds of interrupts we'll use for
+ * vioif.  This value defaults to -1 so that it can be overridden to 0 in
+ * /etc/system.
+ */
+int vioif_allowed_int_types = -1;
+
+/*
  * DMA attribute template for transmit and receive buffers.  The SGL entry
  * count will be modified before using the template.  Note that these
  * allocations are aligned so that VIOIF_HEADER_SKIP places the IP header in
@@ -1576,6 +1585,45 @@
 }
 
 static int
+vioif_select_interrupt_types(void)
+{
+	id_t id;
+	smbios_system_t sys;
+	smbios_info_t info;
+
+	if (vioif_allowed_int_types != -1) {
+		/*
+		 * If this value was tuned via /etc/system or the debugger,
+		 * use the provided value directly.
+		 */
+		return (vioif_allowed_int_types);
+	}
+
+	if ((id = smbios_info_system(ksmbios, &sys)) == SMB_ERR ||
+	    smbios_info_common(ksmbios, id, &info) == SMB_ERR) {
+		/*
+		 * The system may not have valid SMBIOS data, so ignore a
+		 * failure here.
+		 */
+		return (0);
+	}
+
+	if (strcmp(info.smbi_manufacturer, "Google") == 0 &&
+	    strcmp(info.smbi_product, "Google Compute Engine") == 0) {
+		/*
+		 * An undiagnosed issue with the Google Compute Engine (GCE)
+		 * hypervisor exists.  In this environment, no RX interrupts
+		 * are received if MSI-X handlers are installed.  This does not
+		 * appear to be true for the Virtio SCSI driver.  Fixed
+		 * interrupts do appear to work, so we fall back for now:
+		 */
+		return (DDI_INTR_TYPE_FIXED);
+	}
+
+	return (0);
+}
+
+static int
 vioif_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 {
 	int ret;
@@ -1605,7 +1653,8 @@
 		goto fail;
 	}
 
-	if (virtio_init_complete(vio, 0) != DDI_SUCCESS) {
+	if (virtio_init_complete(vio, vioif_select_interrupt_types()) !=
+	    DDI_SUCCESS) {
 		dev_err(dip, CE_WARN, "failed to complete Virtio init");
 		goto fail;
 	}
--- a/usr/src/uts/common/io/virtio/virtio_impl.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/io/virtio/virtio_impl.h	Thu Nov 28 09:20:11 2019 +0100
@@ -258,7 +258,7 @@
 /*
  * This structure is variously known as the "used" ring, or the device-owned
  * portion of the queue structure.  It is used by the device to return
- * completed descriptor chains to the device.
+ * completed descriptor chains to the driver.
  */
 struct virtio_vq_device {
 	uint16_t			vqde_flags;
--- a/usr/src/uts/common/io/virtio/virtio_main.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/io/virtio/virtio_main.c	Thu Nov 28 09:20:11 2019 +0100
@@ -323,7 +323,9 @@
 
 /*
  * This function must be called by the driver once it has completed early setup
- * calls.
+ * calls.  The value of "allowed_interrupt_types" is a mask of interrupt types
+ * (DDI_INTR_TYPE_MSIX, etc) that we'll try to use when installing handlers, or
+ * the special value 0 to allow the system to use any available type.
  */
 int
 virtio_init_complete(virtio_t *vio, int allowed_interrupt_types)
--- a/usr/src/uts/common/os/cap_util.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/os/cap_util.c	Thu Nov 28 09:20:11 2019 +0100
@@ -1298,7 +1298,7 @@
 cu_cpu_kstat_create(pghw_t *pg, cu_cntr_info_t *cntr_info)
 {
 	kstat_t		*ks;
-	char 		*sharing = pghw_type_string(pg->pghw_hw);
+	char		*sharing = pghw_type_string(pg->pghw_hw);
 	char		name[KSTAT_STRLEN + 1];
 
 	/*
@@ -1417,7 +1417,7 @@
 	 * cpu_call() will call func on the CPU specified with given argument
 	 * and return func's return value in last argument
 	 */
-	cpu_call(cp, (cpu_call_func_t)func, arg, (uintptr_t)&error);
+	cpu_call(cp, (cpu_call_func_t)(uintptr_t)func, arg, (uintptr_t)&error);
 	return (error);
 }
 
@@ -1471,7 +1471,7 @@
 	 */
 	retval = 0;
 	if (move_to)
-		(void) cu_cpu_run(cp, (cu_cpu_func_t)kcpc_read,
+		(void) cu_cpu_run(cp, (cu_cpu_func_t)(uintptr_t)kcpc_read,
 		    (uintptr_t)cu_cpu_update_stats);
 	else {
 		retval = kcpc_read((kcpc_update_func_t)cu_cpu_update_stats);
--- a/usr/src/uts/common/vm/seg_kmem.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/common/vm/seg_kmem.c	Thu Nov 28 09:20:11 2019 +0100
@@ -440,7 +440,7 @@
 	panic("segkmem_badop");
 }
 
-#define	SEGKMEM_BADOP(t)	(t(*)())segkmem_badop
+#define	SEGKMEM_BADOP(t)	(t(*)())(uintptr_t)segkmem_badop
 
 /*ARGSUSED*/
 static faultcode_t
@@ -1224,7 +1224,7 @@
 segkmem_free_one_lp(caddr_t addr, size_t size)
 {
 	page_t		*pp, *rootpp = NULL;
-	pgcnt_t 	pgs_left = btopr(size);
+	pgcnt_t		pgs_left = btopr(size);
 
 	ASSERT(size == segkmem_lpsize);
 
@@ -1424,7 +1424,7 @@
 	pgcnt_t		nlpages = size >> segkmem_lpshift;
 	size_t		lpsize = segkmem_lpsize;
 	caddr_t		addr = inaddr;
-	pgcnt_t 	npages = btopr(size);
+	pgcnt_t		npages = btopr(size);
 	int		i;
 
 	ASSERT(vmp == heap_lp_arena);
--- a/usr/src/uts/i86pc/io/apix/apix.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/io/apix/apix.c	Thu Nov 28 09:20:11 2019 +0100
@@ -612,7 +612,7 @@
 	/* add nmi handler - least priority nmi handler */
 	LOCK_INIT_CLEAR(&apic_nmi_lock);
 
-	if (!psm_add_nmintr(0, (avfunc) apic_nmi_intr,
+	if (!psm_add_nmintr(0, apic_nmi_intr,
 	    "apix NMI handler", (caddr_t)NULL))
 		cmn_err(CE_WARN, "apix: Unable to add nmi handler");
 
--- a/usr/src/uts/i86pc/io/cbe.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/io/cbe.c	Thu Nov 28 09:20:11 2019 +0100
@@ -68,15 +68,15 @@
 
 void cbe_hres_tick(void);
 
-int
-cbe_softclock(void)
+uint_t
+cbe_softclock(caddr_t arg1 __unused, caddr_t arg2 __unused)
 {
 	cyclic_softint(CPU, CY_LOCK_LEVEL);
 	return (1);
 }
 
-int
-cbe_low_level(void)
+uint_t
+cbe_low_level(caddr_t arg1 __unused, caddr_t arg2 __unused)
 {
 	cpu_t *cpu = CPU;
 
@@ -90,8 +90,8 @@
  * spurious calls, it would not matter if we called cyclic_fire() in both
  * cases.
  */
-int
-cbe_fire(void)
+uint_t
+cbe_fire(caddr_t arg1 __unused, caddr_t arg2 __unused)
 {
 	cpu_t *cpu = CPU;
 	processorid_t me = cpu->cpu_id, i;
@@ -346,21 +346,21 @@
 	cyclic_init(&cbe, cbe_timer_resolution);
 	mutex_exit(&cpu_lock);
 
-	(void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
+	(void) add_avintr(NULL, CBE_HIGH_PIL, cbe_fire,
 	    "cbe_fire_master", cbe_vector, 0, NULL, NULL, NULL);
 
 	if (psm_get_ipivect != NULL) {
-		(void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
+		(void) add_avintr(NULL, CBE_HIGH_PIL, cbe_fire,
 		    "cbe_fire_slave",
 		    (*psm_get_ipivect)(CBE_HIGH_PIL, PSM_INTR_IPI_HI),
 		    0, NULL, NULL, NULL);
 	}
 
 	(void) add_avsoftintr((void *)&cbe_clock_hdl, CBE_LOCK_PIL,
-	    (avfunc)cbe_softclock, "softclock", NULL, NULL);
+	    cbe_softclock, "softclock", NULL, NULL);
 
 	(void) add_avsoftintr((void *)&cbe_low_hdl, CBE_LOW_PIL,
-	    (avfunc)cbe_low_level, "low level", NULL, NULL);
+	    cbe_low_level, "low level", NULL, NULL);
 
 	mutex_enter(&cpu_lock);
 
--- a/usr/src/uts/i86pc/io/hpet_acpi.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/io/hpet_acpi.c	Thu Nov 28 09:20:11 2019 +0100
@@ -65,9 +65,8 @@
 static void hpet_timer_alloc(uint32_t *allocated_timers, uint32_t n);
 static void hpet_timer_set_up(hpet_info_t *hip, uint32_t timer_n,
     uint32_t interrupt);
-static uint_t hpet_isr(char *arg);
-static uint32_t hpet_install_interrupt_handler(uint_t (*func)(char *),
-    int vector);
+static uint_t hpet_isr(caddr_t, caddr_t);
+static uint32_t hpet_install_interrupt_handler(avfunc func, int vector);
 static void hpet_uninstall_interrupt_handler(void);
 static void hpet_expire_all(void);
 static boolean_t hpet_guaranteed_schedule(hrtime_t required_wakeup_time);
@@ -350,8 +349,7 @@
 static void
 hpet_uninstall_interrupt_handler(void)
 {
-	rem_avintr(NULL, CBE_HIGH_PIL, (avfunc)&hpet_isr,
-	    hpet_info.cstate_timer.intr);
+	rem_avintr(NULL, CBE_HIGH_PIL, &hpet_isr, hpet_info.cstate_timer.intr);
 }
 
 static int
@@ -610,11 +608,11 @@
  * apic_init() psm_ops entry point.
  */
 static uint32_t
-hpet_install_interrupt_handler(uint_t (*func)(char *), int vector)
+hpet_install_interrupt_handler(avfunc func, int vector)
 {
 	uint32_t retval;
 
-	retval = add_avintr(NULL, CBE_HIGH_PIL, (avfunc)func, "HPET Timer",
+	retval = add_avintr(NULL, CBE_HIGH_PIL, func, "HPET Timer",
 	    vector, NULL, NULL, NULL, NULL);
 	if (retval == 0) {
 		cmn_err(CE_WARN, "!hpet_acpi: add_avintr() failed");
@@ -1001,9 +999,8 @@
  * This ISR runs on one CPU which pokes other CPUs out of Deep C-state as
  * needed.
  */
-/* ARGSUSED */
 static uint_t
-hpet_isr(char *arg)
+hpet_isr(caddr_t arg __unused, caddr_t arg1 __unused)
 {
 	uint64_t	timer_status;
 	uint64_t	timer_mask;
--- a/usr/src/uts/i86pc/io/pcplusmp/apic_common.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic_common.c	Thu Nov 28 09:20:11 2019 +0100
@@ -364,17 +364,17 @@
 	    (apic_reg_ops->apic_read(APIC_PCINT_VECT) & ~APIC_LVT_MASK));
 }
 
-/*ARGSUSED*/
 static int
-apic_cmci_enable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
+apic_cmci_enable(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
 	apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect);
 	return (0);
 }
 
-/*ARGSUSED*/
 static int
-apic_cmci_disable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
+apic_cmci_disable(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
 	apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect | AV_MASK);
 	return (0);
@@ -498,7 +498,7 @@
 
 /*ARGSUSED1*/
 int
-apic_cpu_start(processorid_t cpun, caddr_t arg)
+apic_cpu_start(processorid_t cpun, caddr_t arg __unused)
 {
 	ASSERT(MUTEX_HELD(&cpu_lock));
 
@@ -524,7 +524,7 @@
  */
 /*ARGSUSED1*/
 int
-apic_cpu_stop(processorid_t cpun, caddr_t arg)
+apic_cpu_stop(processorid_t cpun, caddr_t arg __unused)
 {
 	int		rc;
 	cpu_t		*cp;
@@ -645,15 +645,13 @@
 	return (apic_pir_vect);
 }
 
-/*ARGSUSED*/
 void
-apic_set_idlecpu(processorid_t cpun)
+apic_set_idlecpu(processorid_t cpun __unused)
 {
 }
 
-/*ARGSUSED*/
 void
-apic_unset_idlecpu(processorid_t cpun)
+apic_unset_idlecpu(processorid_t cpun __unused)
 {
 }
 
@@ -806,21 +804,20 @@
 }
 
 /* apic NMI handler */
-/*ARGSUSED*/
-void
-apic_nmi_intr(caddr_t arg, struct regs *rp)
+uint_t
+apic_nmi_intr(caddr_t arg __unused, caddr_t arg1 __unused)
 {
 	nmi_action_t action = nmi_action;
 
 	if (apic_shutdown_processors) {
 		apic_disable_local_apic();
-		return;
+		return (DDI_INTR_CLAIMED);
 	}
 
 	apic_error |= APIC_ERR_NMI;
 
 	if (!lock_try(&apic_nmi_lock))
-		return;
+		return (DDI_INTR_CLAIMED);
 	apic_num_nmis++;
 
 	/*
@@ -861,6 +858,7 @@
 	}
 
 	lock_clear(&apic_nmi_lock);
+	return (DDI_INTR_CLAIMED);
 }
 
 processorid_t
@@ -1286,7 +1284,7 @@
  * after filesystems are all unmounted.
  */
 void
-apic_preshutdown(int cmd, int fcn)
+apic_preshutdown(int cmd __unused, int fcn __unused)
 {
 	APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
 	    cmd, fcn, apic_poweroff_method, apic_enable_acpi));
@@ -1640,16 +1638,14 @@
 	}
 }
 
-/*ARGSUSED*/
 static void
-apic_record_ioapic_rdt(void *intrmap_private, ioapic_rdt_t *irdt)
+apic_record_ioapic_rdt(void *intrmap_private __unused, ioapic_rdt_t *irdt)
 {
 	irdt->ir_hi <<= APIC_ID_BIT_OFFSET;
 }
 
-/*ARGSUSED*/
 static void
-apic_record_msi(void *intrmap_private, msi_regs_t *mregs)
+apic_record_msi(void *intrmap_private __unused, msi_regs_t *mregs)
 {
 	mregs->mr_addr = MSI_ADDR_HDR |
 	    (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
--- a/usr/src/uts/i86pc/os/cpupm/cpupm_throttle.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/cpupm/cpupm_throttle.c	Thu Nov 28 09:20:11 2019 +0100
@@ -153,9 +153,11 @@
 /*
  * Transition the current processor to the requested throttling state.
  */
-static void
-cpupm_tstate_transition(uint32_t req_state)
+static int
+cpupm_tstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
+	uint32_t req_state = arg1;
 	cpupm_mach_state_t *mach_state =
 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
@@ -174,7 +176,7 @@
 	 */
 	ctrl = CPU_ACPI_TSTATE_CTRL(req_tstate);
 	if (write_ctrl(handle, ctrl) != 0) {
-		return;
+		return (0);
 	}
 
 	/*
@@ -182,7 +184,7 @@
 	 * no status value comparison is required.
 	 */
 	if (CPU_ACPI_TSTATE_STAT(req_tstate) == 0) {
-		return;
+		return (0);
 	}
 
 	/* Wait until switch is complete, but bound the loop just in case. */
@@ -197,11 +199,14 @@
 	if (CPU_ACPI_TSTATE_STAT(req_tstate) != stat) {
 		DTRACE_PROBE(throttle_transition_incomplete);
 	}
+	return (0);
 }
 
 static void
 cpupm_throttle(cpuset_t set,  uint32_t throtl_lvl)
 {
+	xc_arg_t xc_arg = (xc_arg_t)throtl_lvl;
+
 	/*
 	 * If thread is already running on target CPU then just
 	 * make the transition request. Otherwise, we'll need to
@@ -209,12 +214,12 @@
 	 */
 	kpreempt_disable();
 	if (CPU_IN_SET(set, CPU->cpu_id)) {
-		cpupm_tstate_transition(throtl_lvl);
+		cpupm_tstate_transition(xc_arg, 0, 0);
 		CPUSET_DEL(set, CPU->cpu_id);
 	}
 	if (!CPUSET_ISNULL(set)) {
-		xc_call((xc_arg_t)throtl_lvl, 0, 0,
-		    CPUSET2BV(set), (xc_func_t)cpupm_tstate_transition);
+		xc_call(xc_arg, 0, 0,
+		    CPUSET2BV(set), cpupm_tstate_transition);
 	}
 	kpreempt_enable();
 }
--- a/usr/src/uts/i86pc/os/cpupm/pwrnow.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/cpupm/pwrnow.c	Thu Nov 28 09:20:11 2019 +0100
@@ -110,9 +110,11 @@
 /*
  * Transition the current processor to the requested state.
  */
-static void
-pwrnow_pstate_transition(uint32_t req_state)
+static int
+pwrnow_pstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
+	uint32_t req_state = (uint32_t)arg1;
 	cpupm_mach_state_t *mach_state =
 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
@@ -137,6 +139,7 @@
 
 	mach_state->ms_pstate.cma_state.pstate = req_state;
 	cpu_set_curr_clock((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000);
+	return (0);
 }
 
 static void
@@ -149,12 +152,12 @@
 	 */
 	kpreempt_disable();
 	if (CPU_IN_SET(set, CPU->cpu_id)) {
-		pwrnow_pstate_transition(req_state);
+		(void) pwrnow_pstate_transition(req_state, 0, 0);
 		CPUSET_DEL(set, CPU->cpu_id);
 	}
 	if (!CPUSET_ISNULL(set)) {
 		xc_call((xc_arg_t)req_state, 0, 0,
-		    CPUSET2BV(set), (xc_func_t)pwrnow_pstate_transition);
+		    CPUSET2BV(set), pwrnow_pstate_transition);
 	}
 	kpreempt_enable();
 }
--- a/usr/src/uts/i86pc/os/cpupm/speedstep.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/cpupm/speedstep.c	Thu Nov 28 09:20:11 2019 +0100
@@ -126,9 +126,11 @@
 /*
  * Transition the current processor to the requested state.
  */
-void
-speedstep_pstate_transition(uint32_t req_state)
+int
+speedstep_pstate_transition(xc_arg_t arg1, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
+	uint32_t req_state = (uint32_t)arg1;
 	cpupm_mach_state_t *mach_state =
 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
@@ -152,6 +154,7 @@
 
 	mach_state->ms_pstate.cma_state.pstate = req_state;
 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
+	return (0);
 }
 
 static void
@@ -164,12 +167,12 @@
 	 */
 	kpreempt_disable();
 	if (CPU_IN_SET(set, CPU->cpu_id)) {
-		speedstep_pstate_transition(req_state);
+		(void) speedstep_pstate_transition(req_state, 0, 0);
 		CPUSET_DEL(set, CPU->cpu_id);
 	}
 	if (!CPUSET_ISNULL(set)) {
 		xc_call((xc_arg_t)req_state, 0, 0, CPUSET2BV(set),
-		    (xc_func_t)speedstep_pstate_transition);
+		    speedstep_pstate_transition);
 	}
 	kpreempt_enable();
 }
--- a/usr/src/uts/i86pc/os/dtrace_subr.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/dtrace_subr.c	Thu Nov 28 09:20:11 2019 +0100
@@ -134,9 +134,10 @@
 }
 
 static int
-dtrace_xcall_func(dtrace_xcall_t func, void *arg)
+dtrace_xcall_func(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3 __unused)
 {
-	(*func)(arg);
+	dtrace_xcall_t func = (dtrace_xcall_t)arg1;
+	(*func)((void*)arg2);
 
 	return (0);
 }
@@ -157,7 +158,7 @@
 
 	kpreempt_disable();
 	xc_sync((xc_arg_t)func, (xc_arg_t)arg, 0, CPUSET2BV(set),
-	    (xc_func_t)dtrace_xcall_func);
+	    dtrace_xcall_func);
 	kpreempt_enable();
 }
 
--- a/usr/src/uts/i86pc/os/fastboot.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/fastboot.c	Thu Nov 28 09:20:11 2019 +0100
@@ -1293,8 +1293,9 @@
 
 /* ARGSUSED */
 static int
-fastboot_xc_func(fastboot_info_t *nk, xc_arg_t unused2, xc_arg_t unused3)
+fastboot_xc_func(xc_arg_t arg1, xc_arg_t arg2 __unused, xc_arg_t arg3 __unused)
 {
+	fastboot_info_t *nk = (fastboot_info_t *)arg1;
 	void (*fastboot_func)(fastboot_info_t *);
 	fastboot_file_t	*fb = &nk->fi_files[FASTBOOT_SWTCH];
 	fastboot_func = (void (*)())(fb->fb_va);
@@ -1372,11 +1373,11 @@
 		CPUSET_ZERO(cpuset);
 		CPUSET_ADD(cpuset, bootcpuid);
 		xc_priority((xc_arg_t)&newkernel, 0, 0, CPUSET2BV(cpuset),
-		    (xc_func_t)fastboot_xc_func);
+		    fastboot_xc_func);
 
 		panic_idle();
 	} else
-		(void) fastboot_xc_func(&newkernel, 0, 0);
+		(void) fastboot_xc_func((xc_arg_t)&newkernel, 0, 0);
 }
 
 
--- a/usr/src/uts/i86pc/os/machdep.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/machdep.c	Thu Nov 28 09:20:11 2019 +0100
@@ -403,7 +403,7 @@
 	cpuset_t xcset;
 
 	CPUSET_ALL_BUT(xcset, CPU->cpu_id);
-	xc_priority(0, 0, 0, CPUSET2BV(xcset), (xc_func_t)mach_cpu_halt);
+	xc_priority(0, 0, 0, CPUSET2BV(xcset), mach_cpu_halt);
 	restore_int_flag(s);
 }
 
--- a/usr/src/uts/i86pc/os/mp_call.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/mp_call.c	Thu Nov 28 09:20:11 2019 +0100
@@ -37,7 +37,7 @@
 
 /*
  * Interrupt another CPU.
- * 	This is useful to make the other CPU go through a trap so that
+ *	This is useful to make the other CPU go through a trap so that
  *	it recognizes an address space trap (AST) for preempting a thread.
  *
  *	It is possible to be preempted here and be resumed on the CPU
@@ -87,7 +87,7 @@
 	} else {
 		CPUSET_ONLY(set, cp->cpu_id);
 		xc_call((xc_arg_t)arg1, (xc_arg_t)arg2, 0, CPUSET2BV(set),
-		    (xc_func_t)func);
+		    (xc_func_t)(uintptr_t)func);
 	}
 	kpreempt_enable();
 }
--- a/usr/src/uts/i86pc/os/mp_pc.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/mp_pc.c	Thu Nov 28 09:20:11 2019 +0100
@@ -440,15 +440,18 @@
 /*
  * "Enter monitor."  Called via cross-call from stop_other_cpus().
  */
-void
-mach_cpu_halt(char *msg)
+int
+mach_cpu_halt(xc_arg_t arg1, xc_arg_t arg2 __unused, xc_arg_t arg3 __unused)
 {
+	char *msg = (char *)arg1;
+
 	if (msg)
 		prom_printf("%s\n", msg);
 
 	/*CONSTANTCONDITION*/
 	while (1)
 		;
+	return (0);
 }
 
 void
--- a/usr/src/uts/i86pc/os/startup.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/os/startup.c	Thu Nov 28 09:20:11 2019 +0100
@@ -2320,7 +2320,7 @@
 	 */
 	for (i = DDI_IPL_1; i <= DDI_IPL_10; i++) {
 		(void) add_avsoftintr((void *)&softlevel_hdl[i-1], i,
-		    (avfunc)ddi_periodic_softintr, "ddi_periodic",
+		    (avfunc)(uintptr_t)ddi_periodic_softintr, "ddi_periodic",
 		    (caddr_t)(uintptr_t)i, NULL);
 	}
 
--- a/usr/src/uts/i86pc/sys/apic_common.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/sys/apic_common.h	Thu Nov 28 09:20:11 2019 +0100
@@ -167,7 +167,7 @@
 
 extern cyclic_id_t apic_cyclic_id;
 
-extern void apic_nmi_intr(caddr_t arg, struct regs *rp);
+extern uint_t apic_nmi_intr(caddr_t arg, caddr_t);
 extern int	apic_clkinit();
 extern hrtime_t apic_gettime();
 extern hrtime_t apic_gethrtime();
--- a/usr/src/uts/i86pc/sys/machsystm.h	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86pc/sys/machsystm.h	Thu Nov 28 09:20:11 2019 +0100
@@ -71,7 +71,7 @@
 } mach_cpu_add_arg_t;
 
 extern void mach_cpu_idle(void);
-extern void mach_cpu_halt(char *);
+extern int mach_cpu_halt(xc_arg_t, xc_arg_t, xc_arg_t);
 extern int mach_cpu_start(cpu_t *, void *);
 extern int mach_cpuid_start(processorid_t, void *);
 extern int mach_cpu_stop(cpu_t *, void *);
@@ -107,8 +107,8 @@
 
 struct system_hardware {
 	int		hd_nodes;		/* number of nodes */
-	int		hd_cpus_per_node; 	/* max cpus in a node */
-	struct memconf 	hd_mem[MAXNODES];
+	int		hd_cpus_per_node;	/* max cpus in a node */
+	struct memconf	hd_mem[MAXNODES];
 						/*
 						 * memory layout for each
 						 * node.
--- a/usr/src/uts/i86xpv/os/mp_xen.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/i86xpv/os/mp_xen.c	Thu Nov 28 09:20:11 2019 +0100
@@ -558,12 +558,15 @@
 	}
 }
 
-void
-mach_cpu_halt(char *msg)
+int
+mach_cpu_halt(xc_arg_t arg1, xc_arg_t arg2 __unused, xc_arg_t arg3 __unused)
 {
+	char *msg = (char *)arg1;
+
 	if (msg)
 		prom_printf("%s\n", msg);
 	(void) xen_vcpu_down(CPU->cpu_id);
+	return (0);
 }
 
 /*ARGSUSED*/
--- a/usr/src/uts/intel/ia32/os/desctbls.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/intel/ia32/os/desctbls.c	Thu Nov 28 09:20:11 2019 +0100
@@ -103,7 +103,7 @@
 desctbr_t	gdt0_default_r;
 #endif
 
-gate_desc_t	*idt0; 		/* interrupt descriptor table */
+gate_desc_t	*idt0;		/* interrupt descriptor table */
 #if defined(__i386)
 desctbr_t	idt0_default_r;		/* describes idt0 in IDTR format */
 #endif
@@ -147,10 +147,10 @@
 	fast_null,			/* T_FNULL routine */
 	fast_null,			/* T_FGETFP routine (initially null) */
 	fast_null,			/* T_FSETFP routine (initially null) */
-	(void (*)())get_hrtime,		/* T_GETHRTIME */
-	(void (*)())gethrvtime,		/* T_GETHRVTIME */
-	(void (*)())get_hrestime,	/* T_GETHRESTIME */
-	(void (*)())getlgrp		/* T_GETLGRP */
+	(void (*)())(uintptr_t)get_hrtime,	/* T_GETHRTIME */
+	(void (*)())(uintptr_t)gethrvtime,	/* T_GETHRVTIME */
+	(void (*)())(uintptr_t)get_hrestime,	/* T_GETHRESTIME */
+	(void (*)())(uintptr_t)getlgrp		/* T_GETLGRP */
 };
 
 /*
@@ -1356,7 +1356,7 @@
 brand_interpositioning_enable(void)
 {
 	gate_desc_t	*idt = CPU->cpu_idt;
-	int 		i;
+	int		i;
 
 	ASSERT(curthread->t_preempt != 0 || getpil() >= DISP_LEVEL);
 
--- a/usr/src/uts/intel/kdi/kdi_idt.c	Mon Nov 25 18:10:27 2019 +0100
+++ b/usr/src/uts/intel/kdi/kdi_idt.c	Thu Nov 28 09:20:11 2019 +0100
@@ -298,7 +298,8 @@
  * loaded at boot.
  */
 static int
-kdi_cpu_activate(void)
+kdi_cpu_activate(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
 	kdi_idt_gates_install(KCS_SEL, KDI_IDT_SAVE);
 	return (0);
@@ -346,13 +347,13 @@
 	if (boothowto & RB_KMDB) {
 		kdi_idt_gates_install(KMDBCODE_SEL, KDI_IDT_NOSAVE);
 	} else {
-		xc_call(0, 0, 0, CPUSET2BV(cpuset),
-		    (xc_func_t)kdi_cpu_activate);
+		xc_call(0, 0, 0, CPUSET2BV(cpuset), kdi_cpu_activate);
 	}
 }
 
 static int
-kdi_cpu_deactivate(void)
+kdi_cpu_deactivate(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
+    xc_arg_t arg3 __unused)
 {
 	kdi_idt_gates_restore();
 	return (0);
@@ -364,7 +365,7 @@
 	cpuset_t cpuset;
 	CPUSET_ALL(cpuset);
 
-	xc_call(0, 0, 0, CPUSET2BV(cpuset), (xc_func_t)kdi_cpu_deactivate);
+	xc_call(0, 0, 0, CPUSET2BV(cpuset), kdi_cpu_deactivate);
 	kdi_nmemranges = 0;
 }